001: /*
002: * Copyright 2002-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.aop.aspectj.annotation;
018:
019: import org.aspectj.lang.annotation.Aspect;
020: import org.aspectj.lang.reflect.AjType;
021: import org.aspectj.lang.reflect.AjTypeSystem;
022: import org.aspectj.lang.reflect.PerClauseKind;
023:
024: import org.springframework.aop.Pointcut;
025: import org.springframework.aop.aspectj.AspectJExpressionPointcut;
026: import org.springframework.aop.aspectj.TypePatternClassFilter;
027: import org.springframework.aop.framework.AopConfigException;
028: import org.springframework.aop.support.ComposablePointcut;
029:
030: /**
031: * Metadata for an AspectJ aspect class, with an additional Spring AOP pointcut
032: * for the per clause.
033: *
034: * <p>Uses AspectJ 5 AJType reflection API, so is only supported on Java 5.
035: * Enables us to work with different AspectJ instantiation models such as
036: * "singleton", "pertarget" and "perthis".
037: *
038: * @author Rod Johnson
039: * @author Juergen Hoeller
040: * @since 2.0
041: * @see org.springframework.aop.aspectj.AspectJExpressionPointcut
042: */
043: public class AspectMetadata {
044:
045: /**
046: * AspectJ reflection information (AspectJ 5 / Java 5 specific).
047: */
048: private final AjType ajType;
049:
050: /**
051: * Spring AOP pointcut corresponding to the per clause of the
052: * aspect. Will be the Pointcut.TRUE canonical instance in the
053: * case of a singleton, otherwise an AspectJExpressionPointcut.
054: */
055: private final Pointcut perClausePointcut;
056:
057: /**
058: * The name of this aspect as defined to Spring (the bean name) -
059: * allows us to determine if two pieces of advice come from the
060: * same aspect and hence their relative precedence.
061: */
062: private String aspectName;
063:
064: /**
065: * Create a new AspectMetadata instance for the given aspect class.
066: * @param aspectClass the aspect class
067: * @param aspectName the name of the aspect
068: */
069: public AspectMetadata(Class aspectClass, String aspectName) {
070: this .aspectName = aspectName;
071: this .ajType = AjTypeSystem.getAjType(aspectClass);
072:
073: if (!this .ajType.isAspect()) {
074: throw new IllegalArgumentException("Class '"
075: + aspectClass.getName()
076: + "' is not an @AspectJ aspect");
077: }
078: if (this .ajType.getDeclarePrecedence().length > 0) {
079: throw new IllegalArgumentException(
080: "DeclarePrecendence not presently supported in Spring AOP");
081: }
082:
083: switch (this .ajType.getPerClause().getKind()) {
084: case SINGLETON:
085: this .perClausePointcut = Pointcut.TRUE;
086: return;
087: case PERTARGET:
088: case PERTHIS:
089: AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
090: ajexp.setLocation("@Aspect annotation on "
091: + aspectClass.getName());
092: ajexp.setExpression(findPerClause(aspectClass));
093: this .perClausePointcut = ajexp;
094: return;
095: case PERTYPEWITHIN:
096: // Works with a type pattern
097: this .perClausePointcut = new ComposablePointcut(
098: new TypePatternClassFilter(
099: findPerClause(aspectClass)));
100: return;
101: default:
102: throw new AopConfigException("PerClause "
103: + ajType.getPerClause().getKind()
104: + " not supported by Spring AOP for " + aspectClass);
105: }
106: }
107:
108: /**
109: * Extract contents from String of form <code>pertarget(contents)</code>.
110: */
111: private String findPerClause(Class<?> aspectClass) {
112: // TODO when AspectJ provides this, we can remove this hack. Hence we don't
113: // bother to make it elegant. Or efficient. Or robust :-)
114: String str = aspectClass.getAnnotation(Aspect.class).value();
115: str = str.substring(str.indexOf("(") + 1);
116: str = str.substring(0, str.length() - 1);
117: return str;
118: }
119:
120: /**
121: * Return AspectJ reflection information.
122: */
123: public AjType getAjType() {
124: return this .ajType;
125: }
126:
127: /**
128: * Return the aspect class.
129: */
130: public Class getAspectClass() {
131: return this .ajType.getJavaClass();
132: }
133:
134: /**
135: * Return the aspect class.
136: */
137: public String getAspectName() {
138: return this .aspectName;
139: }
140:
141: /**
142: * Return a Spring pointcut expression for a singleton aspect.
143: * (e.g. <code>Pointcut.TRUE</code> if it's a singleton).
144: */
145: public Pointcut getPerClausePointcut() {
146: return this .perClausePointcut;
147: }
148:
149: /**
150: * Return whether the aspect is defined as "perthis" or "pertarget".
151: */
152: public boolean isPerThisOrPerTarget() {
153: PerClauseKind kind = getAjType().getPerClause().getKind();
154: return (kind == PerClauseKind.PERTARGET || kind == PerClauseKind.PERTHIS);
155: }
156:
157: /**
158: * Return whether the aspect is defined as "pertypewithin".
159: */
160: public boolean isPerTypeWithin() {
161: PerClauseKind kind = getAjType().getPerClause().getKind();
162: return (kind == PerClauseKind.PERTYPEWITHIN);
163: }
164:
165: /**
166: * Return whether the aspect needs to be lazily instantiated.
167: */
168: public boolean isLazilyInstantiated() {
169: return (isPerThisOrPerTarget() || isPerTypeWithin());
170: }
171:
172: }
|