/*
* Copyright 2002-2007 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;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.transaction.TransactionRequiredException;
import junit.framework.TestCase;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.DynamicIntroductionAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.adapter.ThrowsAdviceInterceptorTests;
import org.springframework.aop.interceptor.DebugInterceptor;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.interceptor.NopInterceptor;
import org.springframework.aop.interceptor.SerializableNopInterceptor;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
import org.springframework.aop.support.DynamicMethodMatcherPointcut;
import org.springframework.aop.support.NameMatchMethodPointcut;
import org.springframework.aop.support.Pointcuts;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.aop.target.HotSwappableTargetSource;
import org.springframework.aop.target.SingletonTargetSource;
import org.springframework.beans.IOther;
import org.springframework.beans.ITestBean;
import org.springframework.beans.Person;
import org.springframework.beans.SerializablePerson;
import org.springframework.beans.TestBean;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.util.SerializationTestUtils;
import org.springframework.util.StopWatch;
/**
* @author Rod Johnson
* @author Juergen Hoeller
* @since 13.03.2003
*/
public abstract class AbstractAopProxyTests extends TestCase {
protected final MockTargetSource mockTargetSource = new MockTargetSource();
/**
* Make a clean target source available if code wants to use it.
* The target must be set. Verification will be automatic in tearDown
* to ensure that it was used appropriately by code.
*/
protected void setUp() {
mockTargetSource.reset();
}
protected void tearDown() {
mockTargetSource.verify();
}
/**
* Set in CGLIB or JDK mode.
*/
protected abstract Object createProxy(ProxyCreatorSupport as);
protected abstract AopProxy createAopProxy(AdvisedSupport as);
/**
* Is a target always required?
*/
protected boolean requiresTarget() {
return false;
}
public void testNoInterceptorsAndNoTarget() {
AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class});
// Add no interceptors
try {
AopProxy aop = createAopProxy(pc);
aop.getProxy();
fail("Shouldn't allow no interceptors");
}
catch (AopConfigException ex) {
// Ok
}
}
/**
* Simple test that if we set values we can get them out again.
*/
public void testValuesStick() {
int age1 = 33;
int age2 = 37;
String name = "tony";
TestBean target1 = new TestBean();
target1.setAge(age1);
ProxyFactory pf1 = new ProxyFactory(target1);
pf1.addAdvisor(new DefaultPointcutAdvisor(new NopInterceptor()));
pf1.addAdvisor(new DefaultPointcutAdvisor(new TimestampIntroductionInterceptor()));
ITestBean tb = (ITestBean) pf1.getProxy();
assertEquals(age1, tb.getAge());
tb.setAge(age2);
assertEquals(age2, tb.getAge());
assertNull(tb.getName());
tb.setName(name);
assertEquals(name, tb.getName());
}
/**
* This is primarily a test for the efficiency of our
* usage of CGLIB. If we create too many classes with
* CGLIB this will be slow or will run out of memory.
*/
public void testManyProxies() {
int howMany = 10000;
StopWatch sw = new StopWatch();
sw.start("Create " + howMany + " proxies");
testManyProxies(howMany);
sw.stop();
System.out.println(sw.getTotalTimeMillis());
assertTrue("Proxy creation was too slow", sw.getTotalTimeMillis() < 5000);
}
private void testManyProxies(int howMany) {
int age1 = 33;
TestBean target1 = new TestBean();
target1.setAge(age1);
ProxyFactory pf1 = new ProxyFactory(target1);
pf1.addAdvice(new NopInterceptor());
pf1.addAdvice(new NopInterceptor());
ITestBean proxies[] = new ITestBean[howMany];
for (int i = 0; i < howMany; i++) {
proxies[i] = (ITestBean) createAopProxy(pf1).getProxy();
assertEquals(age1, proxies[i].getAge());
}
}
public void testSerializationAdviceAndTargetNotSerializable() throws Exception {
TestBean tb = new TestBean();
assertFalse(SerializationTestUtils.isSerializable(tb));
ProxyFactory pf = new ProxyFactory(tb);
pf.addAdvice(new NopInterceptor());
ITestBean proxy = (ITestBean) createAopProxy(pf).getProxy();
assertFalse(SerializationTestUtils.isSerializable(proxy));
}
public void testSerializationAdviceNotSerializable() throws Exception {
SerializablePerson sp = new SerializablePerson();
assertTrue(SerializationTestUtils.isSerializable(sp));
ProxyFactory pf = new ProxyFactory(sp);
// This isn't serializable
Advice i = new NopInterceptor();
pf.addAdvice(i);
assertFalse(SerializationTestUtils.isSerializable(i));
Object proxy = createAopProxy(pf).getProxy();
assertFalse(SerializationTestUtils.isSerializable(proxy));
}
public void testSerializationSerializableTargetAndAdvice() throws Throwable {
SerializablePerson personTarget = new SerializablePerson();
personTarget.setName("jim");
personTarget.setAge(26);
assertTrue(SerializationTestUtils.isSerializable(personTarget));
ProxyFactory pf = new ProxyFactory(personTarget);
CountingThrowsAdvice cta = new CountingThrowsAdvice();
pf.addAdvice(new SerializableNopInterceptor());
// Try various advice types
pf.addAdvice(new CountingBeforeAdvice());
pf.addAdvice(new CountingAfterReturningAdvice());
pf.addAdvice(cta);
Person p = (Person) createAopProxy(pf).getProxy();
p.echo(null);
assertEquals(0, cta.getCalls());
try {
p.echo(new ServletException());
}
catch (ServletException ex) {
}
assertEquals(1, cta.getCalls());
// Will throw exception if it fails
Person p2 = (Person) SerializationTestUtils.serializeAndDeserialize(p);
assertNotSame(p, p2);
assertEquals(p.getName(), p2.getName());
assertEquals(p.getAge(), p2.getAge());
assertTrue("Deserialized object is an AOP proxy", AopUtils.isAopProxy(p2));
Advised a1 = (Advised) p;
Advised a2 = (Advised) p2;
// Check we can manipulate state of p2
assertEquals(a1.getAdvisors().length, a2.getAdvisors().length);
// This should work as SerializablePerson is equal
assertEquals("Proxies should be equal, even after one was serialized", p, p2);
assertEquals("Proxies should be equal, even after one was serialized", p2, p);
// Check we can add a new advisor to the target
NopInterceptor ni = new NopInterceptor();
p2.getAge();
assertEquals(0, ni.getCount());
a2.addAdvice(ni);
p2.getAge();
assertEquals(1, ni.getCount());
cta = (CountingThrowsAdvice) a2.getAdvisors()[3].getAdvice();
p2.echo(null);
assertEquals(1, cta.getCalls());
try {
p2.echo(new ServletException());
}
catch (ServletException ex) {
}
assertEquals(2, cta.getCalls());
}
/**
* Check that the two MethodInvocations necessary are independent and
* don't conflict.
* Check also proxy exposure.
*/
public void testOneAdvisedObjectCallsAnother() {
int age1 = 33;
int age2 = 37;
TestBean target1 = new TestBean();
ProxyFactory pf1 = new ProxyFactory(target1);
// Permit proxy and invocation checkers to get context from AopContext
pf1.setExposeProxy(true);
NopInterceptor di1 = new NopInterceptor();
pf1.addAdvice(0, di1);
pf1.addAdvice(1, new ProxyMatcherInterceptor());
pf1.addAdvice(2, new CheckMethodInvocationIsSameInAndOutInterceptor());
pf1.addAdvice(1, new CheckMethodInvocationViaThreadLocalIsSameInAndOutInterceptor());
// Must be first
pf1.addAdvice(0, ExposeInvocationInterceptor.INSTANCE);
ITestBean advised1 = (ITestBean) pf1.getProxy();
advised1.setAge(age1); // = 1 invocation
TestBean target2 = new TestBean();
ProxyFactory pf2 = new ProxyFactory(target2);
pf2.setExposeProxy(true);
NopInterceptor di2 = new NopInterceptor();
pf2.addAdvice(0, di2);
pf2.addAdvice(1, new ProxyMatcherInterceptor());
pf2.addAdvice(2, new CheckMethodInvocationIsSameInAndOutInterceptor());
pf2.addAdvice(1, new CheckMethodInvocationViaThreadLocalIsSameInAndOutInterceptor());
pf2.addAdvice(0, ExposeInvocationInterceptor.INSTANCE);
//System.err.println(pf2.toProxyConfigString());
ITestBean advised2 = (ITestBean) createProxy(pf2);
advised2.setAge(age2);
advised1.setSpouse(advised2); // = 2 invocations
assertEquals("Advised one has correct age", age1, advised1.getAge()); // = 3 invocations
assertEquals("Advised two has correct age", age2, advised2.getAge());
// Means extra call on advised 2
assertEquals("Advised one spouse has correct age", age2, advised1.getSpouse().getAge()); // = 4 invocations on 1 and another one on 2
assertEquals("one was invoked correct number of times", 4, di1.getCount());
// Got hit by call to advised1.getSpouse().getAge()
assertEquals("one was invoked correct number of times", 3, di2.getCount());
}
public void testReentrance() {
int age1 = 33;
TestBean target1 = new TestBean();
ProxyFactory pf1 = new ProxyFactory(target1);
NopInterceptor di1 = new NopInterceptor();
pf1.addAdvice(0, di1);
ITestBean advised1 = (ITestBean) createProxy(pf1);
advised1.setAge(age1); // = 1 invocation
advised1.setSpouse(advised1); // = 2 invocations
assertEquals("one was invoked correct number of times", 2, di1.getCount());
assertEquals("Advised one has correct age", age1, advised1.getAge()); // = 3 invocations
assertEquals("one was invoked correct number of times", 3, di1.getCount());
// = 5 invocations, as reentrant call to spouse is advised also
assertEquals("Advised spouse has correct age", age1, advised1.getSpouse().getAge());
assertEquals("one was invoked correct number of times", 5, di1.getCount());
}
public void testTargetCanGetProxy() {
NopInterceptor di = new NopInterceptor();
INeedsToSeeProxy target = new TargetChecker();
ProxyFactory proxyFactory = new ProxyFactory(target);
proxyFactory.setExposeProxy(true);
assertTrue(proxyFactory.isExposeProxy());
proxyFactory.addAdvice(0, di);
INeedsToSeeProxy proxied = (INeedsToSeeProxy) createProxy(proxyFactory);
assertEquals(0, di.getCount());
assertEquals(0, target.getCount());
proxied.incrementViaThis();
assertEquals("Increment happened", 1, target.getCount());
assertEquals("Only one invocation via AOP as use of this wasn't proxied", 1, di.getCount());
// 1 invocation
assertEquals("Increment happened", 1, proxied.getCount());
proxied.incrementViaProxy(); // 2 invoocations
assertEquals("Increment happened", 2, target.getCount());
assertEquals("3 more invocations via AOP as the first call was reentrant through the proxy", 4, di.getCount());
}
public void testTargetCantGetProxyByDefault() {
NeedsToSeeProxy et = new NeedsToSeeProxy();
ProxyFactory pf1 = new ProxyFactory(et);
assertFalse(pf1.isExposeProxy());
INeedsToSeeProxy proxied = (INeedsToSeeProxy) createProxy(pf1);
try {
proxied.incrementViaProxy();
fail("Should have failed to get proxy as exposeProxy wasn't set to true");
}
catch (IllegalStateException ex) {
// Ok
}
}
public void testContext() throws Throwable {
testContext(true);
}
public void testNoContext() throws Throwable {
testContext(false);
}
/**
* @param context if true, want context
*/
private void testContext(final boolean context) throws Throwable {
final String s = "foo";
// Test return value
MethodInterceptor mi = new MethodInterceptor() {
public Object invoke(MethodInvocation invocation) throws Throwable {
if (!context) {
assertNoInvocationContext();
} else {
assertTrue("have context", ExposeInvocationInterceptor.currentInvocation() != null);
}
return s;
}
};
AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class});
if (context) {
pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
}
pc.addAdvice(mi);
// Keep CGLIB happy
if (requiresTarget()) {
pc.setTarget(new TestBean());
}
AopProxy aop = createAopProxy(pc);
assertNoInvocationContext();
ITestBean tb = (ITestBean) aop.getProxy();
assertNoInvocationContext();
assertTrue("correct return value", tb.getName() == s);
}
/**
* Test that the proxy returns itself when the
* target returns <code>this</code>
*/
public void testTargetReturnsThis() throws Throwable {
// Test return value
TestBean raw = new OwnSpouse();
ProxyCreatorSupport pc = new ProxyCreatorSupport();
pc.setInterfaces(new Class[] {ITestBean.class});
pc.setTarget(raw);
ITestBean tb = (ITestBean) createProxy(pc);
assertTrue("this return is wrapped in proxy", tb.getSpouse() == tb);
}
public void testDeclaredException() throws Throwable {
final Exception expectedException = new Exception();
// Test return value
MethodInterceptor mi = new MethodInterceptor() {
public Object invoke(MethodInvocation invocation) throws Throwable {
throw expectedException;
}
};
AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class});
pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
pc.addAdvice(mi);
// We don't care about the object
mockTargetSource.setTarget(new Object());
pc.setTargetSource(mockTargetSource);
AopProxy aop = createAopProxy(pc);
try {
ITestBean tb = (ITestBean) aop.getProxy();
// Note: exception param below isn't used
tb.exceptional(expectedException);
fail("Should have thrown exception raised by interceptor");
}
catch (Exception thrown) {
assertEquals("exception matches", expectedException, thrown);
}
}
/**
* An interceptor throws a checked exception not on the method signature.
* For efficiency, we don't bother unifying java.lang.reflect and
* net.sf.cglib UndeclaredThrowableException
*/
public void testUndeclaredCheckedException() throws Throwable {
final Exception unexpectedException = new Exception();
// Test return value
MethodInterceptor mi = new MethodInterceptor() {
public Object invoke(MethodInvocation invocation) throws Throwable {
throw unexpectedException;
}
};
AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class});
pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
pc.addAdvice(mi);
// We don't care about the object
pc.setTarget(new TestBean());
AopProxy aop = createAopProxy(pc);
ITestBean tb = (ITestBean) aop.getProxy();
try {
// Note: exception param below isn't used
tb.getAge();
fail("Should have wrapped exception raised by interceptor");
}
catch (UndeclaredThrowableException thrown) {
assertEquals("exception matches", unexpectedException, thrown.getUndeclaredThrowable());
}
//catch (net.sf.cglib.proxy.UndeclaredThrowableException thrown) {
// assertEquals("exception matches", unexpectedException, thrown.getUndeclaredThrowable());
//}
catch (Exception ex) {
ex.printStackTrace();
fail("Didn't expect exception: " + ex);
}
}
public void testUndeclaredUnheckedException() throws Throwable {
final RuntimeException unexpectedException = new RuntimeException();
// Test return value
MethodInterceptor mi = new MethodInterceptor() {
public Object invoke(MethodInvocation invocation) throws Throwable {
throw unexpectedException;
}
};
AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class});
pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
pc.addAdvice(mi);
// We don't care about the object
pc.setTarget(new TestBean());
AopProxy aop = createAopProxy(pc);
ITestBean tb = (ITestBean) aop.getProxy();
try {
// Note: exception param below isn't used
tb.getAge();
fail("Should have wrapped exception raised by interceptor");
}
catch (RuntimeException thrown) {
assertEquals("exception matches", unexpectedException, thrown);
}
//catch (net.sf.cglib.proxy.UndeclaredThrowableException thrown) {
// assertEquals("exception matches", unexpectedException, thrown.getUndeclaredThrowable());
//}
}
/**
* Check that although a method is eligible for advice chain optimization and
* direct reflective invocation, it doesn't happen if we've asked to see the proxy,
* so as to guarantee a consistent programming model.
* @throws Throwable
*/
public void testTargetCanGetInvocationEvenIfNoAdviceChain() throws Throwable {
NeedsToSeeProxy target = new NeedsToSeeProxy();
AdvisedSupport pc = new AdvisedSupport(new Class[] {INeedsToSeeProxy.class});
pc.setTarget(target);
pc.setExposeProxy(true);
// Now let's try it with the special target
AopProxy aop = createAopProxy(pc);
INeedsToSeeProxy proxied = (INeedsToSeeProxy) aop.getProxy();
// It will complain if it can't get the proxy
proxied.incrementViaProxy();
}
public void testTargetCanGetInvocation() throws Throwable {
final InvocationCheckExposedInvocationTestBean expectedTarget = new InvocationCheckExposedInvocationTestBean();
AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class, IOther.class});
pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
TrapTargetInterceptor tii = new TrapTargetInterceptor() {
public Object invoke(MethodInvocation invocation) throws Throwable {
// Assert that target matches BEFORE invocation returns
assertEquals("Target is correct", expectedTarget, invocation.getThis());
return super.invoke(invocation);
}
};
pc.addAdvice(tii);
pc.setTarget(expectedTarget);
AopProxy aop = createAopProxy(pc);
ITestBean tb = (ITestBean) aop.getProxy();
tb.getName();
// Not safe to trap invocation
//assertTrue(tii.invocation == target.invocation);
//assertTrue(target.invocation.getProxy() == tb);
// ((IOther) tb).absquatulate();
//MethodInvocation minv = tii.invocation;
//assertTrue("invoked on iother, not " + minv.getMethod().getDeclaringClass(), minv.getMethod().getDeclaringClass() == IOther.class);
//assertTrue(target.invocation == tii.invocation);
}
/**
* Throw an exception if there is an Invocation.
*/
private void assertNoInvocationContext() {
try {
ExposeInvocationInterceptor.currentInvocation();
fail("Expected no invocation context");
}
catch (IllegalStateException ex) {
// ok
}
}
/**
* Test stateful interceptor
*/
public void testMixinWithIntroductionAdvisor() throws Throwable {
TestBean tb = new TestBean();
ProxyFactory pc = new ProxyFactory(new Class[] {ITestBean.class});
pc.addAdvisor(new LockMixinAdvisor());
pc.setTarget(tb);
testTestBeanIntroduction(pc);
}
public void testMixinWithIntroductionInfo() throws Throwable {
TestBean tb = new TestBean();
ProxyFactory pc = new ProxyFactory(new Class[] {ITestBean.class});
// We don't use an IntroductionAdvisor, we can just add an advice that implements IntroductionInfo
pc.addAdvice(new LockMixin());
pc.setTarget(tb);
testTestBeanIntroduction(pc);
}
private void testTestBeanIntroduction(ProxyFactory pc) {
int newAge = 65;
ITestBean itb = (ITestBean) createProxy(pc);
itb.setAge(newAge);
assertTrue(itb.getAge() == newAge);
Lockable lockable = (Lockable) itb;
assertFalse(lockable.locked());
lockable.lock();
assertTrue(itb.getAge() == newAge);
try {
itb.setAge(1);
fail("Setters should fail when locked");
}
catch (LockedException ex) {
// ok
}
assertTrue(itb.getAge() == newAge);
// Unlock
assertTrue(lockable.locked());
lockable.unlock();
itb.setAge(1);
assertTrue(itb.getAge() == 1);
}
public void testReplaceArgument() throws Throwable {
TestBean tb = new TestBean();
ProxyFactory pc = new ProxyFactory(new Class[] {ITestBean.class});
pc.setTarget(tb);
pc.addAdvisor(new StringSetterNullReplacementAdvice());
ITestBean t = (ITestBean) pc.getProxy();
int newAge = 5;
t.setAge(newAge);
assertTrue(t.getAge() == newAge);
String newName = "greg";
t.setName(newName);
assertEquals(newName, t.getName());
t.setName(null);
// Null replacement magic should work
assertTrue(t.getName().equals(""));
}
public void testCanCastProxyToProxyConfig() throws Throwable {
TestBean tb = new TestBean();
ProxyFactory pc = new ProxyFactory(tb);
NopInterceptor di = new NopInterceptor();
pc.addAdvice(0, di);
ITestBean t = (ITestBean) createProxy(pc);
assertEquals(0, di.getCount());
t.setAge(23);
assertEquals(23, t.getAge());
assertEquals(2, di.getCount());
Advised advised = (Advised) t;
assertEquals("Have 1 advisor", 1, advised.getAdvisors().length);
assertEquals(di, advised.getAdvisors()[0].getAdvice());
NopInterceptor di2 = new NopInterceptor();
advised.addAdvice(1, di2);
t.getName();
assertEquals(3, di.getCount());
assertEquals(1, di2.getCount());
// will remove di
advised.removeAdvisor(0);
t.getAge();
// Unchanged
assertEquals(3, di.getCount());
assertEquals(2, di2.getCount());
CountingBeforeAdvice cba = new CountingBeforeAdvice();
assertEquals(0, cba.getCalls());
advised.addAdvice(cba);
t.setAge(16);
assertEquals(16, t.getAge());
assertEquals(2, cba.getCalls());
}
public void testAdviceImplementsIntroductionInfo() throws Throwable {
TestBean tb = new TestBean();
String name = "tony";
tb.setName(name);
ProxyFactory pc = new ProxyFactory(tb);
NopInterceptor di = new NopInterceptor();
pc.addAdvice(di);
final long ts = 37;
pc.addAdvice(new DelegatingIntroductionInterceptor(new TimeStamped() {
public long getTimeStamp() {
return ts;
}
}));
ITestBean proxied = (ITestBean) createProxy(pc);
assertEquals(name, proxied.getName());
TimeStamped intro = (TimeStamped) proxied;
assertEquals(ts, intro.getTimeStamp());
}
public void testCannotAddDynamicIntroductionAdviceExceptInIntroductionAdvice() throws Throwable {
TestBean target = new TestBean();
target.setAge(21);
ProxyFactory pc = new ProxyFactory(target);
try {
pc.addAdvice(new DummyIntroductionAdviceImpl());
fail("Shouldn't be able to add introduction interceptor except via introduction advice");
}
catch (AopConfigException ex) {
assertTrue(ex.getMessage().indexOf("ntroduction") > -1);
}
// Check it still works: proxy factory state shouldn't have been corrupted
ITestBean proxied = (ITestBean) createProxy(pc);
assertEquals(target.getAge(), proxied.getAge());
}
public void testRejectsBogusDynamicIntroductionAdviceWithNoAdapter() throws Throwable {
TestBean target = new TestBean();
target.setAge(21);
ProxyFactory pc = new ProxyFactory(target);
pc.addAdvisor(new DefaultIntroductionAdvisor(new DummyIntroductionAdviceImpl(), Comparable.class));
try {
// TODO May fail on either call: may want to tighten up definition
ITestBean proxied = (ITestBean) createProxy(pc);
proxied.getName();
fail("Bogus introduction");
}
catch (Exception ex) {
// TODO used to catch UnknownAdviceTypeException, but
// with CGLIB some errors are in proxy creation and are wrapped
// in aspect exception. Error message is still fine.
//assertTrue(ex.getMessage().indexOf("ntroduction") > -1);
}
}
/**
* Check that the introduction advice isn't allowed to introduce interfaces
* that are unsupported by the IntroductionInterceptor.
*/
public void testCannotAddIntroductionAdviceWithUnimplementedInterface() throws Throwable {
TestBean target = new TestBean();
target.setAge(21);
ProxyFactory pc = new ProxyFactory(target);
try {
pc.addAdvisor(0, new DefaultIntroductionAdvisor(new TimestampIntroductionInterceptor(), ITestBean.class));
fail("Shouldn't be able to add introduction advice introducing an unimplemented interface");
}
catch (IllegalArgumentException ex) {
//assertTrue(ex.getMessage().indexOf("ntroduction") > -1);
}
// Check it still works: proxy factory state shouldn't have been corrupted
ITestBean proxied = (ITestBean) createProxy(pc);
assertEquals(target.getAge(), proxied.getAge());
}
/**
* Note that an introduction can't throw an unexpected checked exception,
* as it's constained by the interface.
*/
public void testIntroductionThrowsUncheckedException() throws Throwable {
TestBean target = new TestBean();
target.setAge(21);
ProxyFactory pc = new ProxyFactory(target);
class MyDi extends DelegatingIntroductionInterceptor implements TimeStamped {
/**
* @see org.springframework.aop.framework.TimeStamped#getTimeStamp()
*/
public long getTimeStamp() {
throw new UnsupportedOperationException();
}
}
pc.addAdvisor(new DefaultIntroductionAdvisor(new MyDi()));
TimeStamped ts = (TimeStamped) createProxy(pc);
try {
ts.getTimeStamp();
fail("Should throw UnsupportedOperationException");
}
catch (UnsupportedOperationException ex) {
}
}
/**
* Should only be able to introduce interfaces, not classes.
*/
public void testCannotAddIntroductionAdviceToIntroduceClass() throws Throwable {
TestBean target = new TestBean();
target.setAge(21);
ProxyFactory pc = new ProxyFactory(target);
try {
pc.addAdvisor(0, new DefaultIntroductionAdvisor(new TimestampIntroductionInterceptor(), TestBean.class));
fail("Shouldn't be able to add introduction advice that introduces a class, rather than an interface");
}
catch (IllegalArgumentException ex) {
assertTrue(ex.getMessage().indexOf("interface") > -1);
}
// Check it still works: proxy factory state shouldn't have been corrupted
ITestBean proxied = (ITestBean) createProxy(pc);
assertEquals(target.getAge(), proxied.getAge());
}
public void testCannotAddInterceptorWhenFrozen() throws Throwable {
TestBean target = new TestBean();
target.setAge(21);
ProxyFactory pc = new ProxyFactory(target);
assertFalse(pc.isFrozen());
pc.addAdvice(new NopInterceptor());
ITestBean proxied = (ITestBean) createProxy(pc);
pc.setFrozen(true);
try {
pc.addAdvice(0, new NopInterceptor());
fail("Shouldn't be able to add interceptor when frozen");
}
catch (AopConfigException ex) {
assertTrue(ex.getMessage().indexOf("frozen") > -1);
}
// Check it still works: proxy factory state shouldn't have been corrupted
assertEquals(target.getAge(), proxied.getAge());
assertEquals(1, ((Advised) proxied).getAdvisors().length);
}
/**
* Check that casting to Advised can't get around advice freeze.
*/
public void testCannotAddAdvisorWhenFrozenUsingCast() throws Throwable {
TestBean target = new TestBean();
target.setAge(21);
ProxyFactory pc = new ProxyFactory(target);
assertFalse(pc.isFrozen());
pc.addAdvice(new NopInterceptor());
ITestBean proxied = (ITestBean) createProxy(pc);
pc.setFrozen(true);
Advised advised = (Advised) proxied;
assertTrue(pc.isFrozen());
try {
advised.addAdvisor(new DefaultPointcutAdvisor(new NopInterceptor()));
fail("Shouldn't be able to add Advisor when frozen");
}
catch (AopConfigException ex) {
assertTrue(ex.getMessage().indexOf("frozen") > -1);
}
// Check it still works: proxy factory state shouldn't have been corrupted
assertEquals(target.getAge(), proxied.getAge());
assertEquals(1, advised.getAdvisors().length);
}
public void testCannotRemoveAdvisorWhenFrozen() throws Throwable {
TestBean target = new TestBean();
target.setAge(21);
ProxyFactory pc = new ProxyFactory(target);
assertFalse(pc.isFrozen());
pc.addAdvice(new NopInterceptor());
ITestBean proxied = (ITestBean) createProxy(pc);
pc.setFrozen(true);
Advised advised = (Advised) proxied;
assertTrue(pc.isFrozen());
try {
advised.removeAdvisor(0);
fail("Shouldn't be able to remove Advisor when frozen");
}
catch (AopConfigException ex) {
assertTrue(ex.getMessage().indexOf("frozen") > -1);
}
// Didn't get removed
assertEquals(1, advised.getAdvisors().length);
pc.setFrozen(false);
// Can now remove it
advised.removeAdvisor(0);
// Check it still works: proxy factory state shouldn't have been corrupted
assertEquals(target.getAge(), proxied.getAge());
assertEquals(0, advised.getAdvisors().length);
}
public void testUseAsHashKey() {
TestBean target1 = new TestBean();
ProxyFactory pf1 = new ProxyFactory(target1);
pf1.addAdvice(new NopInterceptor());
ITestBean proxy1 = (ITestBean) createProxy(pf1);
TestBean target2 = new TestBean();
ProxyFactory pf2 = new ProxyFactory(target2);
pf2.addAdvisor(new DefaultIntroductionAdvisor(new TimestampIntroductionInterceptor()));
ITestBean proxy2 = (ITestBean) createProxy(pf2);
HashMap h = new HashMap();
Object value1 = "foo";
Object value2 = "bar";
assertNull(h.get(proxy1));
h.put(proxy1, value1);
h.put(proxy2, value2);
assertEquals(h.get(proxy1), value1);
assertEquals(h.get(proxy2), value2);
}
/**
* Check that the string is informative.
*/
public void testProxyConfigString() {
TestBean target = new TestBean();
ProxyFactory pc = new ProxyFactory(target);
pc.setInterfaces(new Class[] {ITestBean.class});
pc.addAdvice(new NopInterceptor());
MethodBeforeAdvice mba = new CountingBeforeAdvice();
Advisor advisor = new DefaultPointcutAdvisor(new NameMatchMethodPointcut(), mba);
pc.addAdvisor(advisor);
ITestBean proxied = (ITestBean) createProxy(pc);
String proxyConfigString = ((Advised) proxied).toProxyConfigString();
assertTrue(proxyConfigString.indexOf(advisor.toString()) != -1);
assertTrue(proxyConfigString.indexOf("1 interface") != -1);
}
public void testCanPreventCastToAdvisedUsingOpaque() {
TestBean target = new TestBean();
ProxyFactory pc = new ProxyFactory(target);
pc.setInterfaces(new Class[] {ITestBean.class});
pc.addAdvice(new NopInterceptor());
CountingBeforeAdvice mba = new CountingBeforeAdvice();
Advisor advisor = new DefaultPointcutAdvisor(new NameMatchMethodPointcut().addMethodName("setAge"), mba);
pc.addAdvisor(advisor);
assertFalse("Opaque defaults to false", pc.isOpaque());
pc.setOpaque(true);
assertTrue("Opaque now true for this config", pc.isOpaque());
ITestBean proxied = (ITestBean) createProxy(pc);
proxied.setAge(10);
assertEquals(10, proxied.getAge());
assertEquals(1, mba.getCalls());
assertFalse("Cannot be cast to Advised", proxied instanceof Advised);
}
public void testAdviceSupportListeners() throws Throwable {
TestBean target = new TestBean();
target.setAge(21);
ProxyFactory pc = new ProxyFactory(target);
CountingAdvisorListener l = new CountingAdvisorListener(pc);
pc.addListener(l);
RefreshCountingAdvisorChainFactory acf = new RefreshCountingAdvisorChainFactory();
// Should be automatically added as a listener
pc.addListener(acf);
assertFalse(pc.isActive());
assertEquals(0, l.activates);
assertEquals(0, acf.refreshes);
ITestBean proxied = (ITestBean) createProxy(pc);
assertEquals(1, acf.refreshes);
assertEquals(1, l.activates);
assertTrue(pc.isActive());
assertEquals(target.getAge(), proxied.getAge());
assertEquals(0, l.adviceChanges);
NopInterceptor di = new NopInterceptor();
pc.addAdvice(0, di);
assertEquals(1, l.adviceChanges);
assertEquals(2, acf.refreshes);
assertEquals(target.getAge(), proxied.getAge());
pc.removeAdvice(di);
assertEquals(2, l.adviceChanges);
assertEquals(3, acf.refreshes);
assertEquals(target.getAge(), proxied.getAge());
pc.getProxy();
assertEquals(1, l.activates);
pc.removeListener(l);
assertEquals(2, l.adviceChanges);
pc.addAdvisor(new DefaultPointcutAdvisor(new NopInterceptor()));
// No longer counting
assertEquals(2, l.adviceChanges);
}
public void testExistingProxyChangesTarget() throws Throwable {
TestBean tb1 = new TestBean();
tb1.setAge(33);
TestBean tb2 = new TestBean();
tb2.setAge(26);
tb2.setName("Juergen");
TestBean tb3 = new TestBean();
tb3.setAge(37);
ProxyFactory pc = new ProxyFactory(tb1);
NopInterceptor nop = new NopInterceptor();
pc.addAdvice(nop);
ITestBean proxy = (ITestBean) createProxy(pc);
assertEquals(nop.getCount(), 0);
assertEquals(tb1.getAge(), proxy.getAge());
assertEquals(nop.getCount(), 1);
// Change to a new static target
pc.setTarget(tb2);
assertEquals(tb2.getAge(), proxy.getAge());
assertEquals(nop.getCount(), 2);
// Change to a new dynamic target
HotSwappableTargetSource hts = new HotSwappableTargetSource(tb3);
pc.setTargetSource(hts);
assertEquals(tb3.getAge(), proxy.getAge());
assertEquals(nop.getCount(), 3);
hts.swap(tb1);
assertEquals(tb1.getAge(), proxy.getAge());
tb1.setName("Colin");
assertEquals(tb1.getName(), proxy.getName());
assertEquals(nop.getCount(), 5);
// Change back, relying on casting to Advised
Advised advised = (Advised) proxy;
assertSame(hts, advised.getTargetSource());
SingletonTargetSource sts = new SingletonTargetSource(tb2);
advised.setTargetSource(sts);
assertEquals(tb2.getName(), proxy.getName());
assertSame(sts, advised.getTargetSource());
assertEquals(tb2.getAge(), proxy.getAge());
}
public void testDynamicMethodPointcutThatAlwaysAppliesStatically() throws Throwable {
TestBean tb = new TestBean();
ProxyFactory pc = new ProxyFactory(new Class[] {ITestBean.class});
TestDynamicPointcutAdvice dp = new TestDynamicPointcutAdvice(new NopInterceptor(), "getAge");
pc.addAdvisor(dp);
pc.setTarget(tb);
ITestBean it = (ITestBean) createProxy(pc);
assertEquals(dp.count, 0);
int age = it.getAge();
assertEquals(dp.count, 1);
it.setAge(11);
assertEquals(it.getAge(), 11);
assertEquals(dp.count, 2);
}
public void testDynamicMethodPointcutThatAppliesStaticallyOnlyToSetters() throws Throwable {
TestBean tb = new TestBean();
ProxyFactory pc = new ProxyFactory(new Class[] {ITestBean.class});
// Could apply dynamically to getAge/setAge but not to getName
TestDynamicPointcutForSettersOnly dp = new TestDynamicPointcutForSettersOnly(new NopInterceptor(), "Age");
pc.addAdvisor(dp);
this.mockTargetSource.setTarget(tb);
pc.setTargetSource(mockTargetSource);
ITestBean it = (ITestBean) createProxy(pc);
assertEquals(dp.count, 0);
int age = it.getAge();
// Statically vetoed
assertEquals(0, dp.count);
it.setAge(11);
assertEquals(it.getAge(), 11);
assertEquals(dp.count, 1);
// Applies statically but not dynamically
it.setName("joe");
assertEquals(dp.count, 1);
}
public void testStaticMethodPointcut() throws Throwable {
TestBean tb = new TestBean();
ProxyFactory pc = new ProxyFactory(new Class[] {ITestBean.class});
NopInterceptor di = new NopInterceptor();
TestStaticPointcutAdvice sp = new TestStaticPointcutAdvice(di, "getAge");
pc.addAdvisor(sp);
pc.setTarget(tb);
ITestBean it = (ITestBean) createProxy(pc);
assertEquals(di.getCount(), 0);
int age = it.getAge();
assertEquals(di.getCount(), 1);
it.setAge(11);
assertEquals(it.getAge(), 11);
assertEquals(di.getCount(), 2);
}
/**
* There are times when we want to call proceed() twice.
* We can do this if we clone the invocation.
*/
public void testCloneInvocationToProceedThreeTimes() throws Throwable {
TestBean tb = new TestBean();
ProxyFactory pc = new ProxyFactory(tb);
pc.addInterface(ITestBean.class);
MethodInterceptor twoBirthdayInterceptor = new MethodInterceptor() {
public Object invoke(MethodInvocation mi) throws Throwable {
// Clone the invocation to proceed three times
// "The Moor's Last Sigh": this technology can cause premature aging
MethodInvocation clone1 = ((ReflectiveMethodInvocation) mi).invocableClone();
MethodInvocation clone2 = ((ReflectiveMethodInvocation) mi).invocableClone();
clone1.proceed();
clone2.proceed();
return mi.proceed();
}
};
StaticMethodMatcherPointcutAdvisor advisor = new StaticMethodMatcherPointcutAdvisor(twoBirthdayInterceptor) {
public boolean matches(Method m, Class targetClass) {
return "haveBirthday".equals(m.getName());
}
};
pc.addAdvisor(advisor);
ITestBean it = (ITestBean) createProxy(pc);
final int age = 20;
it.setAge(age);
assertEquals(age, it.getAge());
// Should return the age before the third, AOP-induced birthday
assertEquals(age + 2, it.haveBirthday());
// Return the final age produced by 3 birthdays
assertEquals(age + 3, it.getAge());
}
/**
* We want to change the arguments on a clone: it shouldn't affect the original.
*/
public void testCanChangeArgumentsIndependentlyOnClonedInvocation() throws Throwable {
TestBean tb = new TestBean();
ProxyFactory pc = new ProxyFactory(tb);
pc.addInterface(ITestBean.class);
/**
* Changes the name, then changes it back.
*/
MethodInterceptor nameReverter = new MethodInterceptor() {
public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation clone = ((ReflectiveMethodInvocation) mi).invocableClone();
String oldName = ((ITestBean) mi.getThis()).getName();
clone.getArguments()[0] = oldName;
// Original method invocation should be unaffected by changes to argument list of clone
mi.proceed();
return clone.proceed();
}
};
class NameSaver implements MethodInterceptor {
private List names = new LinkedList();
public Object invoke(MethodInvocation mi) throws Throwable {
names.add(mi.getArguments()[0]);
return mi.proceed();
}
}
NameSaver saver = new NameSaver();
pc.addAdvisor(new DefaultPointcutAdvisor(Pointcuts.SETTERS, nameReverter));
pc.addAdvisor(new DefaultPointcutAdvisor(Pointcuts.SETTERS, saver));
ITestBean it = (ITestBean) createProxy(pc);
String name1 = "tony";
String name2 = "gordon";
tb.setName(name1);
assertEquals(name1, tb.getName());
it.setName(name2);
// NameReverter saved it back
assertEquals(name1, it.getName());
assertEquals(2, saver.names.size());
assertEquals(name2, saver.names.get(0));
assertEquals(name1, saver.names.get(1));
}
public void testOverloadedMethodsWithDifferentAdvice() throws Throwable {
Overloads target = new Overloads();
ProxyFactory pc = new ProxyFactory(target);
NopInterceptor overLoadVoids = new NopInterceptor();
pc.addAdvisor(new StaticMethodMatcherPointcutAdvisor(overLoadVoids) {
public boolean matches(Method m, Class targetClass) {
return m.getName().equals("overload") && m.getParameterTypes().length == 0;
}
});
NopInterceptor overLoadInts = new NopInterceptor();
pc.addAdvisor(new StaticMethodMatcherPointcutAdvisor(overLoadInts) {
public boolean matches(Method m, Class targetClass) {
return m.getName().equals("overload") && m.getParameterTypes().length == 1 &&
m.getParameterTypes()[0].equals(int.class);
}
});
IOverloads proxy = (IOverloads) createProxy(pc);
assertEquals(0, overLoadInts.getCount());
assertEquals(0, overLoadVoids.getCount());
proxy.overload();
assertEquals(0, overLoadInts.getCount());
assertEquals(1, overLoadVoids.getCount());
assertEquals(25, proxy.overload(25));
assertEquals(1, overLoadInts.getCount());
assertEquals(1, overLoadVoids.getCount());
proxy.noAdvice();
assertEquals(1, overLoadInts.getCount());
assertEquals(1, overLoadVoids.getCount());
}
public void testProxyIsBoundBeforeTargetSourceInvoked() {
final TestBean target = new TestBean();
ProxyFactory pf = new ProxyFactory(target);
pf.addAdvice(new DebugInterceptor());
pf.setExposeProxy(true);
final ITestBean proxy = (ITestBean) createProxy(pf);
Advised config = (Advised) proxy;
// This class just checks proxy is bound before getTarget() call
config.setTargetSource(new TargetSource() {
public Class getTargetClass() {
return TestBean.class;
}
public boolean isStatic() {
return false;
}
public Object getTarget() throws Exception {
assertEquals(proxy, AopContext.currentProxy());
return target;
}
public void releaseTarget(Object target) throws Exception {
}
});
// Just test anything: it will fail if context wasn't found
assertEquals(0, proxy.getAge());
}
public void testEquals() {
IOther a = new AllInstancesAreEqual();
IOther b = new AllInstancesAreEqual();
NopInterceptor i1 = new NopInterceptor();
NopInterceptor i2 = new NopInterceptor();
ProxyFactory pfa = new ProxyFactory(a);
pfa.addAdvice(i1);
ProxyFactory pfb = new ProxyFactory(b);
pfb.addAdvice(i2);
IOther proxyA = (IOther) createProxy(pfa);
IOther proxyB = (IOther) createProxy(pfb);
assertEquals(pfa.getAdvisors().length, pfb.getAdvisors().length);
assertTrue(a.equals(b));
assertTrue(i1.equals(i2));
assertTrue(proxyA.equals(proxyB));
assertEquals(proxyA.hashCode(), proxyB.hashCode());
assertFalse(proxyA.equals(a));
// Equality checks were handled by the proxy
assertEquals(0, i1.getCount());
// When we invoke A, it's NopInterceptor will have count == 1
// and won't think it's equal to B's NopInterceptor
proxyA.absquatulate();
assertEquals(1, i1.getCount());
assertFalse(proxyA.equals(proxyB));
}
public void testBeforeAdvisorIsInvoked() {
CountingBeforeAdvice cba = new CountingBeforeAdvice();
Advisor matchesNoArgs = new StaticMethodMatcherPointcutAdvisor(cba) {
public boolean matches(Method m, Class targetClass) {
return m.getParameterTypes().length == 0;
}
};
TestBean target = new TestBean();
target.setAge(80);
ProxyFactory pf = new ProxyFactory(target);
pf.addAdvice(new NopInterceptor());
pf.addAdvisor(matchesNoArgs);
assertEquals("Advisor was added", matchesNoArgs, pf.getAdvisors()[1]);
ITestBean proxied = (ITestBean) createProxy(pf);
assertEquals(0, cba.getCalls());
assertEquals(0, cba.getCalls("getAge"));
assertEquals(target.getAge(), proxied.getAge());
assertEquals(1, cba.getCalls());
assertEquals(1, cba.getCalls("getAge"));
assertEquals(0, cba.getCalls("setAge"));
// Won't be advised
proxied.setAge(26);
assertEquals(1, cba.getCalls());
assertEquals(26, proxied.getAge());
}
public void testUserAttributes() throws Throwable {
class MapAwareMethodInterceptor implements MethodInterceptor {
private final Map expectedValues;
private final Map valuesToAdd;
public MapAwareMethodInterceptor(Map expectedValues, Map valuesToAdd) {
this.expectedValues = expectedValues;
this.valuesToAdd = valuesToAdd;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
ReflectiveMethodInvocation rmi = (ReflectiveMethodInvocation) invocation;
for (Iterator it = rmi.getUserAttributes().keySet().iterator(); it.hasNext(); ){
Object key = it.next();
assertEquals(expectedValues.get(key), rmi.getUserAttributes().get(key));
}
rmi.getUserAttributes().putAll(valuesToAdd);
return invocation.proceed();
}
};
AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class});
MapAwareMethodInterceptor mami1 = new MapAwareMethodInterceptor(new HashMap(), new HashMap());
Map firstValuesToAdd = new HashMap();
firstValuesToAdd.put("test", "");
MapAwareMethodInterceptor mami2 = new MapAwareMethodInterceptor(new HashMap(), firstValuesToAdd);
MapAwareMethodInterceptor mami3 = new MapAwareMethodInterceptor(firstValuesToAdd, new HashMap());
MapAwareMethodInterceptor mami4 = new MapAwareMethodInterceptor(firstValuesToAdd, new HashMap());
Map secondValuesToAdd = new HashMap();
secondValuesToAdd.put("foo", "bar");
secondValuesToAdd.put("cat", "dog");
MapAwareMethodInterceptor mami5 = new MapAwareMethodInterceptor(firstValuesToAdd, secondValuesToAdd);
Map finalExpected = new HashMap(firstValuesToAdd);
finalExpected.putAll(secondValuesToAdd);
MapAwareMethodInterceptor mami6 = new MapAwareMethodInterceptor(finalExpected, secondValuesToAdd);
pc.addAdvice(mami1);
pc.addAdvice(mami2);
pc.addAdvice(mami3);
pc.addAdvice(mami4);
pc.addAdvice(mami5);
pc.addAdvice(mami6);
// We don't care about the object
pc.setTarget(new TestBean());
AopProxy aop = createAopProxy(pc);
ITestBean tb = (ITestBean) aop.getProxy();
String newName = "foo";
tb.setName(newName);
assertEquals(newName, tb.getName());
}
public void testMultiAdvice() throws Throwable {
CountingMultiAdvice cca = new CountingMultiAdvice();
Advisor matchesNoArgs = new StaticMethodMatcherPointcutAdvisor(cca) {
public boolean matches(Method m, Class targetClass) {
return m.getParameterTypes().length == 0 || "exceptional".equals(m.getName());
}
};
TestBean target = new TestBean();
target.setAge(80);
ProxyFactory pf = new ProxyFactory(target);
pf.addAdvice(new NopInterceptor());
pf.addAdvisor(matchesNoArgs);
assertEquals("Advisor was added", matchesNoArgs, pf.getAdvisors()[1]);
ITestBean proxied = (ITestBean) createProxy(pf);
assertEquals(0, cca.getCalls());
assertEquals(0, cca.getCalls("getAge"));
assertEquals(target.getAge(), proxied.getAge());
assertEquals(2, cca.getCalls());
assertEquals(2, cca.getCalls("getAge"));
assertEquals(0, cca.getCalls("setAge"));
// Won't be advised
proxied.setAge(26);
assertEquals(2, cca.getCalls());
assertEquals(26, proxied.getAge());
assertEquals(4, cca.getCalls());
try {
proxied.exceptional(new CannotGetJdbcConnectionException("foo", (SQLException)null));
fail("Should have thrown CannotGetJdbcConnectionException");
}
catch (CannotGetJdbcConnectionException ex) {
// expected
}
assertEquals(6, cca.getCalls());
}
public void testBeforeAdviceThrowsException() {
final RuntimeException rex = new RuntimeException();
CountingBeforeAdvice ba = new CountingBeforeAdvice() {
public void before(Method m, Object[] args, Object target) throws Throwable {
super.before(m, args, target);
if (m.getName().startsWith("set"))
throw rex;
}
};
TestBean target = new TestBean();
target.setAge(80);
NopInterceptor nop1 = new NopInterceptor();
NopInterceptor nop2 = new NopInterceptor();
ProxyFactory pf = new ProxyFactory(target);
pf.addAdvice(nop1);
pf.addAdvice(ba);
pf.addAdvice(nop2);
ITestBean proxied = (ITestBean) createProxy(pf);
// Won't throw an exception
assertEquals(target.getAge(), proxied.getAge());
assertEquals(1, ba.getCalls());
assertEquals(1, ba.getCalls("getAge"));
assertEquals(1, nop1.getCount());
assertEquals(1, nop2.getCount());
// Will fail, after invoking Nop1
try {
proxied.setAge(26);
fail("before advice should have ended chain");
}
catch (RuntimeException ex) {
assertEquals(rex, ex);
}
assertEquals(2, ba.getCalls());
assertEquals(2, nop1.getCount());
// Nop2 didn't get invoked when the exception was thrown
assertEquals(1, nop2.getCount());
// Shouldn't have changed value in joinpoint
assertEquals(target.getAge(), proxied.getAge());
}
public void testAfterReturningAdvisorIsInvoked() {
class SummingAfterAdvice implements AfterReturningAdvice {
public int sum;
public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable {
sum += ((Integer) returnValue).intValue();
}
}
SummingAfterAdvice aa = new SummingAfterAdvice();
Advisor matchesInt = new StaticMethodMatcherPointcutAdvisor(aa) {
public boolean matches(Method m, Class targetClass) {
return m.getReturnType() == int.class;
}
};
TestBean target = new TestBean();
ProxyFactory pf = new ProxyFactory(target);
pf.addAdvice(new NopInterceptor());
pf.addAdvisor(matchesInt);
assertEquals("Advisor was added", matchesInt, pf.getAdvisors()[1]);
ITestBean proxied = (ITestBean) createProxy(pf);
assertEquals(0, aa.sum);
int i1 = 12;
int i2 = 13;
// Won't be advised
proxied.setAge(i1);
assertEquals(i1, proxied.getAge());
assertEquals(i1, aa.sum);
proxied.setAge(i2);
assertEquals(i2, proxied.getAge());
assertEquals(i1 + i2, aa.sum);
assertEquals(i2, proxied.getAge());
}
public void testAfterReturningAdvisorIsNotInvokedOnException() {
CountingAfterReturningAdvice car = new CountingAfterReturningAdvice();
TestBean target = new TestBean();
ProxyFactory pf = new ProxyFactory(target);
pf.addAdvice(new NopInterceptor());
pf.addAdvice(car);
assertEquals("Advice was wrapped in Advisor and added", car, pf.getAdvisors()[1].getAdvice());
ITestBean proxied = (ITestBean) createProxy(pf);
assertEquals(0, car.getCalls());
int age = 10;
proxied.setAge(age);
assertEquals(age, proxied.getAge());
assertEquals(2, car.getCalls());
Exception exc = new Exception();
// On exception it won't be invoked
try {
proxied.exceptional(exc);
fail();
}
catch (Throwable t) {
assertSame(exc, t);
}
assertEquals(2, car.getCalls());
}
public void testThrowsAdvisorIsInvoked() throws Throwable {
// Reacts to ServletException and RemoteException
ThrowsAdviceInterceptorTests.MyThrowsHandler th = new ThrowsAdviceInterceptorTests.MyThrowsHandler();
Advisor matchesEchoInvocations = new StaticMethodMatcherPointcutAdvisor(th) {
public boolean matches(Method m, Class targetClass) {
return m.getName().startsWith("echo");
}
};
ThrowsAdviceInterceptorTests.Echo target = new ThrowsAdviceInterceptorTests.Echo();
target.setA(16);
ProxyFactory pf = new ProxyFactory(target);
pf.addAdvice(new NopInterceptor());
pf.addAdvisor(matchesEchoInvocations);
assertEquals("Advisor was added", matchesEchoInvocations, pf.getAdvisors()[1]);
ThrowsAdviceInterceptorTests.IEcho proxied = (ThrowsAdviceInterceptorTests.IEcho) createProxy(pf);
assertEquals(0, th.getCalls());
assertEquals(target.getA(), proxied.getA());
assertEquals(0, th.getCalls());
Exception ex = new Exception();
// Will be advised but doesn't match
try {
proxied.echoException(1, ex);
fail();
}
catch (Exception caught) {
assertEquals(ex, caught);
}
ex = new ServletException();
try {
proxied.echoException(1, ex);
fail();
}
catch (ServletException caught) {
assertEquals(ex, caught);
}
assertEquals(1, th.getCalls("servletException"));
}
public void testAddThrowsAdviceWithoutAdvisor() throws Throwable {
// Reacts to ServletException and RemoteException
ThrowsAdviceInterceptorTests.MyThrowsHandler th = new ThrowsAdviceInterceptorTests.MyThrowsHandler();
ThrowsAdviceInterceptorTests.Echo target = new ThrowsAdviceInterceptorTests.Echo();
target.setA(16);
ProxyFactory pf = new ProxyFactory(target);
pf.addAdvice(new NopInterceptor());
pf.addAdvice(th);
ThrowsAdviceInterceptorTests.IEcho proxied = (ThrowsAdviceInterceptorTests.IEcho) createProxy(pf);
assertEquals(0, th.getCalls());
assertEquals(target.getA(), proxied.getA());
assertEquals(0, th.getCalls());
Exception ex = new Exception();
// Will be advised but doesn't match
try {
proxied.echoException(1, ex);
fail();
}
catch (Exception caught) {
assertEquals(ex, caught);
}
// Subclass of RemoteException
ex = new TransactionRequiredException();
try {
proxied.echoException(1, ex);
fail();
}
catch (TransactionRequiredException caught) {
assertEquals(ex, caught);
}
assertEquals(1, th.getCalls("remoteException"));
}
private static class CheckMethodInvocationIsSameInAndOutInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation mi) throws Throwable {
Method m = mi.getMethod();
Object retval = mi.proceed();
assertEquals("Method invocation has same method on way back", m, mi.getMethod());
return retval;
}
}
/**
* ExposeInvocation must be set to true.
*/
private static class CheckMethodInvocationViaThreadLocalIsSameInAndOutInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation mi) throws Throwable {
String task = "get invocation on way IN";
try {
MethodInvocation current = ExposeInvocationInterceptor.currentInvocation();
assertEquals(mi.getMethod(), current.getMethod());
Object retval = mi.proceed();
task = "get invocation on way OUT";
assertEquals(current, ExposeInvocationInterceptor.currentInvocation());
return retval;
}
catch (IllegalStateException ex) {
System.err.println(task + " for " + mi.getMethod());
ex.printStackTrace();
throw ex;
}
}
}
/**
* Same thing for a proxy.
* Only works when exposeProxy is set to true.
* Checks that the proxy is the same on the way in and out.
*/
private static class ProxyMatcherInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation mi) throws Throwable {
Object proxy = AopContext.currentProxy();
Object ret = mi.proceed();
// TODO why does this cause stack overflow?
//assertEquals(proxy, AopContext.currentProxy());
assertTrue(proxy == AopContext.currentProxy());
return ret;
}
}
/**
* Fires on setter methods that take a string. Replaces null arg with "".
*/
protected static class StringSetterNullReplacementAdvice extends DefaultPointcutAdvisor {
private static MethodInterceptor cleaner = new MethodInterceptor() {
public Object invoke(MethodInvocation mi) throws Throwable {
// We know it can only be invoked if there's a single parameter of type string
mi.getArguments()[0] = "";
return mi.proceed();
}
};
public StringSetterNullReplacementAdvice() {
super(cleaner);
setPointcut(new DynamicMethodMatcherPointcut() {
public boolean matches(Method m, Class targetClass, Object[] args) {
return args[0] == null;
}
public boolean matches(Method m, Class targetClass) {
return m.getName().startsWith("set") &&
m.getParameterTypes().length == 1 &&
m.getParameterTypes()[0].equals(String.class);
}
});
}
}
protected static class TestDynamicPointcutAdvice extends DefaultPointcutAdvisor {
public int count;
public TestDynamicPointcutAdvice(MethodInterceptor mi, final String pattern) {
super(mi);
setPointcut(new DynamicMethodMatcherPointcut() {
public boolean matches(Method m, Class targetClass, Object[] args) {
boolean run = m.getName().indexOf(pattern) != -1;
if (run) ++count;
return run;
}
});
}
}
protected static class TestDynamicPointcutForSettersOnly extends DefaultPointcutAdvisor {
public int count;
public TestDynamicPointcutForSettersOnly(MethodInterceptor mi, final String pattern) {
super(mi);
setPointcut(new DynamicMethodMatcherPointcut() {
public boolean matches(Method m, Class targetClass, Object[] args) {
boolean run = m.getName().indexOf(pattern) != -1;
if (run) ++count;
return run;
}
public boolean matches(Method m, Class clazz) {
return m.getName().startsWith("set");
}
});
}
}
protected static class TestStaticPointcutAdvice extends StaticMethodMatcherPointcutAdvisor {
private String pattern;
private int count;
public TestStaticPointcutAdvice(MethodInterceptor mi, String pattern) {
super(mi);
this.pattern = pattern;
}
public boolean matches(Method m, Class targetClass) {
boolean run = m.getName().indexOf(pattern) != -1;
if (run) ++count;
return run;
}
}
/**
* Note that trapping the Invocation as in previous version of this test
* isn't safe, as invocations may be reused
* and hence cleared at the end of each invocation.
* So we trap only the targe.
*/
protected static class TrapTargetInterceptor implements MethodInterceptor {
public Object target;
public Object invoke(MethodInvocation invocation) throws Throwable {
this.target = invocation.getThis();
return invocation.proceed();
}
}
private static class DummyIntroductionAdviceImpl implements DynamicIntroductionAdvice {
public boolean implementsInterface(Class intf) {
return true;
}
}
public static class OwnSpouse extends TestBean {
public ITestBean getSpouse() {
return this;
}
}
public static class AllInstancesAreEqual implements IOther {
public boolean equals(Object other) {
return (other instanceof AllInstancesAreEqual);
}
public int hashCode() {
return getClass().hashCode();
}
public void absquatulate() {
}
}
public interface INeedsToSeeProxy {
int getCount();
void incrementViaThis();
void incrementViaProxy();
void increment();
}
public static class NeedsToSeeProxy implements INeedsToSeeProxy {
private int count;
public int getCount() {
return count;
}
public void incrementViaThis() {
this.increment();
}
public void incrementViaProxy() {
INeedsToSeeProxy thisViaProxy = (INeedsToSeeProxy) AopContext.currentProxy();
thisViaProxy.increment();
Advised advised = (Advised) thisViaProxy;
checkAdvised(advised);
}
protected void checkAdvised(Advised advised) {
}
public void increment() {
++count;
}
}
public static class TargetChecker extends NeedsToSeeProxy {
protected void checkAdvised(Advised advised) {
// TODO replace this check: no longer possible
//assertEquals(advised.getTarget(), this);
}
}
public static class CountingAdvisorListener implements AdvisedSupportListener {
public int adviceChanges;
public int activates;
private AdvisedSupport expectedSource;
public CountingAdvisorListener(AdvisedSupport expectedSource) {
this.expectedSource = expectedSource;
}
public void activated(AdvisedSupport advised) {
assertEquals(expectedSource, advised);
++activates;
}
public void adviceChanged(AdvisedSupport advised) {
assertEquals(expectedSource, advised);
++adviceChanges;
}
}
public static class RefreshCountingAdvisorChainFactory implements AdvisedSupportListener {
public int refreshes;
public void activated(AdvisedSupport advised) {
++refreshes;
}
public void adviceChanged(AdvisedSupport advised) {
++refreshes;
}
}
public static interface IOverloads {
void overload();
int overload(int i);
String overload(String foo);
void noAdvice();
}
public static class Overloads implements IOverloads {
public void overload() {
}
public int overload(int i) {
return i;
}
public String overload(String s) {
return s;
}
public void noAdvice() {
}
}
}