1 /* 2 * Copyright 2002-2008 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.aop.framework.autoproxy; 18 19 import static org.junit.Assert.*; 20 21 import java.io.IOException; 22 import java.lang.reflect.Method; 23 import java.util.List; 24 25 import javax.servlet.ServletException; 26 27 import org.junit.Test; 28 import org.springframework.aop.support.AopUtils; 29 import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor; 30 import org.springframework.beans.factory.BeanFactory; 31 import org.springframework.beans.factory.InitializingBean; 32 import org.springframework.context.support.ClassPathXmlApplicationContext; 33 import org.springframework.transaction.NoTransactionException; 34 import org.springframework.transaction.TransactionDefinition; 35 import org.springframework.transaction.interceptor.TransactionInterceptor; 36 import org.springframework.transaction.support.AbstractPlatformTransactionManager; 37 import org.springframework.transaction.support.DefaultTransactionStatus; 38 39 import test.advice.CountingBeforeAdvice; 40 import test.advice.MethodCounter; 41 import test.beans.ITestBean; 42 import test.interceptor.NopInterceptor; 43 44 /** 45 * Integration tests for auto proxy creation by advisor recognition working in 46 * conjunction with transaction managment resources. 47 * 48 * @see org.springframework.aop.framework.autoproxy.AdvisorAutoProxyCreatorTests; 49 * 50 * @author Rod Johnson 51 * @author Chris Beams 52 */ 53 public final class AdvisorAutoProxyCreatorIntegrationTests { 54 55 private static final Class<?> CLASS = AdvisorAutoProxyCreatorIntegrationTests.class; 56 private static final String CLASSNAME = CLASS.getSimpleName(); 57 58 private static final String DEFAULT_CONTEXT = CLASSNAME + "-context.xml"; 59 60 private static final String ADVISOR_APC_BEAN_NAME = "aapc"; 61 private static final String TXMANAGER_BEAN_NAME = "txManager"; 62 63 /** 64 * Return a bean factory with attributes and EnterpriseServices configured. 65 */ getBeanFactory()66 protected BeanFactory getBeanFactory() throws IOException { 67 return new ClassPathXmlApplicationContext(DEFAULT_CONTEXT, CLASS); 68 } 69 70 @Test testDefaultExclusionPrefix()71 public void testDefaultExclusionPrefix() throws Exception { 72 DefaultAdvisorAutoProxyCreator aapc = (DefaultAdvisorAutoProxyCreator) getBeanFactory().getBean(ADVISOR_APC_BEAN_NAME); 73 assertEquals(ADVISOR_APC_BEAN_NAME + DefaultAdvisorAutoProxyCreator.SEPARATOR, aapc.getAdvisorBeanNamePrefix()); 74 assertFalse(aapc.isUsePrefix()); 75 } 76 77 /** 78 * If no pointcuts match (no attrs) there should be proxying. 79 */ 80 @Test testNoProxy()81 public void testNoProxy() throws Exception { 82 BeanFactory bf = getBeanFactory(); 83 Object o = bf.getBean("noSetters"); 84 assertFalse(AopUtils.isAopProxy(o)); 85 } 86 87 @Test testTxIsProxied()88 public void testTxIsProxied() throws Exception { 89 BeanFactory bf = getBeanFactory(); 90 ITestBean test = (ITestBean) bf.getBean("test"); 91 assertTrue(AopUtils.isAopProxy(test)); 92 } 93 94 @Test testRegexpApplied()95 public void testRegexpApplied() throws Exception { 96 BeanFactory bf = getBeanFactory(); 97 ITestBean test = (ITestBean) bf.getBean("test"); 98 MethodCounter counter = (MethodCounter) bf.getBean("countingAdvice"); 99 assertEquals(0, counter.getCalls()); 100 test.getName(); 101 assertEquals(1, counter.getCalls()); 102 } 103 104 @Test testTransactionAttributeOnMethod()105 public void testTransactionAttributeOnMethod() throws Exception { 106 BeanFactory bf = getBeanFactory(); 107 ITestBean test = (ITestBean) bf.getBean("test"); 108 109 CallCountingTransactionManager txMan = (CallCountingTransactionManager) bf.getBean(TXMANAGER_BEAN_NAME); 110 OrderedTxCheckAdvisor txc = (OrderedTxCheckAdvisor) bf.getBean("orderedBeforeTransaction"); 111 assertEquals(0, txc.getCountingBeforeAdvice().getCalls()); 112 113 assertEquals(0, txMan.commits); 114 assertEquals("Initial value was correct", 4, test.getAge()); 115 int newAge = 5; 116 test.setAge(newAge); 117 assertEquals(1, txc.getCountingBeforeAdvice().getCalls()); 118 119 assertEquals("New value set correctly", newAge, test.getAge()); 120 assertEquals("Transaction counts match", 1, txMan.commits); 121 } 122 123 /** 124 * Should not roll back on servlet exception. 125 */ 126 @Test testRollbackRulesOnMethodCauseRollback()127 public void testRollbackRulesOnMethodCauseRollback() throws Exception { 128 BeanFactory bf = getBeanFactory(); 129 Rollback rb = (Rollback) bf.getBean("rollback"); 130 131 CallCountingTransactionManager txMan = (CallCountingTransactionManager) bf.getBean(TXMANAGER_BEAN_NAME); 132 OrderedTxCheckAdvisor txc = (OrderedTxCheckAdvisor) bf.getBean("orderedBeforeTransaction"); 133 assertEquals(0, txc.getCountingBeforeAdvice().getCalls()); 134 135 assertEquals(0, txMan.commits); 136 rb.echoException(null); 137 // Fires only on setters 138 assertEquals(0, txc.getCountingBeforeAdvice().getCalls()); 139 assertEquals("Transaction counts match", 1, txMan.commits); 140 141 assertEquals(0, txMan.rollbacks); 142 Exception ex = new Exception(); 143 try { 144 rb.echoException(ex); 145 } 146 catch (Exception actual) { 147 assertEquals(ex, actual); 148 } 149 assertEquals("Transaction counts match", 1, txMan.rollbacks); 150 } 151 152 @Test testRollbackRulesOnMethodPreventRollback()153 public void testRollbackRulesOnMethodPreventRollback() throws Exception { 154 BeanFactory bf = getBeanFactory(); 155 Rollback rb = (Rollback) bf.getBean("rollback"); 156 157 CallCountingTransactionManager txMan = (CallCountingTransactionManager) bf.getBean(TXMANAGER_BEAN_NAME); 158 159 assertEquals(0, txMan.commits); 160 // Should NOT roll back on ServletException 161 try { 162 rb.echoException(new ServletException()); 163 } 164 catch (ServletException ex) { 165 166 } 167 assertEquals("Transaction counts match", 1, txMan.commits); 168 } 169 170 @Test testProgrammaticRollback()171 public void testProgrammaticRollback() throws Exception { 172 BeanFactory bf = getBeanFactory(); 173 174 Object bean = bf.getBean(TXMANAGER_BEAN_NAME); 175 assertTrue(bean instanceof CallCountingTransactionManager); 176 CallCountingTransactionManager txMan = (CallCountingTransactionManager) bf.getBean(TXMANAGER_BEAN_NAME); 177 178 Rollback rb = (Rollback) bf.getBean("rollback"); 179 assertEquals(0, txMan.commits); 180 rb.rollbackOnly(false); 181 assertEquals("Transaction counts match", 1, txMan.commits); 182 assertEquals(0, txMan.rollbacks); 183 // Will cause rollback only 184 rb.rollbackOnly(true); 185 assertEquals(1, txMan.rollbacks); 186 } 187 188 } 189 190 191 @SuppressWarnings("serial") 192 class NeverMatchAdvisor extends StaticMethodMatcherPointcutAdvisor { 193 NeverMatchAdvisor()194 public NeverMatchAdvisor() { 195 super(new NopInterceptor()); 196 } 197 198 /** 199 * This method is solely to allow us to create a mixture of dependencies in 200 * the bean definitions. The dependencies don't have any meaning, and don't 201 * <b>do</b> anything. 202 */ setDependencies(List<?> l)203 public void setDependencies(List<?> l) { 204 205 } 206 207 /** 208 * @see org.springframework.aop.MethodMatcher#matches(java.lang.reflect.Method, java.lang.Class) 209 */ matches(Method m, Class<?> targetClass)210 public boolean matches(Method m, Class<?> targetClass) { 211 return false; 212 } 213 214 } 215 216 217 class NoSetters { 218 A()219 public void A() { 220 221 } 222 getB()223 public int getB() { 224 return -1; 225 } 226 227 } 228 229 230 @SuppressWarnings("serial") 231 class OrderedTxCheckAdvisor extends StaticMethodMatcherPointcutAdvisor implements InitializingBean { 232 233 /** 234 * Should we insist on the presence of a transaction attribute or refuse to accept one? 235 */ 236 private boolean requireTransactionContext = false; 237 238 setRequireTransactionContext(boolean requireTransactionContext)239 public void setRequireTransactionContext(boolean requireTransactionContext) { 240 this.requireTransactionContext = requireTransactionContext; 241 } 242 isRequireTransactionContext()243 public boolean isRequireTransactionContext() { 244 return requireTransactionContext; 245 } 246 247 getCountingBeforeAdvice()248 public CountingBeforeAdvice getCountingBeforeAdvice() { 249 return (CountingBeforeAdvice) getAdvice(); 250 } 251 afterPropertiesSet()252 public void afterPropertiesSet() throws Exception { 253 setAdvice(new TxCountingBeforeAdvice()); 254 } 255 matches(Method method, Class<?> targetClass)256 public boolean matches(Method method, Class<?> targetClass) { 257 return method.getName().startsWith("setAge"); 258 } 259 260 261 private class TxCountingBeforeAdvice extends CountingBeforeAdvice { 262 before(Method method, Object[] args, Object target)263 public void before(Method method, Object[] args, Object target) throws Throwable { 264 // do transaction checks 265 if (requireTransactionContext) { 266 TransactionInterceptor.currentTransactionStatus(); 267 } 268 else { 269 try { 270 TransactionInterceptor.currentTransactionStatus(); 271 throw new RuntimeException("Shouldn't have a transaction"); 272 } 273 catch (NoTransactionException ex) { 274 // this is Ok 275 } 276 } 277 super.before(method, args, target); 278 } 279 } 280 281 } 282 283 284 class Rollback { 285 286 /** 287 * Inherits transaction attribute. 288 * Illustrates programmatic rollback. 289 * @param rollbackOnly 290 */ rollbackOnly(boolean rollbackOnly)291 public void rollbackOnly(boolean rollbackOnly) { 292 if (rollbackOnly) { 293 setRollbackOnly(); 294 } 295 } 296 297 /** 298 * Extracted in a protected method to facilitate testing 299 */ setRollbackOnly()300 protected void setRollbackOnly() { 301 TransactionInterceptor.currentTransactionStatus().setRollbackOnly(); 302 } 303 304 /** 305 * @org.springframework.transaction.interceptor.RuleBasedTransaction ( timeout=-1 ) 306 * @org.springframework.transaction.interceptor.RollbackRule ( "java.lang.Exception" ) 307 * @org.springframework.transaction.interceptor.NoRollbackRule ( "ServletException" ) 308 */ echoException(Exception ex)309 public void echoException(Exception ex) throws Exception { 310 if (ex != null) 311 throw ex; 312 } 313 314 } 315 316 317 @SuppressWarnings("serial") 318 class CallCountingTransactionManager extends AbstractPlatformTransactionManager { 319 320 public TransactionDefinition lastDefinition; 321 public int begun; 322 public int commits; 323 public int rollbacks; 324 public int inflight; 325 doGetTransaction()326 protected Object doGetTransaction() { 327 return new Object(); 328 } 329 doBegin(Object transaction, TransactionDefinition definition)330 protected void doBegin(Object transaction, TransactionDefinition definition) { 331 this.lastDefinition = definition; 332 ++begun; 333 ++inflight; 334 } 335 doCommit(DefaultTransactionStatus status)336 protected void doCommit(DefaultTransactionStatus status) { 337 ++commits; 338 --inflight; 339 } 340 doRollback(DefaultTransactionStatus status)341 protected void doRollback(DefaultTransactionStatus status) { 342 ++rollbacks; 343 --inflight; 344 } 345 clear()346 public void clear() { 347 begun = commits = rollbacks = inflight = 0; 348 } 349 350 } 351