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

svn commit: r890068 - in /myfaces/extensions/scripting/trunk/core/core: ./ src/main/java/org/apache/myfaces/extensions/scripting/compiler/ src/main/java/org/apache/myfaces/extensions/scripting/loader/ src/main/java/org/apache/myfaces/extensions/scripti...

Author: bhuemer
Date: Sun Dec 13 16:03:39 2009
New Revision: 890068

URL: http://svn.apache.org/viewvc?rev=890068&view=rev
Log:
- Added some custom class loaders and testcases.
- Corrected the POM, it didn't include the JUnit dependency.

Added:
    myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/ClassLoaderUtils.java
    myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/
    myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/AbstractThrowAwayClassLoader.java
    myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/OverridingClassLoader.java
    myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/ThrowAwayClassLoader.java
    myfaces/extensions/scripting/trunk/core/core/src/test/java/org/apache/myfaces/extensions/
    myfaces/extensions/scripting/trunk/core/core/src/test/java/org/apache/myfaces/extensions/scripting/
    myfaces/extensions/scripting/trunk/core/core/src/test/java/org/apache/myfaces/extensions/scripting/loader/
    myfaces/extensions/scripting/trunk/core/core/src/test/java/org/apache/myfaces/extensions/scripting/loader/support/
    myfaces/extensions/scripting/trunk/core/core/src/test/java/org/apache/myfaces/extensions/scripting/loader/support/OverridingClassLoaderTest.java
Modified:
    myfaces/extensions/scripting/trunk/core/core/pom.xml
    myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/CompilationException.java
    myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/CompilationResult.java
    myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/Compiler.java

Modified: myfaces/extensions/scripting/trunk/core/core/pom.xml
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/core/core/pom.xml?rev=890068&r1=890067&r2=890068&view=diff
==============================================================================
--- myfaces/extensions/scripting/trunk/core/core/pom.xml (original)
+++ myfaces/extensions/scripting/trunk/core/core/pom.xml Sun Dec 13 16:03:39 2009
@@ -57,6 +57,13 @@
             <version>1.8.0</version>
         </dependency>
 
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.3</version>
+            <scope>test</scope>
+        </dependency>
        
 
         <dependency>

Modified: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/CompilationException.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/CompilationException.java?rev=890068&r1=890067&r2=890068&view=diff
==============================================================================
--- myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/CompilationException.java (original)
+++ myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/CompilationException.java Sun Dec 13 16:03:39 2009
@@ -23,17 +23,16 @@
  * set of source files. However, note that it doesn't mean that the source files
  * themselves contained errors but rather that the system couldn't managed to
  * find an appropriate compiler implementation, etc.</p>
- *
+ * <p/>
  * <p>In order to determine whether the compiler sucessfully compiled a certain
  * source file you have to look for the compilation result instead.</p>
