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: Enhancer.java 2057 2007-11-21 15:35:32Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.enhancer;
025:
026: import static org.ow2.easybeans.enhancer.injection.InjectionClassAdapter.JAVA_LANG_OBJECT;
027:
028: import java.io.IOException;
029: import java.io.InputStream;
030: import java.util.List;
031: import java.util.Map;
032:
033: import org.ow2.easybeans.asm.ClassReader;
034: import org.ow2.easybeans.asm.ClassVisitor;
035: import org.ow2.easybeans.asm.ClassWriter;
036: import org.ow2.easybeans.deployment.annotations.metadata.ClassAnnotationMetadata;
037: import org.ow2.easybeans.deployment.annotations.metadata.EjbJarAnnotationMetadata;
038: import org.ow2.easybeans.enhancer.bean.BeanClassAdapter;
039: import org.ow2.easybeans.enhancer.bean.Migration21ClassAdapter;
040: import org.ow2.easybeans.enhancer.injection.InjectionClassAdapter;
041: import org.ow2.easybeans.enhancer.interceptors.InterceptorClassAdapter;
042: import org.ow2.easybeans.loader.EasyBeansClassLoader;
043: import org.ow2.util.log.Log;
044: import org.ow2.util.log.LogFactory;
045:
046: /**
047: * This class is used for enhancing a set of classes (Beans like Stateless,
048: * Stateful, MDB, etc).
049: * @author Florent Benoit
050: */
051: public class Enhancer {
052:
053: /**
054: * Logger.
055: */
056: private static Log logger = LogFactory.getLog(Enhancer.class);
057:
058: /**
059: * Metadata of the classes of a given jar file.
060: */
061: private EjbJarAnnotationMetadata ejbJarAnnotationMetadata = null;
062:
063: /**
064: * Classloader used to load/define classes.
065: */
066: private ClassLoader loader = null;
067:
068: /**
069: * Map containing informations for enhancers.
070: */
071: private Map<String, Object> map = null;
072:
073: /**
074: * Creates an new enhancer.
075: * @param loader classloader where to define enhanced classes.
076: * @param ejbJarAnnotationMetadata object with references to the metadata.
077: * @param map a map allowing to give some objects to the enhancer.
078: */
079: public Enhancer(final ClassLoader loader,
080: final EjbJarAnnotationMetadata ejbJarAnnotationMetadata,
081: final Map<String, Object> map) {
082: this .loader = loader;
083: this .ejbJarAnnotationMetadata = ejbJarAnnotationMetadata;
084: this .map = map;
085: }
086:
087: /**
088: * Enhance all classes which match beans, etc.
089: * @throws EnhancerException if enhancing fails
090: */
091: public void enhance() throws EnhancerException {
092:
093: // Define all interceptors first.
094: for (ClassAnnotationMetadata classAnnotationMetadata : ejbJarAnnotationMetadata
095: .getClassAnnotationMetadataCollection()) {
096: if (classAnnotationMetadata.isInterceptor()
097: && !classAnnotationMetadata.isBean()
098: && !classAnnotationMetadata.wasModified()) {
099: logger.debug("ClassAdapter on interceptor : {0}",
100: classAnnotationMetadata.getClassName());
101:
102: // enhance all super classes of the interceptor. (if any)
103: // And do this only one time.
104: enhanceSuperClass(classAnnotationMetadata);
105:
106: // Create ClassReader/Writer
107: ClassReader cr = getClassReader(classAnnotationMetadata);
108: ClassWriter cw = new ClassWriter(
109: ClassWriter.COMPUTE_MAXS);
110: InterceptorClassAdapter cv = new InterceptorClassAdapter(
111: classAnnotationMetadata, cw);
112: InjectionClassAdapter cv2 = new InjectionClassAdapter(
113: classAnnotationMetadata, cv, map, false);
114: cr.accept(cv2, 0);
115: classAnnotationMetadata.setModified();
116: defineClass(loader, classAnnotationMetadata
117: .getClassName().replace("/", "."), cw
118: .toByteArray());
119: }
120: }
121: // search all beans
122: for (ClassAnnotationMetadata classAnnotationMetadata : ejbJarAnnotationMetadata
123: .getClassAnnotationMetadataCollection()) {
124: if (classAnnotationMetadata.isBean()) {
125:
126: // First, enhance all super classes of the bean. (if any)
127: // And do this only one time.
128: enhanceSuperClass(classAnnotationMetadata);
129:
130: // Create ClassReader/Writer
131: ClassReader cr = getClassReader(classAnnotationMetadata);
132: ClassWriter cw = new ClassWriter(
133: ClassWriter.COMPUTE_MAXS);
134: BeanClassAdapter cv = new BeanClassAdapter(
135: classAnnotationMetadata, cw);
136: InterceptorClassAdapter itcpClassAdapter = new InterceptorClassAdapter(
137: classAnnotationMetadata, cv);
138: InjectionClassAdapter cv2 = new InjectionClassAdapter(
139: classAnnotationMetadata, itcpClassAdapter, map,
140: false);
141:
142: ClassVisitor beanVisitor = cv2;
143: // EJb 2.1 view ?
144: if (classAnnotationMetadata.getRemoteHome() != null
145: || classAnnotationMetadata.getLocalHome() != null) {
146: Migration21ClassAdapter ejb21Adapter = new Migration21ClassAdapter(
147: classAnnotationMetadata, cv2);
148: beanVisitor = ejb21Adapter;
149: }
150:
151: cr.accept(beanVisitor, 0);
152:
153: // define subclasses if interceptor enabled
154: loadDefinedClasses(loader, itcpClassAdapter
155: .getDefinedClasses());
156:
157: defineClass(loader, classAnnotationMetadata
158: .getClassName().replace("/", "."), cw
159: .toByteArray());
160:
161: }
162:
163: }
164: }
165:
166: /**
167: * Enhance all super classes that are available.
168: * @param classAnnotationMetadata the class where to lookup super classes.
169: * @throws EnhancerException if class can't be analyzed.
170: */
171: protected void enhanceSuperClass(
172: final ClassAnnotationMetadata classAnnotationMetadata)
173: throws EnhancerException {
174: // First, enhance all super classes of the bean. (if any)
175: // And do this only one time.
176: String super Class = classAnnotationMetadata.getSuperName();
177: if (!super Class.equals(JAVA_LANG_OBJECT)) {
178: ClassAnnotationMetadata super MetaData = ejbJarAnnotationMetadata
179: .getClassAnnotationMetadata(super Class);
180: if (super MetaData != null && !super MetaData.wasModified()) {
181: ClassReader cr = getClassReader(super MetaData);
182: ClassWriter cw = new ClassWriter(
183: ClassWriter.COMPUTE_MAXS);
184: InterceptorClassAdapter itcpClassAdapter = new InterceptorClassAdapter(
185: super MetaData, cw);
186: InjectionClassAdapter cv = new InjectionClassAdapter(
187: super MetaData, itcpClassAdapter, map, false);
188: cr.accept(cv, 0);
189: super MetaData.setModified();
190: defineClass(loader, super MetaData.getClassName()
191: .replace("/", "."), cw.toByteArray());
192: enhanceSuperClass(super MetaData);
193: }
194: }
195:
196: }
197:
198: /**
199: * Load defined classes in the list.
200: * @param loader classloader to use.
201: * @param lst a list of new generated classes.
202: */
203: private static void loadDefinedClasses(final ClassLoader loader,
204: final List<DefinedClass> lst) {
205: if (lst != null) {
206: for (DefinedClass definedClass : lst) {
207: defineClass(loader, definedClass.getClassName(),
208: definedClass.getBytes());
209: }
210: }
211: }
212:
213: /**
214: * Gets a class reader for a given metadata.
215: * @param classAnnotationMetadata given metadata
216: * @return classreader associated to the given metadata
217: * @throws EnhancerException if no classWriter can be returned
218: */
219: protected ClassReader getClassReader(
220: final ClassAnnotationMetadata classAnnotationMetadata)
221: throws EnhancerException {
222: String className = classAnnotationMetadata.getClassName()
223: + ".class";
224: InputStream is = loader.getResourceAsStream(className);
225: if (is == null) {
226: throw new EnhancerException(
227: "Cannot find input stream in classloader " + loader
228: + " for class " + className);
229: }
230: ClassReader cr = null;
231: try {
232: cr = new ClassReader(is);
233: } catch (IOException e) {
234: throw new EnhancerException(
235: "Cannot load input stream for class '" + className
236: + "' in classloader '" + loader, e);
237: } finally {
238: try {
239: is.close();
240: } catch (IOException e) {
241: throw new EnhancerException(
242: "Cannot close input stream for class '"
243: + className + "' in classloader '"
244: + loader, e);
245: }
246: }
247: return cr;
248: }
249:
250: /**
251: * Loads/defines a class in the current class loader.
252: * @param loader classloader to use.
253: * @param className the name of the class
254: * @param b the bytecode of the class to define
255: */
256: public static void defineClass(final ClassLoader loader,
257: final String className, final byte[] b) {
258: if (loader instanceof EasyBeansClassLoader) {
259: ((EasyBeansClassLoader) loader).addClassDefinition(
260: className, b);
261: } else {
262: // use other way of loading class.
263: // override classDefine (as it is protected) and define the class.
264: try {
265: ///ClassLoader loader = Thread.currentThread().getContextClassLoader();
266: Class cls = Class.forName("java.lang.ClassLoader");
267: java.lang.reflect.Method method = cls
268: .getDeclaredMethod("defineClass", new Class[] {
269: String.class, byte[].class, int.class,
270: int.class });
271:
272: // protected method invocaton
273: method.setAccessible(true);
274: try {
275: Object[] args = new Object[] { className, b,
276: Integer.valueOf(0),
277: Integer.valueOf(b.length) };
278: method.invoke(loader, args);
279: } finally {
280: method.setAccessible(false);
281: }
282: } catch (Exception e) {
283: throw new RuntimeException(e);
284: }
285: }
286:
287: }
288:
289: /**
290: * @return the ejbjar annotation metadata
291: */
292: protected EjbJarAnnotationMetadata getEjbJarAnnotationMetadata() {
293: return ejbJarAnnotationMetadata;
294: }
295:
296: /**
297: * @return map containing informations for enhancers.
298: */
299: protected Map<String, Object> getMap() {
300: return map;
301: }
302:
303: /**
304: * @return the classloader used by this enhancer.
305: */
306: protected ClassLoader getClassLoader() {
307: return loader;
308: }
309:
310: }
|