1 /*
2  * Copyright 2002-2011 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.springframework.transaction.annotation;
18 
19 import static org.hamcrest.CoreMatchers.equalTo;
20 import static org.hamcrest.CoreMatchers.is;
21 import static org.junit.Assert.assertThat;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24 
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 
29 import javax.sql.DataSource;
30 
31 import org.junit.Ignore;
32 import org.junit.Test;
33 import org.springframework.aop.Advisor;
34 import org.springframework.aop.framework.Advised;
35 import org.springframework.aop.support.AopUtils;
36 import org.springframework.cache.Cache;
37 import org.springframework.cache.CacheManager;
38 import org.springframework.cache.concurrent.ConcurrentMapCache;
39 import org.springframework.cache.support.SimpleCacheManager;
40 import org.springframework.context.annotation.AdviceMode;
41 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
42 import org.springframework.context.annotation.Bean;
43 import org.springframework.context.annotation.Configuration;
44 import org.springframework.context.annotation.ImportResource;
45 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
46 import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
47 import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
48 import org.springframework.stereotype.Repository;
49 import org.springframework.transaction.CallCountingTransactionManager;
50 import org.springframework.transaction.PlatformTransactionManager;
51 import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor;
52 
53 /**
54  * Integration tests for the @EnableTransactionManagement annotation.
55  *
56  * @author Chris Beams
57  * @since 3.1
58  */
59 public class EnableTransactionManagementIntegrationTests {
60 
61 	@Test
repositoryIsNotTxProxy()62 	public void repositoryIsNotTxProxy() {
63 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
64 		ctx.register(Config.class);
65 		ctx.refresh();
66 
67 		try {
68 			assertTxProxying(ctx);
69 			fail("expected exception");
70 		} catch (AssertionError ex) {
71 			assertThat(ex.getMessage(), equalTo("FooRepository is not a TX proxy"));
72 		}
73 	}
74 
75 	@Test
repositoryIsTxProxy_withDefaultTxManagerName()76 	public void repositoryIsTxProxy_withDefaultTxManagerName() {
77 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
78 		ctx.register(Config.class, DefaultTxManagerNameConfig.class);
79 		ctx.refresh();
80 
81 		assertTxProxying(ctx);
82 	}
83 
84 	@Test
repositoryIsTxProxy_withCustomTxManagerName()85 	public void repositoryIsTxProxy_withCustomTxManagerName() {
86 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
87 		ctx.register(Config.class, CustomTxManagerNameConfig.class);
88 		ctx.refresh();
89 
90 		assertTxProxying(ctx);
91 	}
92 
93 	@Ignore @Test // TODO SPR-8207
repositoryIsTxProxy_withNonConventionalTxManagerName_fallsBackToByTypeLookup()94 	public void repositoryIsTxProxy_withNonConventionalTxManagerName_fallsBackToByTypeLookup() {
95 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
96 		ctx.register(Config.class, NonConventionalTxManagerNameConfig.class);
97 		ctx.refresh();
98 
99 		assertTxProxying(ctx);
100 	}
101 
102 	@Test
repositoryIsClassBasedTxProxy()103 	public void repositoryIsClassBasedTxProxy() {
104 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
105 		ctx.register(Config.class, ProxyTargetClassTxConfig.class);
106 		ctx.refresh();
107 
108 		assertTxProxying(ctx);
109 		assertThat(AopUtils.isCglibProxy(ctx.getBean(FooRepository.class)), is(true));
110 	}
111 
112 	@Test
repositoryUsesAspectJAdviceMode()113 	public void repositoryUsesAspectJAdviceMode() {
114 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
115 		ctx.register(Config.class, AspectJTxConfig.class);
116 		try {
117 			ctx.refresh();
118 		} catch (Exception ex) {
119 			// this test is a bit fragile, but gets the job done, proving that an
120 			// attempt was made to look up the AJ aspect. It's due to classpath issues
121 			// in .integration-tests that it's not found.
122 			assertTrue(ex.getMessage().endsWith("AspectJTransactionManagementConfiguration.class] cannot be opened because it does not exist"));
123 		}
124 	}
125 
126 	@Test
implicitTxManager()127 	public void implicitTxManager() {
128 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
129 		ctx.register(ImplicitTxManagerConfig.class);
130 		ctx.refresh();
131 
132 		FooRepository fooRepository = ctx.getBean(FooRepository.class);
133 		fooRepository.findAll();
134 
135 		CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
136 		assertThat(txManager.begun, equalTo(1));
137 		assertThat(txManager.commits, equalTo(1));
138 		assertThat(txManager.rollbacks, equalTo(0));
139 	}
140 
141 	@Test
explicitTxManager()142 	public void explicitTxManager() {
143 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
144 		ctx.register(ExplicitTxManagerConfig.class);
145 		ctx.refresh();
146 
147 		FooRepository fooRepository = ctx.getBean(FooRepository.class);
148 		fooRepository.findAll();
149 
150 		CallCountingTransactionManager txManager1 = ctx.getBean("txManager1", CallCountingTransactionManager.class);
151 		assertThat(txManager1.begun, equalTo(1));
152 		assertThat(txManager1.commits, equalTo(1));
153 		assertThat(txManager1.rollbacks, equalTo(0));
154 
155 		CallCountingTransactionManager txManager2 = ctx.getBean("txManager2", CallCountingTransactionManager.class);
156 		assertThat(txManager2.begun, equalTo(0));
157 		assertThat(txManager2.commits, equalTo(0));
158 		assertThat(txManager2.rollbacks, equalTo(0));
159 	}
160 
161 	@Test
apcEscalation()162 	public void apcEscalation() {
163 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
164 		ctx.register(EnableTxAndCachingConfig.class);
165 		ctx.refresh();
166 	}
167 
168 
169 	@Configuration
170 	@EnableTransactionManagement
171 	@ImportResource("org/springframework/transaction/annotation/enable-caching.xml")
172 	static class EnableTxAndCachingConfig {
173 		@Bean
txManager()174 		public PlatformTransactionManager txManager() {
175 			return new CallCountingTransactionManager();
176 		}
177 
178 		@Bean
fooRepository()179 		public FooRepository fooRepository() {
180 			return new DummyFooRepository();
181 		}
182 
183 		@Bean
cacheManager()184 		public CacheManager cacheManager() {
185 			SimpleCacheManager mgr = new SimpleCacheManager();
186 			ArrayList<Cache> caches = new ArrayList<Cache>();
187 			caches.add(new ConcurrentMapCache(""));
188 			mgr.setCaches(caches);
189 			return mgr;
190 		}
191 	}
192 
193 
194 	@Configuration
195 	@EnableTransactionManagement
196 	static class ImplicitTxManagerConfig {
197 		@Bean
txManager()198 		public PlatformTransactionManager txManager() {
199 			return new CallCountingTransactionManager();
200 		}
201 
202 		@Bean
fooRepository()203 		public FooRepository fooRepository() {
204 			return new DummyFooRepository();
205 		}
206 	}
207 
208 
209 	@Configuration
210 	@EnableTransactionManagement
211 	static class ExplicitTxManagerConfig implements TransactionManagementConfigurer {
212 		@Bean
txManager1()213 		public PlatformTransactionManager txManager1() {
214 			return new CallCountingTransactionManager();
215 		}
216 
217 		@Bean
txManager2()218 		public PlatformTransactionManager txManager2() {
219 			return new CallCountingTransactionManager();
220 		}
221 
annotationDrivenTransactionManager()222 		public PlatformTransactionManager annotationDrivenTransactionManager() {
223 			return txManager1();
224 		}
225 
226 		@Bean
fooRepository()227 		public FooRepository fooRepository() {
228 			return new DummyFooRepository();
229 		}
230 	}
231 
assertTxProxying(AnnotationConfigApplicationContext ctx)232 	private void assertTxProxying(AnnotationConfigApplicationContext ctx) {
233 		FooRepository repo = ctx.getBean(FooRepository.class);
234 
235 		boolean isTxProxy = false;
236 		if (AopUtils.isAopProxy(repo)) {
237 			for (Advisor advisor : ((Advised)repo).getAdvisors()) {
238 				if (advisor instanceof BeanFactoryTransactionAttributeSourceAdvisor) {
239 					isTxProxy = true;
240 					break;
241 				}
242 			}
243 		}
244 		assertTrue("FooRepository is not a TX proxy", isTxProxy);
245 
246 		// trigger a transaction
247 		repo.findAll();
248 	}
249 
250 
251 	@Configuration
252 	@EnableTransactionManagement
253 	static class DefaultTxManagerNameConfig {
254 		@Bean
transactionManager(DataSource dataSource)255 		PlatformTransactionManager transactionManager(DataSource dataSource) {
256 			return new DataSourceTransactionManager(dataSource);
257 		}
258 	}
259 
260 
261 	@Configuration
262 	@EnableTransactionManagement
263 	static class CustomTxManagerNameConfig {
264 		@Bean
txManager(DataSource dataSource)265 		PlatformTransactionManager txManager(DataSource dataSource) {
266 			return new DataSourceTransactionManager(dataSource);
267 		}
268 	}
269 
270 
271 	@Configuration
272 	@EnableTransactionManagement
273 	static class NonConventionalTxManagerNameConfig {
274 		@Bean
txManager(DataSource dataSource)275 		PlatformTransactionManager txManager(DataSource dataSource) {
276 			return new DataSourceTransactionManager(dataSource);
277 		}
278 	}
279 
280 
281 	@Configuration
282 	@EnableTransactionManagement(proxyTargetClass=true)
283 	static class ProxyTargetClassTxConfig {
284 		@Bean
transactionManager(DataSource dataSource)285 		PlatformTransactionManager transactionManager(DataSource dataSource) {
286 			return new DataSourceTransactionManager(dataSource);
287 		}
288 	}
289 
290 
291 	@Configuration
292 	@EnableTransactionManagement(mode=AdviceMode.ASPECTJ)
293 	static class AspectJTxConfig {
294 		@Bean
transactionManager(DataSource dataSource)295 		PlatformTransactionManager transactionManager(DataSource dataSource) {
296 			return new DataSourceTransactionManager(dataSource);
297 		}
298 	}
299 
300 
301 	@Configuration
302 	static class Config {
303 		@Bean
fooRepository()304 		FooRepository fooRepository() {
305 			JdbcFooRepository repos = new JdbcFooRepository();
306 			repos.setDataSource(dataSource());
307 			return repos;
308 		}
309 
310 		@Bean
dataSource()311 		DataSource dataSource() {
312 			return new EmbeddedDatabaseBuilder()
313 				.setType(EmbeddedDatabaseType.HSQL)
314 				.build();
315 		}
316 	}
317 
318 
319 	interface FooRepository {
findAll()320 		List<Object> findAll();
321 	}
322 
323 
324 	@Repository
325 	static class JdbcFooRepository implements FooRepository {
326 
setDataSource(DataSource dataSource)327 		public void setDataSource(DataSource dataSource) {
328 		}
329 
330 		@Transactional
findAll()331 		public List<Object> findAll() {
332 			return Collections.emptyList();
333 		}
334 	}
335 
336 	@Repository
337 	static class DummyFooRepository implements FooRepository {
338 
339 		@Transactional
findAll()340 		public List<Object> findAll() {
341 			return Collections.emptyList();
342 		}
343 	}
344 }
345