001: /**
002: * EasyBeans
003: * Copyright (C) 2006-2007 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: InterceptorClassAdapter.java 2057 2007-11-21 15:35:32Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.enhancer.interceptors;
025:
026: import static org.ow2.easybeans.deployment.annotations.InterceptorType.AROUND_INVOKE;
027: import static org.ow2.easybeans.deployment.annotations.InterceptorType.DEP_INJECT;
028: import static org.ow2.easybeans.deployment.annotations.InterceptorType.POST_ACTIVATE;
029: import static org.ow2.easybeans.deployment.annotations.InterceptorType.POST_CONSTRUCT;
030: import static org.ow2.easybeans.deployment.annotations.InterceptorType.PRE_DESTROY;
031: import static org.ow2.easybeans.deployment.annotations.InterceptorType.PRE_PASSIVATE;
032: import static org.ow2.easybeans.deployment.annotations.InterceptorType.TIMED_OBJECT;
033:
034: import java.util.ArrayList;
035: import java.util.Arrays;
036: import java.util.List;
037:
038: import org.ow2.easybeans.asm.ClassAdapter;
039: import org.ow2.easybeans.asm.ClassVisitor;
040: import org.ow2.easybeans.asm.Label;
041: import org.ow2.easybeans.asm.MethodAdapter;
042: import org.ow2.easybeans.asm.MethodVisitor;
043: import org.ow2.easybeans.asm.Opcodes;
044: import org.ow2.easybeans.asm.Type;
045: import org.ow2.easybeans.api.bean.lifecycle.EasyBeansMDBLifeCycle;
046: import org.ow2.easybeans.api.bean.lifecycle.EasyBeansSFSBLifeCycle;
047: import org.ow2.easybeans.api.bean.lifecycle.EasyBeansSLSBLifeCycle;
048: import org.ow2.easybeans.deployment.annotations.InterceptorType;
049: import org.ow2.easybeans.deployment.annotations.JClassInterceptor;
050: import org.ow2.easybeans.deployment.annotations.JMethod;
051: import org.ow2.easybeans.deployment.annotations.metadata.ClassAnnotationMetadata;
052: import org.ow2.easybeans.deployment.annotations.metadata.MethodAnnotationMetadata;
053: import org.ow2.easybeans.enhancer.CommonClassGenerator;
054: import org.ow2.easybeans.enhancer.DefinedClass;
055: import org.ow2.easybeans.enhancer.bean.BeanClassAdapter;
056: import org.ow2.easybeans.enhancer.injection.InjectionClassAdapter;
057: import org.ow2.easybeans.enhancer.lib.MethodRenamer;
058:
059: /**
060: * This class delegates the creation of an implementation of a
061: * EasyBeansInvocationContext interface and intercepts all business methods() of a
062: * Bean.
063: * @author Florent Benoit
064: */
065: public class InterceptorClassAdapter extends ClassAdapter implements
066: Opcodes {
067:
068: /**
069: * Metadata available by this adapter for a class.
070: */
071: private ClassAnnotationMetadata classAnnotationMetadata;
072:
073: /**
074: * List of methods which have been renamed.
075: */
076: private List<JMethod> renamedMethods = null;
077:
078: /**
079: * Mappping between className and the bytecode.
080: */
081: private List<DefinedClass> definedClasses = null;
082:
083: /**
084: * List of generated classes for each interceptor type.
085: */
086: private List<InterceptorType> generatedTypes = null;
087:
088: /**
089: * List of interceptors classes used by the bean.
090: */
091: private List<String> beanInterceptors = null;
092:
093: /**
094: * If it is true, interfaces of interceptor lifecycle will be added.
095: */
096: private boolean addInterface = true;
097:
098: /**
099: * Constructor.
100: * @param classAnnotationMetadata object containing all attributes of the
101: * class
102: * @param cv the class visitor to which this adapter must delegate calls.
103: */
104: public InterceptorClassAdapter(
105: final ClassAnnotationMetadata classAnnotationMetadata,
106: final ClassVisitor cv) {
107: this (classAnnotationMetadata, cv, false);
108: this .beanInterceptors = new ArrayList<String>();
109: }
110:
111: /**
112: * Constructor.
113: * @param classAnnotationMetadata object containing all attributes of the
114: * class
115: * @param cv the class visitor to which this adapter must delegate calls.
116: * @param addInterface adds lifecycle interface for a given bean.
117: */
118: public InterceptorClassAdapter(
119: final ClassAnnotationMetadata classAnnotationMetadata,
120: final ClassVisitor cv, final boolean addInterface) {
121: super (cv);
122: this .classAnnotationMetadata = classAnnotationMetadata;
123: this .renamedMethods = new ArrayList<JMethod>();
124: this .definedClasses = new ArrayList<DefinedClass>();
125: this .addInterface = addInterface;
126: this .generatedTypes = new ArrayList<InterceptorType>();
127: }
128:
129: /**
130: * Visits the header of the class.
131: * @param version the class version.
132: * @param access the class's access flags (see
133: * {@link org.ow2.easybeans.asm.Opcodes}). This parameter also indicates
134: * if the class is deprecated.
135: * @param name the internal name of the class (see
136: * {@link org.ow2.easybeans.asm.Type#getInternalName() getInternalName}).
137: * @param signature the signature of this class. May be <tt>null</tt> if
138: * the class is not a generic one, and does not extend or implement
139: * generic classes or interfaces.
140: * @param superName the internal of name of the super class (see
141: * {@link org.ow2.easybeans.asm.Type#getInternalName() getInternalName}).
142: * For interfaces, the super class is {@link Object}. May be
143: * <tt>null</tt>, but only for the {@link Object} class.
144: * @param interfaces the internal names of the class's interfaces (see
145: * {@link org.ow2.easybeans.asm.Type#getInternalName() getInternalName}).
146: * May be <tt>null</tt>.
147: */
148: @Override
149: public void visit(final int version, final int access,
150: final String name, final String signature,
151: final String super Name, final String[] interfaces) {
152:
153: String[] newInterfaces = null;
154:
155: // Add new interface for lifecycle (if asked)
156: if (classAnnotationMetadata.isBean() && addInterface) {
157: // copy old interfaces in the new array
158: newInterfaces = new String[interfaces.length + 1];
159: System.arraycopy(interfaces, 0, newInterfaces, 0,
160: interfaces.length);
161:
162: int indexElement = newInterfaces.length - 1;
163:
164: // Add the right interface (SLSB, SFSB, MDB)
165: if (classAnnotationMetadata.isStateless()) {
166: newInterfaces[indexElement] = Type
167: .getInternalName(EasyBeansSLSBLifeCycle.class);
168: } else if (classAnnotationMetadata.isStateful()) {
169: newInterfaces[indexElement] = Type
170: .getInternalName(EasyBeansSFSBLifeCycle.class);
171: } else if (classAnnotationMetadata.isMdb()) {
172: newInterfaces[indexElement] = Type
173: .getInternalName(EasyBeansMDBLifeCycle.class);
174: } else {
175: throw new IllegalStateException("Bean '"
176: + classAnnotationMetadata.getClassName()
177: + "' not SLSB, SFSB or MDB");
178: }
179: } else {
180: newInterfaces = interfaces;
181: }
182:
183: super .visit(version, access, name, signature, super Name,
184: newInterfaces);
185:
186: }
187:
188: /**
189: * Visits information about an inner class. This inner class is not
190: * necessarily a member of the class being visited.
191: * @param name the internal name of an inner class (see
192: * {@link org.ow2.easybeans.asm.Type#getInternalName() getInternalName}).
193: * @param outerName the internal name of the class to which the inner class
194: * belongs (see
195: * {@link org.ow2.easybeans.asm.Type#getInternalName() getInternalName}).
196: * May be <tt>null</tt>.
197: * @param innerName the (simple) name of the inner class inside its
198: * enclosing class. May be <tt>null</tt> for anonymous inner
199: * classes.
200: * @param access the access flags of the inner class as originally declared
201: * in the enclosing class.
202: */
203: @Override
204: public void visitInnerClass(final String name,
205: final String outerName, final String innerName,
206: final int access) {
207: super .visitInnerClass(name, outerName, innerName, access);
208: }
209:
210: /**
211: * Visits a method of the class. T
212: * @param access the method's access flags (see {@link Opcodes}). This
213: * parameter also indicates if the method is synthetic and/or
214: * deprecated.
215: * @param name the method's name.
216: * @param desc the method's descriptor (see {@link org.ow2.easybeans.asm.Type}).
217: * @param signature the method's signature. May be <tt>null</tt> if the
218: * method parameters, return type and exceptions do not use generic
219: * types.
220: * @param exceptions the internal names of the method's exception classes
221: * (see
222: * {@link org.ow2.easybeans.asm.Type#getInternalName() getInternalName}).
223: * May be <tt>null</tt>.
224: * @return an object to visit the byte code of the method, or <tt>null</tt>
225: * if this class visitor is not interested in visiting the code of
226: * this method.
227: */
228: @Override
229: public MethodVisitor visitMethod(final int access,
230: final String name, final String desc,
231: final String signature, final String[] exceptions) {
232: JMethod jMethod = new JMethod(access, name, desc, signature,
233: exceptions);
234: String newName = name;
235: int newAccess = access;
236:
237: // Intercepted method : need to change the method name
238: if (isInterceptedMethod(jMethod)) {
239: // Add the method as renamed
240: renamedMethods.add(jMethod);
241:
242: // Rename the method name
243: newName = MethodRenamer.encode(name);
244: }
245:
246: // Interceptor method : need to change access to public.
247: if (!isDependencyInjectionMethod(jMethod)
248: && !isInjectedMethod(jMethod)
249: && isInterceptorMethod(jMethod)) {
250: // Change modifier to public
251: newAccess = Opcodes.ACC_PUBLIC;
252: }
253: return new MethodAdapter(super .visitMethod(newAccess, newName,
254: desc, signature, exceptions));
255: }
256:
257: /**
258: * Visits the end of the class. This method, which is the last one to be
259: * called, is used to inform the visitor that all the fields and methods of
260: * the class have been visited.
261: */
262: @Override
263: public void visitEnd() {
264: super .visitEnd();
265:
266: // For Bean only
267: if (classAnnotationMetadata.isBean()) {
268: // Add default lifecycle methods. These methods will call defined
269: // lifecycle callback method and super methods or will do nothing.
270: MethodAnnotationMetadata posConsMetaData = generateBeanLifeCycleMethod(
271: classAnnotationMetadata, POST_CONSTRUCT);
272: MethodAnnotationMetadata preDesMetaData = generateBeanLifeCycleMethod(
273: classAnnotationMetadata, PRE_DESTROY);
274: MethodAnnotationMetadata postActMetaData = generateBeanLifeCycleMethod(
275: classAnnotationMetadata, POST_ACTIVATE);
276: MethodAnnotationMetadata prePassMetaData = generateBeanLifeCycleMethod(
277: classAnnotationMetadata, PRE_PASSIVATE);
278:
279: // Generate class for dependency injection
280: generateClass(new MethodAnnotationMetadata(
281: InjectionClassAdapter.INJECTED_JMETHOD,
282: classAnnotationMetadata), DEP_INJECT);
283:
284: // Generate class for timer
285: // Create the method by cloning the existing timer method
286: MethodAnnotationMetadata timerMethodAnnotationMetadata = null;
287: for (MethodAnnotationMetadata m : classAnnotationMetadata
288: .getMethodAnnotationMetadataCollection()) {
289: // Found the timer method ?
290: if (m.isTimeout()) {
291: // clone this method (to get the correct interceptors, etc)
292: timerMethodAnnotationMetadata = (MethodAnnotationMetadata) m
293: .clone();
294: // Change the method name to the generated method
295: timerMethodAnnotationMetadata
296: .setJMethod(BeanClassAdapter.TIMER_JMETHOD);
297:
298: // set the class
299: timerMethodAnnotationMetadata
300: .setClassAnnotationMetadata(classAnnotationMetadata);
301: // It is not inherited as it's build on this class level
302: timerMethodAnnotationMetadata.setInherited(false,
303: null);
304: break;
305: }
306:
307: }
308: // build an empty one if not built just before
309: if (timerMethodAnnotationMetadata == null) {
310: timerMethodAnnotationMetadata = new MethodAnnotationMetadata(
311: BeanClassAdapter.TIMER_JMETHOD,
312: classAnnotationMetadata);
313: }
314: // Generate the class
315: generateClass(timerMethodAnnotationMetadata, TIMED_OBJECT);
316:
317: // Need to generate the implementation of EasyBeansInvocationContext Impl on intercepted methods
318: for (MethodAnnotationMetadata method : classAnnotationMetadata
319: .getMethodAnnotationMetadataCollection()) {
320:
321: // No else if, need to generate an invocationcontext for each case
322: if (method.isBusinessMethod()) {
323: generateClass(method, AROUND_INVOKE);
324:
325: // method was not renamed (it is inherited), need to generate a method calling super method().
326: if (!renamedMethods.contains(method.getJMethod())) {
327: generateCallSuperEncodedMethod(method);
328: }
329: }
330: }
331:
332: // First method is the method that has been generated by the call to generateBeanLifeCycleMethod
333: // This is method which needs to be intercepted. (there is always
334: // one method as we added default method, so no need to check
335: // null list)
336: generateClass(posConsMetaData, POST_CONSTRUCT);
337: generateClass(preDesMetaData, PRE_DESTROY);
338: generateClass(prePassMetaData, PRE_PASSIVATE);
339: generateClass(postActMetaData, POST_ACTIVATE);
340:
341: // Then generate the interceptorManager
342: String generatedClName = classAnnotationMetadata
343: .getClassName()
344: + EasyBeansInvocationContextGenerator.SUFFIX_INTERCEPTOR_MANAGER;
345: InterceptorManagerGenerator interceptorManagerGenerator = new InterceptorManagerGenerator(
346: classAnnotationMetadata
347: .getEjbJarAnnotationMetadata(),
348: generatedClName, beanInterceptors);
349: interceptorManagerGenerator.generate();
350: DefinedClass dc = new DefinedClass(generatedClName.replace(
351: "/", "."), interceptorManagerGenerator.getBytes());
352: // this class will be defined later on the classloader
353: definedClasses.add(dc);
354:
355: }
356: }
357:
358: /**
359: * Generates the call to InvocationContext impl proceed method after
360: * building a new object. ie :
361: *
362: * <pre>
363: * public int generatedMethodName(int a, int b) throws MyException {
364: * try {
365: * return ((Integer) new MethodAddInvocationContextImpl(this, a, b).proceed()).intValue();
366: * } catch (MyException e) {
367: * throw e;
368: * } catch (Exception e) {
369: * if (e instanceof RuntimeException) {
370: * throw (RuntimeException) e;
371: * } else {
372: * throw new RuntimeException(e);
373: * }
374: * }
375: * }
376: * </pre>
377: *
378: * @param method the annotation metadata of the method
379: * @param genInvCtx the generator of the EasyBeansInvocationContext impl class.
380: * @param interceptorType the type of method which is intercepted
381: */
382: private void generateCallToInvocationContext(
383: final MethodAnnotationMetadata method,
384: final EasyBeansInvocationContextGenerator genInvCtx,
385: final InterceptorType interceptorType) {
386:
387: /**
388: * Method name, two cases :
389: * - AroundInvoke : method = original name
390: * - LifeCycle : method = postConstructEasyBeansLifeCycle, preDestroyEasyBeansLifeCycle
391: */
392: String generatedMethodName = null;
393: switch (interceptorType) {
394: case AROUND_INVOKE:
395: generatedMethodName = method.getMethodName();
396: break;
397: case DEP_INJECT:
398: case TIMED_OBJECT:
399: generatedMethodName = MethodRenamer.decode(method
400: .getMethodName());
401: break;
402: case POST_CONSTRUCT:
403: generatedMethodName = "postConstructEasyBeansLifeCycle";
404: break;
405: case PRE_DESTROY:
406: generatedMethodName = "preDestroyEasyBeansLifeCycle";
407: break;
408: case PRE_PASSIVATE:
409: generatedMethodName = "prePassivateEasyBeansLifeCycle";
410: break;
411: case POST_ACTIVATE:
412: generatedMethodName = "postActivateEasyBeansLifeCycle";
413: break;
414: default:
415: throw new RuntimeException(
416: "No generated method name found for method '"
417: + method.getMethodName() + "'");
418: }
419:
420: if (generatedMethodName == null) {
421: throw new RuntimeException(
422: "No generated method name found for method '"
423: + method.getMethodName() + "'");
424: }
425:
426: // Adds a method which will call the invocationcontext impl
427: MethodVisitor mv = cv.visitMethod(ACC_PUBLIC,
428: generatedMethodName, method.getJMethod()
429: .getDescriptor(), null, method.getJMethod()
430: .getExceptions());
431: mv.visitCode();
432:
433: // Start of the Try label of Try/Catch
434: Label tryLabel = new Label();
435: mv.visitLabel(tryLabel);
436:
437: // build new object by calling the constructor
438: mv.visitTypeInsn(NEW, genInvCtx.getGeneratedClassName());
439: mv.visitInsn(DUP);
440:
441: // Add bean (as first argument)
442: mv.visitVarInsn(ALOAD, 0);
443:
444: // for each argument
445: Type[] args = Type.getArgumentTypes(method.getJMethod()
446: .getDescriptor());
447: int methodArg = 1;
448: for (Type type : args) {
449: int opCode = CommonClassGenerator.putFieldLoadOpCode(type
450: .getSort());
451: mv.visitVarInsn(opCode, methodArg);
452: // Double and Long are special parameters
453: if (opCode == LLOAD || opCode == DLOAD) {
454: methodArg++;
455: }
456: methodArg++;
457: }
458: Type returnType = Type.getReturnType(method.getJMethod()
459: .getDescriptor());
460:
461: String constructorDesc = genInvCtx.getConstructorDesc();
462: mv.visitMethodInsn(INVOKESPECIAL, genInvCtx
463: .getGeneratedClassName(), "<init>", constructorDesc);
464: mv.visitMethodInsn(INVOKEVIRTUAL, genInvCtx
465: .getGeneratedClassName(), "proceed",
466: "()Ljava/lang/Object;");
467:
468: CommonClassGenerator.transformObjectIntoPrimitive(returnType,
469: mv);
470: CommonClassGenerator.addReturnType(returnType, mv);
471:
472: boolean methodAlreadyThrowJavaLangException = false;
473:
474: // Catch blocks
475: String[] methodExceptions = method.getJMethod().getExceptions();
476: // catch label = exceptions thrown by method + 1
477: Label[] catchsLabel = null;
478: if (methodExceptions != null) {
479: // if the java.lang.Exception is present, don't need two catchs
480: // blocks
481: // for java/lang/Exception
482: if (Arrays.asList(methodExceptions).contains(
483: "java/lang/Exception")) {
484: methodAlreadyThrowJavaLangException = true;
485: catchsLabel = new Label[methodExceptions.length];
486: } else {
487: // else, add a catch for java.lang.Exception
488: catchsLabel = new Label[methodExceptions.length + 1];
489: }
490: } else {
491: catchsLabel = new Label[1];
492: }
493:
494: // init labels
495: for (int i = 0; i < catchsLabel.length; i++) {
496: catchsLabel[i] = new Label();
497: }
498:
499: // First, do method exceptions (just rethrow the given exception)
500: int lastCatchBlockLabel = 0;
501: if (methodAlreadyThrowJavaLangException) {
502: lastCatchBlockLabel = catchsLabel.length;
503: } else {
504: lastCatchBlockLabel = catchsLabel.length - 1;
505: }
506:
507: for (int block = 0; block < lastCatchBlockLabel; block++) {
508: mv.visitLabel(catchsLabel[block]);
509: mv.visitVarInsn(ASTORE, methodArg);
510: mv.visitVarInsn(ALOAD, methodArg);
511: mv.visitInsn(ATHROW);
512: }
513: // Now, do the wrapped of Exception into a RuntimeException
514: if (!methodAlreadyThrowJavaLangException) {
515: // start label
516: mv.visitLabel(catchsLabel[lastCatchBlockLabel]);
517: mv.visitVarInsn(ASTORE, methodArg);
518:
519: // instanceof RuntimeException
520: mv.visitVarInsn(ALOAD, methodArg);
521: mv.visitTypeInsn(INSTANCEOF, "java/lang/RuntimeException");
522: Label notInstanceOfRuntimeExceptionLabel = new Label();
523: mv.visitJumpInsn(IFEQ, notInstanceOfRuntimeExceptionLabel);
524:
525: // throw existing runtime exception (by casting it)
526: mv.visitVarInsn(ALOAD, methodArg);
527: mv.visitTypeInsn(CHECKCAST, "java/lang/RuntimeException");
528: mv.visitInsn(ATHROW);
529:
530: // build Runtime exception with given exception
531: mv.visitLabel(notInstanceOfRuntimeExceptionLabel);
532: mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
533: mv.visitInsn(DUP);
534: mv.visitVarInsn(ALOAD, methodArg);
535: mv.visitMethodInsn(INVOKESPECIAL,
536: "java/lang/RuntimeException", "<init>",
537: "(Ljava/lang/Throwable;)V");
538: mv.visitInsn(ATHROW);
539:
540: }
541:
542: // Perform try/catch blocks with ASM
543: int block = 0;
544: // method exception
545: if (methodExceptions != null) {
546: for (String exception : methodExceptions) {
547: mv.visitTryCatchBlock(tryLabel, catchsLabel[0],
548: catchsLabel[block], exception);
549: block++;
550: }
551: }
552: // Exception thrown by proceed() call
553: if (!methodAlreadyThrowJavaLangException) {
554: mv.visitTryCatchBlock(tryLabel, catchsLabel[0],
555: catchsLabel[lastCatchBlockLabel],
556: "java/lang/Exception");
557: }
558:
559: mv.visitMaxs(0, 0);
560: mv.visitEnd();
561:
562: }
563:
564: /**
565: * Generate an invocation context object.
566: * @param method intercepted method
567: * @param interceptorType the type of method which is intercepted
568: */
569: private void generateClass(final MethodAnnotationMetadata method,
570: final InterceptorType interceptorType) {
571: EasyBeansInvocationContextGenerator genInvCtx = new EasyBeansInvocationContextGenerator(
572: method, interceptorType);
573: genInvCtx.generate();
574:
575: // Get all interceptors used and that are not defined in the bean
576: for (JClassInterceptor interceptor : genInvCtx
577: .getAllInterceptors()) {
578: String interceptorClassName = interceptor.getClassName();
579: if (!interceptorClassName.equals(classAnnotationMetadata
580: .getClassName())) {
581: if (!beanInterceptors.contains(interceptorClassName)) {
582: beanInterceptors.add(interceptorClassName);
583: }
584: }
585: }
586: DefinedClass dc = new DefinedClass(genInvCtx
587: .getGeneratedClassName().replace("/", "."), genInvCtx
588: .getBytes());
589: // this class will be defined later on the classloader
590: definedClasses.add(dc);
591: generatedTypes.add(interceptorType);
592: // generate method calling generated EasyBeansInvocationContext impl
593: generateCallToInvocationContext(method, genInvCtx,
594: interceptorType);
595:
596: }
597:
598: /**
599: * Generates a call to the method defined in the super class.
600: * public int original$add(int i, int j) {
601: * return super.add(i, j);
602: * }
603: * @param method the annotation metadata of the method
604: */
605: private void generateCallSuperEncodedMethod(
606: final MethodAnnotationMetadata method) {
607:
608: String generatedMethodName = MethodRenamer.encode(method
609: .getMethodName());
610: JMethod jMethod = method.getJMethod();
611: MethodVisitor mv = cv.visitMethod(jMethod.getAccess(),
612: generatedMethodName, jMethod.getDescriptor(), jMethod
613: .getSignature(), jMethod.getExceptions());
614: mv.visitCode();
615:
616: // Add bean (as first argument)
617: mv.visitVarInsn(ALOAD, 0);
618:
619: // for each argument
620: Type[] args = Type.getArgumentTypes(jMethod.getDescriptor());
621: int methodArg = 1;
622: for (Type type : args) {
623: int opCode = CommonClassGenerator.putFieldLoadOpCode(type
624: .getSort());
625: mv.visitVarInsn(opCode, methodArg);
626: // Double and Long are special parameters
627: if (opCode == LLOAD || opCode == DLOAD) {
628: methodArg++;
629: }
630: methodArg++;
631: }
632:
633: // call super class method()
634: mv.visitMethodInsn(INVOKESPECIAL, method
635: .getClassAnnotationMetadata().getSuperName(), jMethod
636: .getName(), jMethod.getDescriptor());
637:
638: Type returnType = Type.getReturnType(jMethod.getDescriptor());
639: CommonClassGenerator.addReturnType(returnType, mv);
640:
641: mv.visitMaxs(0, 0);
642: mv.visitEnd();
643: }
644:
645: /**
646: * Generates a default method for lifecycle method events.
647: * It will call the methods in the super classes of the defined method.
648: * @param classMetaData the metadata used to generate method metadata
649: * @param interceptorType the type of intercepted method
650: * @return the generated method metadata
651: */
652: private MethodAnnotationMetadata generateBeanLifeCycleMethod(
653: final ClassAnnotationMetadata classMetaData,
654: final InterceptorType interceptorType) {
655: String generatedMethodName = null;
656: List<MethodAnnotationMetadata> existingLifecycleMethods = null;
657: switch (interceptorType) {
658: case AROUND_INVOKE:
659: case DEP_INJECT:
660: case TIMED_OBJECT:
661: //Nothing to generate
662: return null;
663: case POST_CONSTRUCT:
664: generatedMethodName = "beanPostConstruct$generated";
665: existingLifecycleMethods = classMetaData
666: .getPostConstructMethodsMetadata();
667: break;
668: case PRE_DESTROY:
669: generatedMethodName = "beanPreDestroy$generated";
670: existingLifecycleMethods = classMetaData
671: .getPreDestroyMethodsMetadata();
672: break;
673: case PRE_PASSIVATE:
674: generatedMethodName = "beanPrePassivate$generated";
675: existingLifecycleMethods = classMetaData
676: .getPrePassivateMethodsMetadata();
677: break;
678: case POST_ACTIVATE:
679: generatedMethodName = "beanPostActivate$generated";
680: existingLifecycleMethods = classMetaData
681: .getPostActivateMethodsMetadata();
682: break;
683: default:
684: throw new RuntimeException(
685: "No generated method name found for interceptorType '"
686: + interceptorType + "'");
687: }
688:
689: // Generates the body of this method.
690: MethodVisitor mv = cv.visitMethod(ACC_PUBLIC,
691: generatedMethodName, "()V", null, null);
692: mv.visitCode();
693: // Call methods in their order (if any)
694: if (existingLifecycleMethods != null) {
695: for (MethodAnnotationMetadata method : existingLifecycleMethods) {
696: // Inherited or not ?
697: String clName = method.getClassAnnotationMetadata()
698: .getClassName();
699: mv.visitVarInsn(ALOAD, 0);
700: int opcode = INVOKEVIRTUAL;
701: if (method.isInherited()) {
702: clName = method
703: .getOriginalClassAnnotationMetadata()
704: .getClassName();
705: opcode = INVOKESPECIAL;
706: }
707: mv.visitMethodInsn(opcode, clName, method
708: .getMethodName(), method.getJMethod()
709: .getDescriptor());
710: }
711: }
712:
713: mv.visitInsn(RETURN);
714: mv.visitMaxs(0, 0);
715: mv.visitEnd();
716:
717: // add method in the class metadata
718: JMethod method = new JMethod(ACC_PUBLIC, generatedMethodName,
719: "()V", null, null);
720: MethodAnnotationMetadata generatedMetadata = new MethodAnnotationMetadata(
721: method, classMetaData);
722:
723: // Set value
724: switch (interceptorType) {
725: case POST_CONSTRUCT:
726: generatedMetadata.setPostConstruct(true);
727: break;
728: case PRE_DESTROY:
729: generatedMetadata.setPreDestroy(true);
730: break;
731: case PRE_PASSIVATE:
732: generatedMetadata.setPrePassivate(true);
733: break;
734: case POST_ACTIVATE:
735: generatedMetadata.setPostActivate(true);
736: break;
737: default:
738: throw new RuntimeException(
739: "No generated method name found for interceptorType '"
740: + interceptorType + "'");
741: }
742:
743: classMetaData.addMethodAnnotationMetadata(generatedMetadata);
744: return generatedMetadata;
745: }
746:
747: /**
748: * Check if this method is the injected method used for dependency injection.
749: * @param jMethod object to check
750: * @return true if the given method is the injected method used for dependency
751: * injection
752: */
753: private boolean isDependencyInjectionMethod(final JMethod jMethod) {
754: return InjectionClassAdapter.INJECTED_METHOD.equals(jMethod
755: .getName());
756: }
757:
758: /**
759: * Check if this method is injected or not by injection class adapter : No need to add interceptors on these methods.
760: * @param jMethod object to check
761: * @return true if the given method is injected by injection class adapter.
762: */
763: private boolean isInjectedMethod(final JMethod jMethod) {
764: for (String method : InjectionClassAdapter.INJECTED_METHODS) {
765: if (method.equals(jMethod.getName())) {
766: return true;
767: }
768: }
769: return false;
770: }
771:
772: /**
773: * @param jMethod object to check
774: * @return true if the given method is an intercepted method
775: * (and business method) because lifecycle methods are not renamed
776: */
777: private boolean isInterceptedMethod(final JMethod jMethod) {
778: // needs to be intercepted
779: if (isDependencyInjectionMethod(jMethod)) {
780: return classAnnotationMetadata.isBean();
781: }
782:
783: // other injected methods are helper methods (and not business --> no need to intercept them)
784: if (isInjectedMethod(jMethod)) {
785: return false;
786: }
787:
788: // get method metadata
789: MethodAnnotationMetadata method = classAnnotationMetadata
790: .getMethodAnnotationMetadata(jMethod);
791: if (method == null) {
792: throw new IllegalStateException("Cannot find a method "
793: + jMethod + " in class "
794: + classAnnotationMetadata.getClassName());
795: }
796: return method.isBusinessMethod();
797: }
798:
799: /**
800: * @param jMethod object to check
801: * @return true if the given method is an interceptor method (ie AroundInvoke, PostConstruct, etc).
802: */
803: private boolean isInterceptorMethod(final JMethod jMethod) {
804: // get method metadata
805: MethodAnnotationMetadata method = classAnnotationMetadata
806: .getMethodAnnotationMetadata(jMethod);
807: if (method == null) {
808: throw new IllegalStateException("Cannot find a method "
809: + jMethod + " in class "
810: + classAnnotationMetadata.getClassName());
811: }
812: return (method.isAroundInvoke() || method.isLifeCycleMethod());
813: }
814:
815: /**
816: * @return list of classes generated and that need to be defined in a
817: * classloader
818: */
819: public List<DefinedClass> getDefinedClasses() {
820: return definedClasses;
821: }
822: }
|