You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by we...@apache.org on 2009/09/23 12:40:52 UTC

svn commit: r818046 - in /myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting: core/util/ loaders/java/jdk5/

Author: werpu
Date: Wed Sep 23 10:40:51 2009
New Revision: 818046

URL: http://svn.apache.org/viewvc?rev=818046&view=rev
Log:
https://issues.apache.org/jira/browse/EXTSCRIPT-22
integration of a custom javac compiler binding
thanks bernhard for providing it

Added:
    myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/CompilationException.java   (with props)
    myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/CompilationResult.java   (with props)
    myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/Compiler.java   (with props)
    myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/JavacCompiler.java   (with props)
Modified:
    myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/core/util/ClassUtils.java

Modified: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/core/util/ClassUtils.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/core/util/ClassUtils.java?rev=818046&r1=818045&r2=818046&view=diff
==============================================================================
--- myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/core/util/ClassUtils.java (original)
+++ myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/core/util/ClassUtils.java Wed Sep 23 10:40:51 2009
@@ -51,6 +51,16 @@
         }
     }
 
+    public static boolean isPresent(String clazz) {
+        try {
+            getContextClassLoader().loadClass(clazz);
+        } catch (ClassNotFoundException e) {
+            return false;
+        }
+        return true;
+    }
+
+
     /**
      * We use asm here to add the marker annotation
      * to the list of our public annotations
@@ -69,14 +79,14 @@
             ClassNode node = new ClassNode();
             ClassReader clsReader = new ClassReader(fIstr);
             //ClassWriter wrt = new ClassWriter();
-            clsReader.accept((ClassVisitor) node, ClassReader.SKIP_FRAMES );
+            clsReader.accept((ClassVisitor) node, ClassReader.SKIP_FRAMES);
             //node.accept(wrt);
             ClassWriter wrt = new ClassWriter(0);
 
             if (node.visibleAnnotations == null) {
                 node.visibleAnnotations = new LinkedList<AnnotationNode>();
             }
-            
+
             boolean hasAnnotation = false;
             String annotationMarker = Type.getDescriptor(ScriptingClass.class);
             for (Object elem : node.visibleAnnotations) {
@@ -98,7 +108,6 @@
             foStream = new FileOutputStream(classNameToFile(classPath, className));
             foStream.write(finalClass);
             foStream.flush();
-          
 
         } catch (FileNotFoundException ex) {
             throw new ClassNotFoundException("Class " + className + " not found ");
@@ -134,11 +143,6 @@
     }
 
 
-
-
-
-
-
     public static File classNameToFile(String classPath, String className) {
         String classFileName = classNameToRelativeFileName(className);
         File target = new File(classPath + File.separator + classFileName);

Added: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/CompilationException.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/CompilationException.java?rev=818046&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/CompilationException.java (added)
+++ myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/CompilationException.java Wed Sep 23 10:40:51 2009
@@ -0,0 +1,21 @@
+package org.apache.myfaces.scripting.loaders.java.jdk5;
+
+/**
+ *
+ */
+class CompilationException extends Exception {
+
+    // ------------------------------------------ Constructors
+
+    public CompilationException(String message) {
+        super(message);
+    }
+
+    public CompilationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public CompilationException(Throwable cause) {
+        super(cause);
+    }
+}

