View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.aop.framework;
18  
19  import java.io.FileNotFoundException;
20  import java.io.IOException;
21  import java.lang.reflect.Method;
22  import java.lang.reflect.UndeclaredThrowableException;
23  import java.rmi.MarshalException;
24  import java.sql.SQLException;
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.LinkedList;
28  import java.util.List;
29  import java.util.Map;
30  
31  import junit.framework.TestCase;
32  import org.aopalliance.aop.Advice;
33  import org.aopalliance.intercept.MethodInterceptor;
34  import org.aopalliance.intercept.MethodInvocation;
35  import org.junit.After;
36  import org.junit.Before;
37  import org.junit.Test;
38  import test.mixin.LockMixin;
39  import test.mixin.LockMixinAdvisor;
40  import test.mixin.Lockable;
41  import test.mixin.LockedException;
42  
43  import org.springframework.aop.Advisor;
44  import org.springframework.aop.AfterReturningAdvice;
45  import org.springframework.aop.DynamicIntroductionAdvice;
46  import org.springframework.aop.MethodBeforeAdvice;
47  import org.springframework.aop.TargetSource;
48  import org.springframework.aop.ThrowsAdvice;
49  import org.springframework.aop.interceptor.DebugInterceptor;
50  import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
51  import org.springframework.aop.support.AopUtils;
52  import org.springframework.aop.support.DefaultIntroductionAdvisor;
53  import org.springframework.aop.support.DefaultPointcutAdvisor;
54  import org.springframework.aop.support.DelegatingIntroductionInterceptor;
55  import org.springframework.aop.support.DynamicMethodMatcherPointcut;
56  import org.springframework.aop.support.NameMatchMethodPointcut;
57  import org.springframework.aop.support.Pointcuts;
58  import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
59  import org.springframework.aop.target.HotSwappableTargetSource;
60  import org.springframework.aop.target.SingletonTargetSource;
61  import org.springframework.tests.Assume;
62  import org.springframework.tests.TestGroup;
63  import org.springframework.tests.TimeStamped;
64  import org.springframework.tests.aop.advice.CountingAfterReturningAdvice;
65  import org.springframework.tests.aop.advice.CountingBeforeAdvice;
66  import org.springframework.tests.aop.advice.MethodCounter;
67  import org.springframework.tests.aop.advice.MyThrowsHandler;
68  import org.springframework.tests.aop.interceptor.NopInterceptor;
69  import org.springframework.tests.aop.interceptor.SerializableNopInterceptor;
70  import org.springframework.tests.aop.interceptor.TimestampIntroductionInterceptor;
71  import org.springframework.tests.sample.beans.IOther;
72  import org.springframework.tests.sample.beans.ITestBean;
73  import org.springframework.tests.sample.beans.Person;
74  import org.springframework.tests.sample.beans.SerializablePerson;
75  import org.springframework.tests.sample.beans.TestBean;
76  import org.springframework.util.SerializationTestUtils;
77  import org.springframework.util.StopWatch;
78  
79  import static org.junit.Assert.*;
80  
81  /**
82   * @author Rod Johnson
83   * @author Juergen Hoeller
84   * @author Chris Beams
85   * @since 13.03.2003
86   */
87  public abstract class AbstractAopProxyTests {
88  
89  	protected final MockTargetSource mockTargetSource = new MockTargetSource();
90  
91  
92  	/**
93  	 * Make a clean target source available if code wants to use it.
94  	 * The target must be set. Verification will be automatic in tearDown
95  	 * to ensure that it was used appropriately by code.
96  	 */
97  	@Before
98  	public void setUp() {
99  		mockTargetSource.reset();
100 	}
101 
102 	@After
103 	public void tearDown() {
104 		mockTargetSource.verify();
105 	}
106 
107 
108 	/**
109 	 * Set in CGLIB or JDK mode.
110 	 */
111 	protected abstract Object createProxy(ProxyCreatorSupport as);
112 
113 	protected abstract AopProxy createAopProxy(AdvisedSupport as);
114 
115 	/**
116 	 * Is a target always required?
117 	 */
118 	protected boolean requiresTarget() {
119 		return false;
120 	}
121 
122 
123 	@Test
124 	public void testNoInterceptorsAndNoTarget() {
125 		AdvisedSupport pc = new AdvisedSupport(new Class<?>[] {ITestBean.class});
126 		// Add no interceptors
127 		try {
128 			AopProxy aop = createAopProxy(pc);
129 			aop.getProxy();
130 			fail("Shouldn't allow no interceptors");
131 		}
132 		catch (AopConfigException ex) {
133 			// Ok
134 		}
135 	}
136 
137 	/**
138 	 * Simple test that if we set values we can get them out again.
139 	 */
140 	@Test
141 	public void testValuesStick() {
142 		int age1 = 33;
143 		int age2 = 37;
144 		String name = "tony";
145 
146 		TestBean target1 = new TestBean();
147 		target1.setAge(age1);
148 		ProxyFactory pf1 = new ProxyFactory(target1);
149 		pf1.addAdvisor(new DefaultPointcutAdvisor(new NopInterceptor()));
150 		pf1.addAdvisor(new DefaultPointcutAdvisor(new TimestampIntroductionInterceptor()));
151 		ITestBean tb = (ITestBean) pf1.getProxy();
152 
153 		assertEquals(age1, tb.getAge());
154 		tb.setAge(age2);
155 		assertEquals(age2, tb.getAge());
156 		assertNull(tb.getName());
157 		tb.setName(name);
158 		assertEquals(name, tb.getName());
159 	}
160 
161 	/**
162 	 * This is primarily a test for the efficiency of our
163 	 * usage of CGLIB. If we create too many classes with
164 	 * CGLIB this will be slow or will run out of memory.
165 	 */
166 	@Test
167 	public void testManyProxies() {
168 		Assume.group(TestGroup.PERFORMANCE);
169 		int howMany = 10000;
170 		StopWatch sw = new StopWatch();
171 		sw.start("Create " + howMany + " proxies");
172 		testManyProxies(howMany);
173 		sw.stop();
174 		System.out.println(sw.getTotalTimeMillis());
175 		assertTrue("Proxy creation was too slow",  sw.getTotalTimeMillis() < 5000);
176 	}
177 
178 	private void testManyProxies(int howMany) {
179 		int age1 = 33;
180 		TestBean target1 = new TestBean();
181 		target1.setAge(age1);
182 		ProxyFactory pf1 = new ProxyFactory(target1);
183 		pf1.addAdvice(new NopInterceptor());
184 		pf1.addAdvice(new NopInterceptor());
185 		ITestBean proxies[] = new ITestBean[howMany];
186 		for (int i = 0; i < howMany; i++) {
187 			proxies[i] = (ITestBean) createAopProxy(pf1).getProxy();
188 			assertEquals(age1, proxies[i].getAge());
189 		}
190 	}
191 
192 	@Test
193 	public void testSerializationAdviceAndTargetNotSerializable() throws Exception {
194 		TestBean tb = new TestBean();
195 		assertFalse(SerializationTestUtils.isSerializable(tb));
196 
197 		ProxyFactory pf = new ProxyFactory(tb);
198 
199 		pf.addAdvice(new NopInterceptor());
200 		ITestBean proxy = (ITestBean) createAopProxy(pf).getProxy();
201 
202 		assertFalse(SerializationTestUtils.isSerializable(proxy));
203 	}
204 
205 	@Test
206 	public void testSerializationAdviceNotSerializable() throws Exception {
207 		SerializablePerson sp = new SerializablePerson();
208 		assertTrue(SerializationTestUtils.isSerializable(sp));
209 
210 		ProxyFactory pf = new ProxyFactory(sp);
211 
212 		// This isn't serializable
213 		Advice i = new NopInterceptor();
214 		pf.addAdvice(i);
215 		assertFalse(SerializationTestUtils.isSerializable(i));
216 		Object proxy = createAopProxy(pf).getProxy();
217 
218 		assertFalse(SerializationTestUtils.isSerializable(proxy));
219 	}
220 
221 	@Test
222 	public void testSerializationSerializableTargetAndAdvice() throws Throwable {
223 		SerializablePerson personTarget = new SerializablePerson();
224 		personTarget.setName("jim");
225 		personTarget.setAge(26);
226 
227 		assertTrue(SerializationTestUtils.isSerializable(personTarget));
228 
229 		ProxyFactory pf = new ProxyFactory(personTarget);
230 
231 		CountingThrowsAdvice cta = new CountingThrowsAdvice();
232 
233 		pf.addAdvice(new SerializableNopInterceptor());
234 		// Try various advice types
235 		pf.addAdvice(new CountingBeforeAdvice());
236 		pf.addAdvice(new CountingAfterReturningAdvice());
237 		pf.addAdvice(cta);
238 		Person p = (Person) createAopProxy(pf).getProxy();
239 
240 		p.echo(null);
241 		assertEquals(0, cta.getCalls());
242 		try {
243 			p.echo(new IOException());
244 		}
245 		catch (IOException ex) {
246 
247 		}
248 		assertEquals(1, cta.getCalls());
249 
250 		// Will throw exception if it fails
251 		Person p2 = (Person) SerializationTestUtils.serializeAndDeserialize(p);
252 		assertNotSame(p, p2);
253 		assertEquals(p.getName(), p2.getName());
254 		assertEquals(p.getAge(), p2.getAge());
255 		assertTrue("Deserialized object is an AOP proxy", AopUtils.isAopProxy(p2));
256 
257 		Advised a1 = (Advised) p;
258 		Advised a2 = (Advised) p2;
259 		// Check we can manipulate state of p2
260 		assertEquals(a1.getAdvisors().length, a2.getAdvisors().length);
261 
262 		// This should work as SerializablePerson is equal
263 		assertEquals("Proxies should be equal, even after one was serialized", p, p2);
264 		assertEquals("Proxies should be equal, even after one was serialized", p2, p);
265 
266 		// Check we can add a new advisor to the target
267 		NopInterceptor ni = new NopInterceptor();
268 		p2.getAge();
269 		assertEquals(0, ni.getCount());
270 		a2.addAdvice(ni);
271 		p2.getAge();
272 		assertEquals(1, ni.getCount());
273 
274 		cta = (CountingThrowsAdvice) a2.getAdvisors()[3].getAdvice();
275 		p2.echo(null);
276 		assertEquals(1, cta.getCalls());
277 		try {
278 			p2.echo(new IOException());
279 		}
280 		catch (IOException ex) {
281 
282 		}
283 		assertEquals(2, cta.getCalls());
284 
285 	}
286 
287 	/**
288 	 * Check that the two MethodInvocations necessary are independent and
289 	 * don't conflict.
290 	 * Check also proxy exposure.
291 	 */
292 	@Test
293 	public void testOneAdvisedObjectCallsAnother() {
294 		int age1 = 33;
295 		int age2 = 37;
296 
297 		TestBean target1 = new TestBean();
298 		ProxyFactory pf1 = new ProxyFactory(target1);
299 		// Permit proxy and invocation checkers to get context from AopContext
300 		pf1.setExposeProxy(true);
301 		NopInterceptor di1 = new NopInterceptor();
302 		pf1.addAdvice(0, di1);
303 		pf1.addAdvice(1, new ProxyMatcherInterceptor());
304 		pf1.addAdvice(2, new CheckMethodInvocationIsSameInAndOutInterceptor());
305 		pf1.addAdvice(1, new CheckMethodInvocationViaThreadLocalIsSameInAndOutInterceptor());
306 		// Must be first
307 		pf1.addAdvice(0, ExposeInvocationInterceptor.INSTANCE);
308 		ITestBean advised1 = (ITestBean) pf1.getProxy();
309 		advised1.setAge(age1); // = 1 invocation
310 
311 		TestBean target2 = new TestBean();
312 		ProxyFactory pf2 = new ProxyFactory(target2);
313 		pf2.setExposeProxy(true);
314 		NopInterceptor di2 = new NopInterceptor();
315 		pf2.addAdvice(0, di2);
316 		pf2.addAdvice(1, new ProxyMatcherInterceptor());
317 		pf2.addAdvice(2, new CheckMethodInvocationIsSameInAndOutInterceptor());
318 		pf2.addAdvice(1, new CheckMethodInvocationViaThreadLocalIsSameInAndOutInterceptor());
319 		pf2.addAdvice(0, ExposeInvocationInterceptor.INSTANCE);
320 		//System.err.println(pf2.toProxyConfigString());
321 		ITestBean advised2 = (ITestBean) createProxy(pf2);
322 		advised2.setAge(age2);
323 		advised1.setSpouse(advised2); // = 2 invocations
324 
325 		assertEquals("Advised one has correct age", age1, advised1.getAge()); // = 3 invocations
326 		assertEquals("Advised two has correct age", age2, advised2.getAge());
327 		// Means extra call on advised 2
328 		assertEquals("Advised one spouse has correct age", age2, advised1.getSpouse().getAge()); // = 4 invocations on 1 and another one on 2
329 
330 		assertEquals("one was invoked correct number of times", 4, di1.getCount());
331 		// Got hit by call to advised1.getSpouse().getAge()
332 		assertEquals("one was invoked correct number of times", 3, di2.getCount());
333 	}
334 
335 
336 	@Test
337 	public void testReentrance() {
338 		int age1 = 33;
339 
340 		TestBean target1 = new TestBean();
341 		ProxyFactory pf1 = new ProxyFactory(target1);
342 		NopInterceptor di1 = new NopInterceptor();
343 		pf1.addAdvice(0, di1);
344 		ITestBean advised1 = (ITestBean) createProxy(pf1);
345 		advised1.setAge(age1); // = 1 invocation
346 		advised1.setSpouse(advised1); // = 2 invocations
347 
348 		assertEquals("one was invoked correct number of times", 2, di1.getCount());
349 
350 		assertEquals("Advised one has correct age", age1, advised1.getAge()); // = 3 invocations
351 		assertEquals("one was invoked correct number of times", 3, di1.getCount());
352 
353 		// = 5 invocations, as reentrant call to spouse is advised also
354 		assertEquals("Advised spouse has correct age", age1, advised1.getSpouse().getAge());
355 
356 		assertEquals("one was invoked correct number of times", 5, di1.getCount());
357 	}
358 
359 	@Test
360 	public void testTargetCanGetProxy() {
361 		NopInterceptor di = new NopInterceptor();
362 		INeedsToSeeProxy target = new TargetChecker();
363 		ProxyFactory proxyFactory = new ProxyFactory(target);
364 		proxyFactory.setExposeProxy(true);
365 		assertTrue(proxyFactory.isExposeProxy());
366 
367 		proxyFactory.addAdvice(0, di);
368 		INeedsToSeeProxy proxied = (INeedsToSeeProxy) createProxy(proxyFactory);
369 		assertEquals(0, di.getCount());
370 		assertEquals(0, target.getCount());
371 		proxied.incrementViaThis();
372 		assertEquals("Increment happened", 1, target.getCount());
373 
374 		assertEquals("Only one invocation via AOP as use of this wasn't proxied", 1, di.getCount());
375 		// 1 invocation
376 		assertEquals("Increment happened", 1, proxied.getCount());
377 		proxied.incrementViaProxy(); // 2 invoocations
378 		assertEquals("Increment happened", 2, target.getCount());
379 		assertEquals("3 more invocations via AOP as the first call was reentrant through the proxy", 4, di.getCount());
380 	}
381 
382 
383 	@Test
384 	public void testTargetCantGetProxyByDefault() {
385 		NeedsToSeeProxy et = new NeedsToSeeProxy();
386 		ProxyFactory pf1 = new ProxyFactory(et);
387 		assertFalse(pf1.isExposeProxy());
388 		INeedsToSeeProxy proxied = (INeedsToSeeProxy) createProxy(pf1);
389 		try {
390 			proxied.incrementViaProxy();
391 			fail("Should have failed to get proxy as exposeProxy wasn't set to true");
392 		}
393 		catch (IllegalStateException ex) {
394 			// Ok
395 		}
396 	}
397 
398 	@Test
399 	public void testContext() throws Throwable {
400 		testContext(true);
401 	}
402 
403 	@Test
404 	public void testNoContext() throws Throwable {
405 		testContext(false);
406 	}
407 
408 	/**
409 	 * @param context if true, want context
410 	 */
411 	private void testContext(final boolean context) throws Throwable {
412 		final String s = "foo";
413 		// Test return value
414 		MethodInterceptor mi = new MethodInterceptor() {
415 			@Override
416 			public Object invoke(MethodInvocation invocation) throws Throwable {
417 				if (!context) {
418 					assertNoInvocationContext();
419 				} else {
420 					assertTrue("have context", ExposeInvocationInterceptor.currentInvocation() != null);
421 				}
422 				return s;
423 			}
424 		};
425 		AdvisedSupport pc = new AdvisedSupport(new Class<?>[] {ITestBean.class});
426 		if (context) {
427 			pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
428 		}
429 		pc.addAdvice(mi);
430 		// Keep CGLIB happy
431 		if (requiresTarget()) {
432 			pc.setTarget(new TestBean());
433 		}
434 		AopProxy aop = createAopProxy(pc);
435 
436 		assertNoInvocationContext();
437 		ITestBean tb = (ITestBean) aop.getProxy();
438 		assertNoInvocationContext();
439 		assertTrue("correct return value", tb.getName() == s);
440 	}
441 
442 	/**
443 	 * Test that the proxy returns itself when the
444 	 * target returns {@code this}
445 	 */
446 	@Test
447 	public void testTargetReturnsThis() throws Throwable {
448 		// Test return value
449 		TestBean raw = new OwnSpouse();
450 
451 		ProxyCreatorSupport pc = new ProxyCreatorSupport();
452 		pc.setInterfaces(new Class<?>[] {ITestBean.class});
453 		pc.setTarget(raw);
454 
455 		ITestBean tb = (ITestBean) createProxy(pc);
456 		assertTrue("this return is wrapped in proxy", tb.getSpouse() == tb);
457 	}
458 
459 	@Test
460 	public void testDeclaredException() throws Throwable {
461 		final Exception expectedException = new Exception();
462 		// Test return value
463 		MethodInterceptor mi = new MethodInterceptor() {
464 			@Override
465 			public Object invoke(MethodInvocation invocation) throws Throwable {
466 				throw expectedException;
467 			}
468 		};
469 		AdvisedSupport pc = new AdvisedSupport(new Class<?>[] {ITestBean.class});
470 		pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
471 		pc.addAdvice(mi);
472 
473 		// We don't care about the object
474 		mockTargetSource.setTarget(new Object());
475 		pc.setTargetSource(mockTargetSource);
476 		AopProxy aop = createAopProxy(pc);
477 
478 		try {
479 			ITestBean tb = (ITestBean) aop.getProxy();
480 			// Note: exception param below isn't used
481 			tb.exceptional(expectedException);
482 			fail("Should have thrown exception raised by interceptor");
483 		}
484 		catch (Exception thrown) {
485 			assertEquals("exception matches", expectedException, thrown);
486 		}
487 	}
488 
489 	/**
490 	 * An interceptor throws a checked exception not on the method signature.
491 	 * For efficiency, we don't bother unifying java.lang.reflect and
492 	 * org.springframework.cglib UndeclaredThrowableException
493 	 */
494 	@Test
495 	public void testUndeclaredCheckedException() throws Throwable {
496 		final Exception unexpectedException = new Exception();
497 		// Test return value
498 		MethodInterceptor mi = new MethodInterceptor() {
499 			@Override
500 			public Object invoke(MethodInvocation invocation) throws Throwable {
501 				throw unexpectedException;
502 			}
503 		};
504 		AdvisedSupport pc = new AdvisedSupport(new Class<?>[] {ITestBean.class});
505 		pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
506 		pc.addAdvice(mi);
507 
508 		// We don't care about the object
509 		pc.setTarget(new TestBean());
510 		AopProxy aop = createAopProxy(pc);
511 		ITestBean tb = (ITestBean) aop.getProxy();
512 
513 		try {
514 			// Note: exception param below isn't used
515 			tb.getAge();
516 			fail("Should have wrapped exception raised by interceptor");
517 		}
518 		catch (UndeclaredThrowableException thrown) {
519 			assertEquals("exception matches", unexpectedException, thrown.getUndeclaredThrowable());
520 		}
521 		catch (Exception ex) {
522 			ex.printStackTrace();
523 			fail("Didn't expect exception: " + ex);
524 		}
525 	}
526 
527 	@Test
528 	public void testUndeclaredUnheckedException() throws Throwable {
529 		final RuntimeException unexpectedException = new RuntimeException();
530 		// Test return value
531 		MethodInterceptor mi = new MethodInterceptor() {
532 			@Override
533 			public Object invoke(MethodInvocation invocation) throws Throwable {
534 				throw unexpectedException;
535 			}
536 		};
537 		AdvisedSupport pc = new AdvisedSupport(new Class<?>[] {ITestBean.class});
538 		pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
539 		pc.addAdvice(mi);
540 
541 		// We don't care about the object
542 		pc.setTarget(new TestBean());
543 		AopProxy aop = createAopProxy(pc);
544 		ITestBean tb = (ITestBean) aop.getProxy();
545 
546 		try {
547 			// Note: exception param below isn't used
548 			tb.getAge();
549 			fail("Should have wrapped exception raised by interceptor");
550 		}
551 		catch (RuntimeException thrown) {
552 			assertEquals("exception matches", unexpectedException, thrown);
553 		}
554 	}
555 
556 	/**
557 	 * Check that although a method is eligible for advice chain optimization and
558 	 * direct reflective invocation, it doesn't happen if we've asked to see the proxy,
559 	 * so as to guarantee a consistent programming model.
560 	 * @throws Throwable
561 	 */
562 	@Test
563 	public void testTargetCanGetInvocationEvenIfNoAdviceChain() throws Throwable {
564 		NeedsToSeeProxy target = new NeedsToSeeProxy();
565 		AdvisedSupport pc = new AdvisedSupport(new Class<?>[] {INeedsToSeeProxy.class});
566 		pc.setTarget(target);
567 		pc.setExposeProxy(true);
568 
569 		// Now let's try it with the special target
570 		AopProxy aop = createAopProxy(pc);
571 		INeedsToSeeProxy proxied = (INeedsToSeeProxy) aop.getProxy();
572 		// It will complain if it can't get the proxy
573 		proxied.incrementViaProxy();
574 	}
575 
576 	@Test
577 	public void testTargetCanGetInvocation() throws Throwable {
578 		final InvocationCheckExposedInvocationTestBean expectedTarget = new InvocationCheckExposedInvocationTestBean();
579 
580 		AdvisedSupport pc = new AdvisedSupport(new Class<?>[] {ITestBean.class, IOther.class});
581 		pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
582 		TrapTargetInterceptor tii = new TrapTargetInterceptor() {
583 			@Override
584 			public Object invoke(MethodInvocation invocation) throws Throwable {
585 				// Assert that target matches BEFORE invocation returns
586 				assertEquals("Target is correct", expectedTarget, invocation.getThis());
587 				return super.invoke(invocation);
588 			}
589 		};
590 		pc.addAdvice(tii);
591 		pc.setTarget(expectedTarget);
592 		AopProxy aop = createAopProxy(pc);
593 
594 		ITestBean tb = (ITestBean) aop.getProxy();
595 		tb.getName();
596 		// Not safe to trap invocation
597 		//assertTrue(tii.invocation == target.invocation);
598 
599 		//assertTrue(target.invocation.getProxy() == tb);
600 
601 	//	((IOther) tb).absquatulate();
602 		//MethodInvocation minv =  tii.invocation;
603 		//assertTrue("invoked on iother, not " + minv.getMethod().getDeclaringClass(), minv.getMethod().getDeclaringClass() == IOther.class);
604 		//assertTrue(target.invocation == tii.invocation);
605 	}
606 
607 	/**
608 	 * Throw an exception if there is an Invocation.
609 	 */
610 	private void assertNoInvocationContext() {
611 		try {
612 			ExposeInvocationInterceptor.currentInvocation();
613 			fail("Expected no invocation context");
614 		}
615 		catch (IllegalStateException ex) {
616 			// ok
617 		}
618 	}
619 
620 	/**
621 	 * Test stateful interceptor
622 	 */
623 	@Test
624 	public void testMixinWithIntroductionAdvisor() throws Throwable {
625 		TestBean tb = new TestBean();
626 		ProxyFactory pc = new ProxyFactory(new Class<?>[] {ITestBean.class});
627 		pc.addAdvisor(new LockMixinAdvisor());
628 		pc.setTarget(tb);
629 
630 		testTestBeanIntroduction(pc);
631 	}
632 
633 	@Test
634 	public void testMixinWithIntroductionInfo() throws Throwable {
635 		TestBean tb = new TestBean();
636 		ProxyFactory pc = new ProxyFactory(new Class<?>[] {ITestBean.class});
637 		// We don't use an IntroductionAdvisor, we can just add an advice that implements IntroductionInfo
638 		pc.addAdvice(new LockMixin());
639 		pc.setTarget(tb);
640 
641 		testTestBeanIntroduction(pc);
642 	}
643 
644 	private void testTestBeanIntroduction(ProxyFactory pc) {
645 		int newAge = 65;
646 		ITestBean itb = (ITestBean) createProxy(pc);
647 		itb.setAge(newAge);
648 		assertTrue(itb.getAge() == newAge);
649 
650 		Lockable lockable = (Lockable) itb;
651 		assertFalse(lockable.locked());
652 		lockable.lock();
653 
654 		assertTrue(itb.getAge() == newAge);
655 		try {
656 			itb.setAge(1);
657 			fail("Setters should fail when locked");
658 		}
659 		catch (LockedException ex) {
660 			// ok
661 		}
662 		assertTrue(itb.getAge() == newAge);
663 
664 		// Unlock
665 		assertTrue(lockable.locked());
666 		lockable.unlock();
667 		itb.setAge(1);
668 		assertTrue(itb.getAge() == 1);
669 	}
670 
671 
672 	@Test
673 	public void testReplaceArgument() throws Throwable {
674 		TestBean tb = new TestBean();
675 		ProxyFactory pc = new ProxyFactory(new Class<?>[] {ITestBean.class});
676 		pc.setTarget(tb);
677 		pc.addAdvisor(new StringSetterNullReplacementAdvice());
678 
679 		ITestBean t = (ITestBean) pc.getProxy();
680 		int newAge = 5;
681 		t.setAge(newAge);
682 		assertTrue(t.getAge() == newAge);
683 		String newName = "greg";
684 		t.setName(newName);
685 		assertEquals(newName, t.getName());
686 
687 		t.setName(null);
688 		// Null replacement magic should work
689 		assertTrue(t.getName().equals(""));
690 	}
691 
692 	@Test
693 	public void testCanCastProxyToProxyConfig() throws Throwable {
694 		TestBean tb = new TestBean();
695 		ProxyFactory pc = new ProxyFactory(tb);
696 		NopInterceptor di = new NopInterceptor();
697 		pc.addAdvice(0, di);
698 
699 		ITestBean t = (ITestBean) createProxy(pc);
700 		assertEquals(0, di.getCount());
701 		t.setAge(23);
702 		assertEquals(23, t.getAge());
703 		assertEquals(2, di.getCount());
704 
705 		Advised advised = (Advised) t;
706 		assertEquals("Have 1 advisor", 1, advised.getAdvisors().length);
707 		assertEquals(di, advised.getAdvisors()[0].getAdvice());
708 		NopInterceptor di2 = new NopInterceptor();
709 		advised.addAdvice(1, di2);
710 		t.getName();
711 		assertEquals(3, di.getCount());
712 		assertEquals(1, di2.getCount());
713 		// will remove di
714 		advised.removeAdvisor(0);
715 		t.getAge();
716 		// Unchanged
717 		assertEquals(3, di.getCount());
718 		assertEquals(2, di2.getCount());
719 
720 		CountingBeforeAdvice cba = new CountingBeforeAdvice();
721 		assertEquals(0, cba.getCalls());
722 		advised.addAdvice(cba);
723 		t.setAge(16);
724 		assertEquals(16, t.getAge());
725 		assertEquals(2, cba.getCalls());
726 	}
727 
728 	@Test
729 	public void testAdviceImplementsIntroductionInfo() throws Throwable {
730 		TestBean tb = new TestBean();
731 		String name = "tony";
732 		tb.setName(name);
733 		ProxyFactory pc = new ProxyFactory(tb);
734 		NopInterceptor di = new NopInterceptor();
735 		pc.addAdvice(di);
736 		final long ts = 37;
737 		pc.addAdvice(new DelegatingIntroductionInterceptor(new TimeStamped() {
738 			@Override
739 			public long getTimeStamp() {
740 				return ts;
741 			}
742 		}));
743 
744 		ITestBean proxied = (ITestBean) createProxy(pc);
745 		assertEquals(name, proxied.getName());
746 		TimeStamped intro = (TimeStamped) proxied;
747 		assertEquals(ts, intro.getTimeStamp());
748 	}
749 
750 	@Test
751 	public void testCannotAddDynamicIntroductionAdviceExceptInIntroductionAdvice() throws Throwable {
752 		TestBean target = new TestBean();
753 		target.setAge(21);
754 		ProxyFactory pc = new ProxyFactory(target);
755 		try {
756 			pc.addAdvice(new DummyIntroductionAdviceImpl());
757 			fail("Shouldn't be able to add introduction interceptor except via introduction advice");
758 		}
759 		catch (AopConfigException ex) {
760 			assertTrue(ex.getMessage().indexOf("ntroduction") > -1);
761 		}
762 		// Check it still works: proxy factory state shouldn't have been corrupted
763 		ITestBean proxied = (ITestBean) createProxy(pc);
764 		assertEquals(target.getAge(), proxied.getAge());
765 	}
766 
767 	@Test
768 	public void testRejectsBogusDynamicIntroductionAdviceWithNoAdapter() throws Throwable {
769 		TestBean target = new TestBean();
770 		target.setAge(21);
771 		ProxyFactory pc = new ProxyFactory(target);
772 		pc.addAdvisor(new DefaultIntroductionAdvisor(new DummyIntroductionAdviceImpl(), Comparable.class));
773 		try {
774 			// TODO May fail on either call: may want to tighten up definition
775 			ITestBean proxied = (ITestBean) createProxy(pc);
776 			proxied.getName();
777 			fail("Bogus introduction");
778 		}
779 		catch (Exception ex) {
780 			// TODO used to catch UnknownAdviceTypeException, but
781 			// with CGLIB some errors are in proxy creation and are wrapped
782 			// in aspect exception. Error message is still fine.
783 			//assertTrue(ex.getMessage().indexOf("ntroduction") > -1);
784 		}
785 	}
786 
787 	/**
788 	 * Check that the introduction advice isn't allowed to introduce interfaces
789 	 * that are unsupported by the IntroductionInterceptor.
790 	 */
791 	@Test
792 	public void testCannotAddIntroductionAdviceWithUnimplementedInterface() throws Throwable {
793 		TestBean target = new TestBean();
794 		target.setAge(21);
795 		ProxyFactory pc = new ProxyFactory(target);
796 		try {
797 			pc.addAdvisor(0, new DefaultIntroductionAdvisor(new TimestampIntroductionInterceptor(), ITestBean.class));
798 			fail("Shouldn't be able to add introduction advice introducing an unimplemented interface");
799 		}
800 		catch (IllegalArgumentException ex) {
801 			//assertTrue(ex.getMessage().indexOf("ntroduction") > -1);
802 		}
803 		// Check it still works: proxy factory state shouldn't have been corrupted
804 		ITestBean proxied = (ITestBean) createProxy(pc);
805 		assertEquals(target.getAge(), proxied.getAge());
806 	}
807 
808 	/**
809 	 * Note that an introduction can't throw an unexpected checked exception,
810 	 * as it's constained by the interface.
811 	 */
812 	@Test
813 	public void testIntroductionThrowsUncheckedException() throws Throwable {
814 		TestBean target = new TestBean();
815 		target.setAge(21);
816 		ProxyFactory pc = new ProxyFactory(target);
817 
818 		@SuppressWarnings("serial")
819 		class MyDi extends DelegatingIntroductionInterceptor implements TimeStamped {
820 			/**
821 			 * @see test.util.TimeStamped#getTimeStamp()
822 			 */
823 			@Override
824 			public long getTimeStamp() {
825 				throw new UnsupportedOperationException();
826 			}
827 		}
828 		pc.addAdvisor(new DefaultIntroductionAdvisor(new MyDi()));
829 
830 		TimeStamped ts = (TimeStamped) createProxy(pc);
831 		try {
832 			ts.getTimeStamp();
833 			fail("Should throw UnsupportedOperationException");
834 		}
835 		catch (UnsupportedOperationException ex) {
836 		}
837 	}
838 
839 	/**
840 	 * Should only be able to introduce interfaces, not classes.
841 	 */
842 	@Test
843 	public void testCannotAddIntroductionAdviceToIntroduceClass() throws Throwable {
844 		TestBean target = new TestBean();
845 		target.setAge(21);
846 		ProxyFactory pc = new ProxyFactory(target);
847 		try {
848 			pc.addAdvisor(0, new DefaultIntroductionAdvisor(new TimestampIntroductionInterceptor(), TestBean.class));
849 			fail("Shouldn't be able to add introduction advice that introduces a class, rather than an interface");
850 		}
851 		catch (IllegalArgumentException ex) {
852 			assertTrue(ex.getMessage().indexOf("interface") > -1);
853 		}
854 		// Check it still works: proxy factory state shouldn't have been corrupted
855 		ITestBean proxied = (ITestBean) createProxy(pc);
856 		assertEquals(target.getAge(), proxied.getAge());
857 	}
858 
859 	@Test
860 	public void testCannotAddInterceptorWhenFrozen() throws Throwable {
861 		TestBean target = new TestBean();
862 		target.setAge(21);
863 		ProxyFactory pc = new ProxyFactory(target);
864 		assertFalse(pc.isFrozen());
865 		pc.addAdvice(new NopInterceptor());
866 		ITestBean proxied = (ITestBean) createProxy(pc);
867 		pc.setFrozen(true);
868 		try {
869 			pc.addAdvice(0, new NopInterceptor());
870 			fail("Shouldn't be able to add interceptor when frozen");
871 		}
872 		catch (AopConfigException ex) {
873 			assertTrue(ex.getMessage().indexOf("frozen") > -1);
874 		}
875 		// Check it still works: proxy factory state shouldn't have been corrupted
876 		assertEquals(target.getAge(), proxied.getAge());
877 		assertEquals(1, ((Advised) proxied).getAdvisors().length);
878 	}
879 
880 	/**
881 	 * Check that casting to Advised can't get around advice freeze.
882 	 */
883 	@Test
884 	public void testCannotAddAdvisorWhenFrozenUsingCast() throws Throwable {
885 		TestBean target = new TestBean();
886 		target.setAge(21);
887 		ProxyFactory pc = new ProxyFactory(target);
888 		assertFalse(pc.isFrozen());
889 		pc.addAdvice(new NopInterceptor());
890 		ITestBean proxied = (ITestBean) createProxy(pc);
891 		pc.setFrozen(true);
892 		Advised advised = (Advised) proxied;
893 
894 		assertTrue(pc.isFrozen());
895 		try {
896 			advised.addAdvisor(new DefaultPointcutAdvisor(new NopInterceptor()));
897 			fail("Shouldn't be able to add Advisor when frozen");
898 		}
899 		catch (AopConfigException ex) {
900 			assertTrue(ex.getMessage().indexOf("frozen") > -1);
901 		}
902 		// Check it still works: proxy factory state shouldn't have been corrupted
903 		assertEquals(target.getAge(), proxied.getAge());
904 		assertEquals(1, advised.getAdvisors().length);
905 	}
906 
907 	@Test
908 	public void testCannotRemoveAdvisorWhenFrozen() throws Throwable {
909 		TestBean target = new TestBean();
910 		target.setAge(21);
911 		ProxyFactory pc = new ProxyFactory(target);
912 		assertFalse(pc.isFrozen());
913 		pc.addAdvice(new NopInterceptor());
914 		ITestBean proxied = (ITestBean) createProxy(pc);
915 		pc.setFrozen(true);
916 		Advised advised = (Advised) proxied;
917 
918 		assertTrue(pc.isFrozen());
919 		try {
920 			advised.removeAdvisor(0);
921 			fail("Shouldn't be able to remove Advisor when frozen");
922 		}
923 		catch (AopConfigException ex) {
924 			assertTrue(ex.getMessage().indexOf("frozen") > -1);
925 		}
926 		// Didn't get removed
927 		assertEquals(1, advised.getAdvisors().length);
928 		pc.setFrozen(false);
929 		// Can now remove it
930 		advised.removeAdvisor(0);
931 		// Check it still works: proxy factory state shouldn't have been corrupted
932 		assertEquals(target.getAge(), proxied.getAge());
933 		assertEquals(0, advised.getAdvisors().length);
934 	}
935 
936 	@Test
937 	public void testUseAsHashKey() {
938 		TestBean target1 = new TestBean();
939 		ProxyFactory pf1 = new ProxyFactory(target1);
940 		pf1.addAdvice(new NopInterceptor());
941 		ITestBean proxy1 = (ITestBean) createProxy(pf1);
942 
943 		TestBean target2 = new TestBean();
944 		ProxyFactory pf2 = new ProxyFactory(target2);
945 		pf2.addAdvisor(new DefaultIntroductionAdvisor(new TimestampIntroductionInterceptor()));
946 		ITestBean proxy2 = (ITestBean) createProxy(pf2);
947 
948 		HashMap<ITestBean, Object> h = new HashMap<ITestBean, Object>();
949 		Object value1 = "foo";
950 		Object value2 = "bar";
951 		assertNull(h.get(proxy1));
952 		h.put(proxy1, value1);
953 		h.put(proxy2, value2);
954 		assertEquals(h.get(proxy1), value1);
955 		assertEquals(h.get(proxy2), value2);
956 	}
957 
958 	/**
959 	 * Check that the string is informative.
960 	 */
961 	@Test
962 	public void testProxyConfigString() {
963 		TestBean target = new TestBean();
964 		ProxyFactory pc = new ProxyFactory(target);
965 		pc.setInterfaces(new Class<?>[] {ITestBean.class});
966 		pc.addAdvice(new NopInterceptor());
967 		MethodBeforeAdvice mba = new CountingBeforeAdvice();
968 		Advisor advisor = new DefaultPointcutAdvisor(new NameMatchMethodPointcut(), mba);
969 		pc.addAdvisor(advisor);
970 		ITestBean proxied = (ITestBean) createProxy(pc);
971 
972 		String proxyConfigString = ((Advised) proxied).toProxyConfigString();
973 		assertTrue(proxyConfigString.indexOf(advisor.toString()) != -1);
974 		assertTrue(proxyConfigString.indexOf("1 interface") != -1);
975 	}
976 
977 	@Test
978 	public void testCanPreventCastToAdvisedUsingOpaque() {
979 		TestBean target = new TestBean();
980 		ProxyFactory pc = new ProxyFactory(target);
981 		pc.setInterfaces(new Class<?>[] {ITestBean.class});
982 		pc.addAdvice(new NopInterceptor());
983 		CountingBeforeAdvice mba = new CountingBeforeAdvice();
984 		Advisor advisor = new DefaultPointcutAdvisor(new NameMatchMethodPointcut().addMethodName("setAge"), mba);
985 		pc.addAdvisor(advisor);
986 		assertFalse("Opaque defaults to false", pc.isOpaque());
987 		pc.setOpaque(true);
988 		assertTrue("Opaque now true for this config", pc.isOpaque());
989 		ITestBean proxied = (ITestBean) createProxy(pc);
990 		proxied.setAge(10);
991 		assertEquals(10, proxied.getAge());
992 		assertEquals(1, mba.getCalls());
993 
994 		assertFalse("Cannot be cast to Advised", proxied instanceof Advised);
995 	}
996 
997 	@Test
998 	public void testAdviceSupportListeners() throws Throwable {
999 		TestBean target = new TestBean();
1000 		target.setAge(21);
1001 
1002 		ProxyFactory pc = new ProxyFactory(target);
1003 		CountingAdvisorListener l = new CountingAdvisorListener(pc);
1004 		pc.addListener(l);
1005 		RefreshCountingAdvisorChainFactory acf = new RefreshCountingAdvisorChainFactory();
1006 		// Should be automatically added as a listener
1007 		pc.addListener(acf);
1008 		assertFalse(pc.isActive());
1009 		assertEquals(0, l.activates);
1010 		assertEquals(0, acf.refreshes);
1011 		ITestBean proxied = (ITestBean) createProxy(pc);
1012 		assertEquals(1, acf.refreshes);
1013 		assertEquals(1, l.activates);
1014 		assertTrue(pc.isActive());
1015 		assertEquals(target.getAge(), proxied.getAge());
1016 		assertEquals(0, l.adviceChanges);
1017 		NopInterceptor di = new NopInterceptor();
1018 		pc.addAdvice(0, di);
1019 		assertEquals(1, l.adviceChanges);
1020 		assertEquals(2, acf.refreshes);
1021 		assertEquals(target.getAge(), proxied.getAge());
1022 		pc.removeAdvice(di);
1023 		assertEquals(2, l.adviceChanges);
1024 		assertEquals(3, acf.refreshes);
1025 		assertEquals(target.getAge(), proxied.getAge());
1026 		pc.getProxy();
1027 		assertEquals(1, l.activates);
1028 
1029 		pc.removeListener(l);
1030 		assertEquals(2, l.adviceChanges);
1031 		pc.addAdvisor(new DefaultPointcutAdvisor(new NopInterceptor()));
1032 		// No longer counting
1033 		assertEquals(2, l.adviceChanges);
1034 	}
1035 
1036 	@Test
1037 	public void testExistingProxyChangesTarget() throws Throwable {
1038 		TestBean tb1 = new TestBean();
1039 		tb1.setAge(33);
1040 
1041 		TestBean tb2 = new TestBean();
1042 		tb2.setAge(26);
1043 		tb2.setName("Juergen");
1044 		TestBean tb3 = new TestBean();
1045 		tb3.setAge(37);
1046 		ProxyFactory pc = new ProxyFactory(tb1);
1047 		NopInterceptor nop = new NopInterceptor();
1048 		pc.addAdvice(nop);
1049 		ITestBean proxy = (ITestBean) createProxy(pc);
1050 		assertEquals(nop.getCount(), 0);
1051 		assertEquals(tb1.getAge(), proxy.getAge());
1052 		assertEquals(nop.getCount(), 1);
1053 		// Change to a new static target
1054 		pc.setTarget(tb2);
1055 		assertEquals(tb2.getAge(), proxy.getAge());
1056 		assertEquals(nop.getCount(), 2);
1057 
1058 		// Change to a new dynamic target
1059 		HotSwappableTargetSource hts = new HotSwappableTargetSource(tb3);
1060 		pc.setTargetSource(hts);
1061 		assertEquals(tb3.getAge(), proxy.getAge());
1062 		assertEquals(nop.getCount(), 3);
1063 		hts.swap(tb1);
1064 		assertEquals(tb1.getAge(), proxy.getAge());
1065 		tb1.setName("Colin");
1066 		assertEquals(tb1.getName(), proxy.getName());
1067 		assertEquals(nop.getCount(), 5);
1068 
1069 		// Change back, relying on casting to Advised
1070 		Advised advised = (Advised) proxy;
1071 		assertSame(hts, advised.getTargetSource());
1072 		SingletonTargetSource sts = new SingletonTargetSource(tb2);
1073 		advised.setTargetSource(sts);
1074 		assertEquals(tb2.getName(), proxy.getName());
1075 		assertSame(sts, advised.getTargetSource());
1076 		assertEquals(tb2.getAge(), proxy.getAge());
1077 	}
1078 
1079 	@Test
1080 	public void testDynamicMethodPointcutThatAlwaysAppliesStatically() throws Throwable {
1081 		TestBean tb = new TestBean();
1082 		ProxyFactory pc = new ProxyFactory(new Class<?>[] {ITestBean.class});
1083 		TestDynamicPointcutAdvice dp = new TestDynamicPointcutAdvice(new NopInterceptor(), "getAge");
1084 		pc.addAdvisor(dp);
1085 		pc.setTarget(tb);
1086 		ITestBean it = (ITestBean) createProxy(pc);
1087 		assertEquals(dp.count, 0);
1088 		it.getAge();
1089 		assertEquals(dp.count, 1);
1090 		it.setAge(11);
1091 		assertEquals(it.getAge(), 11);
1092 		assertEquals(dp.count, 2);
1093 	}
1094 
1095 	@Test
1096 	public void testDynamicMethodPointcutThatAppliesStaticallyOnlyToSetters() throws Throwable {
1097 		TestBean tb = new TestBean();
1098 		ProxyFactory pc = new ProxyFactory(new Class<?>[] {ITestBean.class});
1099 		// Could apply dynamically to getAge/setAge but not to getName
1100 		TestDynamicPointcutForSettersOnly dp = new TestDynamicPointcutForSettersOnly(new NopInterceptor(), "Age");
1101 		pc.addAdvisor(dp);
1102 		this.mockTargetSource.setTarget(tb);
1103 		pc.setTargetSource(mockTargetSource);
1104 		ITestBean it = (ITestBean) createProxy(pc);
1105 		assertEquals(dp.count, 0);
1106 		it.getAge();
1107 		// Statically vetoed
1108 		assertEquals(0, dp.count);
1109 		it.setAge(11);
1110 		assertEquals(it.getAge(), 11);
1111 		assertEquals(dp.count, 1);
1112 		// Applies statically but not dynamically
1113 		it.setName("joe");
1114 		assertEquals(dp.count, 1);
1115 	}
1116 
1117 	@Test
1118 	public void testStaticMethodPointcut() throws Throwable {
1119 		TestBean tb = new TestBean();
1120 		ProxyFactory pc = new ProxyFactory(new Class<?>[] {ITestBean.class});
1121 		NopInterceptor di = new NopInterceptor();
1122 		TestStaticPointcutAdvice sp = new TestStaticPointcutAdvice(di, "getAge");
1123 		pc.addAdvisor(sp);
1124 		pc.setTarget(tb);
1125 		ITestBean it = (ITestBean) createProxy(pc);
1126 		assertEquals(di.getCount(), 0);
1127 		it.getAge();
1128 		assertEquals(di.getCount(), 1);
1129 		it.setAge(11);
1130 		assertEquals(it.getAge(), 11);
1131 		assertEquals(di.getCount(), 2);
1132 	}
1133 
1134 	/**
1135 	 * There are times when we want to call proceed() twice.
1136 	 * We can do this if we clone the invocation.
1137 	 */
1138 	@Test
1139 	public void testCloneInvocationToProceedThreeTimes() throws Throwable {
1140 		TestBean tb = new TestBean();
1141 		ProxyFactory pc = new ProxyFactory(tb);
1142 		pc.addInterface(ITestBean.class);
1143 
1144 		MethodInterceptor twoBirthdayInterceptor = new MethodInterceptor() {
1145 			@Override
1146 			public Object invoke(MethodInvocation mi) throws Throwable {
1147 				// Clone the invocation to proceed three times
1148 				// "The Moor's Last Sigh": this technology can cause premature aging
1149 				MethodInvocation clone1 = ((ReflectiveMethodInvocation) mi).invocableClone();
1150 				MethodInvocation clone2 = ((ReflectiveMethodInvocation) mi).invocableClone();
1151 				clone1.proceed();
1152 				clone2.proceed();
1153 				return mi.proceed();
1154 			}
1155 		};
1156 		@SuppressWarnings("serial")
1157 		StaticMethodMatcherPointcutAdvisor advisor = new StaticMethodMatcherPointcutAdvisor(twoBirthdayInterceptor) {
1158 			@Override
1159 			public boolean matches(Method m, Class<?> targetClass) {
1160 				return "haveBirthday".equals(m.getName());
1161 			}
1162 		};
1163 		pc.addAdvisor(advisor);
1164 		ITestBean it = (ITestBean) createProxy(pc);
1165 
1166 		final int age = 20;
1167 		it.setAge(age);
1168 		assertEquals(age, it.getAge());
1169 		// Should return the age before the third, AOP-induced birthday
1170 		assertEquals(age + 2, it.haveBirthday());
1171 		// Return the final age produced by 3 birthdays
1172 		assertEquals(age + 3, it.getAge());
1173 	}
1174 
1175 	/**
1176 	 * We want to change the arguments on a clone: it shouldn't affect the original.
1177 	 */
1178 	@Test
1179 	public void testCanChangeArgumentsIndependentlyOnClonedInvocation() throws Throwable {
1180 		TestBean tb = new TestBean();
1181 		ProxyFactory pc = new ProxyFactory(tb);
1182 		pc.addInterface(ITestBean.class);
1183 
1184 		/**
1185 		 * Changes the name, then changes it back.
1186 		 */
1187 		MethodInterceptor nameReverter = new MethodInterceptor() {
1188 			@Override
1189 			public Object invoke(MethodInvocation mi) throws Throwable {
1190 				MethodInvocation clone = ((ReflectiveMethodInvocation) mi).invocableClone();
1191 				String oldName = ((ITestBean) mi.getThis()).getName();
1192 				clone.getArguments()[0] = oldName;
1193 				// Original method invocation should be unaffected by changes to argument list of clone
1194 				mi.proceed();
1195 				return clone.proceed();
1196 			}
1197 		};
1198 
1199 		class NameSaver implements MethodInterceptor {
1200 			private List<Object> names = new LinkedList<Object>();
1201 
1202 			@Override
1203 			public Object invoke(MethodInvocation mi) throws Throwable {
1204 				names.add(mi.getArguments()[0]);
1205 				return mi.proceed();
1206 			}
1207 		}
1208 
1209 		NameSaver saver = new NameSaver();
1210 
1211 		pc.addAdvisor(new DefaultPointcutAdvisor(Pointcuts.SETTERS, nameReverter));
1212 		pc.addAdvisor(new DefaultPointcutAdvisor(Pointcuts.SETTERS, saver));
1213 		ITestBean it = (ITestBean) createProxy(pc);
1214 
1215 		String name1 = "tony";
1216 		String name2 = "gordon";
1217 
1218 		tb.setName(name1);
1219 		assertEquals(name1, tb.getName());
1220 
1221 		it.setName(name2);
1222 		// NameReverter saved it back
1223 		assertEquals(name1, it.getName());
1224 		assertEquals(2, saver.names.size());
1225 		assertEquals(name2, saver.names.get(0));
1226 		assertEquals(name1, saver.names.get(1));
1227 	}
1228 
1229 	@SuppressWarnings("serial")
1230 	@Test
1231 	public void testOverloadedMethodsWithDifferentAdvice() throws Throwable {
1232 		Overloads target = new Overloads();
1233 		ProxyFactory pc = new ProxyFactory(target);
1234 		NopInterceptor overLoadVoids = new NopInterceptor();
1235 		pc.addAdvisor(new StaticMethodMatcherPointcutAdvisor(overLoadVoids) {
1236 			@Override
1237 			public boolean matches(Method m, Class<?> targetClass) {
1238 				return m.getName().equals("overload") && m.getParameterTypes().length == 0;
1239 			}
1240 		});
1241 		NopInterceptor overLoadInts = new NopInterceptor();
1242 		pc.addAdvisor(new StaticMethodMatcherPointcutAdvisor(overLoadInts) {
1243 			@Override
1244 			public boolean matches(Method m, Class<?> targetClass) {
1245 				return m.getName().equals("overload") && m.getParameterTypes().length == 1 &&
1246 					m.getParameterTypes()[0].equals(int.class);
1247 			}
1248 		});
1249 
1250 		IOverloads proxy = (IOverloads) createProxy(pc);
1251 		assertEquals(0, overLoadInts.getCount());
1252 		assertEquals(0, overLoadVoids.getCount());
1253 		proxy.overload();
1254 		assertEquals(0, overLoadInts.getCount());
1255 		assertEquals(1, overLoadVoids.getCount());
1256 		assertEquals(25, proxy.overload(25));
1257 		assertEquals(1, overLoadInts.getCount());
1258 		assertEquals(1, overLoadVoids.getCount());
1259 		proxy.noAdvice();
1260 		assertEquals(1, overLoadInts.getCount());
1261 		assertEquals(1, overLoadVoids.getCount());
1262 	}
1263 
1264 	@Test
1265 	public void testProxyIsBoundBeforeTargetSourceInvoked() {
1266 		final TestBean target = new TestBean();
1267 		ProxyFactory pf = new ProxyFactory(target);
1268 		pf.addAdvice(new DebugInterceptor());
1269 		pf.setExposeProxy(true);
1270 		final ITestBean proxy = (ITestBean) createProxy(pf);
1271 		Advised config = (Advised) proxy;
1272 		// This class just checks proxy is bound before getTarget() call
1273 		config.setTargetSource(new TargetSource() {
1274 			@Override
1275 			public Class<?> getTargetClass() {
1276 				return TestBean.class;
1277 			}
1278 
1279 			@Override
1280 			public boolean isStatic() {
1281 				return false;
1282 			}
1283 
1284 			@Override
1285 			public Object getTarget() throws Exception {
1286 				assertEquals(proxy, AopContext.currentProxy());
1287 				return target;
1288 			}
1289 
1290 			@Override
1291 			public void releaseTarget(Object target) throws Exception {
1292 			}
1293 		});
1294 
1295 		// Just test anything: it will fail if context wasn't found
1296 		assertEquals(0, proxy.getAge());
1297 	}
1298 
1299 	@Test
1300 	public void testEquals() {
1301 		IOther a = new AllInstancesAreEqual();
1302 		IOther b = new AllInstancesAreEqual();
1303 		NopInterceptor i1 = new NopInterceptor();
1304 		NopInterceptor i2 = new NopInterceptor();
1305 		ProxyFactory pfa = new ProxyFactory(a);
1306 		pfa.addAdvice(i1);
1307 		ProxyFactory pfb = new ProxyFactory(b);
1308 		pfb.addAdvice(i2);
1309 		IOther proxyA = (IOther) createProxy(pfa);
1310 		IOther proxyB = (IOther) createProxy(pfb);
1311 
1312 		assertEquals(pfa.getAdvisors().length, pfb.getAdvisors().length);
1313 		assertTrue(a.equals(b));
1314 		assertTrue(i1.equals(i2));
1315 		assertTrue(proxyA.equals(proxyB));
1316 		assertEquals(proxyA.hashCode(), proxyB.hashCode());
1317 		assertFalse(proxyA.equals(a));
1318 
1319 		// Equality checks were handled by the proxy
1320 		assertEquals(0, i1.getCount());
1321 
1322 		// When we invoke A, it's NopInterceptor will have count == 1
1323 		// and won't think it's equal to B's NopInterceptor
1324 		proxyA.absquatulate();
1325 		assertEquals(1, i1.getCount());
1326 		assertFalse(proxyA.equals(proxyB));
1327 	}
1328 
1329 	@Test
1330 	public void testBeforeAdvisorIsInvoked() {
1331 		CountingBeforeAdvice cba = new CountingBeforeAdvice();
1332 		@SuppressWarnings("serial")
1333 		Advisor matchesNoArgs = new StaticMethodMatcherPointcutAdvisor(cba) {
1334 			@Override
1335 			public boolean matches(Method m, Class<?> targetClass) {
1336 				return m.getParameterTypes().length == 0;
1337 			}
1338 		};
1339 		TestBean target = new TestBean();
1340 		target.setAge(80);
1341 		ProxyFactory pf = new ProxyFactory(target);
1342 		pf.addAdvice(new NopInterceptor());
1343 		pf.addAdvisor(matchesNoArgs);
1344 		assertEquals("Advisor was added", matchesNoArgs, pf.getAdvisors()[1]);
1345 		ITestBean proxied = (ITestBean) createProxy(pf);
1346 		assertEquals(0, cba.getCalls());
1347 		assertEquals(0, cba.getCalls("getAge"));
1348 		assertEquals(target.getAge(), proxied.getAge());
1349 		assertEquals(1, cba.getCalls());
1350 		assertEquals(1, cba.getCalls("getAge"));
1351 		assertEquals(0, cba.getCalls("setAge"));
1352 		// Won't be advised
1353 		proxied.setAge(26);
1354 		assertEquals(1, cba.getCalls());
1355 		assertEquals(26, proxied.getAge());
1356 	}
1357 
1358 	@Test
1359 	public void testUserAttributes() throws Throwable {
1360 		class MapAwareMethodInterceptor implements MethodInterceptor {
1361 			private final Map<String, String> expectedValues;
1362 			private final Map<String, String> valuesToAdd;
1363 			public MapAwareMethodInterceptor(Map<String, String> expectedValues, Map<String, String> valuesToAdd) {
1364 				this.expectedValues = expectedValues;
1365 				this.valuesToAdd = valuesToAdd;
1366 			}
1367 			@Override
1368 			public Object invoke(MethodInvocation invocation) throws Throwable {
1369 				ReflectiveMethodInvocation rmi = (ReflectiveMethodInvocation) invocation;
1370 				for (Iterator<String> it = rmi.getUserAttributes().keySet().iterator(); it.hasNext(); ){
1371 					Object key = it.next();
1372 					assertEquals(expectedValues.get(key), rmi.getUserAttributes().get(key));
1373 				}
1374 				rmi.getUserAttributes().putAll(valuesToAdd);
1375 				return invocation.proceed();
1376 			}
1377 		};
1378 		AdvisedSupport pc = new AdvisedSupport(new Class<?>[] {ITestBean.class});
1379 		MapAwareMethodInterceptor mami1 = new MapAwareMethodInterceptor(new HashMap<String, String>(), new HashMap<String, String>());
1380 		Map<String, String> firstValuesToAdd = new HashMap<String, String>();
1381 		firstValuesToAdd.put("test", "");
1382 		MapAwareMethodInterceptor mami2 = new MapAwareMethodInterceptor(new HashMap<String, String>(), firstValuesToAdd);
1383 		MapAwareMethodInterceptor mami3 = new MapAwareMethodInterceptor(firstValuesToAdd, new HashMap<String, String>());
1384 		MapAwareMethodInterceptor mami4 = new MapAwareMethodInterceptor(firstValuesToAdd, new HashMap<String, String>());
1385 		Map<String, String> secondValuesToAdd = new HashMap<String, String>();
1386 		secondValuesToAdd.put("foo", "bar");
1387 		secondValuesToAdd.put("cat", "dog");
1388 		MapAwareMethodInterceptor mami5 = new MapAwareMethodInterceptor(firstValuesToAdd, secondValuesToAdd);
1389 		Map<String, String> finalExpected = new HashMap<String, String>(firstValuesToAdd);
1390 		finalExpected.putAll(secondValuesToAdd);
1391 		MapAwareMethodInterceptor mami6 = new MapAwareMethodInterceptor(finalExpected, secondValuesToAdd);
1392 
1393 		pc.addAdvice(mami1);
1394 		pc.addAdvice(mami2);
1395 		pc.addAdvice(mami3);
1396 		pc.addAdvice(mami4);
1397 		pc.addAdvice(mami5);
1398 		pc.addAdvice(mami6);
1399 
1400 		// We don't care about the object
1401 		pc.setTarget(new TestBean());
1402 		AopProxy aop = createAopProxy(pc);
1403 		ITestBean tb = (ITestBean) aop.getProxy();
1404 
1405 		String newName = "foo";
1406 		tb.setName(newName);
1407 		assertEquals(newName, tb.getName());
1408 	}
1409 
1410 	@Test
1411 	public void testMultiAdvice() throws Throwable {
1412 		CountingMultiAdvice cca = new CountingMultiAdvice();
1413 		@SuppressWarnings("serial")
1414 		Advisor matchesNoArgs = new StaticMethodMatcherPointcutAdvisor(cca) {
1415 			@Override
1416 			public boolean matches(Method m, Class<?> targetClass) {
1417 				return m.getParameterTypes().length == 0 || "exceptional".equals(m.getName());
1418 			}
1419 		};
1420 		TestBean target = new TestBean();
1421 		target.setAge(80);
1422 		ProxyFactory pf = new ProxyFactory(target);
1423 		pf.addAdvice(new NopInterceptor());
1424 		pf.addAdvisor(matchesNoArgs);
1425 		assertEquals("Advisor was added", matchesNoArgs, pf.getAdvisors()[1]);
1426 		ITestBean proxied = (ITestBean) createProxy(pf);
1427 
1428 		assertEquals(0, cca.getCalls());
1429 		assertEquals(0, cca.getCalls("getAge"));
1430 		assertEquals(target.getAge(), proxied.getAge());
1431 		assertEquals(2, cca.getCalls());
1432 		assertEquals(2, cca.getCalls("getAge"));
1433 		assertEquals(0, cca.getCalls("setAge"));
1434 		// Won't be advised
1435 		proxied.setAge(26);
1436 		assertEquals(2, cca.getCalls());
1437 		assertEquals(26, proxied.getAge());
1438 		assertEquals(4, cca.getCalls());
1439 		try {
1440 			proxied.exceptional(new SpecializedUncheckedException("foo", (SQLException)null));
1441 			fail("Should have thrown CannotGetJdbcConnectionException");
1442 		}
1443 		catch (SpecializedUncheckedException ex) {
1444 			// expected
1445 		}
1446 		assertEquals(6, cca.getCalls());
1447 	}
1448 
1449 	@Test
1450 	public void testBeforeAdviceThrowsException() {
1451 		final RuntimeException rex = new RuntimeException();
1452 		@SuppressWarnings("serial")
1453 		CountingBeforeAdvice ba = new CountingBeforeAdvice() {
1454 			@Override
1455 			public void before(Method m, Object[] args, Object target) throws Throwable {
1456 				super.before(m, args, target);
1457 				if (m.getName().startsWith("set"))
1458 					throw rex;
1459 			}
1460 		};
1461 
1462 		TestBean target = new TestBean();
1463 		target.setAge(80);
1464 		NopInterceptor nop1 = new NopInterceptor();
1465 		NopInterceptor nop2 = new NopInterceptor();
1466 		ProxyFactory pf = new ProxyFactory(target);
1467 		pf.addAdvice(nop1);
1468 		pf.addAdvice(ba);
1469 		pf.addAdvice(nop2);
1470 		ITestBean proxied = (ITestBean) createProxy(pf);
1471 		// Won't throw an exception
1472 		assertEquals(target.getAge(), proxied.getAge());
1473 		assertEquals(1, ba.getCalls());
1474 		assertEquals(1, ba.getCalls("getAge"));
1475 		assertEquals(1, nop1.getCount());
1476 		assertEquals(1, nop2.getCount());
1477 		// Will fail, after invoking Nop1
1478 		try {
1479 			proxied.setAge(26);
1480 			fail("before advice should have ended chain");
1481 		}
1482 		catch (RuntimeException ex) {
1483 			assertEquals(rex, ex);
1484 		}
1485 		assertEquals(2, ba.getCalls());
1486 		assertEquals(2, nop1.getCount());
1487 		// Nop2 didn't get invoked when the exception was thrown
1488 		assertEquals(1, nop2.getCount());
1489 		// Shouldn't have changed value in joinpoint
1490 		assertEquals(target.getAge(), proxied.getAge());
1491 	}
1492 
1493 
1494 	@Test
1495 	public void testAfterReturningAdvisorIsInvoked() {
1496 		class SummingAfterAdvice implements AfterReturningAdvice {
1497 			public int sum;
1498 			@Override
1499 			public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable {
1500 				sum += ((Integer) returnValue).intValue();
1501 			}
1502 		}
1503 		SummingAfterAdvice aa = new SummingAfterAdvice();
1504 		@SuppressWarnings("serial")
1505 		Advisor matchesInt = new StaticMethodMatcherPointcutAdvisor(aa) {
1506 			@Override
1507 			public boolean matches(Method m, Class<?> targetClass) {
1508 				return m.getReturnType() == int.class;
1509 			}
1510 		};
1511 		TestBean target = new TestBean();
1512 		ProxyFactory pf = new ProxyFactory(target);
1513 		pf.addAdvice(new NopInterceptor());
1514 		pf.addAdvisor(matchesInt);
1515 		assertEquals("Advisor was added", matchesInt, pf.getAdvisors()[1]);
1516 		ITestBean proxied = (ITestBean) createProxy(pf);
1517 		assertEquals(0, aa.sum);
1518 		int i1 = 12;
1519 		int i2 = 13;
1520 
1521 		// Won't be advised
1522 		proxied.setAge(i1);
1523 		assertEquals(i1, proxied.getAge());
1524 		assertEquals(i1, aa.sum);
1525 		proxied.setAge(i2);
1526 		assertEquals(i2, proxied.getAge());
1527 		assertEquals(i1 + i2, aa.sum);
1528 		assertEquals(i2, proxied.getAge());
1529 	}
1530 
1531 	@Test
1532 	public void testAfterReturningAdvisorIsNotInvokedOnException() {
1533 		CountingAfterReturningAdvice car = new CountingAfterReturningAdvice();
1534 		TestBean target = new TestBean();
1535 		ProxyFactory pf = new ProxyFactory(target);
1536 		pf.addAdvice(new NopInterceptor());
1537 		pf.addAdvice(car);
1538 		assertEquals("Advice was wrapped in Advisor and added", car, pf.getAdvisors()[1].getAdvice());
1539 		ITestBean proxied = (ITestBean) createProxy(pf);
1540 		assertEquals(0, car.getCalls());
1541 		int age = 10;
1542 		proxied.setAge(age);
1543 		assertEquals(age, proxied.getAge());
1544 		assertEquals(2, car.getCalls());
1545 		Exception exc = new Exception();
1546 		// On exception it won't be invoked
1547 		try {
1548 			proxied.exceptional(exc);
1549 			fail();
1550 		}
1551 		catch (Throwable t) {
1552 			assertSame(exc, t);
1553 		}
1554 		assertEquals(2, car.getCalls());
1555 	}
1556 
1557 
1558 	@Test
1559 	public void testThrowsAdvisorIsInvoked() throws Throwable {
1560 		// Reacts to ServletException and RemoteException
1561 		MyThrowsHandler th = new MyThrowsHandler();
1562 		@SuppressWarnings("serial")
1563 		Advisor matchesEchoInvocations = new StaticMethodMatcherPointcutAdvisor(th) {
1564 			@Override
1565 			public boolean matches(Method m, Class<?> targetClass) {
1566 				return m.getName().startsWith("echo");
1567 			}
1568 		};
1569 
1570 		Echo target = new Echo();
1571 		target.setA(16);
1572 		ProxyFactory pf = new ProxyFactory(target);
1573 		pf.addAdvice(new NopInterceptor());
1574 		pf.addAdvisor(matchesEchoInvocations);
1575 		assertEquals("Advisor was added", matchesEchoInvocations, pf.getAdvisors()[1]);
1576 		IEcho proxied = (IEcho) createProxy(pf);
1577 		assertEquals(0, th.getCalls());
1578 		assertEquals(target.getA(), proxied.getA());
1579 		assertEquals(0, th.getCalls());
1580 		Exception ex = new Exception();
1581 		// Will be advised but doesn't match
1582 		try {
1583 			proxied.echoException(1, ex);
1584 			fail();
1585 		}
1586 		catch (Exception caught) {
1587 			assertEquals(ex, caught);
1588 		}
1589 
1590 		ex = new FileNotFoundException();
1591 		try {
1592 			proxied.echoException(1, ex);
1593 			fail();
1594 		}
1595 		catch (FileNotFoundException caught) {
1596 			assertEquals(ex, caught);
1597 		}
1598 		assertEquals(1, th.getCalls("ioException"));
1599 	}
1600 
1601 	@Test
1602 	public void testAddThrowsAdviceWithoutAdvisor() throws Throwable {
1603 		// Reacts to ServletException and RemoteException
1604 		MyThrowsHandler th = new MyThrowsHandler();
1605 
1606 		Echo target = new Echo();
1607 		target.setA(16);
1608 		ProxyFactory pf = new ProxyFactory(target);
1609 		pf.addAdvice(new NopInterceptor());
1610 		pf.addAdvice(th);
1611 		IEcho proxied = (IEcho) createProxy(pf);
1612 		assertEquals(0, th.getCalls());
1613 		assertEquals(target.getA(), proxied.getA());
1614 		assertEquals(0, th.getCalls());
1615 		Exception ex = new Exception();
1616 		// Will be advised but doesn't match
1617 		try {
1618 			proxied.echoException(1, ex);
1619 			fail();
1620 		}
1621 		catch (Exception caught) {
1622 			assertEquals(ex, caught);
1623 		}
1624 
1625 		// Subclass of RemoteException
1626 		ex = new MarshalException("");
1627 		try {
1628 			proxied.echoException(1, ex);
1629 			fail();
1630 		}
1631 		catch (MarshalException caught) {
1632 			assertEquals(ex, caught);
1633 		}
1634 		assertEquals(1, th.getCalls("remoteException"));
1635 	}
1636 
1637 	private static class CheckMethodInvocationIsSameInAndOutInterceptor implements MethodInterceptor {
1638 
1639 		@Override
1640 		public Object invoke(MethodInvocation mi) throws Throwable {
1641 			Method m = mi.getMethod();
1642 			Object retval = mi.proceed();
1643 			assertEquals("Method invocation has same method on way back", m, mi.getMethod());
1644 			return retval;
1645 		}
1646 	}
1647 
1648 
1649 	/**
1650 	 * ExposeInvocation must be set to true.
1651 	 */
1652 	private static class CheckMethodInvocationViaThreadLocalIsSameInAndOutInterceptor implements MethodInterceptor {
1653 
1654 		@Override
1655 		public Object invoke(MethodInvocation mi) throws Throwable {
1656 			String task = "get invocation on way IN";
1657 			try {
1658 				MethodInvocation current = ExposeInvocationInterceptor.currentInvocation();
1659 				assertEquals(mi.getMethod(), current.getMethod());
1660 				Object retval = mi.proceed();
1661 				task = "get invocation on way OUT";
1662 				assertEquals(current, ExposeInvocationInterceptor.currentInvocation());
1663 				return retval;
1664 			}
1665 			catch (IllegalStateException ex) {
1666 				System.err.println(task + " for " + mi.getMethod());
1667 				ex.printStackTrace();
1668 				throw ex;
1669 			}
1670 		}
1671 	}
1672 
1673 
1674 	/**
1675 	 * Same thing for a proxy.
1676 	 * Only works when exposeProxy is set to true.
1677 	 * Checks that the proxy is the same on the way in and out.
1678 	 */
1679 	private static class ProxyMatcherInterceptor implements MethodInterceptor {
1680 
1681 		@Override
1682 		public Object invoke(MethodInvocation mi) throws Throwable {
1683 			Object proxy = AopContext.currentProxy();
1684 			Object ret = mi.proceed();
1685 			// TODO why does this cause stack overflow?
1686 			//assertEquals(proxy, AopContext.currentProxy());
1687 			assertTrue(proxy == AopContext.currentProxy());
1688 			return ret;
1689 		}
1690 	}
1691 
1692 
1693 	/**
1694 	 * Fires on setter methods that take a string. Replaces null arg with "".
1695 	 */
1696 	@SuppressWarnings("serial")
1697 	protected static class StringSetterNullReplacementAdvice extends DefaultPointcutAdvisor {
1698 
1699 		private static MethodInterceptor cleaner = new MethodInterceptor() {
1700 			@Override
1701 			public Object invoke(MethodInvocation mi) throws Throwable {
1702 				// We know it can only be invoked if there's a single parameter of type string
1703 				mi.getArguments()[0] = "";
1704 				return mi.proceed();
1705 			}
1706 		};
1707 
1708 		public StringSetterNullReplacementAdvice() {
1709 			super(cleaner);
1710 			setPointcut(new DynamicMethodMatcherPointcut() {
1711 				@Override
1712 				public boolean matches(Method m, Class<?> targetClass, Object[] args) {
1713 					return args[0] == null;
1714 				}
1715 				@Override
1716 				public boolean matches(Method m, Class<?> targetClass) {
1717 					return m.getName().startsWith("set") &&
1718 						m.getParameterTypes().length == 1 &&
1719 						m.getParameterTypes()[0].equals(String.class);
1720 				}
1721 			});
1722 		}
1723 	}
1724 
1725 
1726 	@SuppressWarnings("serial")
1727 	protected static class TestDynamicPointcutAdvice extends DefaultPointcutAdvisor {
1728 
1729 		public int count;
1730 
1731 		public TestDynamicPointcutAdvice(MethodInterceptor mi, final String pattern) {
1732 			super(mi);
1733 			setPointcut(new DynamicMethodMatcherPointcut() {
1734 				@Override
1735 				public boolean matches(Method m, Class<?> targetClass, Object[] args) {
1736 					boolean run = m.getName().indexOf(pattern) != -1;
1737 					if (run) ++count;
1738 					return run;
1739 				}
1740 			});
1741 		}
1742 	}
1743 
1744 
1745 	@SuppressWarnings("serial")
1746 	protected static class TestDynamicPointcutForSettersOnly extends DefaultPointcutAdvisor {
1747 
1748 		public int count;
1749 
1750 		public TestDynamicPointcutForSettersOnly(MethodInterceptor mi, final String pattern) {
1751 			super(mi);
1752 			setPointcut(new DynamicMethodMatcherPointcut() {
1753 				@Override
1754 				public boolean matches(Method m, Class<?> targetClass, Object[] args) {
1755 					boolean run = m.getName().indexOf(pattern) != -1;
1756 					if (run) ++count;
1757 					return run;
1758 				}
1759 				@Override
1760 				public boolean matches(Method m, Class<?> clazz) {
1761 					return m.getName().startsWith("set");
1762 				}
1763 			});
1764 		}
1765 	}
1766 
1767 
1768 	@SuppressWarnings("serial")
1769 	protected static class TestStaticPointcutAdvice extends StaticMethodMatcherPointcutAdvisor {
1770 
1771 		private String pattern;
1772 
1773 		public TestStaticPointcutAdvice(MethodInterceptor mi, String pattern) {
1774 			super(mi);
1775 			this.pattern = pattern;
1776 		}
1777 		@Override
1778 		public boolean matches(Method m, Class<?> targetClass) {
1779 			return m.getName().indexOf(pattern) != -1;
1780 		}
1781 	}
1782 
1783 
1784 	/**
1785 	 * Note that trapping the Invocation as in previous version of this test
1786 	 * isn't safe, as invocations may be reused
1787 	 * and hence cleared at the end of each invocation.
1788 	 * So we trap only the targe.
1789 	 */
1790 	protected static class TrapTargetInterceptor implements MethodInterceptor {
1791 
1792 		public Object target;
1793 
1794 		@Override
1795 		public Object invoke(MethodInvocation invocation) throws Throwable {
1796 			this.target = invocation.getThis();
1797 			return invocation.proceed();
1798 		}
1799 	}
1800 
1801 
1802 	private static class DummyIntroductionAdviceImpl implements DynamicIntroductionAdvice {
1803 
1804 		@Override
1805 		public boolean implementsInterface(Class<?> intf) {
1806 			return true;
1807 		}
1808 	}
1809 
1810 
1811 	public static class OwnSpouse extends TestBean {
1812 
1813 		@Override
1814 		public ITestBean getSpouse() {
1815 			return this;
1816 		}
1817 	}
1818 
1819 
1820 	public static class AllInstancesAreEqual implements IOther {
1821 
1822 		@Override
1823 		public boolean equals(Object other) {
1824 			return (other instanceof AllInstancesAreEqual);
1825 		}
1826 
1827 		@Override
1828 		public int hashCode() {
1829 			return getClass().hashCode();
1830 		}
1831 
1832 		@Override
1833 		public void absquatulate() {
1834 		}
1835 	}
1836 
1837 
1838 	public interface INeedsToSeeProxy {
1839 
1840 		int getCount();
1841 
1842 		void incrementViaThis();
1843 
1844 		void incrementViaProxy();
1845 
1846 		void increment();
1847 	}
1848 
1849 
1850 	public static class NeedsToSeeProxy implements INeedsToSeeProxy {
1851 
1852 		private int count;
1853 
1854 		@Override
1855 		public int getCount() {
1856 			return count;
1857 		}
1858 
1859 		@Override
1860 		public void incrementViaThis() {
1861 			this.increment();
1862 		}
1863 
1864 		@Override
1865 		public void incrementViaProxy() {
1866 			INeedsToSeeProxy thisViaProxy = (INeedsToSeeProxy) AopContext.currentProxy();
1867 			thisViaProxy.increment();
1868 			Advised advised = (Advised) thisViaProxy;
1869 			checkAdvised(advised);
1870 		}
1871 
1872 		protected void checkAdvised(Advised advised) {
1873 		}
1874 
1875 		@Override
1876 		public void increment() {
1877 			++count;
1878 		}
1879 	}
1880 
1881 
1882 	public static class TargetChecker extends NeedsToSeeProxy {
1883 
1884 		@Override
1885 		protected void checkAdvised(Advised advised) {
1886 			// TODO replace this check: no longer possible
1887 			//assertEquals(advised.getTarget(), this);
1888 		}
1889 	}
1890 
1891 
1892 	public static class CountingAdvisorListener implements AdvisedSupportListener {
1893 
1894 		public int adviceChanges;
1895 		public int activates;
1896 		private AdvisedSupport expectedSource;
1897 
1898 		public CountingAdvisorListener(AdvisedSupport expectedSource) {
1899 			this.expectedSource = expectedSource;
1900 		}
1901 
1902 		@Override
1903 		public void activated(AdvisedSupport advised) {
1904 			assertEquals(expectedSource, advised);
1905 			++activates;
1906 		}
1907 
1908 		@Override
1909 		public void adviceChanged(AdvisedSupport advised) {
1910 			assertEquals(expectedSource, advised);
1911 			++adviceChanges;
1912 		}
1913 	}
1914 
1915 
1916 	public static class RefreshCountingAdvisorChainFactory implements AdvisedSupportListener {
1917 
1918 		public int refreshes;
1919 
1920 		@Override
1921 		public void activated(AdvisedSupport advised) {
1922 			++refreshes;
1923 		}
1924 
1925 		@Override
1926 		public void adviceChanged(AdvisedSupport advised) {
1927 			++refreshes;
1928 		}
1929 	}
1930 
1931 
1932 	public static interface IOverloads {
1933 
1934 		void overload();
1935 
1936 		int overload(int i);
1937 
1938 		String overload(String foo);
1939 
1940 		void noAdvice();
1941 	}
1942 
1943 
1944 	public static class Overloads implements IOverloads {
1945 
1946 		@Override
1947 		public void overload() {
1948 		}
1949 
1950 		@Override
1951 		public int overload(int i) {
1952 			return i;
1953 		}
1954 
1955 		@Override
1956 		public String overload(String s) {
1957 			return s;
1958 		}
1959 
1960 		@Override
1961 		public void noAdvice() {
1962 		}
1963 	}
1964 
1965 
1966 	@SuppressWarnings("serial")
1967 	public static class CountingMultiAdvice extends MethodCounter implements MethodBeforeAdvice,
1968 			AfterReturningAdvice, ThrowsAdvice {
1969 
1970 		@Override
1971 		public void before(Method m, Object[] args, Object target) throws Throwable {
1972 			count(m);
1973 		}
1974 
1975 		@Override
1976 		public void afterReturning(Object o, Method m, Object[] args, Object target)
1977 				throws Throwable {
1978 			count(m);
1979 		}
1980 
1981 		public void afterThrowing(IOException ex) throws Throwable {
1982 			count(IOException.class.getName());
1983 		}
1984 
1985 		public void afterThrowing(UncheckedException ex) throws Throwable {
1986 			count(UncheckedException.class.getName());
1987 		}
1988 
1989 	}
1990 
1991 
1992 	@SuppressWarnings("serial")
1993 	public static class CountingThrowsAdvice extends MethodCounter implements ThrowsAdvice {
1994 
1995 		public void afterThrowing(IOException ex) throws Throwable {
1996 			count(IOException.class.getName());
1997 		}
1998 
1999 		public void afterThrowing(UncheckedException ex) throws Throwable {
2000 			count(UncheckedException.class.getName());
2001 		}
2002 
2003 	}
2004 
2005 
2006 	@SuppressWarnings("serial")
2007 	static class UncheckedException extends RuntimeException {
2008 
2009 	}
2010 
2011 
2012 	@SuppressWarnings("serial")
2013 	static class SpecializedUncheckedException extends UncheckedException {
2014 
2015 		public SpecializedUncheckedException(String string, SQLException exception) {
2016 		}
2017 
2018 	}
2019 
2020 
2021 	static class MockTargetSource implements TargetSource {
2022 
2023 		private Object target;
2024 
2025 		public int gets;
2026 
2027 		public int releases;
2028 
2029 		public void reset() {
2030 			this.target = null;
2031 			gets = releases = 0;
2032 		}
2033 
2034 		public void setTarget(Object target) {
2035 			this.target = target;
2036 		}
2037 
2038 		/**
2039 		 * @see org.springframework.aop.TargetSource#getTargetClass()
2040 		 */
2041 		@Override
2042 		public Class<?> getTargetClass() {
2043 			return target.getClass();
2044 		}
2045 
2046 		/**
2047 		 * @see org.springframework.aop.TargetSource#getTarget()
2048 		 */
2049 		@Override
2050 		public Object getTarget() throws Exception {
2051 			++gets;
2052 			return target;
2053 		}
2054 
2055 		/**
2056 		 * @see org.springframework.aop.TargetSource#releaseTarget(java.lang.Object)
2057 		 */
2058 		@Override
2059 		public void releaseTarget(Object pTarget) throws Exception {
2060 			if (pTarget != this.target)
2061 				throw new RuntimeException("Released wrong target");
2062 			++releases;
2063 		}
2064 
2065 		/**
2066 		 * Check that gets and releases match
2067 		 *
2068 		 */
2069 		public void verify() {
2070 			if (gets != releases)
2071 				throw new RuntimeException("Expectation failed: " + gets + " gets and " + releases + " releases");
2072 		}
2073 
2074 		/**
2075 		 * @see org.springframework.aop.TargetSource#isStatic()
2076 		 */
2077 		@Override
2078 		public boolean isStatic() {
2079 			return false;
2080 		}
2081 
2082 	}
2083 
2084 
2085 	static abstract class ExposedInvocationTestBean extends TestBean {
2086 
2087 		@Override
2088 		public String getName() {
2089 			MethodInvocation invocation = ExposeInvocationInterceptor.currentInvocation();
2090 			assertions(invocation);
2091 			return super.getName();
2092 		}
2093 
2094 		@Override
2095 		public void absquatulate() {
2096 			MethodInvocation invocation = ExposeInvocationInterceptor.currentInvocation();
2097 			assertions(invocation);
2098 			super.absquatulate();
2099 		}
2100 
2101 		protected abstract void assertions(MethodInvocation invocation);
2102 	}
2103 
2104 
2105 	static class InvocationCheckExposedInvocationTestBean extends ExposedInvocationTestBean {
2106 		@Override
2107 		protected void assertions(MethodInvocation invocation) {
2108 			TestCase.assertTrue(invocation.getThis() == this);
2109 			TestCase.assertTrue("Invocation should be on ITestBean: " + invocation.getMethod(),
2110 					ITestBean.class.isAssignableFrom(invocation.getMethod().getDeclaringClass()));
2111 		}
2112 	}
2113 
2114 }
2115