/* * 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() { } } }