Propchange: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/CompilationException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/CompilationException.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/CompilationResult.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/CompilationResult.java?rev=818046&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/CompilationResult.java (added)
+++ myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/CompilationResult.java Wed Sep 23 10:40:51 2009
@@ -0,0 +1,77 @@
+package org.apache.myfaces.scripting.loaders.java.jdk5;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * 
+ */
+class CompilationResult {
+
+    /** The compiler output */ 
+    private String compilerOutput;
+
+    private List<CompilationMessage> errors = new ArrayList<CompilationMessage>();
+    private List<CompilationMessage> warnings = new ArrayList<CompilationMessage>();
+
+    // ------------------------------------------ CompilationResult
+
+    public CompilationResult(String compilerOutput) {
+        this.compilerOutput = compilerOutput;    
+    }
+
+    // ------------------------------------------ Public methods
+
+    public String getCompilerOutput() {
+        return compilerOutput;
+    }
+
+    public boolean hasErrors() {
+        return !errors.isEmpty();
+    }
+
+    public void registerError(CompilationMessage message) {
+        errors.add(message);
+    }
+
+    public List<CompilationMessage> getErrors() {
+        return errors;
+    }
+
+    public void registerWarning(CompilationMessage message) {
+        warnings.add(message);
+    }
+
+    public List<CompilationMessage> getWarnings() {
+        return warnings;
+    }
+
+    // ------------------------------------------ Public static classes
+
+    public static class CompilationMessage {
+
+        private long lineNumber;
+
+        private String message;
+
+        public CompilationMessage(long lineNumber, String message) {
+            this.lineNumber = lineNumber;
+            this.message = message;
+        }
+
+        public long getLineNumber() {
+            return lineNumber;
+        }
+
+        public String getMessage() {
+            return message;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("CompilationMessage[lineNumber='%s', message='%s']", lineNumber, message);
+        }
+    }
+
+
+}

Propchange: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/CompilationResult.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/CompilationResult.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/Compiler.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/Compiler.java?rev=818046&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/Compiler.java (added)
+++ myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/Compiler.java Wed Sep 23 10:40:51 2009
@@ -0,0 +1,22 @@
+package org.apache.myfaces.scripting.loaders.java.jdk5;
+
+
+import java.io.File;
+
+/**
+ *
+ */
+interface Compiler {
+
+    /**
+     * <p>Compiles the given file and creates an according class file in the given target path.</p>
+     *
+     * @param sourcePath the path to the source directory
+     * @param targetPath the path to the target directory
+     * @param file the relative file name of the class you want to compile
+     * 
+     * @return the compilation result, i.e. the compiler output, a list of errors and a list of warnings
+     */
+    public CompilationResult compile(File sourcePath, File targetPath, String file) throws CompilationException;
+
+}

