Package ma.glasnost.orika.impl.generator

Source Code of ma.glasnost.orika.impl.generator.JavassistCompilerStrategy

/*
* Orika - simpler, better and faster Java bean mapping
*
* Copyright (C) 2011 Orika authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package ma.glasnost.orika.impl.generator;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.Random;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;

import javassist.CannotCompileException;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtNewMethod;
import javassist.LoaderClassPath;
import javassist.NotFoundException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Uses Javassist to generate compiled class for the passed GeneratedSourceCode
* object.<br>
* <br>
*
* By default this compiler strategy writes no source or class files.
*
* @author matt.deboer@gmail.com
*/
public class JavassistCompilerStrategy extends CompilerStrategy {
   
    private static final String WRITE_SOURCE_FILES_BY_DEFAULT = "false";
    private static final String WRITE_CLASS_FILES_BY_DEFAULT = "false";
   
    private final static Logger LOG = LoggerFactory.getLogger(JavassistCompilerStrategy.class);
    private final static Map<Class<?>, Boolean> superClasses = new ConcurrentHashMap<Class<?>,Boolean>(3);
   
    private ClassPool classPool;
   
    /**
     * Keep a set of class-loaders that have already been added to the javassist class-pool
     * Use a WeakHashMap to avoid retaining references to child class-loaders
     */
    private WeakHashMap<ClassLoader,Boolean> referencedLoaders = new WeakHashMap<ClassLoader,Boolean>(8);
   
    /**
     */
    public JavassistCompilerStrategy() {
        super(WRITE_SOURCE_FILES_BY_DEFAULT, WRITE_CLASS_FILES_BY_DEFAULT);
       
        this.classPool = new ClassPool();
        this.classPool.appendSystemPath();
    }
   
    /**
     * Produces the requested class files for debugging purposes.
     *
     * @throws CannotCompileException
     * @throws IOException
     */
    protected void writeClassFile(SourceCodeContext sourceCode, CtClass byteCodeClass) throws IOException {
      if (writeClassFiles) {
            try {
              File parentDir = preparePackageOutputPath(this.pathToWriteClassFiles, "");
                byteCodeClass.writeFile(parentDir.getAbsolutePath());
            } catch (CannotCompileException e) {
                throw new IllegalArgumentException(e);
            }
        }
    }
   
    /**
     * Produces the requested source file for debugging purposes.
     *
     * @throws IOException
     */
    protected void writeSourceFile(SourceCodeContext sourceCode) throws IOException {
      if (writeSourceFiles) {
          File parentDir = preparePackageOutputPath(this.pathToWriteSourceFiles, sourceCode.getPackageName());
          File sourceFile = new File(parentDir, sourceCode.getClassSimpleName() + ".java");
            if (!sourceFile.exists() && !sourceFile.createNewFile()) {
                throw new IOException("Could not write source file for " + sourceCode.getClassName());
            }
          
            FileWriter fw = null;
            try {
                fw = new FileWriter(sourceFile);
                fw.append(sourceCode.toSourceFile());
            } finally {
                if (fw != null)
                    fw.close();
            }
        }
    }
   
   
   
    /**
     * Attempts to register a class-loader in the maintained list of referenced
     * class-loaders. Returns true if the class-loader was registered as a
     * result of the call; false is returned if the class-loader was already
     * registered.
     *
     * @param cl
     * @return true if the class-loader was registered as a result of this call; false
     * if the class-loader was already registered
     */
    private boolean registerClassLoader(ClassLoader cl) {
      Boolean found = referencedLoaders.get(cl);
      if (found==null) {
        synchronized(cl) {
          found = referencedLoaders.get(cl);
          if (found==null) {
            referencedLoaders.put(cl,Boolean.TRUE);
            classPool.insertClassPath(new LoaderClassPath(cl));
          }
        }
      }
      return found==null || !found;
    }
   
