alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Spring Framework example source code file (AbstractAopProxyTests.java)

This example Spring Framework source code file (AbstractAopProxyTests.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Spring Framework tags/keywords

advised, class, exception, itestbean, itestbean, jdbc, methodinterceptor, nopinterceptor, nopinterceptor, object, proxyfactory, proxyfactory, reflection, servlet, sql, testbean, testbean, throwable, transaction, util

The Spring Framework AbstractAopProxyTests.java source code

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

}

Other Spring Framework examples (source code examples)

Here is a short list of links related to this Spring Framework AbstractAopProxyTests.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.