- *
  */
 public class CompilationException extends Exception {
 
     // ------------------------------------------ Constructors
 
     /**
-     * <p>Constructs a new compilation exception with the specified detail message.</p> 
+     * <p>Constructs a new compilation exception with the specified detail message.</p>
      *
      * @param message the detail message. The detail message is saved for
      *                later retrieval by the {@link #getMessage()} method.
@@ -73,6 +72,6 @@
     public CompilationException(Throwable cause) {
         super(cause);
     }
-    
+
 }
 

Modified: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/CompilationResult.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/CompilationResult.java?rev=890068&r1=890067&r2=890068&view=diff
==============================================================================
--- myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/CompilationResult.java (original)
+++ myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/CompilationResult.java Sun Dec 13 16:03:39 2009
@@ -18,30 +18,29 @@
  */
 package org.apache.myfaces.extensions.scripting.compiler;
 
-import java.util.List;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * <p>Contains all information regarding the result of a particular compilation process.</p>
- * 
  */
 public class CompilationResult {
 
     /**
      * The compiler output, i.e. simply everything that the compiler would usually
      * print to the console, if you executed the same process on the command line
-     * instead. 
+     * instead.
      */
     private String compilerOutput;
 
     /**
      * A list of error messages that the compiler has produced. Note that if there
-     * are no error messages, it's safe to assume that compilation succeeded. 
+     * are no error messages, it's safe to assume that compilation succeeded.
      */
     private List<CompilationMessage> errors;
 
     /**
-     * A list of warnings that the compiler has produced. 
+     * A list of warnings that the compiler has produced.
      */
     private List<CompilationMessage> warnings;
 
@@ -52,10 +51,10 @@
      * note that this constructor doesn't attempt to parse the compiler output to get the
      * error messages and warnings. You'll have to register those messages yourself
      * afterwards.</p>
-     * 
+     *
      * @param compilerOutput the compiler output, i.e. simply everything that the compiler would
-     *                        usually print to the console, if you executed the same process on
-     *                        the command line instead
+     *                       usually print to the console, if you executed the same process on
+     *                       the command line instead
      */
     public CompilationResult(String compilerOutput) {
         this.compilerOutput = compilerOutput;
@@ -71,7 +70,7 @@
      * print to the console, if you executed the same process on the command line
      * instead.</p>
      *
-     * @return the compiler output 
+     * @return the compiler output
      */
     public String getCompilerOutput() {
         return compilerOutput;
@@ -82,7 +81,7 @@
      * was successful.</p>
      *
      * @return <code>true</code if no error messages have been registered, i.e. if compilation
-     *          was sucessful; <code>false</code> otherwise
+     *         was sucessful; <code>false</code> otherwise
      */
     public boolean hasErrors() {
         return !errors.isEmpty();
@@ -136,14 +135,17 @@
     /**
      * <p>Utility class that contains all the required information regarding
      * a single compilation message.</p>
-     * 
      */
     public static class CompilationMessage {
 
-        /** the line number of this compilation message */
+        /**
+         * the line number of this compilation message
+         */
         private long lineNumber;
 
-        /** the actual compilation message */
+        /**
+         * the actual compilation message
+         */
         private String message;
 
         // -------------------------------------- Constructors
@@ -153,7 +155,7 @@
          * and the actual compilation message as a string.</p>
          *
          * @param lineNumber the line number
-         * @param message the actual compilation message
+         * @param message    the actual compilation message
          */
         public CompilationMessage(long lineNumber, String message) {
             this.lineNumber = lineNumber;
@@ -165,7 +167,7 @@
         /**
          * <p>The number of the relevant line where this warning or error
          * has occured, or <code>-1</code> if it is not known.</p>
-         * 
+         *
          * @return the line number
          */
         public long getLineNumber() {
@@ -175,7 +177,7 @@
         /**
          * <p>Returns the message itself as a string, i.e. the textual content
          * of whatever the compiler complained about.</p>
-         * 
+         *
          * @return the message itself as a string
          */
         public String getMessage() {

Modified: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/Compiler.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/Compiler.java?rev=890068&r1=890067&r2=890068&view=diff
==============================================================================
--- myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/Compiler.java (original)
+++ myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/compiler/Compiler.java Sun Dec 13 16:03:39 2009
@@ -22,7 +22,6 @@
 
 /**
  * <p>An abstract compiler interface that enables you to compile one particular file at a time.</p>
- *
  */
 public interface Compiler {
 
@@ -36,9 +35,7 @@
      * @param targetPath  the path to the target directory
      * @param file        the file of the class you want to compile
      * @param classLoader the class loader for dependent classes
-     * 
      * @return the compilation result, i.e. the compiler output, a list of errors and a list of warnings
-     *
      * @throws CompilationException if a severe error occured while trying to compile a file
      */
     public CompilationResult compile(File sourcePath, File targetPath, File file, ClassLoader classLoader)
@@ -54,9 +51,7 @@
      * @param targetPath  the path to the target directory
      * @param file        the relative file name of the class you want to compile
      * @param classLoader the class loader for dependent classes
-     * 
      * @return the compilation result, i.e. the compiler output, a list of errors and a list of warnings
-     *
      * @throws CompilationException if a severe error occured while trying to compile a file
      */
     public CompilationResult compile(File sourcePath, File targetPath, String file, ClassLoader classLoader)

Added: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/ClassLoaderUtils.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/ClassLoaderUtils.java?rev=890068&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/ClassLoaderUtils.java (added)
+++ myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/ClassLoaderUtils.java Sun Dec 13 16:03:39 2009
@@ -0,0 +1,107 @@
+/*
+ * 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.loader;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>Utility class for classloading purposes, e.g. to determine the classpath of a
+ * class loader hierachy.</p>
+ */
+public class ClassLoaderUtils {
+
+    /**
+     * The logger instance for this class.
+     */
+    private static final Log logger = LogFactory.getLog(ClassLoaderUtils.class);
+
+    // ------------------------------------------ Public methods
+
+    /**
+     * <p>Resolves the classpath by walking up the hierachy of class loaders. Assuming
+     * that we're only dealing with URLClassLoaders it's possible to determine the
+     * classpath. This method, however, returns the classpath as a String, where each
+     * classpath entry is separated by a ';', i.e. it returns the classpath in a format
+     * that Java tools usually expect it to be.</p>
+     *
+     * @param classLoader the class loader which you want to resolve the class path for
+     * @return the final classpath
+     */
+    public static String buildClasspath(ClassLoader classLoader) {
+        StringBuffer classpath = new StringBuffer();
+
+        URL[] urls = resolveClasspath(classLoader);
+        for (int i = 0; i < urls.length; ++i) {
+            classpath.append(urls[i].getPath());
+
+            // Note that the classpath separator character is platform
+            // dependent. On Windows systems it's ";" whereas on other
+            // UNIX systems it's ":".
+            classpath.append(File.pathSeparatorChar);
+        }
+
+        return classpath.toString();
+    }
+
+    /**
+     * <p>Resolves the classpath by walking up the hierachy of class loaders. Assuming
+     * that we're only dealing with URLClassLoaders it's possible to determine the
+     * classpath.</p>
+     *
+     * @param parent the class loader which you want to resolve the class path for
+     * @return the final classpath
+     */
+    public static URL[] resolveClasspath(ClassLoader parent) {
+        List<URL> classpath = new ArrayList<URL>();
+
+        ClassLoader classLoader = parent;
+        // Walk up the hierachy of class loaders in order to determine the current classpath.
+        while (classLoader != null) {
+            if (classLoader instanceof URLClassLoader) {
+                URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
+
+                URL[] urls = urlClassLoader.getURLs();
+                if (urls != null) {
+                    for (int i = 0; i < urls.length; ++i) {
+                        classpath.add(urls[i]);
+                    }
+                }
+            } else {
+                if (logger.isWarnEnabled()) {
+                    logger.warn("Resolving the classpath of the classloader '" + parent + "' - One of its parent class"
+                            + " loaders is no URLClassLoader '" + classLoader + "', which means it's possible that"
+                            + " some classpath entries aren't in the final outcome of this method call.");
+                }
+            }
+
+            // Inspect the parent class loader next.
+            classLoader = classLoader.getParent();
+        }
+
+        return (URL[]) classpath.toArray(new URL[classpath.size()]);
+    }
+
+}

Added: myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/AbstractThrowAwayClassLoader.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/AbstractThrowAwayClassLoader.java?rev=890068&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/AbstractThrowAwayClassLoader.java (added)
+++ myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/AbstractThrowAwayClassLoader.java Sun Dec 13 16:03:39 2009
@@ -0,0 +1,218 @@
+/*
+ * 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.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 classloader is only supposed to load a specific Class reference,
+        // hence the check agains 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 (this.className.equals(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 timestamp 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 timestamp of this ClassLoader
+     */
+    public boolean isOutdated(long lastModified) {
+        return timestamp < lastModified;
+    }
+
+    // ------------------------------------------ Utility methods
+
+    /**
+     * <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 (getClassName().equals(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/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/OverridingClassLoader.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/OverridingClassLoader.java?rev=890068&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/OverridingClassLoader.java (added)
+++ myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/OverridingClassLoader.java Sun Dec 13 16:03:39 2009
@@ -0,0 +1,65 @@
+/*
+ * 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.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/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/ThrowAwayClassLoader.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/ThrowAwayClassLoader.java?rev=890068&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/ThrowAwayClassLoader.java (added)
+++ myfaces/extensions/scripting/trunk/core/core/src/main/java/org/apache/myfaces/extensions/scripting/loader/support/ThrowAwayClassLoader.java Sun Dec 13 16:03:39 2009
@@ -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.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 classloader 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/core/core/src/test/java/org/apache/myfaces/extensions/scripting/loader/support/OverridingClassLoaderTest.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/scripting/trunk/core/core/src/test/java/org/apache/myfaces/extensions/scripting/loader/support/OverridingClassLoaderTest.java?rev=890068&view=auto
==============================================================================
--- myfaces/extensions/scripting/trunk/core/core/src/test/java/org/apache/myfaces/extensions/scripting/loader/support/OverridingClassLoaderTest.java (added)
+++ myfaces/extensions/scripting/trunk/core/core/src/test/java/org/apache/myfaces/extensions/scripting/loader/support/OverridingClassLoaderTest.java Sun Dec 13 16:03:39 2009
@@ -0,0 +1,76 @@
+/*
+ * 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.loader.support;
+
+import junit.framework.TestCase;
+
+/**
+ * <p>Test class for the class
+ * <code>org.apache.myfaces.extensions.scripting.loader.support.OverridingClassLoader</code></p>
+ */
+public class OverridingClassLoaderTest extends TestCase {
+
+    // ------------------------------------------ Test methods
+
+    /**
+     * <p>Tests whether it is possible to override class definitions using the OverridingClassLoader,
+     * i.e. it tests if you can produce multiple different Class objects using different class loaders
+     * (which is not really a surprising thing at all).</p>
+     *
+     * @throws Exception if an unexpected error occurs
+     */
+    public void testOverrideClass() throws Exception {
+        ClassLoader classLoader = new OverridingClassLoader(
+                Dummy.class.getName(), OverridingClassLoaderTest.class.getClassLoader());
+
+        Class dummyClass = classLoader.loadClass(Dummy.class.getName());
+        assertNotSame("The OverridingClassLoader didn't return a different Class instance.",
+                Dummy.class, dummyClass);
+
+        // .. and another time
+        classLoader = new OverridingClassLoader(Dummy.class.getName(), classLoader);
+        Class secondDummyClass = classLoader.loadClass(Dummy.class.getName());
+        assertNotSame("The OverridingClassLoader didn't return a different Class instance.",
+                Dummy.class, secondDummyClass);
+        assertNotSame("The OverridingClassLoader didn't return a different Class instance.",
+                dummyClass, secondDummyClass);
+    }
+
+    /**
+     * <p>Tests whether the OverridingClassLoader delegates the parent class loader correctly.</p>
+     *
+     * @throws Exception if an unexpected error occurs
+     */
+    public void testOverrideDifferentClass() throws Exception {
+        ClassLoader classLoader = new OverridingClassLoader(
+                Dummy.class.getName(), OverridingClassLoaderTest.class.getClassLoader());
+        assertSame("The OverridingClassLoader replaced a Class instance that he wasn't supposed to replace.",
+                Object.class, classLoader.loadClass("java.lang.Object"));
+    }
+
+    // ------------------------------------------ Dummy classes
+
+    /**
+     * <p>This class will be reloaded in some test cases.</p>
+     */
+    private static class Dummy {
+
+    }
+
+}