/* * Copyright 2002-2017 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.io.Serializable; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.junit.Test; import test.mixin.LockMixinAdvisor; import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; import org.springframework.aop.support.AopUtils; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextException; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.tests.aop.advice.CountingBeforeAdvice; import org.springframework.tests.aop.interceptor.NopInterceptor; import org.springframework.tests.sample.beans.ITestBean; import org.springframework.tests.sample.beans.TestBean; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; /** * Additional and overridden tests for CGLIB proxies. * * @author Rod Johnson * @author Juergen Hoeller * @author Rob Harrop * @author Ramnivas Laddad * @author Chris Beams */ @SuppressWarnings("serial") public class CglibProxyTests extends AbstractAopProxyTests implements Serializable { private static final String DEPENDENCY_CHECK_CONTEXT = CglibProxyTests.class.getSimpleName() + "-with-dependency-checking.xml"; @Override protected Object createProxy(ProxyCreatorSupport as) { as.setProxyTargetClass(true); Object proxy = as.createAopProxy().getProxy(); assertTrue(AopUtils.isCglibProxy(proxy)); return proxy; } @Override protected AopProxy createAopProxy(AdvisedSupport as) { as.setProxyTargetClass(true); return new CglibAopProxy(as); } @Override protected boolean requiresTarget() { return true; } @Test(expected = IllegalArgumentException.class) public void testNullConfig() { new CglibAopProxy(null); } @Test(expected = AopConfigException.class) public void testNoTarget() { AdvisedSupport pc = new AdvisedSupport(ITestBean.class); pc.addAdvice(new NopInterceptor()); AopProxy aop = createAopProxy(pc); aop.getProxy(); } @Test public void testProtectedMethodInvocation() { ProtectedMethodTestBean bean = new ProtectedMethodTestBean(); bean.value = "foo"; mockTargetSource.setTarget(bean); AdvisedSupport as = new AdvisedSupport(); as.setTargetSource(mockTargetSource); as.addAdvice(new NopInterceptor()); AopProxy aop = new CglibAopProxy(as); ProtectedMethodTestBean proxy = (ProtectedMethodTestBean) aop.getProxy(); assertTrue(AopUtils.isCglibProxy(proxy)); assertEquals(proxy.getClass().getClassLoader(), bean.getClass().getClassLoader()); assertEquals("foo", proxy.getString()); } @Test public void testPackageMethodInvocation() { PackageMethodTestBean bean = new PackageMethodTestBean(); bean.value = "foo"; mockTargetSource.setTarget(bean); AdvisedSupport as = new AdvisedSupport(); as.setTargetSource(mockTargetSource); as.addAdvice(new NopInterceptor()); AopProxy aop = new CglibAopProxy(as); PackageMethodTestBean proxy = (PackageMethodTestBean) aop.getProxy(); assertTrue(AopUtils.isCglibProxy(proxy)); assertEquals(proxy.getClass().getClassLoader(), bean.getClass().getClassLoader()); assertEquals("foo", proxy.getString()); } @Test public void testPackageMethodInvocationWithDifferentClassLoader() { ClassLoader child = new ClassLoader(getClass().getClassLoader()) { }; PackageMethodTestBean bean = new PackageMethodTestBean(); bean.value = "foo"; mockTargetSource.setTarget(bean); AdvisedSupport as = new AdvisedSupport(); as.setTargetSource(mockTargetSource); as.addAdvice(new NopInterceptor()); AopProxy aop = new CglibAopProxy(as); PackageMethodTestBean proxy = (PackageMethodTestBean) aop.getProxy(child); assertTrue(AopUtils.isCglibProxy(proxy)); assertNotEquals(proxy.getClass().getClassLoader(), bean.getClass().getClassLoader()); assertNull(proxy.getString()); // we're stuck in the proxy instance } @Test public void testProxyCanBeClassNotInterface() throws Exception { TestBean raw = new TestBean(); raw.setAge(32); mockTargetSource.setTarget(raw); AdvisedSupport pc = new AdvisedSupport(); pc.setTargetSource(mockTargetSource); AopProxy aop = new CglibAopProxy(pc); Object proxy = aop.getProxy(); assertTrue(AopUtils.isCglibProxy(proxy)); assertTrue(proxy instanceof ITestBean); assertTrue(proxy instanceof TestBean); TestBean tb = (TestBean) proxy; assertEquals(32, tb.getAge()); } @Test public void testMethodInvocationDuringConstructor() { CglibTestBean bean = new CglibTestBean(); bean.setName("Rob Harrop"); AdvisedSupport as = new AdvisedSupport(); as.setTarget(bean); as.addAdvice(new NopInterceptor()); AopProxy aop = new CglibAopProxy(as); CglibTestBean proxy = (CglibTestBean) aop.getProxy(); assertEquals("The name property has been overwritten by the constructor", "Rob Harrop", proxy.getName()); } @Test public void testUnadvisedProxyCreationWithCallDuringConstructor() throws Exception { CglibTestBean target = new CglibTestBean(); target.setName("Rob Harrop"); AdvisedSupport pc = new AdvisedSupport(); pc.setFrozen(true); pc.setTarget(target); CglibAopProxy aop = new CglibAopProxy(pc); CglibTestBean proxy = (CglibTestBean) aop.getProxy(); assertNotNull("Proxy should not be null", proxy); assertEquals("Constructor overrode the value of name", "Rob Harrop", proxy.getName()); } @Test public void testMultipleProxies() { TestBean target = new TestBean(); target.setAge(20); TestBean target2 = new TestBean(); target2.setAge(21); ITestBean proxy1 = getAdvisedProxy(target); ITestBean proxy2 = getAdvisedProxy(target2); assertSame(proxy1.getClass(), proxy2.getClass()); assertEquals(target.getAge(), proxy1.getAge()); assertEquals(target2.getAge(), proxy2.getAge()); } private ITestBean getAdvisedProxy(TestBean target) { ProxyFactory pf = new ProxyFactory(new Class<?>[]{ITestBean.class}); pf.setProxyTargetClass(true); MethodInterceptor advice = new NopInterceptor(); Pointcut pointcut = new Pointcut() { @Override public ClassFilter getClassFilter() { return ClassFilter.TRUE; } @Override public MethodMatcher getMethodMatcher() { return MethodMatcher.TRUE; } @Override public boolean equals(Object obj) { return true; } @Override public int hashCode() { return 0; } }; pf.addAdvisor(new DefaultPointcutAdvisor(pointcut, advice)); pf.setTarget(target); pf.setFrozen(true); pf.setExposeProxy(false); return (ITestBean) pf.getProxy(); } @Test public void testMultipleProxiesForIntroductionAdvisor() { TestBean target1 = new TestBean(); target1.setAge(20); TestBean target2 = new TestBean(); target2.setAge(21); ITestBean proxy1 = getIntroductionAdvisorProxy(target1); ITestBean proxy2 = getIntroductionAdvisorProxy(target2); assertSame("Incorrect duplicate creation of proxy classes", proxy1.getClass(), proxy2.getClass()); } private ITestBean getIntroductionAdvisorProxy(TestBean target) { ProxyFactory pf = new ProxyFactory(ITestBean.class); pf.setProxyTargetClass(true); pf.addAdvisor(new LockMixinAdvisor()); pf.setTarget(target); pf.setFrozen(true); pf.setExposeProxy(false); return (ITestBean) pf.getProxy(); } @Test public void testWithNoArgConstructor() { NoArgCtorTestBean target = new NoArgCtorTestBean("b", 1); target.reset(); mockTargetSource.setTarget(target); AdvisedSupport pc = new AdvisedSupport(); pc.setTargetSource(mockTargetSource); CglibAopProxy aop = new CglibAopProxy(pc); aop.setConstructorArguments(new Object[] {"Rob Harrop", 22}, new Class<?>[] {String.class, int.class}); NoArgCtorTestBean proxy = (NoArgCtorTestBean) aop.getProxy(); assertNotNull(proxy); } @Test public void testProxyAProxy() { ITestBean target = new TestBean(); mockTargetSource.setTarget(target); AdvisedSupport as = new AdvisedSupport(); as.setTargetSource(mockTargetSource); as.addAdvice(new NopInterceptor()); CglibAopProxy cglib = new CglibAopProxy(as); ITestBean proxy1 = (ITestBean) cglib.getProxy(); mockTargetSource.setTarget(proxy1); as = new AdvisedSupport(new Class<?>[]{}); as.setTargetSource(mockTargetSource); as.addAdvice(new NopInterceptor()); cglib = new CglibAopProxy(as); assertThat(cglib.getProxy(), instanceOf(ITestBean.class)); } @Test public void testProxyAProxyWithAdditionalInterface() { ITestBean target = new TestBean(); mockTargetSource.setTarget(target); AdvisedSupport as = new AdvisedSupport(); as.setTargetSource(mockTargetSource); as.addAdvice(new NopInterceptor()); as.addInterface(Serializable.class); CglibAopProxy cglib = new CglibAopProxy(as); ITestBean proxy1 = (ITestBean) cglib.getProxy(); mockTargetSource.setTarget(proxy1); as = new AdvisedSupport(new Class<?>[]{}); as.setTargetSource(mockTargetSource); as.addAdvice(new NopInterceptor()); cglib = new CglibAopProxy(as); ITestBean proxy2 = (ITestBean) cglib.getProxy(); assertTrue(proxy2 instanceof Serializable); } @Test public void testExceptionHandling() { ExceptionThrower bean = new ExceptionThrower(); mockTargetSource.setTarget(bean); AdvisedSupport as = new AdvisedSupport(); as.setTargetSource(mockTargetSource); as.addAdvice(new NopInterceptor()); AopProxy aop = new CglibAopProxy(as); ExceptionThrower proxy = (ExceptionThrower) aop.getProxy(); try { proxy.doTest(); } catch (Exception ex) { assertTrue("Invalid exception class", ex instanceof ApplicationContextException); } assertTrue("Catch was not invoked", proxy.isCatchInvoked()); assertTrue("Finally was not invoked", proxy.isFinallyInvoked()); } @Test @SuppressWarnings("resource") public void testWithDependencyChecking() { ApplicationContext ctx = new ClassPathXmlApplicationContext(DEPENDENCY_CHECK_CONTEXT, getClass()); ctx.getBean("testBean"); } @Test public void testAddAdviceAtRuntime() { TestBean bean = new TestBean(); CountingBeforeAdvice cba = new CountingBeforeAdvice(); ProxyFactory pf = new ProxyFactory(); pf.setTarget(bean); pf.setFrozen(false); pf.setOpaque(false); pf.setProxyTargetClass(true); TestBean proxy = (TestBean) pf.getProxy(); assertTrue(AopUtils.isCglibProxy(proxy)); proxy.getAge(); assertEquals(0, cba.getCalls()); ((Advised) proxy).addAdvice(cba); proxy.getAge(); assertEquals(1, cba.getCalls()); } @Test public void testProxyProtectedMethod() throws Exception { CountingBeforeAdvice advice = new CountingBeforeAdvice(); ProxyFactory proxyFactory = new ProxyFactory(new MyBean()); proxyFactory.addAdvice(advice); proxyFactory.setProxyTargetClass(true); MyBean proxy = (MyBean) proxyFactory.getProxy(); assertEquals(4, proxy.add(1, 3)); assertEquals(1, advice.getCalls("add")); } @Test public void testProxyTargetClassInCaseOfNoInterfaces() throws Exception { ProxyFactory proxyFactory = new ProxyFactory(new MyBean()); MyBean proxy = (MyBean) proxyFactory.getProxy(); assertEquals(4, proxy.add(1, 3)); } @Test // SPR-13328 public void testVarargsWithEnumArray() throws Exception { ProxyFactory proxyFactory = new ProxyFactory(new MyBean()); MyBean proxy = (MyBean) proxyFactory.getProxy(); assertTrue(proxy.doWithVarargs(MyEnum.A, MyOtherEnum.C)); } public static class MyBean { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } protected int add(int x, int y) { return x + y; } @SuppressWarnings("unchecked") public <V extends MyInterface> boolean doWithVarargs(V... args) { return true; } } public interface MyInterface { } public enum MyEnum implements MyInterface { A, B; } public enum MyOtherEnum implements MyInterface { C, D; } public static class ExceptionThrower { private boolean catchInvoked; private boolean finallyInvoked; public boolean isCatchInvoked() { return catchInvoked; } public boolean isFinallyInvoked() { return finallyInvoked; } public void doTest() throws Exception { try { throw new ApplicationContextException("foo"); } catch (Exception ex) { catchInvoked = true; throw ex; } finally { finallyInvoked = true; } } } public static class NoArgCtorTestBean { private boolean called = false; public NoArgCtorTestBean(String x, int y) { called = true; } public boolean wasCalled() { return called; } public void reset() { called = false; } } public static class ProtectedMethodTestBean { public String value; protected String getString() { return this.value; } } public static class PackageMethodTestBean { public String value; String getString() { return this.value; } } } class CglibTestBean { private String name; public CglibTestBean() { setName("Some Default"); } public void setName(String name) { this.name = name; } public String getName() { return this.name; } } class UnsupportedInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation mi) throws Throwable { throw new UnsupportedOperationException(mi.getMethod().getName()); } }