Propchange: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/Compiler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/Compiler.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/JavacCompiler.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/JavacCompiler.java?rev=818046&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/JavacCompiler.java (added)
+++ myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/JavacCompiler.java Wed Sep 23 10:40:51 2009
@@ -0,0 +1,225 @@
+package org.apache.myfaces.scripting.loaders.java.jdk5;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.myfaces.scripting.core.util.ClassUtils;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.net.URLClassLoader;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * <p>A compiler implementation that utilizes some internal classes that enable you to
+ * compile Java source code using the javac compiler being provided by your JDK. However,
+ * note that this only works if you're using a Sun JDK up to the version 1.5 (as of Java 6
+ * you should use the JSR-199 API).</p>
+ * <p/>
+ * <p>This class loads some internal classes from $JAVA_HOME$/lib/tools.jar so be sure to
+ * either include this JAR file in your classpath at startup or set the JAVA_HOME property
+ * accordingly so that it points to a valid JDK home directory (it doesn't work if you're
+ * just using a JRE!)</p>
+ */
+class JavacCompiler implements Compiler {
+
+    /**
+     * The logger instance for this class.
+     */
+    private static final Log logger = LogFactory.getLog(JavacCompiler.class);
+
+    /**
+     * The class name of the javac compiler. Note that this class
+     * is only available if you're using a Sun JDK.
+     */
+    private static final String JAVAC_MAIN = "com.sun.tools.javac.Main";
+
+    /**
+     * The class reference to the internal Javac compiler.
+     */
+    private Class compilerClass;
+
+    // ------------------------------------------ Constructors
+
+    /**
+     * <p>Creates a new Javac compiler by searching for the required JAR file '$JAVA_HOME$/lib/tools.jar'
+     * automatically. Note that the user has to specify the JAVA_HOME property in this case.</p>
+     */
+    public JavacCompiler() {
+        this(null);
+    }
+
+    /**
+     * <p>Creates a new Javac compiler by searching for internal classes in the given JAR file.</p>
+     *
+     * @param toolsJar the location of the JAR file '$JAVA_HOME$/lib/tools.jar' or <code>null</code>
+     *                 if you want it to be searched for automatically
+     */
+    public JavacCompiler(URL toolsJar) {
+        ClassLoader classLoader;
+
+        try {
+            classLoader = createJavacAwareClassLoader(toolsJar);
+        } catch (MalformedURLException ex) {
+            throw new IllegalStateException("An error occured while trying to load the Javac compiler class.", ex);
+        }
+
+        try {
+            this.compilerClass = classLoader.loadClass(JAVAC_MAIN);
+        } catch (ClassNotFoundException ex) {
+            throw new IllegalStateException("The Javac compiler class '" + JAVAC_MAIN + "' couldn't be found even though" +
+                                            "the required JAR file '$JAVA_HOME$/lib/tools.jar' has been put on the classpath. Are you sure that " +
+                                            "you're using a valid Sun JDK?");
+        }
+    }
+
+    // ------------------------------------------ Compiler methods
+
+    /**
+     * <p>Compiles the given file and creates an according class file in the given target path.</p>
+     *
+     * @param sourcePath the path to the source directory
+     * @param targetPath the path to the target directory
+     * @param file       the relative file name of the class you want to compile
+     * @return the compilation result, i.e. as of now only the compiler output
+     */
+    public CompilationResult compile(File sourcePath, File targetPath, String file) throws CompilationException {
+        // The destination directory must already exist as javac will not create the destination directory.
+        if (!targetPath.exists()) {
+            if (!targetPath.mkdirs()) {
+                throw new IllegalStateException("It wasn't possible to create the target " +
+                                                "directory for the compiler ['" + targetPath.getAbsolutePath() + "'].");
+            }
+
+            // If we've created the destination directory, we'll delete it as well once the application exits
+            targetPath.deleteOnExit();
+        }
+
+        try {
+            StringWriter compilerOutput = new StringWriter();
+
+            // Invoke the Javac compiler
+            Method compile = compilerClass.getMethod("compile", new Class[]{String[].class, PrintWriter.class});
+            Integer returnCode = (Integer) compile.invoke(null,
+                                                          new Object[]{buildCompilerArguments(sourcePath, targetPath, file), new PrintWriter(compilerOutput)});
+
+            CompilationResult result = new CompilationResult(compilerOutput.toString());
+            if (returnCode == null || returnCode.intValue() != 0) {
+                result.registerError(new CompilationResult.CompilationMessage(-1,
+                                                                              "Executing the javac compiler failed. The return code is '" + returnCode + "'."));
+            }
+
+            return result;
+        } catch (NoSuchMethodException ex) {
+            throw new IllegalStateException("The Javac compiler class '" + compilerClass + "' doesn't provide the method " +
+                                            "compile(String, PrintWriter). Are you sure that you're using a valid Sun JDK?", ex);
+        } catch (InvocationTargetException ex) {
+            throw new IllegalStateException("An error occured while invoking the compile(String, PrintWriter) method of the " +
+                                            "Javac compiler class '" + compilerClass + "'. Are you sure that you're using a valid Sun JDK?", ex);
+        } catch (IllegalAccessException ex) {
+            throw new IllegalStateException("An error occured while invoking the compile(String, PrintWriter) method of the " +
+                                            "Javac compiler class '" + compilerClass + "'. Are you sure that you're using a valid Sun JDK?", ex);
+        }
+    }
+
+    // ------------------------------------------ Utility methods
+
+    /**
+     * <p>Creates the arguments for the compiler, i.e. it builds an array of arguments that one would pass to
+     * the Javac compiler on the command line.</p>
+     *
+     * @param sourcePath the path to the source directory
+     * @param targetPath the path to the target directory
+     * @param file       the relative file name of the class you want to compile
+     * @return an array of arguments that you have to pass to the Javac compiler
+     */
+    protected String[] buildCompilerArguments(File sourcePath, File targetPath, String file) {
+        List arguments = new ArrayList();
+
+        // Set both the source code path to search for class or interface
+        // definitions and the destination directory for class files.
+        arguments.add("-sourcepath");
+        arguments.add(sourcePath.getAbsolutePath());
+        arguments.add("-d");
+        arguments.add(targetPath.getAbsolutePath());
+
+        // Enable verbose output.
+        arguments.add("-verbose");
+
+        // Generate all debugging information, including local variables.
+        arguments.add("-g");
+
+        // Append the source file that is to be compiled. Note that the user specifies only a relative file location.
+        arguments.add(new File(sourcePath, file).getAbsolutePath());
+
+        return (String[]) arguments.toArray(new String[0]);
+    }
+
+    /**
+     * <p>Returns a possibly newly created classloader that you can use in order to load the
+     * Javac compiler class. Usually the user would have to put the JAR file
+     * '$JAVA_HOME$/lib/tools.jar' on the classpath but this method recognizes this on its own
+     * and loads the JAR file if necessary. However, it's not guaranteed that the Javac compiler
+     * class is available (e.g. if one is providing a wrong tools.jar file that doesn't contain
+     * the required classes).</p>
+     *
+     * @param toolsJar the location of the JAR file '$JAVA_HOME$/lib/tools.jar' or <code>null</code>
+     *                 if you want it to be searched for automatically
+     * @return a classloader that you can use in order to load the Javac compiler class
+     * @throws MalformedURLException if an error occurred while constructing the URL
+     */
+    private static ClassLoader createJavacAwareClassLoader(URL toolsJar) throws MalformedURLException {
+        // If the user has already included the tools.jar in the classpath we don't have
+        // to create a custom class loader as the class is already available.
+        if (ClassUtils.isPresent(JAVAC_MAIN)) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Seemingly the required JAR file '$JAVA_HOME$/lib/tools.jar' has already been "
+                             + "put on the classpath as the class '" + JAVAC_MAIN + "' is present. So there's no "
+                             + "need to create a custom classloader for the Javac compiler.");
+            }
+
+            return ClassUtils.getContextClassLoader();
+        } else {
+            // The compiler isn't available in the current classpath, but the user could have specified the tools.jar file.
+            if (toolsJar == null) {
+                String javaHome = System.getProperty("java.home");
+                if (javaHome.toLowerCase().endsWith(File.separator + "jre")) {
+                    // Note that even if the user has installed a valid JDK the $JAVA_HOME$ property might reference
+                    // the JRE, e.g. '/usr/lib/jvm/java-6-sun-1.6.0.16/jre'. However, in this case we just have to
+                    // remove the last four characters (i.e. the '/jre'). 
+                    javaHome = javaHome.substring(0, javaHome.length() - 4);
+                }
+
+                // If the user hasn't specified the URL to the tools.jar file, we'll try to find it on our own.
+                File toolsJarFile = new File(javaHome, "lib" + File.separatorChar + "tools.jar");
+                if (toolsJarFile.exists()) {
+                    if (logger.isDebugEnabled()) {
+                        logger.debug(
+                                "The required JAR file '$JAVA_HOME$/lib/tools.jar' has been found ['" + toolsJarFile.getAbsolutePath()
+                                + "']. A custom URL classloader will be created for the Javac compiler.");
+                    }
+
+                    return new URLClassLoader(
+                            new URL[]{toolsJarFile.toURI().toURL()}, ClassUtils.getContextClassLoader());
+                } else {
+                    throw new IllegalStateException("The Javac compiler class '" + JAVAC_MAIN + "' and the required JAR file " +
+                                                    "'$JAVA_HOME$/lib/tools.jar' couldn't be found. Are you sure that you're using a valid Sun JDK? " +
+                                                    "[$JAVA_HOME$: '" + System.getProperty("java.home") + "']");
+                }
+            } else {
+                if (logger.isDebugEnabled()) {
+                    logger.debug("The user has specified the required JAR file '$JAVA_HOME$/lib/tools.jar' ['"
+                                 + toolsJar.toExternalForm() + "']. A custom URL classloader will be created for the Javac compiler.");
+                }
+
+                return new URLClassLoader(new URL[]{toolsJar}, ClassUtils.getContextClassLoader());
+            }
+        }
+    }
+
+}

Propchange: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/JavacCompiler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/scripting/loaders/java/jdk5/JavacCompiler.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL