View Javadoc
1   /*
2    * Copyright 2002-2013 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.aspectj;
18  
19  import java.lang.reflect.Method;
20  
21  import org.aopalliance.intercept.MethodInterceptor;
22  import org.aopalliance.intercept.MethodInvocation;
23  import org.aspectj.weaver.tools.PointcutExpression;
24  import org.aspectj.weaver.tools.PointcutPrimitive;
25  import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
26  import org.junit.Before;
27  import org.junit.Test;
28  
29  import org.springframework.aop.ClassFilter;
30  import org.springframework.aop.MethodMatcher;
31  import org.springframework.aop.Pointcut;
32  import org.springframework.aop.framework.ProxyFactory;
33  import org.springframework.aop.support.DefaultPointcutAdvisor;
34  import org.springframework.tests.sample.beans.IOther;
35  import org.springframework.tests.sample.beans.ITestBean;
36  import org.springframework.tests.sample.beans.TestBean;
37  import org.springframework.tests.sample.beans.subpkg.DeepBean;
38  
39  import static org.junit.Assert.*;
40  
41  /**
42   * @author Rob Harrop
43   * @author Rod Johnson
44   * @author Chris Beams
45   */
46  public final class AspectJExpressionPointcutTests {
47  
48  	public static final String MATCH_ALL_METHODS = "execution(* *(..))";
49  
50  	private Method getAge;
51  
52  	private Method setAge;
53  
54  	private Method setSomeNumber;
55  
56  	private Method isPostProcessed;
57  
58  
59  	@Before
60  	public void setUp() throws NoSuchMethodException {
61  		getAge = TestBean.class.getMethod("getAge", (Class<?>[])null);
62  		setAge = TestBean.class.getMethod("setAge", new Class[]{int.class});
63  		setSomeNumber = TestBean.class.getMethod("setSomeNumber", new Class[]{Number.class});
64  		isPostProcessed = TestBean.class.getMethod("isPostProcessed", (Class[]) null);
65  	}
66  
67  	@Test
68  	public void testMatchExplicit() {
69  		String expression = "execution(int org.springframework.tests.sample.beans.TestBean.getAge())";
70  
71  		Pointcut pointcut = getPointcut(expression);
72  		ClassFilter classFilter = pointcut.getClassFilter();
73  		MethodMatcher methodMatcher = pointcut.getMethodMatcher();
74  
75  		assertMatchesTestBeanClass(classFilter);
76  
77  		// not currently testable in a reliable fashion
78  		//assertDoesNotMatchStringClass(classFilter);
79  
80  		assertFalse("Should not be a runtime match", methodMatcher.isRuntime());
81  		assertMatchesGetAge(methodMatcher);
82  		assertFalse("Expression should match setAge() method", methodMatcher.matches(setAge, TestBean.class));
83  	}
84  
85  	@Test
86  	public void testMatchWithTypePattern() throws Exception {
87  		String expression = "execution(* *..TestBean.*Age(..))";
88  
89  		Pointcut pointcut = getPointcut(expression);
90  		ClassFilter classFilter = pointcut.getClassFilter();
91  		MethodMatcher methodMatcher = pointcut.getMethodMatcher();
92  
93  		assertMatchesTestBeanClass(classFilter);
94  
95  		// not currently testable in a reliable fashion
96  		//assertDoesNotMatchStringClass(classFilter);
97  
98  		assertFalse("Should not be a runtime match", methodMatcher.isRuntime());
99  		assertMatchesGetAge(methodMatcher);
100 		assertTrue("Expression should match setAge(int) method", methodMatcher.matches(setAge, TestBean.class));
101 	}
102 
103 
104 	@Test
105 	public void testThis() throws SecurityException, NoSuchMethodException{
106 		testThisOrTarget("this");
107 	}
108 
109 	@Test
110 	public void testTarget() throws SecurityException, NoSuchMethodException {
111 		testThisOrTarget("target");
112 	}
113 
114 	public static class OtherIOther implements IOther {
115 
116 		@Override
117 		public void absquatulate() {
118 			// Empty
119 		}
120 
121 	}
122 
123 	/**
124 	 * This and target are equivalent. Really instanceof pointcuts.
125 	 * @param which this or target
126 	 * @throws Exception
127 	 * @throws NoSuchMethodException
128 	 * @throws SecurityException
129 	 */
130 	private void testThisOrTarget(String which) throws SecurityException, NoSuchMethodException {
131 		String matchesTestBean = which + "(org.springframework.tests.sample.beans.TestBean)";
132 		String matchesIOther = which + "(org.springframework.tests.sample.beans.IOther)";
133 		AspectJExpressionPointcut testBeanPc = new AspectJExpressionPointcut();
134 		testBeanPc.setExpression(matchesTestBean);
135 
136 		AspectJExpressionPointcut iOtherPc = new AspectJExpressionPointcut();
137 		iOtherPc.setExpression(matchesIOther);
138 
139 		assertTrue(testBeanPc.matches(TestBean.class));
140 		assertTrue(testBeanPc.matches(getAge, TestBean.class));
141 		assertTrue(iOtherPc.matches(OtherIOther.class.getMethod("absquatulate", (Class<?>[])null),
142 				OtherIOther.class));
143 
144 		assertFalse(testBeanPc.matches(OtherIOther.class.getMethod("absquatulate", (Class<?>[])null),
145 				OtherIOther.class));
146 	}
147 
148 	@Test
149 	public void testWithinRootPackage() throws SecurityException, NoSuchMethodException {
150 		testWithinPackage(false);
151 	}
152 
153 	@Test
154 	public void testWithinRootAndSubpackages() throws SecurityException, NoSuchMethodException {
155 		testWithinPackage(true);
156 	}
157 
158 	private void testWithinPackage(boolean matchSubpackages) throws SecurityException, NoSuchMethodException {
159 		String withinBeansPackage = "within(org.springframework.tests.sample.beans.";
160 		// Subpackages are matched by **
161 		if (matchSubpackages) {
162 			withinBeansPackage += ".";
163 		}
164 		withinBeansPackage = withinBeansPackage + "*)";
165 		AspectJExpressionPointcut withinBeansPc = new AspectJExpressionPointcut();
166 		withinBeansPc.setExpression(withinBeansPackage);
167 
168 		assertTrue(withinBeansPc.matches(TestBean.class));
169 		assertTrue(withinBeansPc.matches(getAge, TestBean.class));
170 		assertEquals(matchSubpackages, withinBeansPc.matches(DeepBean.class));
171 		assertEquals(matchSubpackages, withinBeansPc.matches(
172 				DeepBean.class.getMethod("aMethod", String.class), DeepBean.class));
173 		assertFalse(withinBeansPc.matches(String.class));
174 		assertFalse(withinBeansPc.matches(OtherIOther.class.getMethod("absquatulate", (Class<?>[])null),
175 				OtherIOther.class));
176 	}
177 
178 	@Test
179 	public void testFriendlyErrorOnNoLocationClassMatching() {
180 		AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
181 		try {
182 			pc.matches(ITestBean.class);
183 			fail();
184 		}
185 		catch (IllegalStateException ex) {
186 			assertTrue(ex.getMessage().indexOf("expression") != -1);
187 		}
188 	}
189 
190 	@Test
191 	public void testFriendlyErrorOnNoLocation2ArgMatching() {
192 		AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
193 		try {
194 			pc.matches(getAge, ITestBean.class);
195 			fail();
196 		}
197 		catch (IllegalStateException ex) {
198 			assertTrue(ex.getMessage().indexOf("expression") != -1);
199 		}
200 	}
201 
202 	@Test
203 	public void testFriendlyErrorOnNoLocation3ArgMatching() {
204 		AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
205 		try {
206 			pc.matches(getAge, ITestBean.class, (Object[]) null);
207 			fail();
208 		}
209 		catch (IllegalStateException ex) {
210 			assertTrue(ex.getMessage().indexOf("expression") != -1);
211 		}
212 	}
213 
214 
215 	@Test
216 	public void testMatchWithArgs() throws Exception {
217 		String expression = "execution(void org.springframework.tests.sample.beans.TestBean.setSomeNumber(Number)) && args(Double)";
218 
219 		Pointcut pointcut = getPointcut(expression);
220 		ClassFilter classFilter = pointcut.getClassFilter();
221 		MethodMatcher methodMatcher = pointcut.getMethodMatcher();
222 
223 		assertMatchesTestBeanClass(classFilter);
224 
225 		// not currently testable in a reliable fashion
226 		//assertDoesNotMatchStringClass(classFilter);
227 
228 		assertTrue("Should match with setSomeNumber with Double input",
229 				methodMatcher.matches(setSomeNumber, TestBean.class, new Object[]{new Double(12)}));
230 		assertFalse("Should not match setSomeNumber with Integer input",
231 				methodMatcher.matches(setSomeNumber, TestBean.class, new Object[]{new Integer(11)}));
232 		assertFalse("Should not match getAge", methodMatcher.matches(getAge, TestBean.class, null));
233 		assertTrue("Should be a runtime match", methodMatcher.isRuntime());
234 	}
235 
236 	@Test
237 	public void testSimpleAdvice() {
238 		String expression = "execution(int org.springframework.tests.sample.beans.TestBean.getAge())";
239 
240 		CallCountingInterceptor interceptor = new CallCountingInterceptor();
241 
242 		TestBean testBean = getAdvisedProxy(expression, interceptor);
243 
244 		assertEquals("Calls should be 0", 0, interceptor.getCount());
245 
246 		testBean.getAge();
247 
248 		assertEquals("Calls should be 1", 1, interceptor.getCount());
249 
250 		testBean.setAge(90);
251 
252 		assertEquals("Calls should still be 1", 1, interceptor.getCount());
253 	}
254 
255 	@Test
256 	public void testDynamicMatchingProxy() {
257 		String expression = "execution(void org.springframework.tests.sample.beans.TestBean.setSomeNumber(Number)) && args(Double)";
258 
259 		CallCountingInterceptor interceptor = new CallCountingInterceptor();
260 
261 		TestBean testBean = getAdvisedProxy(expression, interceptor);
262 
263 		assertEquals("Calls should be 0", 0, interceptor.getCount());
264 
265 		testBean.setSomeNumber(new Double(30));
266 
267 		assertEquals("Calls should be 1", 1, interceptor.getCount());
268 
269 		testBean.setSomeNumber(new Integer(90));
270 
271 		assertEquals("Calls should be 1", 1, interceptor.getCount());
272 	}
273 
274 	@Test
275 	public void testInvalidExpression() {
276 		String expression = "execution(void org.springframework.tests.sample.beans.TestBean.setSomeNumber(Number) && args(Double)";
277 
278 		try {
279 			getPointcut(expression).getClassFilter();  // call to getClassFilter forces resolution
280 			fail("Invalid expression should throw IllegalArgumentException");
281 		}
282 		catch (IllegalArgumentException ex) {
283 			assertTrue(true);
284 			System.out.println(ex.getMessage());
285 		}
286 	}
287 
288 	private TestBean getAdvisedProxy(String pointcutExpression, CallCountingInterceptor interceptor) {
289 		TestBean target = new TestBean();
290 
291 		Pointcut pointcut = getPointcut(pointcutExpression);
292 
293 		DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
294 		advisor.setAdvice(interceptor);
295 		advisor.setPointcut(pointcut);
296 
297 		ProxyFactory pf = new ProxyFactory();
298 		pf.setTarget(target);
299 		pf.addAdvisor(advisor);
300 
301 		return (TestBean) pf.getProxy();
302 	}
303 
304 	private void assertMatchesGetAge(MethodMatcher methodMatcher) {
305 		assertTrue("Expression should match getAge() method", methodMatcher.matches(getAge, TestBean.class));
306 	}
307 
308 	private void assertMatchesTestBeanClass(ClassFilter classFilter) {
309 		assertTrue("Expression should match TestBean class", classFilter.matches(TestBean.class));
310 	}
311 
312 	@Test
313 	public void testWithUnsupportedPointcutPrimitive() throws Exception {
314 		String expression = "call(int org.springframework.tests.sample.beans.TestBean.getAge())";
315 
316 		try {
317 			getPointcut(expression).getClassFilter(); // call to getClassFilter forces resolution...
318 			fail("Should not support call pointcuts");
319 		}
320 		catch (UnsupportedPointcutPrimitiveException ex) {
321 			assertEquals("Should not support call pointcut", PointcutPrimitive.CALL, ex.getUnsupportedPrimitive());
322 		}
323 
324 	}
325 
326 	@Test
327 	public void testAndSubstitution() {
328 		Pointcut pc = getPointcut("execution(* *(..)) and args(String)");
329 		PointcutExpression expr =
330 			((AspectJExpressionPointcut) pc).getPointcutExpression();
331 		assertEquals("execution(* *(..)) && args(String)",expr.getPointcutExpression());
332 	}
333 
334 	@Test
335 	public void testMultipleAndSubstitutions() {
336 		Pointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
337 		PointcutExpression expr =
338 			((AspectJExpressionPointcut) pc).getPointcutExpression();
339 		assertEquals("execution(* *(..)) && args(String) && this(Object)",expr.getPointcutExpression());
340 	}
341 
342 	private Pointcut getPointcut(String expression) {
343 		AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
344 		pointcut.setExpression(expression);
345 		return pointcut;
346 	}
347 }
348 
349 
350 class CallCountingInterceptor implements MethodInterceptor {
351 
352 	private int count;
353 
354 	@Override
355 	public Object invoke(MethodInvocation methodInvocation) throws Throwable {
356 		count++;
357 		return methodInvocation.proceed();
358 	}
359 
360 	public int getCount() {
361 		return count;
362 	}
363 
364 	public void reset() {
365 		this.count = 0;
366 	}
367 
368 }