AbstractAopProxyTests.java
Upload User: jiancairen
Upload Date: 2007-08-27
Package Size: 26458k
Code Size: 56k
Category:

Java Develop

Development Platform:

Java

  1. /*
  2.  * Copyright 2002-2004 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. package org.springframework.aop.framework;
  17. import java.lang.reflect.Method;
  18. import java.lang.reflect.UndeclaredThrowableException;
  19. import java.util.HashMap;
  20. import java.util.LinkedList;
  21. import java.util.List;
  22. import javax.servlet.ServletException;
  23. import javax.transaction.TransactionRequiredException;
  24. import junit.framework.TestCase;
  25. import org.aopalliance.aop.Advice;
  26. import org.aopalliance.aop.AspectException;
  27. import org.aopalliance.intercept.MethodInterceptor;
  28. import org.aopalliance.intercept.MethodInvocation;
  29. import org.springframework.aop.Advisor;
  30. import org.springframework.aop.AfterReturningAdvice;
  31. import org.springframework.aop.DynamicIntroductionAdvice;
  32. import org.springframework.aop.MethodBeforeAdvice;
  33. import org.springframework.aop.framework.adapter.ThrowsAdviceInterceptorTests;
  34. import org.springframework.aop.framework.adapter.UnknownAdviceTypeException;
  35. import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
  36. import org.springframework.aop.interceptor.NopInterceptor;
  37. import org.springframework.aop.interceptor.SerializableNopInterceptor;
  38. import org.springframework.aop.support.AopUtils;
  39. import org.springframework.aop.support.DefaultIntroductionAdvisor;
  40. import org.springframework.aop.support.DefaultPointcutAdvisor;
  41. import org.springframework.aop.support.DelegatingIntroductionInterceptor;
  42. import org.springframework.aop.support.DynamicMethodMatcherPointcutAdvisor;
  43. import org.springframework.aop.support.NameMatchMethodPointcut;
  44. import org.springframework.aop.support.Pointcuts;
  45. import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
  46. import org.springframework.aop.target.HotSwappableTargetSource;
  47. import org.springframework.aop.target.SingletonTargetSource;
  48. import org.springframework.beans.IOther;
  49. import org.springframework.beans.ITestBean;
  50. import org.springframework.beans.Person;
  51. import org.springframework.beans.SerializablePerson;
  52. import org.springframework.beans.TestBean;
  53. import org.springframework.util.SerializationTestUtils;
  54. import org.springframework.util.StopWatch;
  55. /**
  56.  * @author Rod Johnson
  57.  * @author Juergen Hoeller
  58.  * @since 13-Mar-2003
  59.  */
  60. public abstract class AbstractAopProxyTests extends TestCase {
  61. protected MockTargetSource mockTargetSource = new MockTargetSource();
  62. /**
  63.  * Make a clean target source available if code wants to use it.
  64.  * The target must be set. Verification will be automatic in tearDown
  65.  * to ensure that it was used appropriately by code.
  66.  * @see junit.framework.TestCase#setUp()
  67.  */
  68. protected void setUp() {
  69. mockTargetSource.reset();
  70. }
  71. protected void tearDown() {
  72. mockTargetSource.verify();
  73. }
  74. /**
  75.  * Set in CGLIB or JDK mode.
  76.  */
  77. protected abstract Object createProxy(AdvisedSupport as);
  78. protected abstract AopProxy createAopProxy(AdvisedSupport as);
  79. /**
  80.  * Is a target always required?
  81.  */
  82. protected boolean requiresTarget() {
  83. return false;
  84. }
  85. public void testNoInterceptorsAndNoTarget() {
  86. AdvisedSupport pc =
  87. new AdvisedSupport(new Class[] { ITestBean.class });
  88. // Add no interceptors
  89. try {
  90. AopProxy aop = createAopProxy(pc);
  91. aop.getProxy();
  92. fail("Shouldn't allow no interceptors");
  93. } catch (AopConfigException ex) {
  94. // Ok
  95. }
  96. }
  97. private static class CheckMethodInvocationIsSameInAndOutInterceptor implements MethodInterceptor {
  98. public Object invoke(MethodInvocation mi) throws Throwable {
  99. Method m = mi.getMethod();
  100. Object retval = mi.proceed();
  101. assertEquals("Method invocation has same method on way back", m, mi.getMethod());
  102. return retval;
  103. }
  104. }
  105. /**
  106.  * ExposeInvocation must be set to true
  107.  */
  108. private static class CheckMethodInvocationViaThreadLocalIsSameInAndOutInterceptor implements MethodInterceptor {
  109. public Object invoke(MethodInvocation mi) throws Throwable {
  110. String task = "get invocation on way IN";
  111. try {
  112. MethodInvocation current = ExposeInvocationInterceptor.currentInvocation();
  113. assertEquals(mi, current);
  114. Object retval = mi.proceed();
  115. task = "get invocation on way OUT";
  116. assertEquals(current, ExposeInvocationInterceptor.currentInvocation());
  117. return retval;
  118. }
  119. catch (AspectException ex) {
  120. System.err.println(task + " for " + mi.getMethod());
  121. ex.printStackTrace();
  122. //fail("Can't find invocation: " + ex);
  123. throw ex;
  124. }
  125. }
  126. }
  127. /**
  128.  * Same thing for a proxy.
  129. * Only works when exposeProxy is set to true.
  130. * Checks that the proxy is the same on the way in and out.
  131. */
  132. public static class ProxyMatcherInterceptor implements MethodInterceptor {
  133. public Object invoke(MethodInvocation mi) throws Throwable {
  134. Object proxy = AopContext.currentProxy();
  135. Object ret = mi.proceed();
  136. // TODO why does this cause stack overflow?
  137. //assertEquals(proxy, AopContext.currentProxy());
  138. assertTrue(proxy == AopContext.currentProxy());
  139. return ret;
  140. }
  141. }
  142. /**
  143.  * Simple test that if we set values we can get them out again
  144.  *
  145.  */
  146. public void testValuesStick() {
  147. int age1 = 33;
  148. int age2 = 37;
  149. String name = "tony";
  150. TestBean target1 = new TestBean();
  151. target1.setAge(age1);
  152. ProxyFactory pf1 = new ProxyFactory(target1);
  153. pf1.addAdvisor(new DefaultPointcutAdvisor(new NopInterceptor()));
  154. pf1.addAdvisor(new DefaultPointcutAdvisor(new TimestampIntroductionInterceptor()));
  155. ITestBean tb = (ITestBean) target1;
  156. assertEquals(age1, tb.getAge());
  157. tb.setAge(age2);
  158. assertEquals(age2, tb.getAge());
  159. assertNull(tb.getName());
  160. tb.setName(name);
  161. assertEquals(name, tb.getName());
  162. }
  163. /**
  164.  * This is primarily a test for the efficiency of our
  165.  * usage of CGLIB. If we create too many classes with
  166.  * CGLIB this will be slow or will run out of memory
  167.  * TODO reenable this
  168.  *
  169.  */
  170. public void testManyProxies() {
  171. int howmany = 10000;
  172. StopWatch sw = new StopWatch();
  173. sw.start(getClass() + getName() + ": create " + howmany + " proxies");
  174. testManyProxies(howmany);
  175. sw.stop();
  176. System.out.println(sw);
  177. // Set a performance benchmark
  178. // It's pretty generous so as not to cause failures
  179. // on slow machines
  180. assertTrue("Proxy creation was too slow", 
  181. sw.getTotalTimeSeconds() < 20);
  182. }
  183. private void testManyProxies(int howmany) {
  184. int age1 = 33;
  185. TestBean target1 = new TestBean();
  186. target1.setAge(age1);
  187. ProxyFactory pf1 = new ProxyFactory(target1);
  188. pf1.addAdvice(new NopInterceptor());
  189. pf1.addAdvice(new NopInterceptor());
  190. ITestBean proxies[] = new ITestBean[howmany];
  191. for (int i = 0; i < howmany; i++) {
  192. proxies[i] = (ITestBean) createAopProxy(pf1).getProxy();
  193. assertEquals(age1, proxies[i].getAge());
  194. }
  195. }
  196. public void testSerializationAdviceAndTargetNotSerializable() throws Exception {
  197. TestBean tb = new TestBean();
  198. assertFalse(SerializationTestUtils.isSerializable(tb));
  199. ProxyFactory pf = new ProxyFactory(tb);
  200. pf.addAdvice(new NopInterceptor());
  201. ITestBean proxy = (ITestBean) createAopProxy(pf).getProxy();
  202. assertFalse(SerializationTestUtils.isSerializable(proxy));
  203. }
  204. public void testSerializationAdviceNotSerializable() throws Exception {
  205. SerializablePerson sp = new SerializablePerson();
  206. assertTrue(SerializationTestUtils.isSerializable(sp));
  207. ProxyFactory pf = new ProxyFactory(sp);
  208. // This isn't serializable
  209. Advice i = new NopInterceptor();
  210. pf.addAdvice(i);
  211. assertFalse(SerializationTestUtils.isSerializable(i));
  212. Object proxy = createAopProxy(pf).getProxy();
  213. assertFalse(SerializationTestUtils.isSerializable(proxy));
  214. }
  215. public void testSerializationSerializableTargetAndAdvice() throws Throwable {
  216. SerializablePerson personTarget = new SerializablePerson();
  217. personTarget.setName("jim");
  218. personTarget.setAge(26);
  219. assertTrue(SerializationTestUtils.isSerializable(personTarget));
  220. ProxyFactory pf = new ProxyFactory(personTarget);
  221. CountingThrowsAdvice cta = new CountingThrowsAdvice();
  222. pf.addAdvice(new SerializableNopInterceptor());
  223. // Try various advice types
  224. pf.addAdvice(new CountingBeforeAdvice());
  225. pf.addAdvice(new CountingAfterReturningAdvice());
  226. pf.addAdvice(cta);
  227. Person p = (Person) createAopProxy(pf).getProxy();
  228. p.echo(null);
  229. assertEquals(0, cta.getCalls());
  230. try {
  231. p.echo(new ServletException());
  232. }
  233. catch (ServletException ex) {
  234. }
  235. assertEquals(1, cta.getCalls());
  236. // Will throw exception if it fails
  237. Person p2 = (Person) SerializationTestUtils.serializeAndDeserialize(p);
  238. assertNotSame(p, p2);
  239. assertEquals(p.getName(), p2.getName());
  240. assertEquals(p.getAge(), p2.getAge());
  241. assertTrue("Deserialized object is an AOP proxy", AopUtils.isAopProxy(p2));
  242. Advised a1 = (Advised) p;
  243. Advised a2 = (Advised) p2;
  244. // Check we can manipulate state of p2
  245. assertEquals(a1.getAdvisors().length, a2.getAdvisors().length);
  246. // This should work as SerializablePerson is equal
  247. assertEquals("Proxies should be equal, even after one was serialized", p, p2);
  248. assertEquals("Proxies should be equal, even after one was serialized", p2, p);
  249. // Check we can add a new advisor to the target
  250. NopInterceptor ni = new NopInterceptor();
  251. p2.getAge();
  252. assertEquals(0, ni.getCount());
  253. a2.addAdvice(ni);
  254. p2.getAge();
  255. assertEquals(1, ni.getCount());
  256. cta = (CountingThrowsAdvice) a2.getAdvisors()[3].getAdvice();
  257. p2.echo(null);
  258. assertEquals(1, cta.getCalls());
  259. try {
  260. p2.echo(new ServletException());
  261. }
  262. catch (ServletException ex) {
  263. }
  264. assertEquals(2, cta.getCalls());
  265. }
  266. /**
  267.  * Check that the two MethodInvocations necessary are independent and
  268.  * don't conflict.
  269.  * Check also proxy exposure.
  270.  */
  271. public void testOneAdvisedObjectCallsAnother() {
  272. int age1 = 33;
  273. int age2 = 37;
  274. TestBean target1 = new TestBean();
  275. ProxyFactory pf1 = new ProxyFactory(target1);
  276. // Permit proxy and invocation checkers to get context from AopContext
  277. pf1.setExposeProxy(true);
  278. NopInterceptor di1 = new NopInterceptor();
  279. pf1.addAdvice(0, di1);
  280. pf1.addAdvice(1, new ProxyMatcherInterceptor());
  281. pf1.addAdvice(2, new CheckMethodInvocationIsSameInAndOutInterceptor());
  282. pf1.addAdvice(1, new CheckMethodInvocationViaThreadLocalIsSameInAndOutInterceptor());
  283. // Must be first
  284. pf1.addAdvice(0, ExposeInvocationInterceptor.INSTANCE);
  285. ITestBean advised1 = (ITestBean) pf1.getProxy();
  286. advised1.setAge(age1); // = 1 invocation
  287. TestBean target2 = new TestBean();
  288. ProxyFactory pf2 = new ProxyFactory(target2);
  289. pf2.setExposeProxy(true);
  290. NopInterceptor di2 = new NopInterceptor();
  291. pf2.addAdvice(0, di2);
  292. pf2.addAdvice(1, new ProxyMatcherInterceptor());
  293. pf2.addAdvice(2, new CheckMethodInvocationIsSameInAndOutInterceptor());
  294. pf2.addAdvice(1, new CheckMethodInvocationViaThreadLocalIsSameInAndOutInterceptor());
  295. pf2.addAdvice(0, ExposeInvocationInterceptor.INSTANCE);
  296. //System.err.println(pf2.toProxyConfigString());
  297. ITestBean advised2 = (ITestBean) createProxy(pf2);
  298. advised2.setAge(age2);
  299. advised1.setSpouse(advised2); // = 2 invocations
  300. assertEquals("Advised one has correct age", age1, advised1.getAge()); // = 3 invocations
  301. assertEquals("Advised two has correct age", age2, advised2.getAge());
  302. // Means extra call on advised 2
  303. assertEquals("Advised one spouse has correct age", age2, advised1.getSpouse().getAge()); // = 4 invocations on 1 and another one on 2
  304. assertEquals("one was invoked correct number of times", 4, di1.getCount());
  305. // Got hit by call to advised1.getSpouse().getAge()
  306. assertEquals("one was invoked correct number of times", 3, di2.getCount());
  307. }
  308. public void testReentrance() {
  309. int age1 = 33;
  310. TestBean target1 = new TestBean();
  311. ProxyFactory pf1 = new ProxyFactory(target1);
  312. NopInterceptor di1 = new NopInterceptor();
  313. pf1.addAdvice(0, di1);
  314. ITestBean advised1 = (ITestBean) createProxy(pf1);
  315. advised1.setAge(age1); // = 1 invocation
  316. advised1.setSpouse(advised1); // = 2 invocations
  317. assertEquals("one was invoked correct number of times", 2, di1.getCount());
  318. assertEquals("Advised one has correct age", age1, advised1.getAge()); // = 3 invocations
  319. assertEquals("one was invoked correct number of times", 3, di1.getCount());
  320. // = 5 invocations, as reentrant call to spouse is advised also
  321. assertEquals("Advised spouse has correct age", age1, advised1.getSpouse().getAge()); 
  322. assertEquals("one was invoked correct number of times", 5, di1.getCount());
  323. }
  324. public interface INeedsToSeeProxy {
  325. int getCount();
  326. void incrementViaThis();
  327. void incrementViaProxy();
  328. void increment();
  329. }
  330. public static class NeedsToSeeProxy implements INeedsToSeeProxy {
  331. private int count;
  332. public int getCount() {
  333. return count;
  334. }
  335. public void incrementViaThis() {
  336. this.increment();
  337. }
  338. public void incrementViaProxy() {
  339. INeedsToSeeProxy thisViaProxy = (INeedsToSeeProxy) AopContext.currentProxy();
  340. thisViaProxy.increment();
  341. Advised advised = (Advised) thisViaProxy;
  342. checkAdvised(advised);
  343. }
  344. protected void checkAdvised(Advised advised) {
  345. }
  346. public void increment() {
  347. ++count;
  348. }
  349. }
  350. public static class TargetChecker extends NeedsToSeeProxy {
  351. protected void checkAdvised(Advised advised) {
  352. // TODO replace this check: no longer possible
  353. //assertEquals(advised.getTarget(), this);
  354. }
  355. }
  356. public void testTargetCanGetProxy() {
  357. NopInterceptor di = new NopInterceptor();
  358. INeedsToSeeProxy target = new TargetChecker();
  359. ProxyFactory proxyFactory = new ProxyFactory(target);
  360. proxyFactory.setExposeProxy(true);
  361. assertTrue(proxyFactory.getExposeProxy());
  362. proxyFactory.addAdvice(0, di);
  363. INeedsToSeeProxy proxied = (INeedsToSeeProxy) createProxy(proxyFactory);
  364. assertEquals(0, di.getCount());
  365. assertEquals(0, target.getCount());
  366. proxied.incrementViaThis();
  367. assertEquals("Increment happened", 1, target.getCount());
  368. assertEquals("Only one invocation via AOP as use of this wasn't proxied", 1, di.getCount());
  369. // 1 invocation
  370. assertEquals("Increment happened", 1, proxied.getCount());
  371. proxied.incrementViaProxy(); // 2 invoocations
  372. assertEquals("Increment happened", 2, target.getCount());
  373. assertEquals("3 more invocations via AOP as the first call was reentrant through the proxy", 4, di.getCount());
  374. }
  375. public void testTargetCantGetProxyByDefault() {
  376. NeedsToSeeProxy et = new NeedsToSeeProxy();
  377. ProxyFactory pf1 = new ProxyFactory(et);
  378. assertFalse(pf1.getExposeProxy());
  379. INeedsToSeeProxy proxied = (INeedsToSeeProxy) createProxy(pf1);
  380. try {
  381. proxied.incrementViaProxy();
  382. fail("Should have failed to get proxy as exposeProxy wasn't set to true");
  383. }
  384. catch (AspectException ex) {
  385. // Ok
  386. }
  387. }
  388. public void testContext() throws Throwable {
  389. testContext(true);
  390. }
  391. public void testNoContext() throws Throwable {
  392. testContext(false);
  393. }
  394. /**
  395.  * @param context if true, want context
  396.  * @throws Throwable
  397.  */
  398. private void testContext(final boolean context) throws Throwable {
  399. final String s = "foo";
  400. // Test return value
  401. MethodInterceptor mi = new MethodInterceptor() {
  402. public Object invoke(MethodInvocation invocation) throws Throwable {
  403. if (!context) {
  404. assertNoInvocationContext();
  405. } else {
  406. assertTrue("have context", ExposeInvocationInterceptor.currentInvocation() != null);
  407. }
  408. return s;
  409. }
  410. };
  411. AdvisedSupport pc = new AdvisedSupport(new Class[] { ITestBean.class });
  412. if (context) {
  413. pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
  414. }
  415. pc.addAdvice(mi);
  416. // Keep CGLIB happy
  417. if (requiresTarget()) {
  418. pc.setTarget(new TestBean());
  419. }
  420. AopProxy aop = createAopProxy(pc);
  421. assertNoInvocationContext();
  422. ITestBean tb = (ITestBean) aop.getProxy();
  423. assertNoInvocationContext();
  424. assertTrue("correct return value", tb.getName() == s);
  425. }
  426. public static class OwnSpouse extends TestBean {
  427. public ITestBean getSpouse() {
  428. return this;
  429. }
  430. }
  431. /**
  432.  * Test that the proxy returns itself when the
  433.  * target returns <code>this</code>
  434.  * @throws Throwable
  435.  */
  436. public void testTargetReturnsThis() throws Throwable {
  437. // Test return value
  438. TestBean raw = new OwnSpouse();
  439. AdvisedSupport pc = new AdvisedSupport(new Class[] { ITestBean.class });
  440. pc.setTarget(raw);
  441. ITestBean tb = (ITestBean) createProxy(pc);
  442. assertTrue("this return is wrapped in proxy", tb.getSpouse() == tb);
  443. }
  444. /*
  445. public void testCanAttach() throws Throwable {
  446. final TrapInterceptor tii = new TrapInvocationInterceptor();
  447. ProxyConfig pc = new ProxyConfigSupport(new Class[] { ITestBean.class }, false);
  448. pc.addAdvice(tii);
  449. pc.addAdvice(new MethodInterceptor() {
  450. public Object invoke(MethodInvocation invocation) throws Throwable {
  451. assertTrue("Saw same interceptor", invocation == tii.invocation);
  452. return null;
  453. }
  454. });
  455. AopProxy aop = new AopProxy(pc, new MethodInvocationFactorySupport());
  456. ITestBean tb = (ITestBean) aop.getProxy();
  457. tb.getSpouse();
  458. assertTrue(tii.invocation != null);
  459. // TODO strengthen this
  460. // assertTrue(tii.invocation.getProxy() == tb);
  461. assertTrue(tii.invocation.getThis() == null);
  462. }
  463. */
  464. public void testDeclaredException() throws Throwable {
  465. final Exception expectedException = new Exception();
  466. // Test return value
  467. MethodInterceptor mi = new MethodInterceptor() {
  468. public Object invoke(MethodInvocation invocation) throws Throwable {
  469. throw expectedException;
  470. }
  471. };
  472. AdvisedSupport pc = new AdvisedSupport(new Class[] { ITestBean.class });
  473. pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
  474. pc.addAdvice(mi);
  475. // We don't care about the object
  476. mockTargetSource.setTarget(new Object());
  477. pc.setTargetSource(mockTargetSource);
  478. AopProxy aop = createAopProxy(pc);
  479. try {
  480. ITestBean tb = (ITestBean) aop.getProxy();
  481. // Note: exception param below isn't used
  482. tb.exceptional(expectedException);
  483. fail("Should have thrown exception raised by interceptor");
  484. catch (Exception thrown) {
  485. assertEquals("exception matches", expectedException, thrown);
  486. }
  487. }
  488. /**
  489.  * An interceptor throws a checked exception not on the method signature.
  490.  * For efficiency, we don't bother unifying java.lang.reflect and
  491.  * net.sf.cglib UndeclaredThrowableException
  492.  * @throws Throwable
  493.  */
  494. public void testUndeclaredCheckedException() throws Throwable {
  495. final Exception unexpectedException = new Exception();
  496. // Test return value
  497. MethodInterceptor mi = new MethodInterceptor() {
  498. public Object invoke(MethodInvocation invocation) throws Throwable {
  499. throw unexpectedException;
  500. }
  501. };
  502. AdvisedSupport pc = new AdvisedSupport(new Class[] { ITestBean.class });
  503. pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
  504. pc.addAdvice(mi);
  505. // We don't care about the object
  506. pc.setTarget(new TestBean());
  507. AopProxy aop = createAopProxy(pc);
  508. ITestBean tb = (ITestBean) aop.getProxy();
  509. try {
  510. // Note: exception param below isn't used
  511. tb.getAge();
  512. fail("Should have wrapped exception raised by interceptor");
  513. catch (UndeclaredThrowableException thrown) {
  514. assertEquals("exception matches", unexpectedException, thrown.getUndeclaredThrowable());
  515. }
  516. //catch (net.sf.cglib.proxy.UndeclaredThrowableException thrown) {
  517. // assertEquals("exception matches", unexpectedException, thrown.getUndeclaredThrowable());
  518. //}
  519. catch (Exception ex) {
  520. ex.printStackTrace();
  521. fail("Didn't expect exception: " + ex);
  522. }
  523. }
  524. public void testUndeclaredUnheckedException() throws Throwable {
  525. final RuntimeException unexpectedException = new RuntimeException();
  526. // Test return value
  527. MethodInterceptor mi = new MethodInterceptor() {
  528. public Object invoke(MethodInvocation invocation) throws Throwable {
  529. throw unexpectedException;
  530. }
  531. };
  532. AdvisedSupport pc = new AdvisedSupport(new Class[] { ITestBean.class });
  533. pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
  534. pc.addAdvice(mi);
  535. // We don't care about the object
  536. pc.setTarget(new TestBean());
  537. AopProxy aop = createAopProxy(pc);
  538. ITestBean tb = (ITestBean) aop.getProxy();
  539. try {
  540. // Note: exception param below isn't used
  541. tb.getAge();
  542. fail("Should have wrapped exception raised by interceptor");
  543. catch (RuntimeException thrown) {
  544. assertEquals("exception matches", unexpectedException, thrown);
  545. }
  546. //catch (net.sf.cglib.proxy.UndeclaredThrowableException thrown) {
  547. // assertEquals("exception matches", unexpectedException, thrown.getUndeclaredThrowable());
  548. //}
  549. }
  550. /**
  551.  * Check that although a method is eligible for advice chain optimization and
  552.  * direct reflective invocation, it doesn't happen if we've asked to see the proxy,
  553.  * so as to guarantee a consistent programming model.
  554.  * @throws Throwable
  555.  */
  556. public void testTargetCanGetInvocationEvenIfNoAdviceChain() throws Throwable {
  557. NeedsToSeeProxy target = new NeedsToSeeProxy();
  558. AdvisedSupport pc = new AdvisedSupport(new Class[] { INeedsToSeeProxy.class } );
  559. pc.setTarget(target);
  560. pc.setExposeProxy(true);
  561. // Now let's try it with the special target
  562. AopProxy aop = createAopProxy(pc);
  563. INeedsToSeeProxy proxied = (INeedsToSeeProxy) aop.getProxy();
  564. // It will complain if it can't get the proxy
  565. proxied.incrementViaProxy();
  566. }
  567. public void testTargetCanGetInvocation() throws Throwable {
  568. final InvocationCheckExposedInvocationTestBean expectedTarget = new InvocationCheckExposedInvocationTestBean();
  569. AdvisedSupport pc = new AdvisedSupport(new Class[] { ITestBean.class, IOther.class });
  570. pc.addAdvice(ExposeInvocationInterceptor.INSTANCE);
  571. TrapTargetInterceptor tii = new TrapTargetInterceptor() {
  572. public Object invoke(MethodInvocation invocation) throws Throwable {
  573. // Assert that target matches BEFORE invocation returns
  574. assertEquals("Target is correct", expectedTarget, invocation.getThis());
  575. return super.invoke(invocation);
  576. }
  577. };
  578. pc.addAdvice(tii);
  579. pc.setTarget(expectedTarget);
  580. AopProxy aop = createAopProxy(pc);
  581. ITestBean tb = (ITestBean) aop.getProxy();
  582. tb.getName();
  583. // Not safe to trap invocation
  584. //assertTrue(tii.invocation == target.invocation);
  585. //assertTrue(target.invocation.getProxy() == tb);
  586. // ((IOther) tb).absquatulate();
  587. //MethodInvocation minv =  tii.invocation;
  588. //assertTrue("invoked on iother, not " + minv.getMethod().getDeclaringClass(), minv.getMethod().getDeclaringClass() == IOther.class);
  589. //assertTrue(target.invocation == tii.invocation);
  590. }
  591. /**
  592.  * Throw an exception if there is an Invocation
  593.  */
  594. private void assertNoInvocationContext() {
  595. try {
  596. ExposeInvocationInterceptor.currentInvocation();
  597. fail("Expected no invocation context");
  598. } catch (AspectException ex) {
  599. // ok
  600. }
  601. }
  602. /**
  603.  * Test stateful interceptor
  604.  * @throws Throwable
  605.  */
  606. public void testMixin() throws Throwable {
  607. TestBean tb = new TestBean();
  608. ProxyFactory pc = new ProxyFactory(new Class[] { Lockable.class, ITestBean.class });
  609. pc.addAdvisor(new LockMixinAdvisor());
  610. pc.setTarget(tb);
  611. int newAge = 65;
  612. ITestBean itb = (ITestBean) createProxy(pc);
  613. itb.setAge(newAge);
  614. assertTrue(itb.getAge() == newAge);
  615. Lockable lockable = (Lockable) itb;
  616. assertFalse(lockable.locked());
  617. lockable.lock();
  618. assertTrue(itb.getAge() == newAge);
  619. try {
  620. itb.setAge(1);
  621. fail("Setters should fail when locked");
  622. catch (LockedException ex) {
  623. // ok
  624. }
  625. assertTrue(itb.getAge() == newAge);
  626. // Unlock
  627. assertTrue(lockable.locked());
  628. lockable.unlock();
  629. itb.setAge(1);
  630. assertTrue(itb.getAge() == 1);
  631. }
  632. public void testReplaceArgument() throws Throwable {
  633. TestBean tb = new TestBean();
  634. ProxyFactory pc = new ProxyFactory(new Class[] { ITestBean.class });
  635. pc.setTarget(tb);
  636. pc.addAdvisor(new StringSetterNullReplacementAdvice());
  637. ITestBean t = (ITestBean) pc.getProxy();
  638. int newAge = 5;
  639. t.setAge(newAge);
  640. assertTrue(t.getAge() == newAge);
  641. String newName = "greg";
  642. t.setName(newName);
  643. assertEquals(newName, t.getName());
  644. t.setName(null);
  645. // Null replacement magic should work
  646. assertTrue(t.getName().equals(""));
  647. }
  648. public void testCanCastProxyToProxyConfig() throws Throwable {
  649. TestBean tb = new TestBean();
  650. ProxyFactory pc = new ProxyFactory(tb);
  651. NopInterceptor di = new NopInterceptor();
  652. pc.addAdvice(0, di);
  653. ITestBean t = (ITestBean) createProxy(pc);
  654. assertEquals(0, di.getCount());
  655. t.setAge(23);
  656. assertEquals(23, t.getAge());
  657. assertEquals(2, di.getCount());
  658. Advised advised = (Advised) t;
  659. assertEquals("Have 1 advisor", 1, advised.getAdvisors().length);
  660. assertEquals(di, advised.getAdvisors()[0].getAdvice());
  661. NopInterceptor di2 = new NopInterceptor();
  662. advised.addAdvice(1, di2);
  663. t.getName();
  664. assertEquals(3, di.getCount());
  665. assertEquals(1, di2.getCount());
  666. // will remove di
  667. advised.removeAdvisor(0);
  668. t.getAge();
  669. // Unchanged
  670. assertEquals(3, di.getCount());
  671. assertEquals(2, di2.getCount());
  672. CountingBeforeAdvice cba = new CountingBeforeAdvice();
  673. assertEquals(0, cba.getCalls());
  674. advised.addAdvice(cba);
  675. t.setAge(16);
  676. assertEquals(16, t.getAge());
  677. assertEquals(2, cba.getCalls());
  678. }
  679. public void testAdviceImplementsIntroductionInfo() throws Throwable {
  680. TestBean tb = new TestBean();
  681. String name = "tony";
  682. tb.setName(name);
  683. ProxyFactory pc = new ProxyFactory(tb);
  684. NopInterceptor di = new NopInterceptor();
  685. pc.addAdvice(di);
  686. final long ts = 37;
  687. pc.addAdvice(new DelegatingIntroductionInterceptor(new TimeStamped() {
  688. /**
  689.  * @see org.springframework.aop.framework.TimeStamped#getTimeStamp()
  690.  */
  691. public long getTimeStamp() {
  692. return ts;
  693. }
  694. }));
  695. ITestBean proxied = (ITestBean) createProxy(pc);
  696. assertEquals(name, proxied.getName());
  697. TimeStamped intro = (TimeStamped) proxied;
  698. assertEquals(ts, intro.getTimeStamp());
  699. }
  700. public void testCannotAddDynamicIntroductionAdviceExceptInIntroductionAdvice() throws Throwable {
  701. TestBean target = new TestBean();
  702. target.setAge(21);
  703. ProxyFactory pc = new ProxyFactory(target);
  704. try {
  705. pc.addAdvice(new DummyIntroductionAdviceImpl());
  706. fail("Shouldn't be able to add introduction interceptor except via introduction advice");
  707. }
  708. catch (AopConfigException ex) {
  709. assertTrue(ex.getMessage().indexOf("ntroduction") > -1);
  710. }
  711. // Check it still works: proxy factory state shouldn't have been corrupted
  712. ITestBean proxied = (ITestBean) createProxy(pc);
  713. assertEquals(target.getAge(), proxied.getAge());
  714. }
  715. public void testRejectsBogusDynamicIntroductionAdviceWithNoAdapter() throws Throwable {
  716. TestBean target = new TestBean();
  717. target.setAge(21);
  718. ProxyFactory pc = new ProxyFactory(target);
  719. pc.addAdvisor(new DefaultIntroductionAdvisor(new DummyIntroductionAdviceImpl(), Comparable.class));
  720. try {
  721. // TODO May fail on either call: may want to tighten up definition
  722. ITestBean proxied = (ITestBean) createProxy(pc);
  723. proxied.getName();
  724. fail("Bogus introduction");
  725. }
  726. catch (Exception ex) {
  727. // TODO used to catch UnknownAdviceTypeException, but
  728. // with CGLIB some errors are in proxy creation and are wrapped
  729. // in aspect exception. Error message is still fine.
  730. //assertTrue(ex.getMessage().indexOf("ntroduction") > -1);
  731. }
  732. }
  733. private class DummyIntroductionAdviceImpl implements DynamicIntroductionAdvice {
  734. /**
  735.  * @see org.springframework.aop.DynamicIntroductionAdvice#implementsInterface(java.lang.Class)
  736.  */
  737. public boolean implementsInterface(Class intf) {
  738. return true;
  739. }
  740. }
  741. /**
  742.  * Check that the introduction advice isn't allowed to introduce interfaces
  743.  * that are unsupported by the IntroductionInterceptor
  744.  * @throws Throwable
  745.  */
  746. public void testCannotAddIntroductionAdviceWithUnimplementedInterface() throws Throwable {
  747. TestBean target = new TestBean();
  748. target.setAge(21);
  749. ProxyFactory pc = new ProxyFactory(target);
  750. try {
  751. pc.addAdvisor(0, new DefaultIntroductionAdvisor(new TimestampIntroductionInterceptor(), ITestBean.class));
  752. fail("Shouldn't be able to add introduction advice introducing an unimplemented interface");
  753. }
  754. catch (IllegalArgumentException ex) {
  755. //assertTrue(ex.getMessage().indexOf("ntroduction") > -1);
  756. }
  757. // Check it still works: proxy factory state shouldn't have been corrupted
  758. ITestBean proxied = (ITestBean) createProxy(pc);
  759. assertEquals(target.getAge(), proxied.getAge());
  760. }
  761. /**
  762.  * Note that an introduction can't throw an unexpected checked exception, 
  763.  * as it's constained by the interface
  764.  * @throws Throwable
  765.  */
  766. public void testIntroductionThrowsUncheckedException() throws Throwable {
  767. TestBean target = new TestBean();
  768. target.setAge(21);
  769. ProxyFactory pc = new ProxyFactory(target);
  770. class MyDi extends DelegatingIntroductionInterceptor implements TimeStamped {
  771. /**
  772.  * @see org.springframework.aop.framework.TimeStamped#getTimeStamp()
  773.  */
  774. public long getTimeStamp() {
  775. throw new UnsupportedOperationException();
  776. }
  777. }
  778. pc.addAdvisor(new DefaultIntroductionAdvisor(new MyDi()));
  779. TimeStamped ts = (TimeStamped) createProxy(pc);
  780. try {
  781. ts.getTimeStamp();
  782. fail("Should throw UnsupportedOperationException");
  783. }
  784. catch (UnsupportedOperationException ex) {
  785. }
  786. }
  787. /**
  788.  * Should only be able to introduce interfaces, not classes
  789.  * @throws Throwable
  790.  */
  791. public void testCannotAddIntroductionAdviceToIntroduceClass() throws Throwable {
  792. TestBean target = new TestBean();
  793. target.setAge(21);
  794. ProxyFactory pc = new ProxyFactory(target);
  795. try {
  796. pc.addAdvisor(0, new DefaultIntroductionAdvisor(new TimestampIntroductionInterceptor(), TestBean.class));
  797. fail("Shouldn't be able to add introduction advice that introduces a class, rather than an interface");
  798. }
  799. catch (IllegalArgumentException ex) {
  800. assertTrue(ex.getMessage().indexOf("interface") > -1);
  801. }
  802. // Check it still works: proxy factory state shouldn't have been corrupted
  803. ITestBean proxied = (ITestBean) createProxy(pc);
  804. assertEquals(target.getAge(), proxied.getAge());
  805. }
  806. public void testCannotAddInterceptorWhenFrozen() throws Throwable {
  807. TestBean target = new TestBean();
  808. target.setAge(21);
  809. ProxyFactory pc = new ProxyFactory(target);
  810. assertFalse(pc.isFrozen());
  811. pc.addAdvice(new NopInterceptor());
  812. ITestBean proxied = (ITestBean) createProxy(pc);
  813. pc.setFrozen(true);
  814. try {
  815. pc.addAdvice(0, new NopInterceptor());
  816. fail("Shouldn't be able to add interceptor when frozen");
  817. }
  818. catch (AopConfigException ex) {
  819. assertTrue(ex.getMessage().indexOf("frozen") > -1);
  820. }
  821. // Check it still works: proxy factory state shouldn't have been corrupted
  822. assertEquals(target.getAge(), proxied.getAge());
  823. assertEquals(1, ((Advised) proxied).getAdvisors().length);
  824. }
  825. /**
  826.  * Check that casting to Advised can't get around advice freeze
  827.  * @throws Throwable
  828.  */
  829. public void testCannotAddAdvisorWhenFrozenUsingCast() throws Throwable {
  830. TestBean target = new TestBean();
  831. target.setAge(21);
  832. ProxyFactory pc = new ProxyFactory(target);
  833. assertFalse(pc.isFrozen());
  834. pc.addAdvice(new NopInterceptor());
  835. ITestBean proxied = (ITestBean) createProxy(pc);
  836. pc.setFrozen(true);
  837. Advised advised = (Advised) proxied;
  838. assertTrue(pc.isFrozen());
  839. try {
  840. advised.addAdvisor(new DefaultPointcutAdvisor(new NopInterceptor()));
  841. fail("Shouldn't be able to add Advisor when frozen");
  842. }
  843. catch (AopConfigException ex) {
  844. assertTrue(ex.getMessage().indexOf("frozen") > -1);
  845. }
  846. // Check it still works: proxy factory state shouldn't have been corrupted
  847. assertEquals(target.getAge(), proxied.getAge());
  848. assertEquals(1, advised.getAdvisors().length);
  849. }
  850. public void testCannotRemoveAdvisorWhenFrozen() throws Throwable {
  851. TestBean target = new TestBean();
  852. target.setAge(21);
  853. ProxyFactory pc = new ProxyFactory(target);
  854. assertFalse(pc.isFrozen());
  855. pc.addAdvice(new NopInterceptor());
  856. ITestBean proxied = (ITestBean) createProxy(pc);
  857. pc.setFrozen(true);
  858. Advised advised = (Advised) proxied;
  859. assertTrue(pc.isFrozen());
  860. try {
  861. advised.removeAdvisor(0);
  862. fail("Shouldn't be able to remove Advisor when frozen");
  863. }
  864. catch (AopConfigException ex) {
  865. assertTrue(ex.getMessage().indexOf("frozen") > -1);
  866. }
  867. // Didn't get removed
  868. assertEquals(1, advised.getAdvisors().length);
  869. pc.setFrozen(false);
  870. // Can now remove it
  871. advised.removeAdvisor(0);
  872. // Check it still works: proxy factory state shouldn't have been corrupted
  873. assertEquals(target.getAge(), proxied.getAge());
  874. assertEquals(0, advised.getAdvisors().length);
  875. }
  876. public void testUseAsHashKey() {
  877. TestBean target1 = new TestBean();
  878. ProxyFactory pf1 = new ProxyFactory(target1);
  879. pf1.addAdvice(new NopInterceptor());
  880. ITestBean proxy1 = (ITestBean) createProxy(pf1);
  881. TestBean target2 = new TestBean();
  882. ProxyFactory pf2 = new ProxyFactory(target2);
  883. pf2.addAdvisor(new DefaultIntroductionAdvisor(new TimestampIntroductionInterceptor()));
  884. ITestBean proxy2 = (ITestBean) createProxy(pf2);
  885. HashMap h = new HashMap();
  886. Object value1 = new Object();
  887. Object value2 = new Object();
  888. assertNull(h.get(proxy1));
  889. h.put(proxy1, value1);
  890. h.put(proxy2, value2);
  891. assertEquals(h.get(proxy1), value1);
  892. assertEquals(h.get(proxy2), value2);
  893. }
  894. /**
  895.  * Check that the string is informative.
  896.  *
  897.  */
  898. public void testProxyConfigString() {
  899. TestBean target = new TestBean();
  900. ProxyFactory pc = new ProxyFactory(target);
  901. pc.setInterfaces(new Class[] { ITestBean.class } );
  902. pc.addAdvice(new NopInterceptor());
  903. MethodBeforeAdvice mba = new CountingBeforeAdvice();
  904. Advisor advisor = new DefaultPointcutAdvisor(new NameMatchMethodPointcut(), mba);
  905. pc.addAdvisor(advisor);
  906. ITestBean proxied = (ITestBean) createProxy(pc);
  907. String proxyConfigString = ((Advised) proxied).toProxyConfigString();
  908. System.err.println(proxyConfigString);
  909. assertTrue(proxyConfigString.indexOf(advisor.toString()) != -1);
  910. assertTrue(proxyConfigString.indexOf("1 interface") != -1);
  911. }
  912. public void testCanPreventCastToAdvisedUsingOpaque() {
  913. TestBean target = new TestBean();
  914. ProxyFactory pc = new ProxyFactory(target);
  915. pc.setInterfaces(new Class[] { ITestBean.class } );
  916. pc.addAdvice(new NopInterceptor());
  917. CountingBeforeAdvice mba = new CountingBeforeAdvice();
  918. Advisor advisor = new DefaultPointcutAdvisor(new NameMatchMethodPointcut().addMethodName("setAge"), mba);
  919. pc.addAdvisor(advisor);
  920. assertFalse("Opaque defaults to false", pc.getOpaque());
  921. pc.setOpaque(true);
  922. assertTrue("Opaque now true for this config", pc.getOpaque());
  923. ITestBean proxied = (ITestBean) createProxy(pc);
  924. proxied.setAge(10);
  925. assertEquals(10, proxied.getAge());
  926. assertEquals(1, mba.getCalls());
  927. assertFalse("Cannot be cast to Advised", proxied instanceof Advised);
  928. }
  929. public void testAdviceSupportListeners() throws Throwable {
  930. TestBean target = new TestBean();
  931. target.setAge(21);
  932. ProxyFactory pc = new ProxyFactory(target);
  933. CountingAdvisorListener l = new CountingAdvisorListener(pc);
  934. pc.addListener(l);
  935. RefreshCountingAdvisorChainFactory acf = new RefreshCountingAdvisorChainFactory();
  936. // Should be automatically added as a listener
  937. pc.setAdvisorChainFactory(acf);
  938. assertFalse(pc.isActive());
  939. assertEquals(0, l.activates);
  940. assertEquals(0, acf.refreshes);
  941. ITestBean proxied = (ITestBean) createProxy(pc);
  942. assertEquals(1, acf.refreshes);
  943. assertEquals(1, l.activates);
  944. assertTrue(pc.isActive());
  945. assertEquals(target.getAge(), proxied.getAge());
  946. assertEquals(0, l.adviceChanges);
  947. NopInterceptor di = new NopInterceptor();
  948. pc.addAdvice(0, di);
  949. assertEquals(1, l.adviceChanges);
  950. assertEquals(2, acf.refreshes);
  951. assertEquals(target.getAge(), proxied.getAge());
  952. pc.removeAdvice(di);
  953. assertEquals(2, l.adviceChanges);
  954. assertEquals(3, acf.refreshes);
  955. assertEquals(target.getAge(), proxied.getAge());
  956. pc.getProxy();
  957. assertEquals(1, l.activates);
  958. pc.removeListener(l);
  959. assertEquals(2, l.adviceChanges);
  960. pc.addAdvisor(new DefaultPointcutAdvisor(new NopInterceptor()));
  961. // No longer counting
  962. assertEquals(2, l.adviceChanges);
  963. }
  964. public void testExistingProxyChangesTarget() throws Throwable {
  965. TestBean tb1 = new TestBean();
  966. tb1.setAge(33);
  967. TestBean tb2 = new TestBean();
  968. tb2.setAge(26);
  969. tb2.setName("Juergen");
  970. TestBean tb3 = new TestBean();
  971. tb3.setAge(37);
  972. ProxyFactory pc = new ProxyFactory(tb1);
  973. NopInterceptor nop = new NopInterceptor();
  974. pc.addAdvice(nop);
  975. ITestBean proxy = (ITestBean) createProxy(pc);
  976. assertEquals(nop.getCount(), 0);
  977. assertEquals(tb1.getAge(), proxy.getAge());
  978. assertEquals(nop.getCount(), 1);
  979. // Change to a new static target
  980. pc.setTarget(tb2);
  981. assertEquals(tb2.getAge(), proxy.getAge());
  982. assertEquals(nop.getCount(), 2);
  983. // Change to a new dynamic target
  984. HotSwappableTargetSource hts = new HotSwappableTargetSource(tb3);
  985. pc.setTargetSource(hts);
  986. assertEquals(tb3.getAge(), proxy.getAge());
  987. assertEquals(nop.getCount(), 3);
  988. hts.swap(tb1);
  989. assertEquals(tb1.getAge(), proxy.getAge());
  990. tb1.setName("Colin");
  991. assertEquals(tb1.getName(), proxy.getName());
  992. assertEquals(nop.getCount(), 5);
  993. // Change back, relying on casting to Advised
  994. Advised advised = (Advised) proxy;
  995. assertSame(hts, advised.getTargetSource());
  996. SingletonTargetSource sts = new SingletonTargetSource(tb2);
  997. advised.setTargetSource(sts);
  998. assertEquals(tb2.getName(), proxy.getName());
  999. assertSame(sts, advised.getTargetSource());
  1000. assertEquals(tb2.getAge(), proxy.getAge());
  1001. }
  1002. public static class CountingAdvisorListener implements AdvisedSupportListener {
  1003. public int adviceChanges;
  1004. public int activates;
  1005. private AdvisedSupport expectedSource;
  1006. public CountingAdvisorListener(AdvisedSupport expectedSource) {
  1007. this.expectedSource = expectedSource;
  1008. }
  1009. public void adviceChanged(AdvisedSupport as) {
  1010. assertEquals(expectedSource, as);
  1011. ++adviceChanges;
  1012. }
  1013. public void activated(AdvisedSupport as) {
  1014. assertEquals(expectedSource, as);
  1015. ++activates;
  1016. }
  1017. }
  1018. public class RefreshCountingAdvisorChainFactory implements AdvisorChainFactory {
  1019. public int refreshes;
  1020. public void adviceChanged(AdvisedSupport pc) {
  1021. ++refreshes;
  1022. }
  1023. public List getInterceptorsAndDynamicInterceptionAdvice(Advised pc, Object proxy, Method method, Class targetClass) {
  1024. return AdvisorChainFactoryUtils.calculateInterceptorsAndDynamicInterceptionAdvice(pc, proxy, method, targetClass);
  1025. }
  1026. public void activated(AdvisedSupport as) {
  1027. ++refreshes;
  1028. }
  1029. }
  1030. /**
  1031.  * Fires on setter methods that take a string. Replaces null arg
  1032.  * with ""
  1033.  */
  1034. public static class StringSetterNullReplacementAdvice extends DynamicMethodMatcherPointcutAdvisor {
  1035. private static MethodInterceptor cleaner = new MethodInterceptor() {
  1036. public Object invoke(MethodInvocation mi) throws Throwable {
  1037. // We know it can only be invoked if there's a single parameter of type string
  1038. mi.getArguments()[0] = "";
  1039. return mi.proceed();
  1040. }
  1041. };
  1042. public StringSetterNullReplacementAdvice() {
  1043. super(cleaner);
  1044. }
  1045. public boolean matches(Method m, Class targetClass, Object[] args){//, AttributeRegistry attributeRegistry) {
  1046. return args[0] == null;
  1047. }
  1048. public boolean matches(Method m, Class targetClass){//, AttributeRegistry attributeRegistry) {
  1049. return m.getName().startsWith("set") &&
  1050. m.getParameterTypes().length == 1 &&
  1051. m.getParameterTypes()[0].equals(String.class);
  1052. }
  1053. }
  1054. public void testDynamicMethodPointcutThatAlwaysAppliesStatically() throws Throwable {
  1055. TestBean tb = new TestBean();
  1056. ProxyFactory pc = new ProxyFactory(new Class[] { ITestBean.class });
  1057. TestDynamicPointcutAdvice dp = new TestDynamicPointcutAdvice(new NopInterceptor(), "getAge");
  1058. pc.addAdvisor(dp);
  1059. pc.setTarget(tb);
  1060. ITestBean it = (ITestBean) createProxy(pc);
  1061. assertEquals(dp.count, 0);
  1062. int age = it.getAge();
  1063. assertEquals(dp.count, 1);
  1064. it.setAge(11);
  1065. assertEquals(it.getAge(), 11);
  1066. assertEquals(dp.count, 2);
  1067. }
  1068. public void testDynamicMethodPointcutThatAppliesStaticallyOnlyToSetters() throws Throwable {
  1069. TestBean tb = new TestBean();
  1070. ProxyFactory pc = new ProxyFactory(new Class[] { ITestBean.class });
  1071. // Could apply dynamically to getAge/setAge but not to getName
  1072. TestDynamicPointcutAdvice dp = new TestDynamicPointcutForSettersOnly(new NopInterceptor(), "Age");
  1073. pc.addAdvisor(dp);
  1074. this.mockTargetSource.setTarget(tb);
  1075. pc.setTargetSource(mockTargetSource);
  1076. ITestBean it = (ITestBean) createProxy(pc);
  1077. assertEquals(dp.count, 0);
  1078. int age = it.getAge();
  1079. // Statically vetoed
  1080. assertEquals(0, dp.count);
  1081. it.setAge(11);
  1082. assertEquals(it.getAge(), 11);
  1083. assertEquals(dp.count, 1);
  1084. // Applies statically but not dynamically
  1085. it.setName("joe");
  1086. assertEquals(dp.count, 1);
  1087. }
  1088. public void testStaticMethodPointcut() throws Throwable {
  1089. TestBean tb = new TestBean();
  1090. ProxyFactory pc = new ProxyFactory(new Class[] { ITestBean.class });
  1091. NopInterceptor di = new NopInterceptor();
  1092. TestStaticPointcutAdvice sp = new TestStaticPointcutAdvice(di, "getAge");
  1093. pc.addAdvisor(sp);
  1094. pc.setTarget(tb);
  1095. ITestBean it = (ITestBean) createProxy(pc);
  1096. assertEquals(di.getCount(), 0);
  1097. int age = it.getAge();
  1098. assertEquals(di.getCount(), 1);
  1099. it.setAge(11);
  1100. assertEquals(it.getAge(), 11);
  1101. assertEquals(di.getCount(), 2);
  1102. }
  1103. /**
  1104.  * There are times when we want to call proceed()
  1105.  * twice. We can do this if we clone the invocation.
  1106.  * @throws Throwable
  1107.  */
  1108. public void testCloneInvocationToProceedThreeTimes() throws Throwable {
  1109. TestBean tb = new TestBean();
  1110. ProxyFactory pc = new ProxyFactory(tb);
  1111. pc.addInterface(ITestBean.class);
  1112. MethodInterceptor twoBirthdayInterceptor = new MethodInterceptor() {
  1113. public Object invoke(MethodInvocation mi) throws Throwable {
  1114. // Clone the invocation to proceed three times
  1115. // "The Moor's Last Sigh": this technology can cause premature aging
  1116. MethodInvocation clone1 = ((ReflectiveMethodInvocation) mi).invocableClone();
  1117. MethodInvocation clone2 = ((ReflectiveMethodInvocation) mi).invocableClone();
  1118. clone1.proceed();
  1119. clone2.proceed();
  1120. return mi.proceed();
  1121. }
  1122. };
  1123. StaticMethodMatcherPointcutAdvisor advisor = new StaticMethodMatcherPointcutAdvisor(twoBirthdayInterceptor) {
  1124. public boolean matches(Method m, Class targetClass) {
  1125. return "haveBirthday".equals(m.getName());
  1126. }
  1127. };
  1128. pc.addAdvisor(advisor);
  1129. ITestBean it = (ITestBean) createProxy(pc);
  1130. final int age = 20;
  1131. it.setAge(age);
  1132. assertEquals(age, it.getAge());
  1133. // Should return the age before the third, AOP-induced birthday
  1134. assertEquals(age + 2, it.haveBirthday());
  1135. // Return the final age produced by 3 birthdays
  1136. assertEquals(age + 3, it.getAge());
  1137. }
  1138. /**
  1139.  * We want to change the arguments on a clone: it shouldn't affect the original
  1140.  * @throws Throwable
  1141.  */
  1142. public void testCanChangeArgumentsIndependentlyOnClonedInvocation() throws Throwable {
  1143. TestBean tb = new TestBean();
  1144. ProxyFactory pc = new ProxyFactory(tb);
  1145. pc.addInterface(ITestBean.class);
  1146. /**
  1147.  * Changes the name, then changes it back
  1148.  */
  1149. MethodInterceptor nameReverter = new MethodInterceptor() {
  1150. public Object invoke(MethodInvocation mi) throws Throwable {
  1151. MethodInvocation clone = ((ReflectiveMethodInvocation) mi).invocableClone();
  1152. String oldName = ((ITestBean) mi.getThis()).getName();
  1153. clone.getArguments()[0] = oldName;
  1154. // Original method invocation should be unaffected by changes to argument list of clone
  1155. mi.proceed();
  1156. return clone.proceed();
  1157. }
  1158. };
  1159. class NameSaver implements MethodInterceptor { 
  1160. private List names = new LinkedList();
  1161. public Object invoke(MethodInvocation mi) throws Throwable {
  1162. names.add(mi.getArguments()[0]);
  1163. return mi.proceed();
  1164. }
  1165. };
  1166. NameSaver saver = new NameSaver();
  1167. pc.addAdvisor(new DefaultPointcutAdvisor(Pointcuts.SETTERS, nameReverter));
  1168. pc.addAdvisor(new DefaultPointcutAdvisor(Pointcuts.SETTERS, saver));
  1169. ITestBean it = (ITestBean) createProxy(pc);
  1170. String name1 = "tony";
  1171. String name2 = "gordon";
  1172. tb.setName(name1);
  1173. assertEquals(name1, tb.getName());
  1174. it.setName(name2);
  1175. // NameReverter saved it back
  1176. assertEquals(name1, it.getName());
  1177. assertEquals(2, saver.names.size());
  1178. assertEquals(name2, saver.names.get(0));
  1179. assertEquals(name1, saver.names.get(1));
  1180. }
  1181. public static interface IOverloads {
  1182. void overload();
  1183. int overload(int i);
  1184. String overload(String foo);
  1185. void noAdvice();
  1186. }
  1187. public static class Overloads implements IOverloads {
  1188. public void overload() {
  1189. }
  1190. public int overload(int i) {
  1191. return i;
  1192. }
  1193. public String overload(String s) {
  1194. return s;
  1195. }
  1196. public void noAdvice() {
  1197. }
  1198. }
  1199. public void testOverloadedMethodsWithDifferentAdvice() throws Throwable {
  1200. Overloads target = new Overloads();
  1201. ProxyFactory pc = new ProxyFactory(target);
  1202. NopInterceptor overLoadVoids = new NopInterceptor();
  1203. pc.addAdvisor(new StaticMethodMatcherPointcutAdvisor(overLoadVoids) {
  1204. public boolean matches(Method m, Class targetClass) {
  1205. return m.getName().equals("overload") && m.getParameterTypes().length == 0;
  1206. }
  1207. });
  1208. NopInterceptor overLoadInts = new NopInterceptor();
  1209. pc.addAdvisor(new StaticMethodMatcherPointcutAdvisor(overLoadInts) {
  1210. public boolean matches(Method m, Class targetClass) {
  1211. return m.getName().equals("overload") && m.getParameterTypes().length == 1 &&
  1212. m.getParameterTypes()[0].equals(int.class);
  1213. }
  1214. });
  1215. IOverloads proxy = (IOverloads) createProxy(pc);
  1216. assertEquals(0, overLoadInts.getCount());
  1217. assertEquals(0, overLoadVoids.getCount());
  1218. proxy.overload();
  1219. assertEquals(0, overLoadInts.getCount());
  1220. assertEquals(1, overLoadVoids.getCount());
  1221. assertEquals(25, proxy.overload(25));
  1222. assertEquals(1, overLoadInts.getCount());
  1223. assertEquals(1, overLoadVoids.getCount());
  1224. proxy.noAdvice();
  1225. assertEquals(1, overLoadInts.getCount());
  1226. assertEquals(1, overLoadVoids.getCount());
  1227. }
  1228. public void testEquals() {
  1229. IOther a = new AllInstancesAreEqual();
  1230. IOther b = new AllInstancesAreEqual();
  1231. NopInterceptor i1 = new NopInterceptor();
  1232. NopInterceptor i2 = new NopInterceptor();
  1233. ProxyFactory pfa = new ProxyFactory(a);
  1234. pfa.addAdvice(i1);
  1235. ProxyFactory pfb = new ProxyFactory(b);
  1236. pfb.addAdvice(i2);
  1237. IOther proxyA = (IOther) createProxy(pfa);
  1238. IOther proxyB = (IOther) createProxy(pfb);
  1239. assertEquals(pfa.getAdvisors().length, pfb.getAdvisors().length);
  1240. assertTrue(a.equals(b));
  1241. assertTrue(i1.equals(i2));
  1242. assertTrue(proxyA.equals(proxyB));
  1243. //assertTrue(a.equals(proxyA));
  1244. assertFalse(proxyA.equals(a));
  1245. // Equality checks were handled by the proxy
  1246. assertEquals(0, i1.getCount());
  1247. // When we invoke A, it's NopInterceptor will have count == 1
  1248. // and won't think it's equal to B's NopInterceptor
  1249. proxyA.absquatulate();
  1250. assertEquals(1, i1.getCount());
  1251. assertFalse(proxyA.equals(proxyB));
  1252. }
  1253. public void testBeforeAdvisorIsInvoked() {
  1254. CountingBeforeAdvice cba = new CountingBeforeAdvice();
  1255. Advisor matchesNoArgs = new StaticMethodMatcherPointcutAdvisor(cba) {
  1256. public boolean matches(Method m, Class targetClass) {
  1257. return m.getParameterTypes().length == 0;
  1258. }
  1259. };
  1260. TestBean target = new TestBean();
  1261. target.setAge(80);
  1262. ProxyFactory pf = new ProxyFactory(target);
  1263. pf.addAdvice(new NopInterceptor());
  1264. pf.addAdvisor(matchesNoArgs);
  1265. assertEquals("Advisor was added", matchesNoArgs, pf.getAdvisors()[1]);
  1266. ITestBean proxied = (ITestBean) createProxy(pf);
  1267. assertEquals(0, cba.getCalls());
  1268. assertEquals(0, cba.getCalls("getAge"));
  1269. assertEquals(target.getAge(), proxied.getAge());
  1270. assertEquals(1, cba.getCalls());
  1271. assertEquals(1, cba.getCalls("getAge"));
  1272. assertEquals(0, cba.getCalls("setAge"));
  1273. // Won't be advised
  1274. proxied.setAge(26);
  1275. assertEquals(1, cba.getCalls());
  1276. assertEquals(26, proxied.getAge());
  1277. }
  1278. public void testBeforeAdviceThrowsException() {
  1279. final RuntimeException rex = new RuntimeException();
  1280. CountingBeforeAdvice ba = new CountingBeforeAdvice() {
  1281. public void before(Method m, Object[] args, Object target) throws Throwable {
  1282. super.before(m, args, target);
  1283. if (m.getName().startsWith("set"))
  1284. throw rex;
  1285. }
  1286. };
  1287. TestBean target = new TestBean();
  1288. target.setAge(80);
  1289. NopInterceptor nop1 = new NopInterceptor();
  1290. NopInterceptor nop2 = new NopInterceptor();
  1291. ProxyFactory pf = new ProxyFactory(target);
  1292. pf.addAdvice(nop1);
  1293. pf.addAdvice(ba);
  1294. pf.addAdvice(nop2);
  1295. ITestBean proxied = (ITestBean) createProxy(pf);
  1296. // Won't throw an exception
  1297. assertEquals(target.getAge(), proxied.getAge());
  1298. assertEquals(1, ba.getCalls());
  1299. assertEquals(1, ba.getCalls("getAge"));
  1300. assertEquals(1, nop1.getCount());
  1301. assertEquals(1, nop2.getCount());
  1302. // Will fail, after invoking Nop1
  1303. try {
  1304. proxied.setAge(26);
  1305. fail("before advice should have ended chain");
  1306. }
  1307. catch (RuntimeException ex) {
  1308. assertEquals(rex, ex);
  1309. }
  1310. assertEquals(2, ba.getCalls());
  1311. assertEquals(2, nop1.getCount());
  1312. // Nop2 didn't get invoked when the exception was thrown
  1313. assertEquals(1, nop2.getCount());
  1314. // Shouldn't have changed value in joinpoint
  1315. assertEquals(target.getAge(), proxied.getAge());
  1316. }
  1317. public void testAfterReturningAdvisorIsInvoked() {
  1318. class SummingAfterAdvice implements AfterReturningAdvice {
  1319. public int sum;
  1320. public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable {
  1321. sum += ((Integer) returnValue).intValue();
  1322. }
  1323. }
  1324. SummingAfterAdvice aa = new SummingAfterAdvice();
  1325. Advisor matchesInt = new StaticMethodMatcherPointcutAdvisor(aa) {
  1326. public boolean matches(Method m, Class targetClass) {
  1327. return m.getReturnType() == int.class;
  1328. }
  1329. };
  1330. TestBean target = new TestBean();
  1331. ProxyFactory pf = new ProxyFactory(target);
  1332. pf.addAdvice(new NopInterceptor());
  1333. pf.addAdvisor(matchesInt);
  1334. assertEquals("Advisor was added", matchesInt, pf.getAdvisors()[1]);
  1335. ITestBean proxied = (ITestBean) createProxy(pf);
  1336. assertEquals(0, aa.sum);
  1337. int i1 = 12;
  1338. int i2 = 13;
  1339. // Won't be advised
  1340. proxied.setAge(i1);
  1341. assertEquals(i1, proxied.getAge());
  1342. assertEquals(i1, aa.sum);
  1343. proxied.setAge(i2);
  1344. assertEquals(i2, proxied.getAge());
  1345. assertEquals(i1 + i2, aa.sum);
  1346. assertEquals(i2, proxied.getAge());
  1347. }
  1348. public void testAfterReturningAdvisorIsNotInvokedOnException() {
  1349. CountingAfterReturningAdvice car = new CountingAfterReturningAdvice();
  1350. TestBean target = new TestBean();
  1351. ProxyFactory pf = new ProxyFactory(target);
  1352. pf.addAdvice(new NopInterceptor());
  1353. pf.addAdvice(car);
  1354. assertEquals("Advice was wrapped in Advisor and added", car, pf.getAdvisors()[1].getAdvice());
  1355. ITestBean proxied = (ITestBean) createProxy(pf);
  1356. assertEquals(0, car.getCalls());
  1357. int age = 10;
  1358. proxied.setAge(age);
  1359. assertEquals(age, proxied.getAge());
  1360. assertEquals(2, car.getCalls());
  1361. Exception exc = new Exception();
  1362. // On exception it won't be invoked
  1363. try {
  1364. proxied.exceptional(exc);
  1365. fail();
  1366. }
  1367. catch (Throwable t) {
  1368. assertSame(exc, t);
  1369. }
  1370. assertEquals(2, car.getCalls());
  1371. }
  1372. public void testThrowsAdvisorIsInvoked() throws Throwable {
  1373. // Reacts to ServletException and RemoteException
  1374. ThrowsAdviceInterceptorTests.MyThrowsHandler th = new ThrowsAdviceInterceptorTests.MyThrowsHandler();
  1375. Advisor matchesEchoInvocations = new StaticMethodMatcherPointcutAdvisor(th) {
  1376. public boolean matches(Method m, Class targetClass) {
  1377. return m.getName().startsWith("echo");
  1378. }
  1379. };
  1380. ThrowsAdviceInterceptorTests.Echo target = new ThrowsAdviceInterceptorTests.Echo();
  1381. target.setA(16);
  1382. ProxyFactory pf = new ProxyFactory(target);
  1383. pf.addAdvice(new NopInterceptor());
  1384. pf.addAdvisor(matchesEchoInvocations);
  1385. assertEquals("Advisor was added", matchesEchoInvocations, pf.getAdvisors()[1]);
  1386. ThrowsAdviceInterceptorTests.IEcho proxied = (ThrowsAdviceInterceptorTests.IEcho) createProxy(pf);
  1387. assertEquals(0, th.getCalls());
  1388. assertEquals(target.getA(), proxied.getA());
  1389. assertEquals(0, th.getCalls());
  1390. Exception ex = new Exception();
  1391. // Will be advised but doesn't match
  1392. try {
  1393. proxied.echoException(1, ex);
  1394. fail();
  1395. }
  1396. catch (Exception caught) {
  1397. assertEquals(ex, caught);
  1398. }
  1399. ex = new ServletException();
  1400. try {
  1401. proxied.echoException(1, ex);
  1402. fail();
  1403. }
  1404. catch (ServletException caught) {
  1405. assertEquals(ex, caught);
  1406. }
  1407. assertEquals(1, th.getCalls("servletException"));
  1408. }
  1409. public void testAddThrowsAdviceWithoutAdvisor() throws Throwable {
  1410. // Reacts to ServletException and RemoteException
  1411. ThrowsAdviceInterceptorTests.MyThrowsHandler th = new ThrowsAdviceInterceptorTests.MyThrowsHandler();
  1412. ThrowsAdviceInterceptorTests.Echo target = new ThrowsAdviceInterceptorTests.Echo();
  1413. target.setA(16);
  1414. ProxyFactory pf = new ProxyFactory(target);
  1415. pf.addAdvice(new NopInterceptor());
  1416. pf.addAdvice(th);
  1417. ThrowsAdviceInterceptorTests.IEcho proxied = (ThrowsAdviceInterceptorTests.IEcho) createProxy(pf);
  1418. assertEquals(0, th.getCalls());
  1419. assertEquals(target.getA(), proxied.getA());
  1420. assertEquals(0, th.getCalls());
  1421. Exception ex = new Exception();
  1422. // Will be advised but doesn't match
  1423. try {
  1424. proxied.echoException(1, ex);
  1425. fail();
  1426. }
  1427. catch (Exception caught) {
  1428. assertEquals(ex, caught);
  1429. }
  1430. // Subclass of RemoteException
  1431. ex = new TransactionRequiredException();
  1432. try {
  1433. proxied.echoException(1, ex);
  1434. fail();
  1435. }
  1436. catch (TransactionRequiredException caught) {
  1437. assertEquals(ex, caught);
  1438. }
  1439. assertEquals(1, th.getCalls("remoteException"));
  1440. }
  1441. protected static class TestDynamicPointcutAdvice extends DynamicMethodMatcherPointcutAdvisor {
  1442. private String pattern;
  1443. public int count;
  1444. public TestDynamicPointcutAdvice(MethodInterceptor mi, String pattern) {
  1445. super(mi);
  1446. this.pattern = pattern;
  1447. }
  1448. public boolean matches(Method m, Class targetClass, Object[] args) {
  1449. boolean run = m.getName().indexOf(pattern) != -1;
  1450. if (run) ++count;
  1451. return run;
  1452. }
  1453. }
  1454. protected static class TestDynamicPointcutForSettersOnly extends TestDynamicPointcutAdvice {
  1455. public TestDynamicPointcutForSettersOnly(MethodInterceptor mi, String pattern) {
  1456. super(mi, pattern);
  1457. }
  1458. public boolean matches(Method m, Class clazz) {
  1459. return m.getName().startsWith("set");
  1460. }
  1461. }
  1462. protected static class TestStaticPointcutAdvice extends StaticMethodMatcherPointcutAdvisor {
  1463. private String pattern;
  1464. private int count;
  1465. public TestStaticPointcutAdvice(MethodInterceptor mi, String pattern) {
  1466. super(mi);
  1467. this.pattern = pattern;
  1468. }
  1469. public boolean matches(Method m, Class targetClass) {
  1470. boolean run = m.getName().indexOf(pattern) != -1;
  1471. if (run) ++count;
  1472. return run;
  1473. }
  1474. }
  1475. /**
  1476.  * Note that trapping the Invocation as in previous version of this test
  1477.  * isn't safe, as invocations may be reused
  1478.  * and hence cleared at the end of each invocation.
  1479.  * So we trap only the targe.
  1480.  */
  1481. protected static class TrapTargetInterceptor implements MethodInterceptor {
  1482. public Object target;
  1483. public Object invoke(MethodInvocation invocation) throws Throwable {
  1484. this.target = invocation.getThis();
  1485. return invocation.proceed();
  1486. }
  1487. }
  1488. public static class EqualsTestBean extends TestBean {
  1489. public ITestBean getSpouse() {
  1490. return this;
  1491. }
  1492. }
  1493. public static class AllInstancesAreEqual implements IOther {
  1494. public boolean equals(Object o) {
  1495. return o instanceof AllInstancesAreEqual;
  1496. }
  1497. public void absquatulate() {
  1498. }
  1499. }
  1500. }