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.Serializable;
20  
21  import org.aopalliance.intercept.MethodInterceptor;
22  import org.aopalliance.intercept.MethodInvocation;
23  import org.junit.Test;
24  import test.mixin.LockMixinAdvisor;
25  
26  import org.springframework.aop.ClassFilter;
27  import org.springframework.aop.MethodMatcher;
28  import org.springframework.aop.Pointcut;
29  import org.springframework.aop.support.AopUtils;
30  import org.springframework.aop.support.DefaultPointcutAdvisor;
31  import org.springframework.context.ApplicationContext;
32  import org.springframework.context.ApplicationContextException;
33  import org.springframework.context.support.ClassPathXmlApplicationContext;
34  import org.springframework.tests.aop.advice.CountingBeforeAdvice;
35  import org.springframework.tests.aop.interceptor.NopInterceptor;
36  import org.springframework.tests.sample.beans.ITestBean;
37  import org.springframework.tests.sample.beans.TestBean;
38  
39  import static org.hamcrest.CoreMatchers.*;
40  import static org.junit.Assert.*;
41  
42  /**
43   * Additional and overridden tests for CGLIB proxies.
44   *
45   * @author Rod Johnson
46   * @author Juergen Hoeller
47   * @author Rob Harrop
48   * @author Ramnivas Laddad
49   * @author Chris Beams
50   */
51  @SuppressWarnings("serial")
52  public final class CglibProxyTests extends AbstractAopProxyTests implements Serializable {
53  
54  	private static final String DEPENDENCY_CHECK_CONTEXT =
55  			CglibProxyTests.class.getSimpleName() + "-with-dependency-checking.xml";
56  
57  
58  	@Override
59  	protected Object createProxy(ProxyCreatorSupport as) {
60  		as.setProxyTargetClass(true);
61  		Object proxy = as.createAopProxy().getProxy();
62  		assertTrue(AopUtils.isCglibProxy(proxy));
63  		return proxy;
64  	}
65  
66  	@Override
67  	protected AopProxy createAopProxy(AdvisedSupport as) {
68  		as.setProxyTargetClass(true);
69  		return new CglibAopProxy(as);
70  	}
71  
72  	@Override
73  	protected boolean requiresTarget() {
74  		return true;
75  	}
76  
77  	@Test
78  	public void testNullConfig() {
79  		try {
80  			new CglibAopProxy(null);
81  			fail("Shouldn't allow null interceptors");
82  		}
83  		catch (IllegalArgumentException ex) {
84  			// Ok
85  		}
86  	}
87  
88  	@Test
89  	public void testNoTarget() {
90  		AdvisedSupport pc = new AdvisedSupport(new Class<?>[]{ITestBean.class});
91  		pc.addAdvice(new NopInterceptor());
92  		try {
93  			AopProxy aop = createAopProxy(pc);
94  			aop.getProxy();
95  			fail("Shouldn't allow no target with CGLIB proxy");
96  		}
97  		catch (AopConfigException ex) {
98  			// Ok
99  		}
100 	}
101 
102 	@Test
103 	public void testProtectedMethodInvocation() {
104 		ProtectedMethodTestBean bean = new ProtectedMethodTestBean();
105 		bean.value = "foo";
106 		mockTargetSource.setTarget(bean);
107 
108 		AdvisedSupport as = new AdvisedSupport(new Class<?>[]{});
109 		as.setTargetSource(mockTargetSource);
110 		as.addAdvice(new NopInterceptor());
111 		AopProxy aop = new CglibAopProxy(as);
112 
113 		ProtectedMethodTestBean proxy = (ProtectedMethodTestBean) aop.getProxy();
114 		assertTrue(AopUtils.isCglibProxy(proxy));
115 		assertEquals(proxy.getClass().getClassLoader(), bean.getClass().getClassLoader());
116 		assertEquals("foo", proxy.getString());
117 	}
118 
119 	@Test
120 	public void testPackageMethodInvocation() {
121 		PackageMethodTestBean bean = new PackageMethodTestBean();
122 		bean.value = "foo";
123 		mockTargetSource.setTarget(bean);
124 
125 		AdvisedSupport as = new AdvisedSupport(new Class<?>[]{});
126 		as.setTargetSource(mockTargetSource);
127 		as.addAdvice(new NopInterceptor());
128 		AopProxy aop = new CglibAopProxy(as);
129 
130 		PackageMethodTestBean proxy = (PackageMethodTestBean) aop.getProxy();
131 		assertTrue(AopUtils.isCglibProxy(proxy));
132 		assertEquals(proxy.getClass().getClassLoader(), bean.getClass().getClassLoader());
133 		assertEquals("foo", proxy.getString());
134 	}
135 
136 	@Test
137 	public void testPackageMethodInvocationWithDifferentClassLoader() {
138 		ClassLoader child = new ClassLoader(getClass().getClassLoader()) {
139 		};
140 
141 		PackageMethodTestBean bean = new PackageMethodTestBean();
142 		bean.value = "foo";
143 		mockTargetSource.setTarget(bean);
144 
145 		AdvisedSupport as = new AdvisedSupport(new Class<?>[]{});
146 		as.setTargetSource(mockTargetSource);
147 		as.addAdvice(new NopInterceptor());
148 		AopProxy aop = new CglibAopProxy(as);
149 
150 		PackageMethodTestBean proxy = (PackageMethodTestBean) aop.getProxy(child);
151 		assertTrue(AopUtils.isCglibProxy(proxy));
152 		assertNotEquals(proxy.getClass().getClassLoader(), bean.getClass().getClassLoader());
153 		assertNull(proxy.getString());  // we're stuck in the proxy instance
154 	}
155 
156 	@Test
157 	public void testProxyCanBeClassNotInterface() throws Exception {
158 		TestBean raw = new TestBean();
159 		raw.setAge(32);
160 		mockTargetSource.setTarget(raw);
161 		AdvisedSupport pc = new AdvisedSupport();
162 		pc.setTargetSource(mockTargetSource);
163 		AopProxy aop = new CglibAopProxy(pc);
164 
165 		Object proxy = aop.getProxy();
166 		assertTrue(AopUtils.isCglibProxy(proxy));
167 		assertTrue(proxy instanceof ITestBean);
168 		assertTrue(proxy instanceof TestBean);
169 
170 		TestBean tb = (TestBean) proxy;
171 		assertEquals(32, tb.getAge());
172 	}
173 
174 	@Test
175 	public void testMethodInvocationDuringConstructor() {
176 		CglibTestBean bean = new CglibTestBean();
177 		bean.setName("Rob Harrop");
178 
179 		AdvisedSupport as = new AdvisedSupport(new Class<?>[]{});
180 		as.setTarget(bean);
181 		as.addAdvice(new NopInterceptor());
182 		AopProxy aop = new CglibAopProxy(as);
183 
184 		CglibTestBean proxy = (CglibTestBean) aop.getProxy();
185 		assertEquals("The name property has been overwritten by the constructor", "Rob Harrop", proxy.getName());
186 	}
187 
188 	@Test
189 	public void testUnadvisedProxyCreationWithCallDuringConstructor() throws Exception {
190 		CglibTestBean target = new CglibTestBean();
191 		target.setName("Rob Harrop");
192 
193 		AdvisedSupport pc = new AdvisedSupport(new Class<?>[]{});
194 		pc.setFrozen(true);
195 		pc.setTarget(target);
196 
197 		CglibAopProxy aop = new CglibAopProxy(pc);
198 		CglibTestBean proxy = (CglibTestBean) aop.getProxy();
199 		assertNotNull("Proxy should not be null", proxy);
200 		assertEquals("Constructor overrode the value of name", "Rob Harrop", proxy.getName());
201 	}
202 
203 	@Test
204 	public void testMultipleProxies() {
205 		TestBean target = new TestBean();
206 		target.setAge(20);
207 		TestBean target2 = new TestBean();
208 		target2.setAge(21);
209 
210 		ITestBean proxy1 = getAdvisedProxy(target);
211 		ITestBean proxy2 = getAdvisedProxy(target2);
212 		assertTrue(proxy1.getClass() == proxy2.getClass());
213 		assertEquals(target.getAge(), proxy1.getAge());
214 		assertEquals(target2.getAge(), proxy2.getAge());
215 	}
216 
217 	private ITestBean getAdvisedProxy(TestBean target) {
218 		ProxyFactory pf = new ProxyFactory(new Class<?>[]{ITestBean.class});
219 		pf.setProxyTargetClass(true);
220 
221 		MethodInterceptor advice = new NopInterceptor();
222 		Pointcut pointcut = new Pointcut() {
223 			@Override
224 			public ClassFilter getClassFilter() {
225 				return ClassFilter.TRUE;
226 			}
227 			@Override
228 			public MethodMatcher getMethodMatcher() {
229 				return MethodMatcher.TRUE;
230 			}
231 			@Override
232 			public boolean equals(Object obj) {
233 				return true;
234 			}
235 			@Override
236 			public int hashCode() {
237 				return 0;
238 			}
239 		};
240 		pf.addAdvisor(new DefaultPointcutAdvisor(pointcut, advice));
241 
242 		pf.setTarget(target);
243 		pf.setFrozen(true);
244 		pf.setExposeProxy(false);
245 
246 		return (ITestBean) pf.getProxy();
247 	}
248 
249 	@Test
250 	public void testMultipleProxiesForIntroductionAdvisor() {
251 		TestBean target = new TestBean();
252 		target.setAge(20);
253 		TestBean target2 = new TestBean();
254 		target2.setAge(21);
255 
256 		ITestBean proxy1 = getIntroductionAdvisorProxy(target);
257 		ITestBean proxy2 = getIntroductionAdvisorProxy(target2);
258 		assertTrue("Incorrect duplicate creation of proxy classes", proxy1.getClass() == proxy2.getClass());
259 	}
260 
261 	private ITestBean getIntroductionAdvisorProxy(TestBean target) {
262 		ProxyFactory pf = new ProxyFactory(new Class<?>[] {ITestBean.class});
263 		pf.setProxyTargetClass(true);
264 
265 		pf.addAdvisor(new LockMixinAdvisor());
266 		pf.setTarget(target);
267 		pf.setFrozen(true);
268 		pf.setExposeProxy(false);
269 
270 		return (ITestBean) pf.getProxy();
271 	}
272 
273 	@Test
274 	public void testWithNoArgConstructor() {
275 		NoArgCtorTestBean target = new NoArgCtorTestBean("b", 1);
276 		target.reset();
277 
278 		mockTargetSource.setTarget(target);
279 		AdvisedSupport pc = new AdvisedSupport(new Class<?>[]{});
280 		pc.setTargetSource(mockTargetSource);
281 		CglibAopProxy aop = new CglibAopProxy(pc);
282 		aop.setConstructorArguments(new Object[] {"Rob Harrop", 22}, new Class<?>[] {String.class, int.class});
283 
284 		NoArgCtorTestBean proxy = (NoArgCtorTestBean) aop.getProxy();
285 		proxy = (NoArgCtorTestBean) aop.getProxy();
286 
287 		assertNotNull("Proxy should be null", proxy);
288 	}
289 
290 	@Test
291 	public void testProxyAProxy() {
292 		ITestBean target = new TestBean();
293 
294 		mockTargetSource.setTarget(target);
295 		AdvisedSupport as = new AdvisedSupport(new Class<?>[]{});
296 		as.setTargetSource(mockTargetSource);
297 		as.addAdvice(new NopInterceptor());
298 		CglibAopProxy cglib = new CglibAopProxy(as);
299 
300 		ITestBean proxy1 = (ITestBean) cglib.getProxy();
301 
302 		mockTargetSource.setTarget(proxy1);
303 		as = new AdvisedSupport(new Class<?>[]{});
304 		as.setTargetSource(mockTargetSource);
305 		as.addAdvice(new NopInterceptor());
306 		cglib = new CglibAopProxy(as);
307 
308 		assertThat(cglib.getProxy(), instanceOf(ITestBean.class));
309 	}
310 
311 	@Test
312 	public void testProxyAProxyWithAdditionalInterface() {
313 		ITestBean target = new TestBean();
314 		mockTargetSource.setTarget(target);
315 
316 		AdvisedSupport as = new AdvisedSupport(new Class<?>[]{});
317 		as.setTargetSource(mockTargetSource);
318 		as.addAdvice(new NopInterceptor());
319 		as.addInterface(Serializable.class);
320 		CglibAopProxy cglib = new CglibAopProxy(as);
321 
322 		ITestBean proxy1 = (ITestBean) cglib.getProxy();
323 
324 		mockTargetSource.setTarget(proxy1);
325 		as = new AdvisedSupport(new Class<?>[]{});
326 		as.setTargetSource(mockTargetSource);
327 		as.addAdvice(new NopInterceptor());
328 		cglib = new CglibAopProxy(as);
329 
330 		ITestBean proxy2 = (ITestBean) cglib.getProxy();
331 		assertTrue(proxy2 instanceof Serializable);
332 	}
333 
334 	@Test
335 	public void testExceptionHandling() {
336 		ExceptionThrower bean = new ExceptionThrower();
337 		mockTargetSource.setTarget(bean);
338 
339 		AdvisedSupport as = new AdvisedSupport(new Class<?>[]{});
340 		as.setTargetSource(mockTargetSource);
341 		as.addAdvice(new NopInterceptor());
342 		AopProxy aop = new CglibAopProxy(as);
343 
344 		ExceptionThrower proxy = (ExceptionThrower) aop.getProxy();
345 
346 		try {
347 			proxy.doTest();
348 		}
349 		catch (Exception ex) {
350 			assertTrue("Invalid exception class", ex instanceof ApplicationContextException);
351 		}
352 
353 		assertTrue("Catch was not invoked", proxy.isCatchInvoked());
354 		assertTrue("Finally was not invoked", proxy.isFinallyInvoked());
355 	}
356 
357 	@Test
358 	@SuppressWarnings("resource")
359 	public void testWithDependencyChecking() {
360 		ApplicationContext ctx = new ClassPathXmlApplicationContext(DEPENDENCY_CHECK_CONTEXT, getClass());
361 		ctx.getBean("testBean");
362 	}
363 
364 	@Test
365 	public void testAddAdviceAtRuntime() {
366 		TestBean bean = new TestBean();
367 		CountingBeforeAdvice cba = new CountingBeforeAdvice();
368 
369 		ProxyFactory pf = new ProxyFactory();
370 		pf.setTarget(bean);
371 		pf.setFrozen(false);
372 		pf.setOpaque(false);
373 		pf.setProxyTargetClass(true);
374 
375 		TestBean proxy = (TestBean) pf.getProxy();
376 		assertTrue(AopUtils.isCglibProxy(proxy));
377 
378 		proxy.getAge();
379 		assertEquals(0, cba.getCalls());
380 
381 		((Advised) proxy).addAdvice(cba);
382 		proxy.getAge();
383 		assertEquals(1, cba.getCalls());
384 	}
385 
386 	@Test
387 	public void testProxyProtectedMethod() throws Exception {
388 		CountingBeforeAdvice advice = new CountingBeforeAdvice();
389 		ProxyFactory proxyFactory = new ProxyFactory(new MyBean());
390 		proxyFactory.addAdvice(advice);
391 		proxyFactory.setProxyTargetClass(true);
392 
393 		MyBean proxy = (MyBean) proxyFactory.getProxy();
394 		assertEquals(4, proxy.add(1, 3));
395 		assertEquals(1, advice.getCalls("add"));
396 	}
397 
398 	@Test
399 	public void testProxyTargetClassInCaseOfNoInterfaces() throws Exception {
400 		ProxyFactory proxyFactory = new ProxyFactory(new MyBean());
401 		MyBean proxy = (MyBean) proxyFactory.getProxy();
402 		assertEquals(4, proxy.add(1, 3));
403 	}
404 
405 
406 	public static class MyBean {
407 
408 		private String name;
409 
410 		public String getName() {
411 			return name;
412 		}
413 
414 		public void setName(String name) {
415 			this.name = name;
416 		}
417 
418 		protected int add(int x, int y) {
419 			return x + y;
420 		}
421 	}
422 
423 
424 	public static class ExceptionThrower {
425 
426 		private boolean catchInvoked;
427 
428 		private boolean finallyInvoked;
429 
430 		public boolean isCatchInvoked() {
431 			return catchInvoked;
432 		}
433 
434 		public boolean isFinallyInvoked() {
435 			return finallyInvoked;
436 		}
437 
438 		public void doTest() throws Exception {
439 			try {
440 				throw new ApplicationContextException("foo");
441 			}
442 			catch (Exception ex) {
443 				catchInvoked = true;
444 				throw ex;
445 			}
446 			finally {
447 				finallyInvoked = true;
448 			}
449 		}
450 	}
451 
452 
453 	public static class NoArgCtorTestBean {
454 
455 		private boolean called = false;
456 
457 		public NoArgCtorTestBean(String x, int y) {
458 			called = true;
459 		}
460 
461 		public boolean wasCalled() {
462 			return called;
463 		}
464 
465 		public void reset() {
466 			called = false;
467 		}
468 	}
469 
470 
471 	public static class ProtectedMethodTestBean {
472 
473 		public String value;
474 
475 		protected String getString() {
476 			return this.value;
477 		}
478 	}
479 
480 
481 	public static class PackageMethodTestBean {
482 
483 		public String value;
484 
485 		String getString() {
486 			return this.value;
487 		}
488 	}
489 }
490 
491 
492 class CglibTestBean {
493 
494 	private String name;
495 
496 	public CglibTestBean() {
497 		setName("Some Default");
498 	}
499 
500 	public void setName(String name) {
501 		this.name = name;
502 	}
503 
504 	public String getName() {
505 		return this.name;
506 	}
507 }
508 
509 
510 class UnsupportedInterceptor implements MethodInterceptor {
511 
512 	@Override
513 	public Object invoke(MethodInvocation mi) throws Throwable {
514 		throw new UnsupportedOperationException(mi.getMethod().getName());
515 	}
516 }