Package org.ow2.easybeans.enhancer

Source Code of org.ow2.easybeans.enhancer.Enhancer

/**
* EasyBeans
* Copyright (C) 2006-2007 Bull S.A.S.
* Contact: easybeans@ow2.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
* USA
*
* --------------------------------------------------------------------------
* $Id: Enhancer.java 5477 2010-04-26 08:24:20Z benoitf $
* --------------------------------------------------------------------------
*/

package org.ow2.easybeans.enhancer;

import static org.ow2.easybeans.enhancer.injection.InjectionClassAdapter.JAVA_LANG_OBJECT;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;

import org.ow2.easybeans.api.loader.EZBClassLoader;
import org.ow2.easybeans.asm.ClassReader;
import org.ow2.easybeans.asm.ClassVisitor;
import org.ow2.easybeans.asm.ClassWriter;
import org.ow2.easybeans.deployment.metadata.ejbjar.EasyBeansEjbJarClassMetadata;
import org.ow2.easybeans.deployment.metadata.ejbjar.EjbJarArchiveMetadata;
import org.ow2.easybeans.enhancer.bean.BeanClassAdapter;
import org.ow2.easybeans.enhancer.bean.Migration21ClassAdapter;
import org.ow2.easybeans.enhancer.injection.InjectionClassAdapter;
import org.ow2.easybeans.enhancer.interceptors.InterceptorClassAdapter;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
* This class is used for enhancing a set of classes (Beans like Stateless,
* Stateful, MDB, etc).
* @author Florent Benoit
*/
public class Enhancer {

    /**
     * Logger.
     */
    private static Log logger = LogFactory.getLog(Enhancer.class);

    /**
     * Metadata of the classes of a given jar file.
     */
    private EjbJarArchiveMetadata ejbJarAnnotationMetadata = null;

    /**
     * Classloader used to load/define classes.
     */
    private ClassLoader loader = null;

    /**
     * Map containing informations for enhancers.
     */
    private Map<String, Object> map = null;

    /**
     * Creates an new enhancer.
     * @param loader classloader where to define enhanced classes.
     * @param ejbJarAnnotationMetadata object with references to the metadata.
     * @param map a map allowing to give some objects to the enhancer.
     */
    public Enhancer(final ClassLoader loader, final EjbJarArchiveMetadata ejbJarAnnotationMetadata,
            final Map<String, Object> map) {
        this.loader = loader;
        this.ejbJarAnnotationMetadata = ejbJarAnnotationMetadata;
        this.map = map;
    }

    /**
     * Enhance all classes which match beans, etc.
     * @throws EnhancerException if enhancing fails
     */
    public void enhance() throws EnhancerException {

        // Define all interceptors first.
        for (EasyBeansEjbJarClassMetadata classAnnotationMetadata : this.ejbJarAnnotationMetadata
                .getEjbJarClassMetadataCollection()) {
            if (classAnnotationMetadata.isInterceptor() && !classAnnotationMetadata.isBean()
                    && !classAnnotationMetadata.wasModified()) {
                logger.debug("ClassAdapter on interceptor : {0}", classAnnotationMetadata.getClassName());

                // enhance all super classes of the interceptor. (if any)
                // And do this only one time.
                enhanceSuperClass(classAnnotationMetadata, null);

                // Create ClassReader/Writer
                ClassReader cr = getClassReader(classAnnotationMetadata);
                ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
                InterceptorClassAdapter cv = new InterceptorClassAdapter(classAnnotationMetadata, cw);
                InjectionClassAdapter cv2 = new InjectionClassAdapter(classAnnotationMetadata, cv, this.map, null, false);
                cr.accept(cv2, 0);
                classAnnotationMetadata.setModified();
                defineClass(this.loader, classAnnotationMetadata.getClassName().replace("/", "."), cw.toByteArray());
            }
        }
        // search all beans
        logger.info("Beans found are {0}", this.ejbJarAnnotationMetadata.getBeanNames());


        List<String> beanNames = this.ejbJarAnnotationMetadata.getBeanNames();
        for (String beanName : beanNames) {
            for (EasyBeansEjbJarClassMetadata classAnnotationMetadata : this.ejbJarAnnotationMetadata
                    .getClassesForBean(beanName)) {
                if (classAnnotationMetadata.isBean()) {

                    // First, enhance all super classes of the bean. (if any)
                    // And do this only one time.
                    enhanceSuperClass(classAnnotationMetadata, classAnnotationMetadata);
                    //logger.info("Enhancement of {0} done !", classAnnotationMetadata);

                    // Create ClassReader/Writer
                    ClassReader cr = getClassReader(classAnnotationMetadata);
                    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
                    BeanClassAdapter cv = new BeanClassAdapter(classAnnotationMetadata, cw);
                    InterceptorClassAdapter itcpClassAdapter = new InterceptorClassAdapter(classAnnotationMetadata, cv);
                    InjectionClassAdapter cv2 = new InjectionClassAdapter(classAnnotationMetadata, itcpClassAdapter, this.map,
                            classAnnotationMetadata, false);

                    ClassVisitor beanVisitor = cv2;
                    // EJb 2.1 view ?
                    if (classAnnotationMetadata.getRemoteHome() != null || classAnnotationMetadata.getLocalHome() != null) {
                        Migration21ClassAdapter ejb21Adapter = new Migration21ClassAdapter(classAnnotationMetadata, cv2);
                        beanVisitor = ejb21Adapter;
                    }


                    cr.accept(beanVisitor, 0);

                    // define subclasses if interceptor enabled
                    loadDefinedClasses(this.loader, itcpClassAdapter.getDefinedClasses());

                    defineClass(this.loader, classAnnotationMetadata.getClassName().replace("/", "."), cw.toByteArray());

                }
            }
        }
    }


