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 2010/04/12 21:43:37 UTC
svn commit: r933379 [7/15] - in /myfaces/extensions/scripting/trunk:
extscript-core-root/extscript-core-java6/src/main/java/org/apache/myfaces/extensions/
extscript-core-root/extscript-core-java6/src/main/java/org/apache/myfaces/extensions/scripting/
e...
Added: myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/compiler/GroovyCompiler.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/compiler/GroovyCompiler.java?rev=933379&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/compiler/GroovyCompiler.java (added)
+++ myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/compiler/GroovyCompiler.java Mon Apr 12 19:43:30 2010
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.myfaces.extensions.scripting.sandbox.compiler;
+
+import org.apache.myfaces.extensions.scripting.api.CompilationException;
+import org.apache.myfaces.extensions.scripting.api.CompilationResult;
+import org.apache.myfaces.extensions.scripting.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.util.ClassLoaderUtils;
+import org.apache.myfaces.extensions.scripting.core.util.FileUtils;
+import org.apache.myfaces.extensions.scripting.core.util.WeavingContext;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.ErrorCollector;
+import org.codehaus.groovy.control.messages.Message;
+import org.codehaus.groovy.control.messages.SimpleMessage;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>A compiler implementation that can be used to compile Groovy source files.</p>
+ */
+public class GroovyCompiler implements Compiler {
+
+ /**
+ * The logger instance for this class.
+ */
+ private static final Logger logger = Logger.getLogger(GroovyCompiler.class.getName());
+
+ // ------------------------------------------ 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 file of the class you want to compile
+ * @param classLoader the class loader to use to determine the classpath
+ * @return the compilation result
+ */
+ public CompilationResult compile(File sourcePath, File targetPath, String file, ClassLoader classLoader)
+ throws CompilationException {
+ return compile(sourcePath, targetPath, new File(sourcePath, file), classLoader);
+ }
+
+ public CompilationResult compile(File sourcePath, File targetPath, ClassLoader classLoader) {
+
+ List<File> sourceFiles = FileUtils.fetchSourceFiles(WeavingContext.getConfiguration().getWhitelistedSourceDirs(ScriptingConst.ENGINE_TYPE_JSF_GROOVY), "*.groovy");
+
+ StringWriter compilerOutput = new StringWriter();
+
+ CompilationUnit compilationUnit = new CompilationUnit(
+ buildCompilerConfiguration(sourcePath, targetPath, classLoader));
+ compilationUnit.getConfiguration().setOutput(new PrintWriter(compilerOutput));
+
+ for (File sourceFile : sourceFiles) {
+ compilationUnit.addSource(sourceFile);
+ }
+
+ CompilationResult result;
+
+ try {
+ compilationUnit.compile();
+
+ result = new CompilationResult(compilerOutput.toString());
+ WeavingContext.setCompilationResult(ScriptingConst.ENGINE_TYPE_JSF_GROOVY, result);
+
+ } catch (CompilationFailedException ex) {
+ // Register all collected error messages from the Groovy compiler
+ result = new CompilationResult(compilerOutput.toString());
+ ErrorCollector collector = compilationUnit.getErrorCollector();
+ for (int i = 0; i < collector.getErrorCount(); ++i) {
+ result.registerError(convertMessage(collector.getError(i)));
+ }
+ }
+
+ // Register all collected warnings from the Groovy compiler
+ ErrorCollector collector = compilationUnit.getErrorCollector();
+ for (int i = 0; i < collector.getWarningCount(); ++i) {
+ result.registerWarning(convertMessage(collector.getWarning(i)));
+ }
+
+ return result;
+ }
+
+ /**
+ * <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 file of the class you want to compile
+ * @param classLoader the class loader to use to determine the classpath
+ * @return the compilation result
+ */
+ public CompilationResult compile(File sourcePath, File targetPath, File file, ClassLoader classLoader)
+ throws CompilationException {
+ StringWriter compilerOutput = new StringWriter();
+
+ CompilationUnit compilationUnit = new CompilationUnit(
+ buildCompilerConfiguration(sourcePath, targetPath, classLoader));
+ compilationUnit.getConfiguration().setOutput(new PrintWriter(compilerOutput));
+ compilationUnit.addSource(file);
+
+ CompilationResult result;
+
+ try {
+ compilationUnit.compile();
+
+ result = new CompilationResult(compilerOutput.toString());
+
+ WeavingContext.setCompilationResult(ScriptingConst.ENGINE_TYPE_JSF_GROOVY, result);
+ } catch (CompilationFailedException ex) {
+ // Register all collected error messages from the Groovy compiler
+ result = new CompilationResult(compilerOutput.toString());
+ ErrorCollector collector = compilationUnit.getErrorCollector();
+ for (int i = 0; i < collector.getErrorCount(); ++i) {
+ result.registerError(convertMessage(collector.getError(i)));
+ }
+ }
+
+ // Register all collected warnings from the Groovy compiler
+ ErrorCollector collector = compilationUnit.getErrorCollector();
+ for (int i = 0; i < collector.getWarningCount(); ++i) {
+ result.registerWarning(convertMessage(collector.getWarning(i)));
+ }
+
+ return result;
+ }
+
+ // ------------------------------------------ Utility methods
+
+ /**
+ * <p>Converts the given Groovy compiler message into a compilation message that
+ * our compilation API consists of.</p>
+ *
+ * @param message the Groovy compiler message you want to convert
+ * @return the final converted compilation message
+ */
+ protected CompilationResult.CompilationMessage convertMessage(Message message) {
+ if (message instanceof SimpleMessage) {
+ SimpleMessage simpleMessage = (SimpleMessage) message;
+ return new CompilationResult.CompilationMessage(-1, simpleMessage.getMessage());
+ } else if (message instanceof SyntaxErrorMessage) {
+ SyntaxErrorMessage syntaxErrorMessage = (SyntaxErrorMessage) message;
+ return new CompilationResult.CompilationMessage(
+ syntaxErrorMessage.getCause().getLine(), syntaxErrorMessage.getCause().getMessage());
+ } else {
+ if (logger.isLoggable(Level.FINE)) {
+ logger.log(Level.FINE,
+ "This compiler came across an unknown message kind ['" + message + "']. It will be ignored.");
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * <p>Configures the compiler by building its configuration object.</p>
+ *
+ * @param sourcePath the path to the source directory
+ * @param targetPath the path to the target directory
+ * @param classLoader the class loader to use to determine the classpath
+ * @return the compiler configuration
+ */
+ protected CompilerConfiguration buildCompilerConfiguration(File sourcePath, File targetPath, ClassLoader classLoader) {
+ CompilerConfiguration configuration = new CompilerConfiguration();
+
+ // Set the destination / target directory for the compiled .class files.
+ configuration.setTargetDirectory(targetPath.getAbsoluteFile());
+
+ // Specify the classpath of the given class loader. This enables the user to write new Java
+ // "scripts" that depend on classes that have already been loaded previously. Otherwise he
+ // wouldn't be able to use for example classes that are available in a library.
+ configuration.setClasspath(ClassLoaderUtils.buildClasspath(classLoader));
+
+ // Enable verbose output.
+ configuration.setVerbose(true);
+
+ // Generate debugging information.
+ configuration.setDebug(true);
+
+ return configuration;
+ }
+
+}
Added: myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/compiler/JavacCompiler.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/compiler/JavacCompiler.java?rev=933379&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/compiler/JavacCompiler.java (added)
+++ myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/compiler/JavacCompiler.java Mon Apr 12 19:43:30 2010
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.myfaces.extensions.scripting.sandbox.compiler;
+
+import org.apache.myfaces.extensions.scripting.api.CompilationException;
+import org.apache.myfaces.extensions.scripting.api.CompilationResult;
+import org.apache.myfaces.extensions.scripting.core.util.ClassLoaderUtils;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <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>
+ */
+public class JavacCompiler implements Compiler {
+
+ /**
+ * The logger instance for this class.
+ */
+ private static final Logger logger = Logger.getLogger(JavacCompiler.class.getName());
+
+ /**
+ * 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 occurred 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, ClassLoader classLoader)
+ throws CompilationException {
+ return compile(sourcePath, targetPath, new File(sourcePath, file), classLoader);
+ }
+
+ /**
+ * <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 file 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, File file, ClassLoader classLoader)
+ 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, classLoader),
+ 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 occurred 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 occurred 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, File file, ClassLoader classLoader) {
+ List<String> arguments = new ArrayList<String>();
+
+ // Note that we're knowingly not specifying the sourcepath as the compiler really should compile
+ // only a single file (see 'file'). The dependent classes are available on the classpath anyway.
+ // Otherwise the compiler would also compile dependent classes, which we want to avoid! This
+ // would result in different versions of a Class file being in use (the system doesn't know that
+ // it has to update itself due to a newer version of a Class file whilst the dynamic class loader
+ // will already start using it!)
+ // arguments.add("-sourcepath");
+ // arguments.add(sourcePath.getAbsolutePath());
+
+ // Set the destination / target directory for the compiled .class files.
+ arguments.add("-d");
+ arguments.add(targetPath.getAbsolutePath());
+
+ // Specify the classpath of the given class loader. This enables the user to write new Java
+ // "scripts" that depend on classes that have already been loaded previously. Otherwise he
+ // wouldn't be able to use for example classes that are available in a library.
+ arguments.add("-classpath");
+ arguments.add(ClassLoaderUtils.buildClasspath(classLoader));
+
+ // 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(file.getAbsolutePath());
+
+ return arguments.toArray(new String[arguments.size()]);
+ }
+
+ /**
+ * <p>Returns a possibly newly created class loader 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 class loader 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 (ClassLoaderUtils.isClassAvailable(JAVAC_MAIN, ClassLoaderUtils.getDefaultClassLoader())) {
+ if (logger.isLoggable(Level.FINE)) {
+ logger.log(Level.FINE, "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 class loader for the Javac compiler.");
+ }
+
+ return ClassLoaderUtils.getDefaultClassLoader();
+ } 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(Locale.getDefault()).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.isLoggable(Level.FINE)) {
+ logger.log(Level.FINE,
+ "The required JAR file '$JAVA_HOME$/lib/tools.jar' has been found ['" + toolsJarFile.getAbsolutePath()
+ + "']. A custom URL class loader will be created for the Javac compiler.");
+ }
+
+ return new URLClassLoader(
+ new URL[]{toolsJarFile.toURI().toURL()}, ClassLoaderUtils.getDefaultClassLoader());
+ } 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.isLoggable(Level.FINE)) {
+ logger.log(Level.FINE, "The user has specified the required JAR file '$JAVA_HOME$/lib/tools.jar' ['"
+ + toolsJar.toExternalForm() + "']. A custom URL class loader will be created for the Javac compiler.");
+ }
+
+ return new URLClassLoader(new URL[]{toolsJar}, ClassLoaderUtils.getDefaultClassLoader());
+ }
+ }
+ }
+
+}
Added: myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/ReloadingClassLoader.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/ReloadingClassLoader.java?rev=933379&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/ReloadingClassLoader.java (added)
+++ myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/ReloadingClassLoader.java Mon Apr 12 19:43:30 2010
@@ -0,0 +1,321 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.myfaces.extensions.scripting.sandbox.loader;
+
+import org.apache.myfaces.extensions.scripting.core.util.FileUtils;
+import org.apache.myfaces.extensions.scripting.loaders.java.ThrowawayClassloader;
+import org.apache.myfaces.extensions.scripting.sandbox.loader.support.ClassFileLoader;
+import org.apache.myfaces.extensions.scripting.sandbox.loader.support.OverridingClassLoader;
+import org.apache.myfaces.extensions.scripting.sandbox.loader.support.ThrowAwayClassLoader;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>A class loader implementation that enables you to reload certain classes. It automatically
+ * reloads classes if there's a newer version of a .class file available in a specified compilation
+ * target path. However, it's also possible to explicitly reload other classes.</p>
+ * <p/>
+ * <p>This enables you to do both modify and reload various classes that you've used for Spring
+ * bean definitions, but it also enables you to reload for example classes depending on those
+ * dynamically compiled classes, like factory bean classes. By explicitly reloading a factory
+ * bean class the newly loaded factory bean will return updated bean instances as well!</p>
+ * <p/>
+ * <p>Note that even though this class extends the class URLClassLoader it doesn't use any
+ * of its functionalities. This class loader just works similar and provides a similar interface
+ * so it's useful to extend the class URLClassLoader as you can treat it like one (especially
+ * when it comes to resolving the classpath of a class loader).</p>
+ */
+public class ReloadingClassLoader extends URLClassLoader {
+
+ /**
+ * The system-dependent default name-separator character. Note that it's safe to
+ * use this version of the file separator in regex methods, like replaceAll().
+ */
+ private static String FILE_SEPARATOR = FileUtils.getFileSeparatorForRegex();
+
+ /**
+ * The logger instance for this class.
+ */
+ private static final Logger _logger = Logger.getLogger(ReloadingClassLoader.class.getName());
+
+ /**
+ * A table of class names and the according class loaders. It's basically like
+ * a list of classes that this class loader has already loaded. However, the
+ * thing is that this class loader isn't actually going to load them as we
+ * would loose the possibility to override them then, which is the reason why
+ * each class has got its own class loader.
+ */
+ private Map<String, ThrowAwayClassLoader> _classLoaders =
+ new HashMap<String, ThrowAwayClassLoader>();
+
+ /**
+ * The target directory for the compiler, i.e. the directory that contains the
+ * dynamically compiled .class files.
+ */
+ private File _compilationDirectory;
+
+ // ------------------------------------------ Constructors
+
+ /**
+ * <p>Constructs a new reloading classloader for the specified compilation
+ * directory using the default delegation parent classloader. Note that this
+ * classloader will only delegate to the parent classloader if there's no
+ * dynamically compiled class available.</p>
+ *
+ * @param compilationDirectory the compilation directory
+ */
+ public ReloadingClassLoader(File compilationDirectory) {
+ super(new URL[0]);
+ this._compilationDirectory = compilationDirectory;
+ }
+
+ /**
+ * <p>Constructs a new reloading classloader for the specified compilation
+ * directory using the given delegation parent classloader. Note that this
+ * classloader will only delegate to the parent classloader if there's no
+ * dynamically compiled class available.</p>
+ *
+ * @param parentClassLoader the parent classloader
+ * @param compilationDirectory the compilation directory
+ */
+ public ReloadingClassLoader(ClassLoader parentClassLoader, File compilationDirectory) {
+ super(new URL[0], parentClassLoader);
+ this._compilationDirectory = compilationDirectory;
+ }
+
+ // ------------------------------------------ URLClassLoader methods
+
+ /**
+ * <p>Loads the class with the specified binary name. This method searches for classes in the
+ * compilation directory that you've specified previously. Note that this class loader recognizes
+ * whether the class files have changed, that means, if you recompile and reload a class, you'll
+ * get a Class object that represents the recompiled class.</p>
+ *
+ * @param className the binary name of the class you want to load
+ * @param resolve <tt>true</tt>, if the class is to be resolved
+ * @return The resulting <tt>Class</tt> object
+ * @throws ClassNotFoundException if the class could not be found
+ */
+ protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
+ // First of all, check if there's a class file available in the compilation target path.
+ // It doesn't matter which class we're dealing with at the moment as there's always the
+ // possibility that the user is either trying to override a statically compiled class
+ // (i.e. a class that has been compiled before deploying the application) or he/she is
+ // trying to modify a dynamically compiled class, in which case we should compare
+ // timestamps, etc.
+ File classFile = resolveClassFile(className);
+ if (classFile != null && classFile.exists()) {
+ if (_classLoaders.containsKey(className)) {
+ // Check if the class loader is already outdated, i.e. there is a newer class file available
+ // for the class we want to load than the class file we've already loaded. If that's the case
+ // we're going to throw away this ClassLoader and create a new one for linkage reasons.
+ ThrowAwayClassLoader classLoader = _classLoaders.get(className);
+ if (classLoader.isOutdated(classFile.lastModified())) {
+ // If the class loader is outdated, create a new one. Otherwise the same class loader
+ // would have to load the same class twice or more often which would cause severe
+ // linkage errors. Actually the JVM wouldn't permit that anyway and throw some
+ // linkage errors / exceptions.
+ reloadClass(className);
+ }
+ } else {
+ if (_logger.isLoggable(Level.FINEST)) {
+ _logger.log(Level.FINEST, "A new dynamic class '"
+ + className + "' has been found by this class loader '" + this + "'.");
+ }
+
+ // We haven't loaded this class so far, but there is a .class file available,
+ // so we have to reload the given class.
+ reloadClass(className);
+ }
+
+ ThrowAwayClassLoader classLoader = _classLoaders.get(className);
+ return classLoader.loadClass(className, resolve);
+ } else {
+ // Even though there is no class file available, there's still a chance that this
+ // class loader has forcefully reloaded a statically compiled class.
+ if (_classLoaders.containsKey(className)) {
+ ThrowAwayClassLoader classLoader = _classLoaders.get(className);
+ return classLoader.loadClass(className, resolve);
+ } else {
+ // However, if there's neither a .class file nor a reloadable class loader
+ // available, just delegate to the parent class loader.
+ return super.loadClass(className, resolve);
+ }
+ }
+ }
+
+ /**
+ * <p>Returns the search path of URLs for loading classes, i.e. the
+ * given compilation target directory, the directory that contains the
+ * dynamically compiled .class files.</p>
+ *
+ * @return the search path of URLs for loading classes
+ */
+ public URL[] getURLs() {
+ try {
+ return new URL[]{_compilationDirectory.toURI().toURL()};
+ } catch (IOException ex) {
+ _logger.log(Level.SEVERE, "Couldn't resolve the URL to the compilation directory {0} {1} {2} ", new String[]{_compilationDirectory.getAbsolutePath(), ex.getMessage(), ex.toString()});
+ return new URL[0];
+ }
+ }
+
+ // ------------------------------------------ Public methods
+
+ /**
+ * <p>Determines whether the given class has been loaded by a class
+ * loader that is already outdated.</p>
+ *
+ * @param classObj the class you want to check
+ * @return <code>true</code, if there is a newer class file available for the given object
+ */
+ public boolean isOutdated(Class classObj) {
+ // Is there even a dynamically compiled class file available for the given class?
+ File classFile = resolveClassFile(classObj.getName());
+ if (classFile.exists()) {
+ // If so, check if we the Class reference has been loaded by a ThrowAwayClassLoader.
+ // Otherwise it's definitely outdated and we don't have to compare timestamps.
+ if (classObj.getClassLoader() instanceof ThrowAwayClassLoader) {
+ // Compare the timestamps in order to determine whether the given Class
+ // reference is already outdated.
+ ThrowAwayClassLoader classLoader = (ThrowAwayClassLoader) classObj.getClassLoader();
+ return classLoader.isOutdated(classFile.lastModified());
+ } else {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * <p>Reloads the given class internally explicitly. Note that this classloader usually
+ * reloads classes automatically, i.e. this classloader detects if there is a newer
+ * version of a class file available in the compilation directory. However, by using
+ * this method you tell this classloader to forcefully reload the given class. For
+ * example, if you've got a newer version of a dynamically recompiled class and a
+ * statically compiled class depending on this one, you can tell this classloader to
+ * reload the statically compiled class as well so that it references the correct
+ * version of the Class object.</p>
+ *
+ * @param className the class you want to reload
+ */
+ public void reloadClass(final String className) {
+ ThrowAwayClassLoader classLoader;
+
+ final File classFile = resolveClassFile(className);
+ final ReloadingClassLoader _this = this;
+ try {
+ if (classFile != null && classFile.exists()) {
+
+ classLoader = AccessController.doPrivileged(new PrivilegedExceptionAction<ClassFileLoader>() {
+ public ClassFileLoader run() {
+ return new ClassFileLoader(className, classFile, _this);
+ }
+ });
+
+ } else {
+ classLoader = AccessController.doPrivileged(new PrivilegedExceptionAction<OverridingClassLoader>() {
+ public OverridingClassLoader run() {
+ return new OverridingClassLoader(className, _this);
+ }
+ });
+ }
+ } catch (PrivilegedActionException e) {
+ _logger.log(Level.SEVERE, "", e);
+ return;
+ }
+
+ ThrowAwayClassLoader oldClassLoader = _classLoaders.put(className, classLoader);
+ if (_logger.isLoggable(Level.INFO)) {
+ if (oldClassLoader != null) {
+ _logger.info("Replaced the class loader '" + oldClassLoader + "' with the class loader '"
+ + classLoader + "' as this class loader is supposed to reload the class '" + className + "'.");
+ } else {
+ _logger.info("Installed a new class loader '" + classLoader + "' for the class '"
+ + className + "' as this class loader is supposed to reload it.");
+ }
+ }
+ }
+
+ /**
+ * <p>Returns a copy of the current reloading class loader with the only difference
+ * being the parent class loader to use. Use this method if you just want to replace
+ * the parent class loader (obviously you can't do that after a ClassLoader has been
+ * created, hence a copy is created).</p>
+ *
+ * @param parentClassLoader the parent ClassLoader to use
+ * @return a copy of the current reloading class loader
+ */
+ @SuppressWarnings("unused")
+ public ReloadingClassLoader cloneWithParentClassLoader(final ClassLoader parentClassLoader) {
+ ReloadingClassLoader classLoader = null;
+ try {
+ classLoader = AccessController.doPrivileged(new PrivilegedExceptionAction<ReloadingClassLoader>() {
+ public ReloadingClassLoader run() {
+ return new ReloadingClassLoader(parentClassLoader, _compilationDirectory);
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ _logger.log(Level.SEVERE, "", e);
+ return null;
+ }
+
+ // Note that we don't have to create "deep copies" as the class loaders in the map
+ // are immutable anyway (they are only supposed to load a single class) and additionally
+ // this map doesn't contain any classes that have been loaded using the current parent
+ // class loader!
+ classLoader._classLoaders = new HashMap<String, ThrowAwayClassLoader>(_classLoaders);
+
+ return classLoader;
+ }
+
+ // ------------------------------------------ Utility methods
+
+ /**
+ * <p>Resolves and returns a File handle that represents the class file of
+ * the given class on the file system. However, note that this method only
+ * returns <code>null</code> if an error occured while resolving the class
+ * file. A non-null valuee doesn't necessarily mean that the class file
+ * actually exists. In oder to check the existence call the according
+ * method on the returned object.</p>
+ *
+ * @param className the name of the class that you want to resolve
+ * @return a File handle that represents the class file of the given class
+ * on the file system
+ * @see java.io.File#exists()
+ */
+ protected File resolveClassFile(String className) {
+ // This method just has to look in the specified compilation directory. The
+ // relative class file path can be computed from the class name.
+ return new File(_compilationDirectory,
+ className.replaceAll("\\.", FILE_SEPARATOR).concat(".class"));
+ }
+
+}
Added: myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/AbstractThrowAwayClassLoader.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/AbstractThrowAwayClassLoader.java?rev=933379&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/AbstractThrowAwayClassLoader.java (added)
+++ myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/AbstractThrowAwayClassLoader.java Mon Apr 12 19:43:30 2010
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.myfaces.extensions.scripting.sandbox.loader.support;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ *
+ */
+public abstract class AbstractThrowAwayClassLoader extends URLClassLoader
+ implements ThrowAwayClassLoader {
+
+ /**
+ * The size of the buffer we're going to use to copy the contents from a stream to a byte array.
+ */
+ private static final int BUFFER_SIZE = 4096;
+
+ /**
+ * Indicates when this ClassLoader has been created.
+ */
+ private final long _timestamp;
+
+ /**
+ * The name of the class that this class loader is going to load.
+ */
+ private final String _className;
+
+ // ------------------------------------------ Constructors
+
+ public AbstractThrowAwayClassLoader(String className, ClassLoader parentClassLoader) {
+ super(new URL[0], parentClassLoader);
+
+ if (className == null) {
+ throw new IllegalArgumentException("The given class name must not be null.");
+ }
+
+ // Save a timestamp of the time this class loader has been created. In doing
+ // so, we're able to tell if this class loader is already outdated or not.
+ this._timestamp = System.currentTimeMillis();
+ this._className = className;
+ }
+
+ // ------------------------------------------ ThrowAwayClassLoader methods
+
+ /**
+ * <p>Loads the class with the specified class name. However, note that implementing
+ * classes are just supposed to load a single class, so if you want to load a different
+ * class than that, this class loader will just delegate to the parent class loader.</p>
+ *
+ * @param className the name of the class you want to load
+ * @param resolve if <tt>true</tt> then resolve the class
+ * @return the resulting Class reference
+ * @throws ClassNotFoundException if the class could not be found
+ */
+ public Class loadClass(String className, boolean resolve) throws ClassNotFoundException {
+ Class c;
+
+ // Note that this class loader is only supposed to load a specific Class reference,
+ // hence the check against the class name. Otherwise this class loader would try to
+ // resolve class files for dependent classes as well, which means that there would
+ // be different versions of the same Class reference in the system.
+ if (isEligibleForLoading(className)) {
+ // First, check if the class has already been loaded
+ c = findLoadedClass(className);
+ if (c == null) {
+ // Note that execution reaches this point only if we're either updating a
+ // dynamically loaded class or loading it for the first time. Otherwise
+ // this ClassLoader would have returned an already loaded class (see the
+ // call to findLoadedClass()).
+ c = findClass(className);
+ if (resolve) {
+ resolveClass(c);
+ }
+ }
+ }
+
+ // If this class loader isn't supposed to load the given class it doesn't
+ // necessarily mean, that we're not dealing with a dynamic class here.
+ // However, if that's the case, we really want to use the same class loader
+ // (i.e. the same ClassFileLoader instance) as Spring does, hence the
+ // delegation to the parent class loader (i.e. the ReloadingClassLoader
+ // again).
+ else {
+ c = super.loadClass(className, resolve);
+ }
+
+ return c;
+ }
+
+ /**
+ * <p>Returns <code>true</code> if the given "last modified"-timestamp is
+ * more recent than the time stamp of this class loader, i.e. if this class loader
+ * is to be destroyed as there is a newer class file available.
+ *
+ * @param lastModified the "last modified"-timestamp of the class file you want to load
+ * @return <code>true</code> if the given "last modified"-timestamp is
+ * more recent than the time stamp of this ClassLoader
+ */
+ public boolean isOutdated(long lastModified) {
+ return _timestamp < lastModified;
+ }
+
+ // ------------------------------------------ Utility methods
+
+ /**
+ * <p>Determines whether this class loader is supposed to load the given class.</p>
+ *
+ * @param className the name of the class
+ * @return <code>true</code>, if this class loader is supposed to load the
+ * given class, <code>false</code> otherwise
+ */
+ protected boolean isEligibleForLoading(String className) {
+ return getClassName().equals(className);
+ }
+
+ /**
+ * <p>Finds and loads the class with the specified name from the compilation path.</p>
+ *
+ * @param className the name of the class
+ * @return the resulting class
+ * @throws ClassNotFoundException if the class could not be found
+ */
+ protected Class findClass(final String className) throws ClassNotFoundException {
+ if (isEligibleForLoading(className)) {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Class<?>>() {
+ public Class<?> run() throws Exception {
+ InputStream stream = null;
+
+ try {
+ // Load the raw bytes of the class file into the memory ..
+ stream = openStreamForClass(className);
+ if (stream != null) {
+ byte[] buffer = loadClassFromStream(stream);
+
+ // .. and create an according Class object.
+ return defineClass(className, buffer, 0, buffer.length);
+ } else {
+ throw new ClassNotFoundException(
+ "Cannot find the resource that defines the class '" + className + "'.");
+ }
+ }
+ catch (IOException ex) {
+ throw new ClassNotFoundException(
+ "Cannot load the raw byte contents for the class '" + className + "'.", ex);
+ }
+ finally {
+ if (stream != null) {
+ stream.close();
+ }
+ }
+ }
+ });
+ }
+ catch (PrivilegedActionException e) {
+ throw (ClassNotFoundException) e.getException();
+ }
+ } else {
+ throw new ClassNotFoundException(
+ "This class loader only knows how to load the class '" + getClassName() + "'.");
+ }
+ }
+
+ /**
+ * <p>Returns the name of the class that this class loader is going to load.</p>
+ *
+ * @return the name of the class that this class loader is going to load
+ */
+ protected String getClassName() {
+ return _className;
+ }
+
+ /**
+ * <p>Loads the byte array that you can use to define the given class
+ * afterwards using a call to {@link #defineClass}.</p>
+ *
+ * @param stream a stream referencing e.g. a .class file
+ * @return the byte array that you can use to define the given class
+ * @throws IOException if an I/O error occurs
+ */
+ private byte[] loadClassFromStream(InputStream stream) throws IOException {
+ ByteArrayOutputStream result = new ByteArrayOutputStream(BUFFER_SIZE * 5);
+
+ byte[] buffer = new byte[BUFFER_SIZE];
+
+ int readBytes;
+ while ((readBytes = stream.read(buffer)) != -1) {
+ result.write(buffer, 0, readBytes);
+ }
+
+ return result.toByteArray();
+ }
+
+ // ------------------------------------------ Abstract methods
+
+ /**
+ * <p>Opens a stream to the resource that defines the given class. If it
+ * cannot be found, return <code>null</code>.</p>
+ *
+ * @param className the class to load
+ * @return a stream to the resource that defines the given class
+ * @throws IOException if an I/O error occurs
+ */
+ protected abstract InputStream openStreamForClass(String className) throws IOException;
+
+}
Added: myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/ClassFileLoader.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/ClassFileLoader.java?rev=933379&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/ClassFileLoader.java (added)
+++ myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/ClassFileLoader.java Mon Apr 12 19:43:30 2010
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.myfaces.extensions.scripting.sandbox.loader.support;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>This class loader actually loads the newly compiled classes. Each newly compiled class
+ * requires one to instantiate a new instance of this class loader, in doing so we don't cause
+ * severe linkage errors (the JVM doesn't permit class loaders to load classes more than once).
+ * That means on the other hand that each class loader is just going to load a single class.
+ * </p>
+ */
+public class ClassFileLoader extends AbstractThrowAwayClassLoader {
+
+ /**
+ * The logger instance for this class.
+ */
+ private static final Logger logger = Logger.getLogger(ClassFileLoader.class.getName());
+
+ /**
+ * The .class file that contains the byte code for the class that this class loader is going to load.
+ */
+ private final File classFile;
+
+ // -------------------------------------- Constructors
+
+ /**
+ * <p>Constructs a new class loader that is just going to load the given class file. If one
+ * requests to load a different class than that, this class loader will just delegate the
+ * request to the given parent class loader.</p>
+ *
+ * @param className the name of the class that this class loader is going to load
+ * @param classFile a reference to the .class file that contains the bytecode for the class
+ * @param parentClassLoader the parent class loader
+ */
+ public ClassFileLoader(String className, File classFile, ClassLoader parentClassLoader) {
+ super(className, parentClassLoader);
+
+ if (classFile == null) {
+ throw new IllegalArgumentException("The given class file must not be null.");
+ }
+
+ this.classFile = classFile;
+ }
+
+ // ------------------------------------------ URLClassLoader methods
+
+ /**
+ * <p>Returns the search path of URLs for loading classes, i.e. the
+ * given compilation target directory, the directory that contains the
+ * dynamically compiled .class files.</p>
+ *
+ * @return the search path of URLs for loading classes
+ */
+ public URL[] getURLs() {
+ try {
+ return new URL[]{classFile.toURI().toURL()};
+ }
+ catch (IOException ex) {
+ logger.log(Level.SEVERE, "Couldn't resolve the URL to the class file {0} that this class loader {1} should load, {2} {3}", new String [] {classFile.getAbsolutePath() , this.getClassName(), ex.getMessage(), ex.toString()});
+
+ return new URL[0];
+ }
+ }
+
+ // -------------------------------------- ThrowAwayClassLoader methods
+
+ /**
+ * <p>Opens a stream to the resource that defines the given class. If it
+ * cannot be found, return <code>null</code>.</p>
+ *
+ * @param className the class to load
+ * @return a stream to the resource that defines the given class
+ * @throws java.io.IOException if an I/O error occurs
+ */
+ protected InputStream openStreamForClass(String className) throws IOException {
+ return new FileInputStream(classFile);
+ }
+
+}
Added: myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/OverridingClassLoader.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/OverridingClassLoader.java?rev=933379&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/OverridingClassLoader.java (added)
+++ myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/OverridingClassLoader.java Mon Apr 12 19:43:30 2010
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.myfaces.extensions.scripting.sandbox.loader.support;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * <p>A reloadable class loader implementation that you can use to forcefully reload classes
+ * even if you don't want to recompile them and hence haven't got the actual .class file. Use
+ * this class loader if you want to reload a class that depends on a dynamically compiled
+ * class, for example, in case of Spring if you've got a factory bean constructing bean
+ * instances of a dynamically compiled class. Once the dynamically compiled class changes,
+ * the class of the factory bean has to be reloaded as well even though it somehow didn't
+ * really change.</p>
+ */
+public class OverridingClassLoader extends AbstractThrowAwayClassLoader {
+
+ // ------------------------------------------ Constructors
+
+ /**
+ * <p>Constructs a new overriding class loader using the name of the class that
+ * it's going to override and the parent class loader. Note that this class loader
+ * only loads the class definition for the given class name. Otherwise it will
+ * delegate to the parent class loader.</p>
+ *
+ * @param className the name of the class that it's going to override
+ * @param parent the parent class loader
+ */
+ public OverridingClassLoader(String className, ClassLoader parent) {
+ super(className, parent);
+ }
+
+ // ------------------------------------------ AbstractThrowAwayClassLoader methods
+
+ /**
+ * <p>Opens a stream to the resource that defines the given class using the parent
+ * class loader. If it cannot be found, return <code>null</code>.</p>
+ *
+ * @param className the class to load
+ * @return a stream to the resource that defines the given class
+ * @throws IOException if an I/O error occurs
+ */
+ protected InputStream openStreamForClass(String className) throws IOException {
+ return getParent().getResourceAsStream(className.replace('.', '/') + ".class");
+ }
+
+}
Added: myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/ThrowAwayClassLoader.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/ThrowAwayClassLoader.java?rev=933379&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/ThrowAwayClassLoader.java (added)
+++ myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/loader/support/ThrowAwayClassLoader.java Mon Apr 12 19:43:30 2010
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.myfaces.extensions.scripting.sandbox.loader.support;
+
+/**
+ * <p>A class loader that implements this interface is able to throw away class definitions.
+ * Well, to be more precise, no class loader is really able to do that, but you can get this
+ * behaviour by just throwing away this class loader. Note that a class loader implementing
+ * this interface is supposed to load just a single class definition, i.e. there's a 1:1
+ * relationship between the class loader and the class. In doing so, we're somehow able to
+ * throw away class definitions and replace them with newer versions.</p>
+ */
+public interface ThrowAwayClassLoader {
+
+ /**
+ * <p>Loads the class with the specified class name. However, note that implementing
+ * classes are just supposed to load a single class, so if you want to load a different
+ * class than that, this class loader will just delegate to the parent class loader.</p>
+ *
+ * @param className the name of the class you want to load
+ * @param resolve if <tt>true</tt> then resolve the class
+ * @return the resulting Class reference
+ * @throws ClassNotFoundException if the class could not be found
+ */
+ public Class loadClass(String className, boolean resolve) throws ClassNotFoundException;
+
+ /**
+ * <p>Returns <code>true</code> if the given "last modified"-timestamp is
+ * more recent than the timestamp of this ClassLoader, i.e. if this ClassLoader
+ * is to be destroyed as there is a newer class file available.
+ *
+ * @param lastModified the "last modified"-timestamp of the class file you want to load
+ * @return <code>true</code> if the given "last modified"-timestamp is
+ * more recent than the timestamp of this class loader
+ */
+ public boolean isOutdated(long lastModified);
+
+}
\ No newline at end of file
Added: myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/readme.txt
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/readme.txt?rev=933379&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/readme.txt (added)
+++ myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/sandbox/readme.txt Mon Apr 12 19:43:30 2010
@@ -0,0 +1,3 @@
+This files are sandbox they will be merged over time into the core
+nothing of this code is used outside of package, but code
+parts will be recycled and once done the class is removed
Added: myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/CustomChainLoader.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/CustomChainLoader.java?rev=933379&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/CustomChainLoader.java (added)
+++ myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/CustomChainLoader.java Mon Apr 12 19:43:30 2010
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.myfaces.extensions.scripting.servlet;
+
+import org.apache.myfaces.extensions.scripting.api.ScriptingWeaver;
+import org.apache.myfaces.extensions.scripting.core.util.WeavingContext;
+import org.apache.myfaces.shared_impl.util.ClassLoaderExtension;
+
+import javax.servlet.ServletContext;
+import java.util.logging.Logger;
+
+/**
+ * a custom chainloader which adds a groovy loading
+ * facility to our myfaces loading plugin system
+ *
+ * @author Werner Punz
+ */
+@SuppressWarnings("unused")
+public class CustomChainLoader extends ClassLoaderExtension {
+
+ /*
+ * servlet context init var for additional chain loader paths which have
+ * higher priority than the default ones
+ */
+
+ ScriptingWeaver _scriptingWeaver = null;
+
+ public CustomChainLoader(ServletContext servletContext) {
+ _scriptingWeaver = WeavingContext.getWeaver();
+ }
+
+
+ public Class forName(String name) {
+ if(_scriptingWeaver == null || name == null) {
+ return null;
+ }
+
+ if (name.endsWith(";")) {
+ name = name.substring(1, name.length() - 1);
+ }
+
+ if (name.startsWith("java.")) /*the entire java namespace is reserved so no use to do a specific classloading check here*/
+ return null;
+ if (name.startsWith("javax.")) /*the entire java namespace is reserved so no use to do a specific classloading check here*/
+ return null;
+ else if (name.startsWith("com.sun")) /*internal java specific namespace*/
+ return null;
+ else if (name.startsWith("sun.")) /*internal java specific namespace*/
+ return null;
+ else if (name.startsWith("org.apache") && !name.startsWith("org.apache.myfaces")) {
+ return null;
+ }
+
+ return _scriptingWeaver.loadScriptingClassFromName(name);
+ }
+
+
+
+}
\ No newline at end of file
Added: myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/ScriptingServletFilter.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/ScriptingServletFilter.java?rev=933379&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/ScriptingServletFilter.java (added)
+++ myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/ScriptingServletFilter.java Mon Apr 12 19:43:30 2010
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.myfaces.extensions.scripting.servlet;
+
+import org.apache.myfaces.extensions.scripting.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.util.WeavingContext;
+
+import javax.servlet.*;
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+/**
+ * Scripting servlet filter
+ * <p/>
+ * hits the filter while the
+ * init system is not entirely finished yet
+ *
+ * @author Werner Punz
+ */
+public class ScriptingServletFilter implements Filter {
+
+ ServletContext _context = null;
+ static volatile boolean _active = false;
+ static volatile boolean _warned = false;
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ _context = filterConfig.getServletContext();
+ /*we cannot use the context listener here
+ * because we have the problem that we do not want to parse the web.xml*/
+ WeavingContext.setFilterEnabled(true);
+ }
+
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ assertInitialized();
+ if (!_active) {
+ filterChain.doFilter(servletRequest, servletResponse);
+ return;
+ }
+ markRequestStart();
+ WeavingContext.initThread(_context);
+ WeavingContext.getRefreshContext().setCurrentlyRunningRequests(getRequestCnt());
+
+ try {
+ filterChain.doFilter(servletRequest, servletResponse);
+ } finally {
+ markRequestEnd();
+ }
+ }
+
+ /**
+ * Checks for an initialized system and if not the filter will be deactivated
+ * <p/>
+ * the idea is to check the context in regular intervals
+ * whether the startup process has been finished and then
+ * allow the requests to pass through
+ */
+ private void assertInitialized() {
+ if (_active) return;
+
+ AtomicBoolean startup = (AtomicBoolean) _context.getAttribute(ScriptingConst.CTX_ATTR_STARTUP);
+ if (startup == null) {
+ if (!_warned) {
+ Logger log = Logger.getLogger(ScriptingServletFilter.class.getName());
+ log.warning("[EXT-SCRIPTING] the Startup plugin chainloader has not been set, ext scripting is not working" +
+ "please refer to the documentation for the org.apache.myfaces.FACES_INIT_PLUGINS parameter, deactivating servlet filter");
+ _active = false;
+ }
+ } else {
+ _active = !startup.get();
+ }
+ }
+
+ public void destroy() {
+
+ WeavingContext.clean();
+ }
+
+ //we mark the request beginning and end for further synchronisation issues
+
+ private final AtomicInteger getRequestCnt() {
+ AtomicInteger retVal = (AtomicInteger) _context.getAttribute(ScriptingConst.CTX_ATTR_REQUEST_CNT);
+
+ return retVal;
+ }
+
+ private int markRequestStart() {
+ return getRequestCnt().incrementAndGet();
+ }
+
+ private int markRequestEnd() {
+ return getRequestCnt().decrementAndGet();
+ }
+
+}
\ No newline at end of file
Added: myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/StartupServletContextPluginChainLoader.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/StartupServletContextPluginChainLoader.java?rev=933379&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/StartupServletContextPluginChainLoader.java (added)
+++ myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/StartupServletContextPluginChainLoader.java Mon Apr 12 19:43:30 2010
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.myfaces.extensions.scripting.servlet;
+
+import org.apache.myfaces.extensions.scripting.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.util.ClassUtils;
+import org.apache.myfaces.extensions.scripting.core.util.WeavingContext;
+import org.apache.myfaces.extensions.scripting.refresh.RefreshContext;
+import org.apache.myfaces.webapp.StartupListener;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+/**
+ * Note, since MyFaces 1.2.8 we have a startup and shutdown event system
+ * which allows us to to hook event listener on servlet level before JSF is initialized
+ * and after it is destroyed (and of course in the phases in between)
+ * <p/>
+ * We use this to start our scripting engine and to hook in our class loading
+ * facilities before MyFaces performs its startup routines.
+ *
+ * @author Werner Punz
+ */
+public class StartupServletContextPluginChainLoader implements StartupListener {
+ final Logger _log = Logger.getLogger(this.getClass().getName());
+
+ public void preInit(ServletContextEvent servletContextEvent) {
+
+ _log.info("[EXT-SCRIPTING] Instantiating StartupServletContextPluginChainLoader");
+
+ ServletContext servletContext = servletContextEvent.getServletContext();
+ if (servletContext == null) return;
+
+ servletContext.setAttribute(ScriptingConst.CTX_ATTR_REQUEST_CNT, new AtomicInteger(0));
+ servletContext.setAttribute(ScriptingConst.CTX_ATTR_STARTUP, new AtomicBoolean(Boolean.TRUE));
+
+ initContext(servletContext);
+ initChainLoader(servletContext);
+ initCompileAndScan();
+ }
+
+ private void initCompileAndScan() {
+ if (WeavingContext.isScriptingEnabled()) {
+ _log.info("[EXT-SCRIPTING] Compiling all sources for the first time");
+ WeavingContext.getWeaver().postStartupActions();
+ }
+ }
+
+ /**
+ * initializes our custom chain loader which gets plugged into
+ * the MyFaces loading part for classes!
+ *
+ * @param servletContext the servlet context to be passed down
+ * @return the custom chain loader for loading our classes over our classloaders
+ */
+ private CustomChainLoader initChainLoader(ServletContext servletContext) {
+ CustomChainLoader loader = new CustomChainLoader(servletContext);
+ ClassUtils.addClassLoadingExtension(loader, true);
+ return loader;
+ }
+
+ /**
+ * initializes the central config storage!
+ *
+ * @param servletContext the applications servlet context
+ */
+ private void initContext(ServletContext servletContext) {
+ WeavingContext.startup(servletContext);
+ }
+
+ public void postInit(ServletContextEvent evt) {
+ //tell the system that the startup phase is done
+ evt.getServletContext().setAttribute(ScriptingConst.CTX_ATTR_STARTUP, new AtomicBoolean(Boolean.FALSE));
+ }
+
+ public void preDestroy(ServletContextEvent evt) {
+
+ }
+
+ public void postDestroy(ServletContextEvent evt) {
+ //context is destroyed we have to shut down our daemon as well, by giving it
+ //a hint to shutdown
+
+ //TODO this is probably not needed because we run in a daemon thread anyway
+ //so the servlet should not have a problem to shut it down externally
+ RefreshContext rContext = (RefreshContext) evt.getServletContext().getAttribute("RefreshContext");
+ rContext.getDaemon().setRunning(false);
+ }
+
+}
\ No newline at end of file
Modified: myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/resources/META-INF/extscripting.taglib.xml
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/resources/META-INF/extscripting.taglib.xml?rev=933379&r1=933378&r2=933379&view=diff
==============================================================================
--- myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/resources/META-INF/extscripting.taglib.xml (original)
+++ myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/main/resources/META-INF/extscripting.taglib.xml Mon Apr 12 19:43:30 2010
@@ -33,15 +33,15 @@ one for jsf 2.0 but for now this approac
<tag>
<tag-name>compilerOutput</tag-name>
<component>
- <component-type>org.apache.myfaces.scripting.components.CompilerComponent</component-type>
- <renderer-type>org.apache.myfaces.scripting.components.CompilerComponentRenderer</renderer-type>
+ <component-type>org.apache.myfaces.extensions.scripting.components.CompilerComponent</component-type>
+ <renderer-type>org.apache.myfaces.extensions.scripting.components.CompilerComponentRenderer</renderer-type>
</component>
</tag>
<tag>
<tag-name>taintHistory</tag-name>
<component>
- <component-type>org.apache.myfaces.scripting.components.TaintHistory</component-type>
- <renderer-type>org.apache.myfaces.scripting.components.TaintHistoryRenderer</renderer-type>
+ <component-type>org.apache.myfaces.extensions.scripting.components.TaintHistory</component-type>
+ <renderer-type>org.apache.myfaces.extensions.scripting.components.TaintHistoryRenderer</renderer-type>
</component>
</tag>
</facelet-taglib>
Added: myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/api/CompilationExceptionTest.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/api/CompilationExceptionTest.java?rev=933379&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/api/CompilationExceptionTest.java (added)
+++ myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/api/CompilationExceptionTest.java Mon Apr 12 19:43:30 2010
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.myfaces.extensions.scripting.api;
+
+import org.apache.myfaces.extensions.scripting.api.CompilationException;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * dummy test for the class to have class level coverage
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class CompilationExceptionTest {
+ private static final String ERR_MSG = "BlaMessage";
+
+ @Test
+ public void testCompilationException() {
+ CompilationException ex = new CompilationException(ERR_MSG);
+
+ assertTrue(ex.getMessage().equals(ERR_MSG));
+
+ ex = new CompilationException(ERR_MSG, new NullPointerException(ERR_MSG));
+ assertTrue(ex.getMessage().equals(ERR_MSG));
+ assertTrue(ex.getCause() instanceof NullPointerException);
+
+ ex = new CompilationException(new NullPointerException(ERR_MSG));
+ assertTrue(ex.getCause() instanceof NullPointerException);
+ }
+
+}
Added: myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/api/CompilationResultTest.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/api/CompilationResultTest.java?rev=933379&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/api/CompilationResultTest.java (added)
+++ myfaces/extensions/scripting/trunk/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/api/CompilationResultTest.java Mon Apr 12 19:43:30 2010
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.myfaces.extensions.scripting.api;
+
+import org.apache.myfaces.extensions.scripting.api.CompilationResult;
+import org.junit.Before;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class CompilationResultTest {
+ CompilationResult result = null;
+ private static final String BOOGA = "booga";
+
+ @Before
+ public void init() {
+ result = new CompilationResult(BOOGA);
+ }
+
+ @Test
+ public void testGetCompilerOutput() throws Exception {
+ assertTrue(result.getCompilerOutput().equals(BOOGA));
+ }
+
+ @Test
+ public void testHasErrors() throws Exception {
+ assertFalse(result.hasErrors());
+ result.getErrors().add(new CompilationResult.CompilationMessage(1, BOOGA));
+ assertTrue(result.hasErrors());
+ }
+
+ @Test
+ public void testRegisterError() throws Exception {
+ assertFalse(result.hasErrors());
+ result.registerError(new CompilationResult.CompilationMessage(1, BOOGA));
+ assertTrue(result.hasErrors());
+ }
+
+ @Test
+ public void testGetErrors() throws Exception {
+ assertTrue(result.getErrors().isEmpty());
+ result.registerError(new CompilationResult.CompilationMessage(1, BOOGA));
+ result.registerError(null);
+ assertFalse(result.getErrors().isEmpty());
+ assertTrue(result.getErrors().size() == 1);
+ result.registerError(new CompilationResult.CompilationMessage(1, BOOGA));
+ result.registerError(null);
+ assertTrue(result.getErrors().size() == 2);
+
+ }
+
+ @Test
+ public void testRegisterWarning() throws Exception {
+ assertTrue(result.getWarnings().isEmpty());
+ result.registerWarning(new CompilationResult.CompilationMessage(1, BOOGA));
+ result.registerWarning(null);
+ assertFalse(result.getWarnings().isEmpty());
+ assertTrue(result.getWarnings().size() == 1);
+ result.registerWarning(new CompilationResult.CompilationMessage(1, BOOGA));
+
+ assertFalse(result.getWarnings().isEmpty());
+ assertTrue(result.getWarnings().size() == 2);
+ assertTrue(result.getErrors().isEmpty());
+ }
+
+}