001: /**
002: * EasyBeans
003: * Copyright (C) 2006 Bull S.A.S.
004: * Contact: easybeans@ow2.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: InterceptorsClassResolver.java 2057 2007-11-21 15:35:32Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.deployment.annotations.helper.bean;
025:
026: import java.util.ArrayList;
027: import java.util.HashMap;
028: import java.util.LinkedList;
029: import java.util.List;
030: import java.util.Map;
031:
032: import javax.ejb.Remove;
033:
034: import org.ow2.easybeans.asm.Type;
035: import org.ow2.easybeans.container.session.stateful.interceptors.RemoveAlwaysInterceptor;
036: import org.ow2.easybeans.container.session.stateful.interceptors.RemoveOnlyWithoutExceptionInterceptor;
037: import org.ow2.easybeans.deployment.annotations.InterceptorType;
038: import org.ow2.easybeans.deployment.annotations.JClassInterceptor;
039: import org.ow2.easybeans.deployment.annotations.JMethod;
040: import org.ow2.easybeans.deployment.annotations.exceptions.ResolverException;
041: import org.ow2.easybeans.deployment.annotations.impl.JInterceptors;
042: import org.ow2.easybeans.deployment.annotations.metadata.ClassAnnotationMetadata;
043: import org.ow2.easybeans.deployment.annotations.metadata.EjbJarAnnotationMetadata;
044: import org.ow2.easybeans.deployment.annotations.metadata.MethodAnnotationMetadata;
045: import org.ow2.easybeans.naming.interceptors.ENCManager;
046:
047: import static org.ow2.easybeans.deployment.annotations.helper.bean.InheritanceInterfacesHelper.JAVA_LANG_OBJECT;
048:
049: /**
050: * This class sets the EasyBeans interceptors used when invoking business methods and also for life cycle events.
051: * @author Florent Benoit
052: */
053: public final class InterceptorsClassResolver {
054:
055: /**
056: * Signature of EasyBeans interceptors.
057: */
058: private static final JMethod EASYBEANS_INTERCEPTOR = new JMethod(
059: 0,
060: "intercept",
061: "(Lorg/ow2/easybeans/api/EasyBeansInvocationContext;)Ljava/lang/Object;",
062: null, new String[] { "java/lang/Exception" });
063:
064: /**
065: * Helper class, no public constructor.
066: */
067: private InterceptorsClassResolver() {
068:
069: }
070:
071: /**
072: * Found all interceptors of the class (including business and lifecycle events) and also set EasyBeans interceptors.
073: * @param classAnnotationMetadata class to analyze
074: * @throws ResolverException if metadata is missing
075: */
076: public static void resolve(
077: final ClassAnnotationMetadata classAnnotationMetadata)
078: throws ResolverException {
079:
080: // First, EasyBeans interceptors
081: List<String> easyBeansInterceptorsClasses = new ArrayList<String>();
082: // Add EasyBeans interceptors for all methods
083: easyBeansInterceptorsClasses.add(Type
084: .getInternalName(ENCManager.getInterceptorClass()));
085: // Set list of global interceptors (applied on all business methods)
086: List<JClassInterceptor> easyBeansGlobalInterceptors = new ArrayList<JClassInterceptor>();
087: for (String easyBeansInterceptor : easyBeansInterceptorsClasses) {
088: easyBeansGlobalInterceptors.add(new JClassInterceptor(
089: easyBeansInterceptor, EASYBEANS_INTERCEPTOR));
090: }
091: classAnnotationMetadata
092: .setGlobalEasyBeansInterceptors(easyBeansGlobalInterceptors);
093:
094: // Default interceptors (only once as it is stored in the ejb metadata)
095: EjbJarAnnotationMetadata ejbJarAnnotationMetadata = classAnnotationMetadata
096: .getEjbJarAnnotationMetadata();
097: JInterceptors defaultInterceptorsClasses = ejbJarAnnotationMetadata
098: .getDefaultInterceptorsClasses();
099: Map<InterceptorType, List<JClassInterceptor>> mapDefaultInterceptors = ejbJarAnnotationMetadata
100: .getDefaultInterceptors();
101: if (mapDefaultInterceptors == null
102: && defaultInterceptorsClasses != null
103: && defaultInterceptorsClasses.size() > 0) {
104: Map<InterceptorType, List<JClassInterceptor>> defaultInterceptors = getInterceptors(
105: classAnnotationMetadata.getClassName(),
106: classAnnotationMetadata
107: .getEjbJarAnnotationMetadata(),
108: defaultInterceptorsClasses.getClasses());
109: ejbJarAnnotationMetadata
110: .setDefaultInterceptors(defaultInterceptors);
111: }
112:
113: // And then, set the user interceptors (found in external class)
114: List<String> externalInterceptorsClasses = new ArrayList<String>();
115:
116: // Interceptors from other classes (will be analyzed after)
117: // See 3.5.3 of simplified EJB 3.0 spec : multiple interceptors
118: // Add interceptor classes found on super classes (if any)
119:
120: // Invert list of the inheritance on the current class
121: LinkedList<ClassAnnotationMetadata> invertedInheritanceClassesList = getInvertedSuperClassesMetadata(classAnnotationMetadata);
122: // Add interceptors found on these classes (order is super super class
123: // before super class, ie : top level first)
124: for (ClassAnnotationMetadata super MetaData : invertedInheritanceClassesList) {
125: JInterceptors classIinterceptors = super MetaData
126: .getAnnotationInterceptors();
127: if (classIinterceptors != null) {
128: for (String cls : classIinterceptors.getClasses()) {
129: externalInterceptorsClasses.add(cls);
130: }
131: }
132: }
133:
134: // Get the interceptors defined by the user on external classes
135: Map<InterceptorType, List<JClassInterceptor>> externalMapClassInterceptors = getInterceptors(
136: classAnnotationMetadata.getClassName(),
137: classAnnotationMetadata.getEjbJarAnnotationMetadata(),
138: externalInterceptorsClasses);
139: classAnnotationMetadata
140: .setExternalUserInterceptors(externalMapClassInterceptors);
141:
142: // interceptor in the bean class ? (LifeCycle event interceptors are not in the bean
143: // because they don't take an InvocationContext as parameter, this is the intercepted method
144: List<String> internalInterceptorsClasses = new ArrayList<String>();
145:
146: if (classAnnotationMetadata.isAroundInvokeMethodMetadata()) {
147: internalInterceptorsClasses.add(classAnnotationMetadata
148: .getClassName());
149: }
150: // Get the interceptors defined by the user on the class
151: Map<InterceptorType, List<JClassInterceptor>> internalMapClassInterceptors = getInterceptors(
152: classAnnotationMetadata.getClassName(),
153: classAnnotationMetadata.getEjbJarAnnotationMetadata(),
154: internalInterceptorsClasses);
155: classAnnotationMetadata
156: .setInternalUserInterceptors(internalMapClassInterceptors);
157:
158: // Now, analyze each interceptors found on methods.
159: for (MethodAnnotationMetadata methodAnnotationMetaData : classAnnotationMetadata
160: .getMethodAnnotationMetadataCollection()) {
161:
162: // Set global interceptors for a given method (ie : Remove)
163: Remove remove = methodAnnotationMetaData.getJRemove();
164: if (remove != null) {
165: List<JClassInterceptor> easyBeansMethodGlobalInterceptors = new ArrayList<JClassInterceptor>();
166: String classType = null;
167: // choose right interceptor class
168: if (remove.retainIfException()) {
169: classType = Type
170: .getInternalName(RemoveOnlyWithoutExceptionInterceptor.class);
171: } else {
172: classType = Type
173: .getInternalName(RemoveAlwaysInterceptor.class);
174: }
175: easyBeansMethodGlobalInterceptors
176: .add(new JClassInterceptor(classType,
177: EASYBEANS_INTERCEPTOR));
178:
179: // set list
180: methodAnnotationMetaData
181: .setGlobalEasyBeansInterceptors(easyBeansMethodGlobalInterceptors);
182: }
183:
184: JInterceptors methodAnnotationInterceptors = methodAnnotationMetaData
185: .getAnnotationInterceptors();
186: if (methodAnnotationInterceptors != null) {
187: List<String> methodInterceptorsClasses = new ArrayList<String>();
188: for (String cls : methodAnnotationInterceptors
189: .getClasses()) {
190: methodInterceptorsClasses.add(cls);
191: }
192: Map<InterceptorType, List<JClassInterceptor>> mapMethodInterceptors = getInterceptors(
193: classAnnotationMetadata.getClassName()
194: + "/Method "
195: + methodAnnotationMetaData
196: .getMethodName(),
197: classAnnotationMetadata
198: .getEjbJarAnnotationMetadata(),
199: methodInterceptorsClasses);
200: methodAnnotationMetaData
201: .setUserInterceptors(mapMethodInterceptors);
202: }
203:
204: }
205:
206: }
207:
208: /**
209: * Found interceptors method in the given class. It will analyze each interceptor class and
210: * fill a structure with a mapping between the annotation type and the corresponding interceptors.
211: * @param referencingName name of the class/method that reference these interceptors
212: * @param ejbJarAnnotationMetadata root of all annotations (used to find the metadata)
213: * @param interceptorsClasses list of classes that contains interceptors
214: * @return the map between the type of interceptor (PostConstrut, AroundInvoke, ...) and the JClassInterceptor objects
215: * @throws ResolverException if analyze fails
216: */
217: private static Map<InterceptorType, List<JClassInterceptor>> getInterceptors(
218: final String referencingName,
219: final EjbJarAnnotationMetadata ejbJarAnnotationMetadata,
220: final List<String> interceptorsClasses)
221: throws ResolverException {
222: // Define the mapping object.
223: Map<InterceptorType, List<JClassInterceptor>> mapInterceptors = new HashMap<InterceptorType, List<JClassInterceptor>>();
224: // Init the map for each interceptor type
225: for (InterceptorType type : InterceptorType.values()) {
226: mapInterceptors.put(type,
227: new ArrayList<JClassInterceptor>());
228: }
229:
230: int interceptorClassAnalyzed = 0;
231:
232: // For each interceptors classes, take the method with @AroundInvoke or @PostConstruct, etc. and build list
233: for (String className : interceptorsClasses) {
234: ClassAnnotationMetadata interceptorMetadata = ejbJarAnnotationMetadata
235: .getClassAnnotationMetadata(className);
236: if (interceptorMetadata == null) {
237: throw new ResolverException(
238: "No medata for interceptor class " + className
239: + " referenced by " + referencingName);
240: }
241:
242: // Another interceptor class
243: interceptorClassAnalyzed++;
244:
245: // No inner class of the bean
246: if (interceptorMetadata.getClassName().contains("$")) {
247: throw new IllegalStateException(
248: "Interceptor can't be defined in an inner class.");
249: }
250:
251: // Takes all methods of the super class and add them to the current class.
252: InheritanceMethodResolver.resolve(interceptorMetadata);
253:
254: // Invert list of the inheritance on the current class
255: LinkedList<ClassAnnotationMetadata> invertedInheritanceClassesList = getInvertedSuperClassesMetadata(interceptorMetadata);
256:
257: // For each class (starting super class first, add the interceptor methods)
258: for (ClassAnnotationMetadata currentMetaData : invertedInheritanceClassesList) {
259: // Analyze methods of the interceptor meta-data and add it in the map
260: for (MethodAnnotationMetadata method : currentMetaData
261: .getMethodAnnotationMetadataCollection()) {
262: // Don't look inherited methods.
263: if (method.isInherited()) {
264: continue;
265: }
266: JClassInterceptor jInterceptor = new JClassInterceptor(
267: className, method.getJMethod(),
268: interceptorClassAnalyzed);
269:
270: // If the method is overriden, take care of using the
271: // annotation of the lower class in the inheritance classes.
272: // As the method is only add once for a single interceptor
273: // class.
274: MethodAnnotationMetadata analyzedMethod = method;
275: MethodAnnotationMetadata methodSubClass = interceptorMetadata
276: .getMethodAnnotationMetadata(method
277: .getJMethod());
278: if (methodSubClass != null) {
279: analyzedMethod = methodSubClass;
280: }
281:
282: // A method can be designed to run for all annotation, so no "else if" !
283: if (analyzedMethod.isAroundInvoke()) {
284: addOnlyIfNotPresent(mapInterceptors
285: .get(InterceptorType.AROUND_INVOKE),
286: jInterceptor);
287: }
288: // Only if interceptor class is not a bean's class. Else, it is only simple methods.
289: if (!currentMetaData.isBean()) {
290: // build interceptor object.
291: if (analyzedMethod.isPostActivate()) {
292: addOnlyIfNotPresent(
293: mapInterceptors
294: .get(InterceptorType.POST_ACTIVATE),
295: jInterceptor);
296: }
297: if (analyzedMethod.isPostConstruct()) {
298: addOnlyIfNotPresent(
299: mapInterceptors
300: .get(InterceptorType.POST_CONSTRUCT),
301: jInterceptor);
302: }
303: if (analyzedMethod.isPreDestroy()) {
304: addOnlyIfNotPresent(mapInterceptors
305: .get(InterceptorType.PRE_DESTROY),
306: jInterceptor);
307: }
308: if (analyzedMethod.isPrePassivate()) {
309: addOnlyIfNotPresent(
310: mapInterceptors
311: .get(InterceptorType.PRE_PASSIVATE),
312: jInterceptor);
313: }
314: }
315: }
316: }
317:
318: }
319:
320: return mapInterceptors;
321: }
322:
323: /**
324: * Adds in the given interceptors list the interceptor object.
325: * If the object is already present in the list, doesn't add it again.
326: * @param interceptors the list of interceptors.
327: * @param jInterceptor the interceptor to add.
328: */
329: private static void addOnlyIfNotPresent(
330: final List<JClassInterceptor> interceptors,
331: final JClassInterceptor jInterceptor) {
332: if (!interceptors.contains(jInterceptor)) {
333: interceptors.add(jInterceptor);
334: }
335: }
336:
337: /**
338: * Gets the inverted list of metadata for a given class (super class is the first one in the list).
339: * @param classAnnotationMetadata the class to analyze
340: * @return the given list
341: */
342: private static LinkedList<ClassAnnotationMetadata> getInvertedSuperClassesMetadata(
343: final ClassAnnotationMetadata classAnnotationMetadata) {
344:
345: // get list of super classes
346: LinkedList<ClassAnnotationMetadata> super ClassesList = new LinkedList<ClassAnnotationMetadata>();
347: String super ClassName = classAnnotationMetadata.getSuperName();
348: // loop while super class is not java.lang.Object
349: while (!JAVA_LANG_OBJECT.equals(super ClassName)) {
350: ClassAnnotationMetadata superMetaData = classAnnotationMetadata
351: .getEjbJarAnnotationMetadata()
352: .getClassAnnotationMetadata(superClassName);
353: if (superMetaData != null) {
354: superClassName = superMetaData.getSuperName();
355: superClassesList.addFirst(superMetaData);
356: } else {
357: superClassName = JAVA_LANG_OBJECT;
358: }
359: }
360: superClassesList.addLast(classAnnotationMetadata);
361: return superClassesList;
362: }
363:
364: }
|