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());
+    }
+   
+}