/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.aop.framework.autoproxy;
import java.io.IOException;
import javax.servlet.ServletException;
import junit.framework.TestCase;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.CountingBeforeAdvice;
import org.springframework.aop.framework.Lockable;
import org.springframework.aop.framework.MethodCounter;
import org.springframework.aop.interceptor.NopInterceptor;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.target.CommonsPoolTargetSource;
import org.springframework.aop.target.LazyInitTargetSource;
import org.springframework.aop.target.PrototypeTargetSource;
import org.springframework.aop.target.ThreadLocalTargetSource;
import org.springframework.beans.ITestBean;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.transaction.CallCountingTransactionManager;
/**
* Tests for auto proxy creation by advisor recognition.
*
* @author Rod Johnson
*/
public class AdvisorAutoProxyCreatorTests extends TestCase {
private static final String ADVISOR_APC_BEAN_NAME = "aapc";
private static final String TXMANAGER_BEAN_NAME = "txManager";
/**
* Return a bean factory with attributes and EnterpriseServices configured.
*/
protected BeanFactory getBeanFactory() throws IOException {
return new ClassPathXmlApplicationContext("/org/springframework/aop/framework/autoproxy/advisorAutoProxyCreator.xml");
}
public void testDefaultExclusionPrefix() throws Exception {
DefaultAdvisorAutoProxyCreator aapc = (DefaultAdvisorAutoProxyCreator) getBeanFactory().getBean(ADVISOR_APC_BEAN_NAME);
assertEquals(ADVISOR_APC_BEAN_NAME + DefaultAdvisorAutoProxyCreator.SEPARATOR, aapc.getAdvisorBeanNamePrefix());
assertFalse(aapc.isUsePrefix());
}
/**
* If no pointcuts match (no attrs) there should be proxying.
*/
public void testNoProxy() throws Exception {
BeanFactory bf = getBeanFactory();
Object o = bf.getBean("noSetters");
assertFalse(AopUtils.isAopProxy(o));
}
public void testTxIsProxied() throws Exception {
BeanFactory bf = getBeanFactory();
ITestBean test = (ITestBean) bf.getBean("test");
assertTrue(AopUtils.isAopProxy(test));
}
public void testRegexpApplied() throws Exception {
BeanFactory bf = getBeanFactory();
ITestBean test = (ITestBean) bf.getBean("test");
MethodCounter counter = (MethodCounter) bf.getBean("countingAdvice");
assertEquals(0, counter.getCalls());
test.getName();
assertEquals(1, counter.getCalls());
}
/**
* Check that we can provide a common interceptor that will
* appear in the chain before "specific" interceptors,
* which are sourced from matching advisors
*/
public void testCommonInterceptorAndAdvisor() throws Exception {
BeanFactory bf = new ClassPathXmlApplicationContext("/org/springframework/aop/framework/autoproxy/advisorAutoProxyCreatorWithCommonInterceptors.xml");
ITestBean test1 = (ITestBean) bf.getBean("test1");
assertTrue(AopUtils.isAopProxy(test1));
Lockable lockable1 = (Lockable) test1;
NopInterceptor nop = (NopInterceptor) bf.getBean("nopInterceptor");
assertEquals(0, nop.getCount());
ITestBean test2 = (ITestBean) bf.getBean("test2");
Lockable lockable2 = (Lockable) test2;
// Locking should be independent; nop is shared
assertFalse(lockable1.locked());
assertFalse(lockable2.locked());
// equals 2 calls on shared nop, because it's first
// and sees calls against the Lockable interface introduced
// by the specific advisor
assertEquals(2, nop.getCount());
lockable1.lock();
assertTrue(lockable1.locked());
assertFalse(lockable2.locked());
assertEquals(5, nop.getCount());
}
/**
* We have custom TargetSourceCreators but there's no match, and
* hence no proxying, for this bean
*/
public void testCustomTargetSourceNoMatch() throws Exception {
BeanFactory bf = new ClassPathXmlApplicationContext("/org/springframework/aop/framework/autoproxy/customTargetSource.xml");
ITestBean test = (ITestBean) bf.getBean("test");
assertFalse(AopUtils.isAopProxy(test));
assertEquals("Rod", test.getName());
assertEquals("Kerry", test.getSpouse().getName());
}
public void testCustomPrototypeTargetSource() throws Exception {
CountingTestBean.count = 0;
BeanFactory bf = new ClassPathXmlApplicationContext("/org/springframework/aop/framework/autoproxy/customTargetSource.xml");
ITestBean test = (ITestBean) bf.getBean("prototypeTest");
assertTrue(AopUtils.isAopProxy(test));
Advised advised = (Advised) test;
assertTrue(advised.getTargetSource() instanceof PrototypeTargetSource);
assertEquals("Rod", test.getName());
// Check that references survived prototype creation
assertEquals("Kerry", test.getSpouse().getName());
assertEquals("Only 2 CountingTestBeans instantiated", 2, CountingTestBean.count);
CountingTestBean.count = 0;
}
public void testLazyInitTargetSource() throws Exception {
CountingTestBean.count = 0;
BeanFactory bf = new ClassPathXmlApplicationContext("/org/springframework/aop/framework/autoproxy/customTargetSource.xml");
ITestBean test = (ITestBean) bf.getBean("lazyInitTest");
assertTrue(AopUtils.isAopProxy(test));
Advised advised = (Advised) test;
assertTrue(advised.getTargetSource() instanceof LazyInitTargetSource);
assertEquals("No CountingTestBean instantiated yet", 0, CountingTestBean.count);
assertEquals("Rod", test.getName());
assertEquals("Kerry", test.getSpouse().getName());
assertEquals("Only 1 CountingTestBean instantiated", 1, CountingTestBean.count);
CountingTestBean.count = 0;
}
public void testQuickTargetSourceCreator() throws Exception {
ClassPathXmlApplicationContext bf =
new ClassPathXmlApplicationContext("/org/springframework/aop/framework/autoproxy/quickTargetSource.xml");
ITestBean test = (ITestBean) bf.getBean("test");
assertFalse(AopUtils.isAopProxy(test));
assertEquals("Rod", test.getName());
// Check that references survived pooling
assertEquals("Kerry", test.getSpouse().getName());
// Now test the pooled one
test = (ITestBean) bf.getBean(":test");
assertTrue(AopUtils.isAopProxy(test));
Advised advised = (Advised) test;
assertTrue(advised.getTargetSource() instanceof CommonsPoolTargetSource);
assertEquals("Rod", test.getName());
// Check that references survived pooling
assertEquals("Kerry", test.getSpouse().getName());
// Now test the ThreadLocal one
test = (ITestBean) bf.getBean("%test");
assertTrue(AopUtils.isAopProxy(test));
advised = (Advised) test;
assertTrue(advised.getTargetSource() instanceof ThreadLocalTargetSource);
assertEquals("Rod", test.getName());
// Check that references survived pooling
assertEquals("Kerry", test.getSpouse().getName());
// Now test the Prototype TargetSource
test = (ITestBean) bf.getBean("!test");
assertTrue(AopUtils.isAopProxy(test));
advised = (Advised) test;
assertTrue(advised.getTargetSource() instanceof PrototypeTargetSource);
assertEquals("Rod", test.getName());
// Check that references survived pooling
assertEquals("Kerry", test.getSpouse().getName());
ITestBean test2 = (ITestBean) bf.getBean("!test");
assertFalse("Prototypes cannot be the same object", test == test2);
assertEquals("Rod", test2.getName());
assertEquals("Kerry", test2.getSpouse().getName());
bf.close();
}
/*
public void testIntroductionIsProxied() throws Exception {
BeanFactory bf = getBeanFactory();
Object modifiable = bf.getBean("modifiable1");
// We can tell it's a CGLIB proxy by looking at the class name
System.out.println(modifiable.getClass().getName());
assertFalse(modifiable.getClass().getName().equals(ModifiableTestBean.class.getName()));
}
*/
public void testTransactionAttributeOnMethod() throws Exception {
BeanFactory bf = getBeanFactory();
ITestBean test = (ITestBean) bf.getBean("test");
CallCountingTransactionManager txMan = (CallCountingTransactionManager) bf.getBean(TXMANAGER_BEAN_NAME);
OrderedTxCheckAdvisor txc = (OrderedTxCheckAdvisor) bf.getBean("orderedBeforeTransaction");
assertEquals(0, txc.getCountingBeforeAdvice().getCalls());
assertEquals(0, txMan.commits);
assertEquals("Initial value was correct", 4, test.getAge());
int newAge = 5;
test.setAge(newAge);
assertEquals(1, txc.getCountingBeforeAdvice().getCalls());
assertEquals("New value set correctly", newAge, test.getAge());
assertEquals("Transaction counts match", 1, txMan.commits);
}
/**
* Should not roll back on servlet exception.
*/
public void testRollbackRulesOnMethodCauseRollback() throws Exception {
BeanFactory bf = getBeanFactory();
Rollback rb = (Rollback) bf.getBean("rollback");
CallCountingTransactionManager txMan = (CallCountingTransactionManager) bf.getBean(TXMANAGER_BEAN_NAME);
OrderedTxCheckAdvisor txc = (OrderedTxCheckAdvisor) bf.getBean("orderedBeforeTransaction");
assertEquals(0, txc.getCountingBeforeAdvice().getCalls());
assertEquals(0, txMan.commits);
rb.echoException(null);
// Fires only on setters
assertEquals(0, txc.getCountingBeforeAdvice().getCalls());
assertEquals("Transaction counts match", 1, txMan.commits);
assertEquals(0, txMan.rollbacks);
Exception ex = new Exception();
try {
rb.echoException(ex);
}
catch (Exception actual) {
assertEquals(ex, actual);
}
assertEquals("Transaction counts match", 1, txMan.rollbacks);
}
public void testRollbackRulesOnMethodPreventRollback() throws Exception {
BeanFactory bf = getBeanFactory();
Rollback rb = (Rollback) bf.getBean("rollback");
CallCountingTransactionManager txMan = (CallCountingTransactionManager) bf.getBean(TXMANAGER_BEAN_NAME);
assertEquals(0, txMan.commits);
// Should NOT roll back on ServletException
try {
rb.echoException(new ServletException());
}
catch (ServletException ex) {
}
assertEquals("Transaction counts match", 1, txMan.commits);
}
public void testProgrammaticRollback() throws Exception {
BeanFactory bf = getBeanFactory();
Object bean = bf.getBean(TXMANAGER_BEAN_NAME);
assertTrue(bean instanceof CallCountingTransactionManager);
CallCountingTransactionManager txMan = (CallCountingTransactionManager) bf.getBean(TXMANAGER_BEAN_NAME);
Rollback rb = (Rollback) bf.getBean("rollback");
assertEquals(0, txMan.commits);
rb.rollbackOnly(false);
assertEquals("Transaction counts match", 1, txMan.commits);
assertEquals(0, txMan.rollbacks);
// Will cause rollback only
rb.rollbackOnly(true);
assertEquals(1, txMan.rollbacks);
}
public void testWithOptimizedProxy() throws Exception {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("org/springframework/aop/framework/autoproxy/optimizedAutoProxyCreator.xml");
ITestBean testBean = (ITestBean) beanFactory.getBean("optimizedTestBean");
assertTrue(AopUtils.isAopProxy(testBean));
CountingBeforeAdvice beforeAdvice = (CountingBeforeAdvice) beanFactory.getBean("countingAdvice");
testBean.setAge(23);
testBean.getAge();
assertEquals("Incorrect number of calls to proxy", 2, beforeAdvice.getCalls());
}
/**
* Tests an introduction pointcut. This is a prototype, so that it can add
* a Modifiable mixin. Tests that the autoproxy infrastructure can create
* advised objects with independent interceptor instances.
* The Modifiable behaviour of each instance of TestBean should be distinct.
*/
/*
public void testIntroductionViaPrototype() throws Exception {
BeanFactory bf = getBeanFactory();
Object o = bf.getBean("modifiable1");
ITestBean modifiable1 = (ITestBean) bf.getBean("modifiable1");
ITestBean modifiable2 = (ITestBean) bf.getBean("modifiable2");
Advised pc = (Advised) modifiable1;
System.err.println(pc.toProxyConfigString());
// For convenience only
Modifiable mod1 = (Modifiable) modifiable1;
Modifiable mod2 = (Modifiable) modifiable2;
assertFalse(mod1.isModified());
assertFalse(mod2.isModified());
int newAge = 33;
modifiable1.setAge(newAge);
assertTrue(mod1.isModified());
// Changes to one shouldn't have affected the other
assertFalse("Instances of prototype introduction pointcut don't seem distinct", mod2.isModified());
mod1.acceptChanges();
assertFalse(mod1.isModified());
assertEquals(modifiable1.getAge(), newAge);
assertFalse(mod1.isModified());
}
*/
}