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: BeanClassAdapter.java 2057 2007-11-21 15:35:32Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.enhancer.bean;
025:
026: import org.ow2.easybeans.asm.ClassAdapter;
027: import org.ow2.easybeans.asm.ClassVisitor;
028: import org.ow2.easybeans.asm.MethodAdapter;
029: import org.ow2.easybeans.asm.MethodVisitor;
030: import org.ow2.easybeans.asm.Opcodes;
031: import org.ow2.easybeans.asm.Type;
032: import org.ow2.easybeans.api.Factory;
033: import org.ow2.easybeans.api.bean.EasyBeansBean;
034: import org.ow2.easybeans.api.bean.EasyBeansMDB;
035: import org.ow2.easybeans.api.bean.EasyBeansSFSB;
036: import org.ow2.easybeans.api.bean.EasyBeansSLSB;
037: import org.ow2.easybeans.api.container.EZBEJBContext;
038: import org.ow2.easybeans.deployment.annotations.JMethod;
039: import org.ow2.easybeans.deployment.annotations.metadata.ClassAnnotationMetadata;
040: import org.ow2.easybeans.deployment.annotations.metadata.MethodAnnotationMetadata;
041: import org.ow2.easybeans.enhancer.CommonClassGenerator;
042: import org.ow2.easybeans.enhancer.interceptors.EasyBeansInvocationContextGenerator;
043: import org.ow2.easybeans.enhancer.lib.MethodRenamer;
044:
045: /**
046: * This class adds a bean interface to the parsed object.<br>
047: * It also adds getEasyBeansFactory method defined in the EasyBeansBean interface.<br>
048: * Stateless bean will have EasyBeansStatelessSessionBean interface, etc.
049: * @author Florent Benoit
050: */
051: public class BeanClassAdapter extends ClassAdapter implements Opcodes {
052:
053: /**
054: * Metadata available by this adapter for a class.
055: */
056: private ClassAnnotationMetadata classAnnotationMetadata = null;
057:
058: /**
059: * Timer method name.
060: */
061: public static final String TIMER_METHOD = "timeoutCallByEasyBeans";
062:
063: /**
064: * JMethod object for timeoutCallByEasyBeans.
065: */
066: public static final JMethod TIMER_JMETHOD = new JMethod(ACC_PUBLIC,
067: MethodRenamer.encode(TIMER_METHOD), "(Ljavax/ejb/Timer;)V",
068: null, null);
069:
070: /**
071: * Constructor.
072: * @param classAnnotationMetadata object containing all attributes of the
073: * class
074: * @param cv the class visitor to which this adapter must delegate calls.
075: */
076: public BeanClassAdapter(
077: final ClassAnnotationMetadata classAnnotationMetadata,
078: final ClassVisitor cv) {
079: super (cv);
080: this .classAnnotationMetadata = classAnnotationMetadata;
081: }
082:
083: /**
084: * Visits the header of the class.
085: * @param version the class version.
086: * @param access the class's access flags (see
087: * {@link org.ow2.easybeans.asm.Opcodes}). This parameter also indicates
088: * if the class is deprecated.
089: * @param name the internal name of the class (see
090: * {@link org.ow2.easybeans.asm.Type#getInternalName() getInternalName}).
091: * @param signature the signature of this class. May be <tt>null</tt> if
092: * the class is not a generic one, and does not extend or implement
093: * generic classes or interfaces.
094: * @param superName the internal of name of the super class (see
095: * {@link org.ow2.easybeans.asm.Type#getInternalName() getInternalName}).
096: * For interfaces, the super class is {@link Object}. May be
097: * <tt>null</tt>, but only for the {@link Object} class.
098: * @param interfaces the internal names of the class's interfaces (see
099: * {@link org.ow2.easybeans.asm.Type#getInternalName() getInternalName}).
100: * May be <tt>null</tt>.
101: */
102: @Override
103: public void visit(final int version, final int access,
104: final String name, final String signature,
105: final String super Name, final String[] interfaces) {
106:
107: String[] newInterfaces = null;
108:
109: // Add new interface with bean
110: if (classAnnotationMetadata.isBean()) {
111: // copy old interfaces in the new array
112: newInterfaces = new String[interfaces.length + 1];
113: System.arraycopy(interfaces, 0, newInterfaces, 0,
114: interfaces.length);
115:
116: int indexElement = newInterfaces.length - 1;
117:
118: // Add the right interface (SLSB, SFSB, MDB)
119: if (classAnnotationMetadata.isStateless()) {
120: newInterfaces[indexElement] = Type
121: .getInternalName(EasyBeansSLSB.class);
122: } else if (classAnnotationMetadata.isStateful()) {
123: newInterfaces[indexElement] = Type
124: .getInternalName(EasyBeansSFSB.class);
125: } else if (classAnnotationMetadata.isMdb()) {
126: newInterfaces[indexElement] = Type
127: .getInternalName(EasyBeansMDB.class);
128: } else {
129: newInterfaces[indexElement] = Type
130: .getInternalName(EasyBeansBean.class);
131: }
132: } else {
133: newInterfaces = interfaces;
134: }
135:
136: super .visit(version, access, name, signature, super Name,
137: newInterfaces);
138:
139: }
140:
141: /**
142: * Visits a method of the class. This method <i>must</i> return a new
143: * {@link MethodVisitor} instance (or <tt>null</tt>) each time it is
144: * called, i.e., it should not return a previously returned visitor.
145: *
146: * @param access the method's access flags (see {@link Opcodes}). This
147: * parameter also indicates if the method is synthetic and/or
148: * deprecated.
149: * @param name the method's name.
150: * @param desc the method's descriptor (see {@link Type Type}).
151: * @param signature the method's signature. May be <tt>null</tt> if the
152: * method parameters, return type and exceptions do not use generic
153: * types.
154: * @param exceptions the internal names of the method's exception classes
155: * (see {@link Type#getInternalName() getInternalName}). May be
156: * <tt>null</tt>.
157: * @return an object to visit the byte code of the method, or <tt>null</tt>
158: * if this class visitor is not interested in visiting the code of
159: * this method.
160: */
161: @Override
162: public MethodVisitor visitMethod(final int access,
163: final String name, final String desc,
164: final String signature, final String[] exceptions) {
165:
166: // if default constructor, then needs to add a call to create a new InterceptorManager
167: if ("<init>".equals(name) && "()V".equals(desc)) {
168: MethodVisitor mv = super .visitMethod(access, name, desc,
169: signature, exceptions);
170: return new AddMethodConstructorAdapter(mv);
171: }
172: return super .visitMethod(access, name, desc, signature,
173: exceptions);
174:
175: }
176:
177: /**
178: * Visits the end of the class. This method, which is the last one to be
179: * called, is used to inform the visitor that all the fields and methods of
180: * the class have been visited.
181: */
182: @Override
183: public void visitEnd() {
184: super .visitEnd();
185:
186: // Adds the factory attribute and its getter/setter.
187: CommonClassGenerator.addFieldGettersSetters(cv,
188: classAnnotationMetadata.getClassName(),
189: "easyBeansFactory", Factory.class);
190:
191: if (classAnnotationMetadata.isBean()) {
192: // Adds interceptor manager
193: CommonClassGenerator
194: .addFieldGettersSetters(
195: cv,
196: classAnnotationMetadata.getClassName(),
197: "easyBeansInterceptorManager",
198: "L"
199: + classAnnotationMetadata
200: .getClassName()
201: + EasyBeansInvocationContextGenerator.SUFFIX_INTERCEPTOR_MANAGER
202: + ";");
203:
204: // Adds the context attribute and its getter/setter.
205: CommonClassGenerator.addFieldGettersSetters(cv,
206: classAnnotationMetadata.getClassName(),
207: "easyBeansContext", EZBEJBContext.class);
208:
209: // Add the removed attribute (if bean has been removed)
210: if (classAnnotationMetadata.isSession()) {
211: CommonClassGenerator.addFieldGettersSetters(cv,
212: classAnnotationMetadata.getClassName(),
213: "easyBeansRemoved", Boolean.TYPE);
214: }
215:
216: // Add id field for stateful
217: if (classAnnotationMetadata.isStateful()) {
218: CommonClassGenerator.addFieldGettersSetters(cv,
219: classAnnotationMetadata.getClassName(),
220: "easyBeansStatefulID", Long.class);
221: }
222:
223: // Add the timer method
224: addTimerMethod(cv);
225:
226: }
227:
228: }
229:
230: /**
231: * This method will call the timeout method (if any). <br />
232: * The timeout method may be defined on the super class.
233: * If there is no timeout method defined on the bean, throw an exception
234: * @param cv the class visitor.
235: */
236: private void addTimerMethod(final ClassVisitor cv) {
237: MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, MethodRenamer
238: .encode(TIMER_METHOD), "(Ljavax/ejb/Timer;)V", null,
239: null);
240: mv.visitCode();
241:
242: // Found a timer method ?
243: boolean found = false;
244:
245: // get timer method if any and do a call on this timer method
246: for (MethodAnnotationMetadata method : classAnnotationMetadata
247: .getMethodAnnotationMetadataCollection()) {
248: if (method.isTimeout()) {
249: // Write a call to this method
250: mv.visitVarInsn(ALOAD, 0);
251: mv.visitVarInsn(ALOAD, 1);
252:
253: // The name of the class where the method is defined (can be a super class)
254: String className = classAnnotationMetadata
255: .getClassName();
256: if (method.isInherited()) {
257: className = method
258: .getOriginalClassAnnotationMetadata()
259: .getClassName();
260: }
261:
262: mv.visitMethodInsn(INVOKESPECIAL, className, method
263: .getMethodName(), "(Ljavax/ejb/Timer;)V");
264: found = true;
265: break;
266: }
267: }
268:
269: // No timeout method, then needs to throw an exception
270: // throw new EJBException("No timeout method has been defined on this bean");
271: if (!found) {
272: mv.visitTypeInsn(NEW, "javax/ejb/EJBException");
273: mv.visitInsn(DUP);
274: mv
275: .visitLdcInsn("No timeout method has been defined on this bean");
276: mv.visitMethodInsn(INVOKESPECIAL, "javax/ejb/EJBException",
277: "<init>", "(Ljava/lang/String;)V");
278: mv.visitInsn(ATHROW);
279: }
280:
281: // else, throw an exception
282:
283: mv.visitInsn(RETURN);
284: mv.visitMaxs(0, 0);
285: mv.visitEnd();
286: }
287:
288: /**
289: * Adds an entry in the constructor of the bean.
290: * It will initialize the interceptorManager.
291: * @author Florent Benoit
292: */
293: public class AddMethodConstructorAdapter extends MethodAdapter {
294:
295: /**
296: * Constructs a new AddMethodConstructorAdapter object.
297: * @param mv the code visitor to which this adapter must delegate calls.
298: */
299: public AddMethodConstructorAdapter(final MethodVisitor mv) {
300: super (mv);
301: }
302:
303: /**
304: * Adds instruction just after the start of the method code.
305: * TODO: Analyze when call to super() constructor is done and add instruction after.
306: */
307: @Override
308: public void visitCode() {
309: super .visitCode();
310: String clManager = classAnnotationMetadata.getClassName()
311: + EasyBeansInvocationContextGenerator.SUFFIX_INTERCEPTOR_MANAGER;
312: mv.visitVarInsn(ALOAD, 0);
313: mv.visitTypeInsn(NEW, clManager);
314: mv.visitInsn(DUP);
315: mv.visitMethodInsn(INVOKESPECIAL, clManager, "<init>",
316: "()V");
317: mv.visitFieldInsn(PUTFIELD, classAnnotationMetadata
318: .getClassName(), "easyBeansInterceptorManager", "L"
319: + clManager + ";");
320:
321: }
322:
323: }
324:
325: }
|