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