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