1 /*
2  * Copyright 2002-2009 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.annotation;
18 
19 import org.aspectj.lang.annotation.Aspect;
20 import org.aspectj.lang.reflect.AjType;
21 import org.aspectj.lang.reflect.AjTypeSystem;
22 import org.aspectj.lang.reflect.PerClauseKind;
23 
24 import org.springframework.aop.Pointcut;
25 import org.springframework.aop.aspectj.AspectJExpressionPointcut;
26 import org.springframework.aop.aspectj.TypePatternClassFilter;
27 import org.springframework.aop.framework.AopConfigException;
28 import org.springframework.aop.support.ComposablePointcut;
29 
30 /**
31  * Metadata for an AspectJ aspect class, with an additional Spring AOP pointcut
32  * for the per clause.
33  *
34  * <p>Uses AspectJ 5 AJType reflection API, so is only supported on Java 5.
35  * Enables us to work with different AspectJ instantiation models such as
36  * "singleton", "pertarget" and "perthis".
37  *
38  * @author Rod Johnson
39  * @author Juergen Hoeller
40  * @since 2.0
41  * @see org.springframework.aop.aspectj.AspectJExpressionPointcut
42  */
43 public class AspectMetadata {
44 
45 	/**
46 	 * AspectJ reflection information (AspectJ 5 / Java 5 specific).
47 	 */
48 	private final AjType ajType;
49 
50 	/**
51 	 * Spring AOP pointcut corresponding to the per clause of the
52 	 * aspect. Will be the Pointcut.TRUE canonical instance in the
53 	 * case of a singleton, otherwise an AspectJExpressionPointcut.
54 	 */
55 	private final Pointcut perClausePointcut;
56 
57 	/**
58 	 * The name of this aspect as defined to Spring (the bean name) -
59 	 * allows us to determine if two pieces of advice come from the
60 	 * same aspect and hence their relative precedence.
61 	 */
62 	private String aspectName;
63 
64 
65 	/**
66 	 * Create a new AspectMetadata instance for the given aspect class.
67 	 * @param aspectClass the aspect class
68 	 * @param aspectName the name of the aspect
69 	 */
AspectMetadata(Class<?> aspectClass, String aspectName)70 	public AspectMetadata(Class<?> aspectClass, String aspectName) {
71 		this.aspectName = aspectName;
72 
73 		Class<?> currClass = aspectClass;
74 		AjType ajType = null;
75 		while (!currClass.equals(Object.class)) {
76 			AjType ajTypeToCheck = AjTypeSystem.getAjType(currClass);
77 			if (ajTypeToCheck.isAspect()) {
78 				ajType = ajTypeToCheck;
79 				break;
80 			}
81 			currClass = currClass.getSuperclass();
82 		}
83 		if (ajType == null) {
84 			throw new IllegalArgumentException("Class '" + aspectClass.getName() + "' is not an @AspectJ aspect");
85 		}
86 		this.ajType = ajType;
87 		if (this.ajType.getDeclarePrecedence().length > 0) {
88 			throw new IllegalArgumentException("DeclarePrecendence not presently supported in Spring AOP");
89 		}
90 
91 		switch (this.ajType.getPerClause().getKind()) {
92 			case SINGLETON :
93 				this.perClausePointcut = Pointcut.TRUE;
94 				return;
95 			case PERTARGET : case PERTHIS :
96 				AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
97 				ajexp.setLocation("@Aspect annotation on " + aspectClass.getName());
98 				ajexp.setExpression(findPerClause(aspectClass));
99 				this.perClausePointcut = ajexp;
100 				return;
101 			case PERTYPEWITHIN :
102 				// Works with a type pattern
103 				this.perClausePointcut = new ComposablePointcut(new TypePatternClassFilter(findPerClause(aspectClass)));
104 				return;
105 			default :
106 				throw new AopConfigException(
107 						"PerClause " + ajType.getPerClause().getKind() + " not supported by Spring AOP for " + aspectClass);
108 		}
109 	}
110 
111 	/**
112 	 * Extract contents from String of form <code>pertarget(contents)</code>.
113 	 */
findPerClause(Class<?> aspectClass)114 	private String findPerClause(Class<?> aspectClass) {
115 		// TODO when AspectJ provides this, we can remove this hack. Hence we don't
116 		// bother to make it elegant. Or efficient. Or robust :-)
117 		String str = aspectClass.getAnnotation(Aspect.class).value();
118 		str = str.substring(str.indexOf("(") + 1);
119 		str = str.substring(0, str.length() - 1);
120 		return str;
121 	}
122 
123 
124 	/**
125 	 * Return AspectJ reflection information.
126 	 */
getAjType()127 	public AjType getAjType() {
128 		return this.ajType;
129 	}
130 
131 	/**
132 	 * Return the aspect class.
133 	 */
getAspectClass()134 	public Class getAspectClass() {
135 		return this.ajType.getJavaClass();
136 	}
137 
138 	/**
139 	 * Return the aspect class.
140 	 */
getAspectName()141 	public String getAspectName() {
142 		return this.aspectName;
143 	}
144 
145 	/**
146 	 * Return a Spring pointcut expression for a singleton aspect.
147 	 * (e.g. <code>Pointcut.TRUE</code> if it's a singleton).
148 	 */
getPerClausePointcut()149 	public Pointcut getPerClausePointcut() {
150 		return this.perClausePointcut;
151 	}
152 
153 	/**
154 	 * Return whether the aspect is defined as "perthis" or "pertarget".
155 	 */
isPerThisOrPerTarget()156 	public boolean isPerThisOrPerTarget() {
157 		PerClauseKind kind = getAjType().getPerClause().getKind();
158 		return (kind == PerClauseKind.PERTARGET || kind == PerClauseKind.PERTHIS);
159 	}
160 
161 	/**
162 	 * Return whether the aspect is defined as "pertypewithin".
163 	 */
isPerTypeWithin()164 	public boolean isPerTypeWithin() {
165 		PerClauseKind kind = getAjType().getPerClause().getKind();
166 		return (kind == PerClauseKind.PERTYPEWITHIN);
167 	}
168 
169 	/**
170 	 * Return whether the aspect needs to be lazily instantiated.
171 	 */
isLazilyInstantiated()172 	public boolean isLazilyInstantiated() {
173 		return (isPerThisOrPerTarget() || isPerTypeWithin());
174 	}
175 
176 }
177