    /*
     * (non-Javadoc)
     *
     * @see ma.glasnost.orika.impl.GeneratedSourceCodeCompilerStrategy#
     * assertClassLoaderAccessible(java.lang.Class)
     */
    public void assureTypeIsAccessible(Class<?> type) throws SourceCodeGenerationException {
       
        if (!type.isPrimitive() && type.getClassLoader() != null) {
           
            if (!Modifier.isPublic(type.getModifiers())) {
                throw new SourceCodeGenerationException(type + " is not accessible");
            } else if (type.isMemberClass()) {
                /*
                 * The type needs to be publicly accessible (including it's
                 * enclosing classes if any)
                 */
                Class<?> currentType = type;
                while (currentType != null) {
                    if (!Modifier.isPublic(type.getModifiers())) {
                        throw new SourceCodeGenerationException(type + " is not accessible");
                    }
                    currentType = currentType.getEnclosingClass();
                }
            }
           
            String className = type.getName();
            if (type.isArray()) {
                // Strip off the "[L" prefix from the internal name
                className = type.getComponentType().getName();
            }
           
            try {
                classPool.get(className);
            } catch (NotFoundException e) {
               
                if (registerClassLoader(type.getClassLoader())) {
                    try {
                        classPool.get(className);
                    } catch (NotFoundException e2) {
                        throw new SourceCodeGenerationException(type + " is not accessible", e2);
                    }
                } else {
                    throw new SourceCodeGenerationException(type + " is not accessible", e);
                }
            }
        } 
    }
   
    /*
     * (non-Javadoc)
     *
     * @see
     * ma.glasnost.orika.impl.GeneratedSourceCodeCompilerStrategy#compileClass
     * (ma.glasnost.orika.impl.GeneratedSourceCode)
     */
    public Class<?> compileClass(SourceCodeContext sourceCode) throws SourceCodeGenerationException {
       
        StringBuilder className = new StringBuilder(sourceCode.getClassName());
        CtClass byteCodeClass = null;
        int attempts = 0;
        Random rand = new Random();
        while (byteCodeClass==null) {
          try {
            byteCodeClass = classPool.makeClass(className.toString());
      } catch (RuntimeException e) {
        if (attempts < 5) {
          className.append(Integer.toHexString(rand.nextInt()));
        } else {
          // No longer likely to be accidental name collision; propagate the error
          throw e;
        }
      }
      }
       
        CtClass abstractMapperClass;
        Class<?> compiledClass;
       
        try {
          writeSourceFile(sourceCode);
         
          // TODO: do we really need this check here?
            assureTypeIsAccessible(this.getClass());
           
            Boolean existing = superClasses.put(sourceCode.getSuperClass(), true);
            if (existing==null || !existing) {
                classPool.insertClassPath(new ClassClassPath(sourceCode.getSuperClass()));
            }
           
            if (registerClassLoader(Thread.currentThread().getContextClassLoader())) {
              classPool.insertClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
            }
           
            abstractMapperClass = classPool.get(sourceCode.getSuperClass().getCanonicalName());
            byteCodeClass.setSuperclass(abstractMapperClass);
           
            for (String fieldDef : sourceCode.getFields()) {
                try {
                    byteCodeClass.addField(CtField.make(fieldDef, byteCodeClass));
                } catch (CannotCompileException e) {
                    LOG.error("An exception occured while compiling: " + fieldDef + " for " + sourceCode.getClassName(), e);
                    throw e;
                }
            }
           
            for (String methodDef : sourceCode.getMethods()) {
                try {
                    byteCodeClass.addMethod(CtNewMethod.make(methodDef, byteCodeClass));
                } catch (CannotCompileException e) {
                    throw new SourceCodeGenerationException(
                        "An exception occured while compiling the following method:\n\n " + methodDef +
                        "\n\n for " + sourceCode.getClassName() + "\n", e);
                   
                }
               
            }
            compiledClass = byteCodeClass.toClass();
           
            writeClassFile(sourceCode, byteCodeClass);
           
        } catch (NotFoundException e) {
            throw new SourceCodeGenerationException(e);
        } catch (CannotCompileException e) {
            throw new SourceCodeGenerationException(e);
        } catch (IOException e) {
            throw new SourceCodeGenerationException("Could not write files for " + sourceCode.getClassName(), e);
        }
       
        return compiledClass;
    }
   
}
TOP

Related Classes of ma.glasnost.orika.impl.generator.JavassistCompilerStrategy

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.