    /**
     * Enhance all super classes that are available.
     * @param classAnnotationMetadata the class where to lookup super classes.
     * @param beanClassMetadata the original class (bean class for example)
     * @throws EnhancerException if class can't be analyzed.
     */
    protected void enhanceSuperClass(final EasyBeansEjbJarClassMetadata classAnnotationMetadata,
            final EasyBeansEjbJarClassMetadata beanClassMetadata) throws EnhancerException {
        // First, enhance all super classes of the bean. (if any)
        // And do this only one time.
        String superClass = classAnnotationMetadata.getSuperName();
        if (!superClass.equals(JAVA_LANG_OBJECT)) {
            EasyBeansEjbJarClassMetadata superMetaData = classAnnotationMetadata.getLinkedClassMetadata(superClass);
            if (superMetaData != null && !superMetaData.wasModified()) {
                ClassReader cr = getClassReader(superMetaData);
                ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
                InterceptorClassAdapter itcpClassAdapter = new InterceptorClassAdapter(superMetaData, cw);
                InjectionClassAdapter cv = new InjectionClassAdapter(superMetaData, itcpClassAdapter, this.map,
                        beanClassMetadata, false);
                cr.accept(cv, 0);
                superMetaData.setModified();
                defineClass(this.loader, superMetaData.getClassName().replace("/", "."), cw.toByteArray());
                enhanceSuperClass(superMetaData, beanClassMetadata);
            }
        }

    }

    /**
     * Load defined classes in the list.
     * @param loader classloader to use.
     * @param lst a list of new generated classes.
     */
    protected void loadDefinedClasses(final ClassLoader loader, final List<DefinedClass> lst) {
        if (lst != null) {
            for (DefinedClass definedClass : lst) {
                defineClass(loader, definedClass.getClassName(), definedClass.getBytes());
            }
        }
    }


    /**
     * Gets a class reader for a given metadata.
     * @param classAnnotationMetadata given metadata
     * @return classreader associated to the given metadata
     * @throws EnhancerException if no classWriter can be returned
     */
    protected ClassReader getClassReader(final EasyBeansEjbJarClassMetadata classAnnotationMetadata)
            throws EnhancerException {
        String className = classAnnotationMetadata.getClassName() + ".class";
        InputStream is = this.loader.getResourceAsStream(className);
        if (is == null) {
            throw new EnhancerException("Cannot find input stream in classloader " + this.loader + " for class " + className);
        }
        ClassReader cr = null;
        try {
            cr = new ClassReader(is);
        } catch (IOException e) {
            throw new EnhancerException("Cannot load input stream for class '" + className + "' in classloader '"
                    + this.loader, e);
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                throw new EnhancerException("Cannot close input stream for class '" + className + "' in classloader '"
                        + this.loader, e);
            }
        }
        return cr;
    }

    /**
     * Loads/defines a class in the current class loader.
     * @param loader classloader to use.
     * @param className the name of the class
     * @param b the bytecode of the class to define
     */
    protected void defineClass(final ClassLoader loader, final String className, final byte[] b) {

        if (loader instanceof EZBClassLoader) {
            ((EZBClassLoader) loader).addClassDefinition(className, b);
        } else {
            // use other way of loading class.
            // override classDefine (as it is protected) and define the class.
            try {
                ///ClassLoader loader = Thread.currentThread().getContextClassLoader();
                Class<?> cls = Class.forName("java.lang.ClassLoader");
                java.lang.reflect.Method method = cls.getDeclaredMethod("defineClass", new Class[] {String.class,
                        byte[].class, int.class, int.class});

                // protected method invocaton
                method.setAccessible(true);
                try {
                    Object[] args = new Object[] {className, b, Integer.valueOf(0), Integer.valueOf(b.length)};
                    method.invoke(loader, args);
                } finally {
                    method.setAccessible(false);
                }
            } catch (InvocationTargetException ite) {
                if (ite.getCause() instanceof LinkageError) {
                    logger.error("Unable to define the class ''{0}''", className, ite);
                } else {
                    logger.warn("Unable to define the class ''{0}''", className, ite);
                }

            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }


    }

    /**
     * @return the ejbjar annotation metadata
     */
    protected EjbJarArchiveMetadata getEjbJarAnnotationMetadata() {
        return this.ejbJarAnnotationMetadata;
    }

    /**
     * @return map containing informations for enhancers.
     */
    protected Map<String, Object> getMap() {
        return this.map;
    }

    /**
     * @return the classloader used by this enhancer.
     */
    protected ClassLoader getClassLoader() {
        return this.loader;
    }

}
TOP

Related Classes of org.ow2.easybeans.enhancer.Enhancer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.