/* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.aop.framework; import java.util.ArrayList; import java.util.List; import javax.accessibility.Accessible; import javax.swing.JFrame; import javax.swing.RootPaneContainer; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.junit.Ignore; import org.junit.Test; import org.springframework.aop.Advisor; import org.springframework.aop.interceptor.DebugInterceptor; import org.springframework.aop.support.AopUtils; import org.springframework.aop.support.DefaultIntroductionAdvisor; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.aop.support.DelegatingIntroductionInterceptor; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.Order; import org.springframework.tests.TimeStamped; import org.springframework.tests.aop.advice.CountingBeforeAdvice; import org.springframework.tests.aop.interceptor.NopInterceptor; import org.springframework.tests.sample.beans.IOther; import org.springframework.tests.sample.beans.ITestBean; import org.springframework.tests.sample.beans.TestBean; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; /** * Also tests AdvisedSupport and ProxyCreatorSupport superclasses. * * @author Rod Johnson * @author Juergen Hoeller * @author Chris Beams * @since 14.05.2003 */ public class ProxyFactoryTests { @Test public void testIndexOfMethods() { TestBean target = new TestBean(); ProxyFactory pf = new ProxyFactory(target); NopInterceptor nop = new NopInterceptor(); Advisor advisor = new DefaultPointcutAdvisor(new CountingBeforeAdvice()); Advised advised = (Advised) pf.getProxy(); // Can use advised and ProxyFactory interchangeably advised.addAdvice(nop); pf.addAdvisor(advisor); assertEquals(-1, pf.indexOf(new NopInterceptor())); assertEquals(0, pf.indexOf(nop)); assertEquals(1, pf.indexOf(advisor)); assertEquals(-1, advised.indexOf(new DefaultPointcutAdvisor(null))); } @Test public void testRemoveAdvisorByReference() { TestBean target = new TestBean(); ProxyFactory pf = new ProxyFactory(target); NopInterceptor nop = new NopInterceptor(); CountingBeforeAdvice cba = new CountingBeforeAdvice(); Advisor advisor = new DefaultPointcutAdvisor(cba); pf.addAdvice(nop); pf.addAdvisor(advisor); ITestBean proxied = (ITestBean) pf.getProxy(); proxied.setAge(5); assertEquals(1, cba.getCalls()); assertEquals(1, nop.getCount()); assertTrue(pf.removeAdvisor(advisor)); assertEquals(5, proxied.getAge()); assertEquals(1, cba.getCalls()); assertEquals(2, nop.getCount()); assertFalse(pf.removeAdvisor(new DefaultPointcutAdvisor(null))); } @Test public void testRemoveAdvisorByIndex() { TestBean target = new TestBean(); ProxyFactory pf = new ProxyFactory(target); NopInterceptor nop = new NopInterceptor(); CountingBeforeAdvice cba = new CountingBeforeAdvice(); Advisor advisor = new DefaultPointcutAdvisor(cba); pf.addAdvice(nop); pf.addAdvisor(advisor); NopInterceptor nop2 = new NopInterceptor(); pf.addAdvice(nop2); ITestBean proxied = (ITestBean) pf.getProxy(); proxied.setAge(5); assertEquals(1, cba.getCalls()); assertEquals(1, nop.getCount()); assertEquals(1, nop2.getCount()); // Removes counting before advisor pf.removeAdvisor(1); assertEquals(5, proxied.getAge()); assertEquals(1, cba.getCalls()); assertEquals(2, nop.getCount()); assertEquals(2, nop2.getCount()); // Removes Nop1 pf.removeAdvisor(0); assertEquals(5, proxied.getAge()); assertEquals(1, cba.getCalls()); assertEquals(2, nop.getCount()); assertEquals(3, nop2.getCount()); // Check out of bounds try { pf.removeAdvisor(-1); } catch (AopConfigException ex) { // Ok } try { pf.removeAdvisor(2); } catch (AopConfigException ex) { // Ok } assertEquals(5, proxied.getAge()); assertEquals(4, nop2.getCount()); } @Test public void testReplaceAdvisor() { TestBean target = new TestBean(); ProxyFactory pf = new ProxyFactory(target); NopInterceptor nop = new NopInterceptor(); CountingBeforeAdvice cba1 = new CountingBeforeAdvice(); CountingBeforeAdvice cba2 = new CountingBeforeAdvice(); Advisor advisor1 = new DefaultPointcutAdvisor(cba1); Advisor advisor2 = new DefaultPointcutAdvisor(cba2); pf.addAdvisor(advisor1); pf.addAdvice(nop); ITestBean proxied = (ITestBean) pf.getProxy(); // Use the type cast feature // Replace etc methods on advised should be same as on ProxyFactory Advised advised = (Advised) proxied; proxied.setAge(5); assertEquals(1, cba1.getCalls()); assertEquals(0, cba2.getCalls()); assertEquals(1, nop.getCount()); assertFalse(advised.replaceAdvisor(new DefaultPointcutAdvisor(new NopInterceptor()), advisor2)); assertTrue(advised.replaceAdvisor(advisor1, advisor2)); assertEquals(advisor2, pf.getAdvisors()[0]); assertEquals(5, proxied.getAge()); assertEquals(1, cba1.getCalls()); assertEquals(2, nop.getCount()); assertEquals(1, cba2.getCalls()); assertFalse(pf.replaceAdvisor(new DefaultPointcutAdvisor(null), advisor1)); } @Test public void testAddRepeatedInterface() { TimeStamped tst = new TimeStamped() { @Override public long getTimeStamp() { throw new UnsupportedOperationException("getTimeStamp"); } }; ProxyFactory pf = new ProxyFactory(tst); // We've already implicitly added this interface. // This call should be ignored without error pf.addInterface(TimeStamped.class); // All cool assertThat(pf.getProxy(), instanceOf(TimeStamped.class)); } @Test public void testGetsAllInterfaces() throws Exception { // Extend to get new interface class TestBeanSubclass extends TestBean implements Comparable<Object> { @Override public int compareTo(Object arg0) { throw new UnsupportedOperationException("compareTo"); } } TestBeanSubclass raw = new TestBeanSubclass(); ProxyFactory factory = new ProxyFactory(raw); //System.out.println("Proxied interfaces are " + StringUtils.arrayToDelimitedString(factory.getProxiedInterfaces(), ",")); assertEquals("Found correct number of interfaces", 5, factory.getProxiedInterfaces().length); ITestBean tb = (ITestBean) factory.getProxy(); assertThat("Picked up secondary interface", tb, instanceOf(IOther.class)); raw.setAge(25); assertTrue(tb.getAge() == raw.getAge()); long t = 555555L; TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(t); Class<?>[] oldProxiedInterfaces = factory.getProxiedInterfaces(); factory.addAdvisor(0, new DefaultIntroductionAdvisor(ti, TimeStamped.class)); Class<?>[] newProxiedInterfaces = factory.getProxiedInterfaces(); assertEquals("Advisor proxies one more interface after introduction", oldProxiedInterfaces.length + 1, newProxiedInterfaces.length); TimeStamped ts = (TimeStamped) factory.getProxy(); assertTrue(ts.getTimeStamp() == t); // Shouldn't fail; ((IOther) ts).absquatulate(); } @Test public void testInterceptorInclusionMethods() { class MyInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { throw new UnsupportedOperationException(); } } NopInterceptor di = new NopInterceptor(); NopInterceptor diUnused = new NopInterceptor(); ProxyFactory factory = new ProxyFactory(new TestBean()); factory.addAdvice(0, di); assertThat(factory.getProxy(), instanceOf(ITestBean.class)); assertTrue(factory.adviceIncluded(di)); assertTrue(!factory.adviceIncluded(diUnused)); assertTrue(factory.countAdvicesOfType(NopInterceptor.class) == 1); assertTrue(factory.countAdvicesOfType(MyInterceptor.class) == 0); factory.addAdvice(0, diUnused); assertTrue(factory.adviceIncluded(diUnused)); assertTrue(factory.countAdvicesOfType(NopInterceptor.class) == 2); } /** * Should see effect immediately on behavior. */ @Test public void testCanAddAndRemoveAspectInterfacesOnSingleton() { ProxyFactory config = new ProxyFactory(new TestBean()); assertFalse("Shouldn't implement TimeStamped before manipulation", config.getProxy() instanceof TimeStamped); long time = 666L; TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(); ti.setTime(time); // Add to front of interceptor chain int oldCount = config.getAdvisors().length; config.addAdvisor(0, new DefaultIntroductionAdvisor(ti, TimeStamped.class)); assertTrue(config.getAdvisors().length == oldCount + 1); TimeStamped ts = (TimeStamped) config.getProxy(); assertTrue(ts.getTimeStamp() == time); // Can remove config.removeAdvice(ti); assertTrue(config.getAdvisors().length == oldCount); try { // Existing reference will fail ts.getTimeStamp(); fail("Existing object won't implement this interface any more"); } catch (RuntimeException ex) { } assertFalse("Should no longer implement TimeStamped", config.getProxy() instanceof TimeStamped); // Now check non-effect of removing interceptor that isn't there config.removeAdvice(new DebugInterceptor()); assertTrue(config.getAdvisors().length == oldCount); ITestBean it = (ITestBean) ts; DebugInterceptor debugInterceptor = new DebugInterceptor(); config.addAdvice(0, debugInterceptor); it.getSpouse(); assertEquals(1, debugInterceptor.getCount()); config.removeAdvice(debugInterceptor); it.getSpouse(); // not invoked again assertTrue(debugInterceptor.getCount() == 1); } @Test public void testProxyTargetClassWithInterfaceAsTarget() { ProxyFactory pf = new ProxyFactory(); pf.setTargetClass(ITestBean.class); Object proxy = pf.getProxy(); assertTrue("Proxy is a JDK proxy", AopUtils.isJdkDynamicProxy(proxy)); assertTrue(proxy instanceof ITestBean); assertEquals(ITestBean.class, AopProxyUtils.ultimateTargetClass(proxy)); ProxyFactory pf2 = new ProxyFactory(proxy); Object proxy2 = pf2.getProxy(); assertTrue("Proxy is a JDK proxy", AopUtils.isJdkDynamicProxy(proxy2)); assertTrue(proxy2 instanceof ITestBean); assertEquals(ITestBean.class, AopProxyUtils.ultimateTargetClass(proxy2)); } @Test public void testProxyTargetClassWithConcreteClassAsTarget() { ProxyFactory pf = new ProxyFactory(); pf.setTargetClass(TestBean.class); Object proxy = pf.getProxy(); assertTrue("Proxy is a CGLIB proxy", AopUtils.isCglibProxy(proxy)); assertTrue(proxy instanceof TestBean); assertEquals(TestBean.class, AopProxyUtils.ultimateTargetClass(proxy)); ProxyFactory pf2 = new ProxyFactory(proxy); pf2.setProxyTargetClass(true); Object proxy2 = pf2.getProxy(); assertTrue("Proxy is a CGLIB proxy", AopUtils.isCglibProxy(proxy2)); assertTrue(proxy2 instanceof TestBean); assertEquals(TestBean.class, AopProxyUtils.ultimateTargetClass(proxy2)); } @Test @Ignore("Not implemented yet, see http://jira.springframework.org/browse/SPR-5708") public void testExclusionOfNonPublicInterfaces() { JFrame frame = new JFrame(); ProxyFactory proxyFactory = new ProxyFactory(frame); Object proxy = proxyFactory.getProxy(); assertTrue(proxy instanceof RootPaneContainer); assertTrue(proxy instanceof Accessible); } @Test public void testInterfaceProxiesCanBeOrderedThroughAnnotations() { Object proxy1 = new ProxyFactory(new A()).getProxy(); Object proxy2 = new ProxyFactory(new B()).getProxy(); List<Object> list = new ArrayList<>(2); list.add(proxy1); list.add(proxy2); AnnotationAwareOrderComparator.sort(list); assertSame(proxy2, list.get(0)); assertSame(proxy1, list.get(1)); } @Test public void testTargetClassProxiesCanBeOrderedThroughAnnotations() { ProxyFactory pf1 = new ProxyFactory(new A()); pf1.setProxyTargetClass(true); ProxyFactory pf2 = new ProxyFactory(new B()); pf2.setProxyTargetClass(true); Object proxy1 = pf1.getProxy(); Object proxy2 = pf2.getProxy(); List<Object> list = new ArrayList<>(2); list.add(proxy1); list.add(proxy2); AnnotationAwareOrderComparator.sort(list); assertSame(proxy2, list.get(0)); assertSame(proxy1, list.get(1)); } @SuppressWarnings("serial") private static class TimestampIntroductionInterceptor extends DelegatingIntroductionInterceptor implements TimeStamped { private long ts; public TimestampIntroductionInterceptor() { } public TimestampIntroductionInterceptor(long ts) { this.ts = ts; } public void setTime(long ts) { this.ts = ts; } @Override public long getTimeStamp() { return ts; } } @Order(2) public static class A implements Runnable { @Override public void run() { } } @Order(1) public static class B implements Runnable{ @Override public void run() { } } }