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 {
+
+ }
+
+}