You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by ko...@apache.org on 2005/08/11 07:55:06 UTC
svn commit: r231405 - in
/jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow:
ContinuationClassLoader.java ContinuationURLClassLoader.java
bytecode/ClassTransformer.java
Author: kohsuke
Date: Wed Aug 10 22:55:01 2005
New Revision: 231405
URL: http://svn.apache.org/viewcvs?rev=231405&view=rev
Log:
- added another class loader that works like URLClassLoader.
- improved javadoc on a few existing classes.
Added:
jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/ContinuationURLClassLoader.java
Modified:
jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/ContinuationClassLoader.java
jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/bytecode/ClassTransformer.java
Modified: jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/ContinuationClassLoader.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/ContinuationClassLoader.java?rev=231405&r1=231404&r2=231405&view=diff
==============================================================================
--- jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/ContinuationClassLoader.java (original)
+++ jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/ContinuationClassLoader.java Wed Aug 10 22:55:01 2005
@@ -27,9 +27,20 @@
* {@link ClassLoader} that instruments class files for javaflow on-the-fly.
*
* <p>
- * This <tt>ClassLoader</tt> enhances the
- * @author tcurdt
+ * This <tt>ClassLoader</tt> uses its ancestor classloaders to locate the class
+ * file, read its contents, transform the bytecodes, and then loads it into the VM.
+ * Thus this <tt>ClassLoader</tt> is useful where the application is running
+ * inside a single class loader.
+ *
+ * <p>
+ * The downside of this approach is that the classes loaded by this class loader
+ * is also always visible in its parent class loader. Therefore, in this way,
+ * you always get two {@link Class}es of the same name. Unless you are careful,
+ * this may result in a strange <tt>ClassCastException</tt>, or some other
+ * confusing runtime errors.
*
+ * @author tcurdt
+ * @see ContinuationURLClassLoader
*/
public final class ContinuationClassLoader extends ClassLoader {
Added: jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/ContinuationURLClassLoader.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/ContinuationURLClassLoader.java?rev=231405&view=auto
==============================================================================
--- jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/ContinuationURLClassLoader.java (added)
+++ jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/ContinuationURLClassLoader.java Wed Aug 10 22:55:01 2005
@@ -0,0 +1,927 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * Licensed 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.commons.javaflow;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.javaflow.bytecode.ClassTransformer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * {@link URLClassLoader} with bytecode instrumentation for javaflow.
+ *
+ * <p>
+ * This <tt>ClassLoader</tt> works like an <tt>URLClassLoader</tt>
+ * in the sense that it locates class files from specified {@link URL}s.
+ * But instead of simply loading classes into the VM, this class loader
+ * performs the bytecode instrumentation necessary for javaflow.
+ *
+ * <p>
+ * This class loader is useful where the application can set up multiple
+ * class loaders (such as in a container environment,
+ * <a href="http://classworlds.codehaus.org/">ClassWorlds</a>, or
+ * <a href="http://forehead.werken.com/">Forehead</a>) and when you can
+ * isolate the continuation-enabled portion of your application into a separate
+ * jar file.
+ *
+ * <p>
+ * When you mess up your class loader hierarchy or jar file placement,
+ * this class loader tends to give you {@link NoClassDefFoundError} early,
+ * unlike {@link ContinuationClassLoader}.
+ *
+ * <p>
+ * This code is based on <tt>AntClassLoader</tt>.
+ *
+ * @see ContinuationClassLoader
+ */
+public final class ContinuationURLClassLoader extends ClassLoader {
+
+ private final static Log log = LogFactory.getLog(ContinuationURLClassLoader.class);
+
+ /**
+ * An enumeration of all resources of a given name found within the
+ * classpath of this class loader. This enumeration is used by the
+ * ClassLoader.findResources method, which is in
+ * turn used by the ClassLoader.getResources method.
+ *
+ * @see ContinuationURLClassLoader#findResources(String)
+ * @see java.lang.ClassLoader#getResources(String)
+ */
+ private class ResourceEnumeration implements Enumeration {
+ /**
+ * The name of the resource being searched for.
+ */
+ private String resourceName;
+
+ /**
+ * The index of the next classpath element to search.
+ */
+ private int pathElementsIndex;
+
+ /**
+ * The URL of the next resource to return in the enumeration. If this
+ * field is <code>null</code> then the enumeration has been completed,
+ * i.e., there are no more elements to return.
+ */
+ private URL nextResource;
+
+ /**
+ * Constructs a new enumeration of resources of the given name found
+ * within this class loader's classpath.
+ *
+ * @param name the name of the resource to search for.
+ */
+ ResourceEnumeration(String name) {
+ this.resourceName = name;
+ this.pathElementsIndex = 0;
+ findNextResource();
+ }
+
+ /**
+ * Indicates whether there are more elements in the enumeration to
+ * return.
+ *
+ * @return <code>true</code> if there are more elements in the
+ * enumeration; <code>false</code> otherwise.
+ */
+ public boolean hasMoreElements() {
+ return (this.nextResource != null);
+ }
+
+ /**
+ * Returns the next resource in the enumeration.
+ *
+ * @return the next resource in the enumeration
+ */
+ public Object nextElement() {
+ URL ret = this.nextResource;
+ findNextResource();
+ return ret;
+ }
+
+ /**
+ * Locates the next resource of the correct name in the classpath and
+ * sets <code>nextResource</code> to the URL of that resource. If no
+ * more resources can be found, <code>nextResource</code> is set to
+ * <code>null</code>.
+ */
+ private void findNextResource() {
+ synchronized(ContinuationURLClassLoader.this) {
+ URL url = null;
+ while ((pathElementsIndex < pathComponents.size())
+ && (url == null)) {
+ File pathComponent
+ = (File) pathComponents.get(pathElementsIndex);
+ url = getResourceURL(pathComponent, this.resourceName);
+ pathElementsIndex++;
+ }
+ this.nextResource = url;
+ }
+ }
+ }
+
+ private final ClassTransformer transformer;
+
+ /**
+ * The size of buffers to be used in this classloader.
+ */
+ private static final int BUFFER_SIZE = 8192;
+
+ /**
+ * The components of the classpath that the classloader searches
+ * for classes.
+ */
+ private List pathComponents = new ArrayList();
+
+ /**
+ * Indicates whether the parent class loader should be
+ * consulted before trying to load with this class loader.
+ */
+ private boolean parentFirst = true;
+
+ /**
+ * These are the package roots that are to be loaded by the parent class
+ * loader regardless of whether the parent class loader is being searched
+ * first or not.
+ */
+ private List systemPackages = new ArrayList();
+
+ /**
+ * These are the package roots that are to be loaded by this class loader
+ * regardless of whether the parent class loader is being searched first
+ * or not.
+ */
+ private List loaderPackages = new ArrayList();
+
+ /**
+ * Whether or not this classloader will ignore the base
+ * classloader if it can't find a class.
+ *
+ * @see #setIsolated(boolean)
+ */
+ private boolean ignoreBase = false;
+
+ /**
+ * The parent class loader, if one is given or can be determined.
+ */
+ private ClassLoader parent = null;
+
+ /**
+ * A hashtable of zip files opened by the classloader (File to ZipFile).
+ */
+ private Map zipFiles = new HashMap();
+
+
+ /**
+ * Creates a classloader for the given project using the classpath given.
+ *
+ * @param parent The parent classloader to which unsatisfied loading
+ * attempts are delegated. May be <code>null</code>,
+ * in which case the classloader which loaded this
+ * class is used as the parent.
+ * @param classpath
+ * a {@link Collection} of {@link File}s.
+ * the classpath to use to load the classes.
+ * May be <code>null</code>, in which case no path
+ * elements are set up to start with.
+ */
+ public ContinuationURLClassLoader(ClassLoader parent, ClassTransformer transformer, Collection classpath) {
+ this.transformer = transformer;
+ if (parent != null) {
+ setParent(parent);
+ }
+ if(classpath!=null) {
+ for (Iterator itr = classpath.iterator(); itr.hasNext();) {
+ File f = (File) itr.next();
+ addPathFile(f);
+ }
+ }
+ }
+
+ /**
+ * Set the parent for this class loader. This is the class loader to which
+ * this class loader will delegate to load classes
+ *
+ * @param parent the parent class loader.
+ */
+ public void setParent(ClassLoader parent) {
+ if (parent == null) {
+ this.parent = ContinuationURLClassLoader.class.getClassLoader();
+ } else {
+ this.parent = parent;
+ }
+ }
+
+ /**
+ * Control whether class lookup is delegated to the parent loader first
+ * or after this loader. Use with extreme caution. Setting this to
+ * false violates the class loader hierarchy and can lead to Linkage errors
+ *
+ * @param parentFirst if true, delegate initial class search to the parent
+ * classloader.
+ */
+ public void setParentFirst(boolean parentFirst) {
+ this.parentFirst = parentFirst;
+ }
+
+ /**
+ * Add a file to the path
+ *
+ * @param pathComponent the file which is to be added to the path for
+ * this class loader
+ */
+ protected synchronized void addPathFile(File pathComponent) {
+ pathComponents.add(pathComponent);
+ }
+
+ /**
+ * Returns the classpath this classloader will consult.
+ *
+ * @return the classpath used for this classloader, with elements
+ * separated by the path separator for the system.
+ */
+ public synchronized String getClasspath() {
+ StringBuffer sb = new StringBuffer();
+ boolean firstPass = true;
+ for (Iterator itr = pathComponents.iterator(); itr.hasNext();) {
+ File file = (File) itr.next();
+ if (!firstPass) {
+ sb.append(System.getProperty("path.separator"));
+ } else {
+ firstPass = false;
+ }
+ sb.append(file.getAbsolutePath());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Sets whether this classloader should run in isolated mode. In
+ * isolated mode, classes not found on the given classpath will
+ * not be referred to the parent class loader but will cause a
+ * ClassNotFoundException.
+ *
+ * @param isolated Whether or not this classloader should run in
+ * isolated mode.
+ */
+ public void setIsolated(boolean isolated) {
+ ignoreBase = isolated;
+ }
+
+ /**
+ * Adds a package root to the list of packages which must be loaded on the
+ * parent loader.
+ *
+ * All subpackages are also included.
+ *
+ * @param packageRoot The root of all packages to be included.
+ * Should not be <code>null</code>.
+ */
+ public synchronized void addSystemPackageRoot(String packageRoot) {
+ systemPackages.add(appendDot(packageRoot));
+ }
+
+ /**
+ * Adds a package root to the list of packages which must be loaded using
+ * this loader.
+ *
+ * All subpackages are also included.
+ *
+ * @param packageRoot The root of all packages to be included.
+ * Should not be <code>null</code>.
+ */
+ public synchronized void addLoaderPackageRoot(String packageRoot) {
+ loaderPackages.add(appendDot(packageRoot));
+ }
+
+ private String appendDot(String str) {
+ if(str.endsWith("."))
+ str += '.';
+ return str;
+ }
+
+ /**
+ * Loads a class through this class loader even if that class is available
+ * on the parent classpath.
+ *
+ * This ensures that any classes which are loaded by the returned class
+ * will use this classloader.
+ *
+ * @param classname The name of the class to be loaded.
+ * Must not be <code>null</code>.
+ *
+ * @return the required Class object
+ *
+ * @exception ClassNotFoundException if the requested class does not exist
+ * on this loader's classpath.
+ */
+ public Class forceLoadClass(String classname)
+ throws ClassNotFoundException {
+ log.debug("force loading " + classname);
+
+ Class theClass = findLoadedClass(classname);
+
+ if (theClass == null) {
+ theClass = findClass(classname);
+ }
+
+ return theClass;
+ }
+
+ /**
+ * Loads a class through this class loader but defer to the parent class
+ * loader.
+ *
+ * This ensures that instances of the returned class will be compatible
+ * with instances which have already been loaded on the parent
+ * loader.
+ *
+ * @param classname The name of the class to be loaded.
+ * Must not be <code>null</code>.
+ *
+ * @return the required Class object
+ *
+ * @exception ClassNotFoundException if the requested class does not exist
+ * on this loader's classpath.
+ */
+ public Class forceLoadSystemClass(String classname)
+ throws ClassNotFoundException {
+ log.debug("force system loading " + classname);
+
+ Class theClass = findLoadedClass(classname);
+
+ if (theClass == null) {
+ theClass = findBaseClass(classname);
+ }
+
+ return theClass;
+ }
+
+ /**
+ * Returns a stream to read the requested resource name.
+ *
+ * @param name The name of the resource for which a stream is required.
+ * Must not be <code>null</code>.
+ *
+ * @return a stream to the required resource or <code>null</code> if the
+ * resource cannot be found on the loader's classpath.
+ */
+ public InputStream getResourceAsStream(String name) {
+
+ InputStream resourceStream;
+ if (isParentFirst(name)) {
+ resourceStream = loadBaseResource(name);
+ if (resourceStream != null) {
+ log.debug("ResourceStream for " + name
+ + " loaded from parent loader");
+
+ } else {
+ resourceStream = loadResource(name);
+ if (resourceStream != null) {
+ log.debug("ResourceStream for " + name
+ + " loaded from ant loader");
+ }
+ }
+ } else {
+ resourceStream = loadResource(name);
+ if (resourceStream != null) {
+ log.debug("ResourceStream for " + name
+ + " loaded from ant loader");
+
+ } else {
+ resourceStream = loadBaseResource(name);
+ if (resourceStream != null) {
+ log.debug("ResourceStream for " + name
+ + " loaded from parent loader");
+ }
+ }
+ }
+
+ if (resourceStream == null) {
+ log.debug("Couldn't load ResourceStream for " + name);
+ }
+
+ return resourceStream;
+ }
+
+ /**
+ * Returns a stream to read the requested resource name from this loader.
+ *
+ * @param name The name of the resource for which a stream is required.
+ * Must not be <code>null</code>.
+ *
+ * @return a stream to the required resource or <code>null</code> if
+ * the resource cannot be found on the loader's classpath.
+ */
+ private synchronized InputStream loadResource(String name) {
+ // we need to search the components of the path to see if we can
+ // find the class we want.
+ InputStream stream = null;
+
+ for (Iterator itr = pathComponents.iterator(); itr.hasNext() && stream==null;) {
+ File pathComponent = (File) itr.next();
+ stream = getResourceStream(pathComponent, name);
+ }
+ return stream;
+ }
+
+ /**
+ * Finds a system resource (which should be loaded from the parent
+ * classloader).
+ *
+ * @param name The name of the system resource to load.
+ * Must not be <code>null</code>.
+ *
+ * @return a stream to the named resource, or <code>null</code> if
+ * the resource cannot be found.
+ */
+ private InputStream loadBaseResource(String name) {
+ if (parent == null) {
+ return getSystemResourceAsStream(name);
+ } else {
+ return parent.getResourceAsStream(name);
+ }
+ }
+
+ /**
+ * Returns an inputstream to a given resource in the given file which may
+ * either be a directory or a zip file.
+ *
+ * @param file the file (directory or jar) in which to search for the
+ * resource. Must not be <code>null</code>.
+ * @param resourceName The name of the resource for which a stream is
+ * required. Must not be <code>null</code>.
+ *
+ * @return a stream to the required resource or <code>null</code> if
+ * the resource cannot be found in the given file.
+ */
+ private synchronized InputStream getResourceStream(File file, String resourceName) {
+ try {
+ if (!file.exists()) {
+ return null;
+ }
+
+ if (file.isDirectory()) {
+ File resource = new File(file, resourceName);
+
+ if (resource.exists()) {
+ return new FileInputStream(resource);
+ }
+ } else {
+ // is the zip file in the cache
+ ZipFile zipFile = (ZipFile) zipFiles.get(file);
+ if (zipFile == null) {
+ zipFile = new ZipFile(file);
+ zipFiles.put(file, zipFile);
+ }
+ ZipEntry entry = zipFile.getEntry(resourceName);
+ if (entry != null) {
+ return zipFile.getInputStream(entry);
+ }
+ }
+ } catch (Exception e) {
+ log.debug("Ignoring Exception " + e.getClass().getName()
+ + ": " + e.getMessage() + " reading resource " + resourceName
+ + " from " + file);
+ }
+
+ return null;
+ }
+
+ /**
+ * Tests whether or not the parent classloader should be checked for
+ * a resource before this one. If the resource matches both the
+ * "use parent classloader first" and the "use this classloader first"
+ * lists, the latter takes priority.
+ *
+ * @param resourceName The name of the resource to check.
+ * Must not be <code>null</code>.
+ *
+ * @return whether or not the parent classloader should be checked for a
+ * resource before this one is.
+ */
+ private synchronized boolean isParentFirst(String resourceName) {
+ // default to the global setting and then see
+ // if this class belongs to a package which has been
+ // designated to use a specific loader first
+ // (this one or the parent one)
+
+ // XXX - shouldn't this always return false in isolated mode?
+
+ boolean useParentFirst = parentFirst;
+
+ for (Iterator itr = systemPackages.iterator(); itr.hasNext();) {
+ String packageName = (String) itr.next();
+ if (resourceName.startsWith(packageName)) {
+ useParentFirst = true;
+ break;
+ }
+ }
+
+ for (Iterator itr = loaderPackages.iterator(); itr.hasNext();) {
+ String packageName = (String) itr.next();
+ if (resourceName.startsWith(packageName)) {
+ useParentFirst = false;
+ break;
+ }
+ }
+
+ return useParentFirst;
+ }
+
+ /**
+ * Finds the resource with the given name. A resource is
+ * some data (images, audio, text, etc) that can be accessed by class
+ * code in a way that is independent of the location of the code.
+ *
+ * @param name The name of the resource for which a stream is required.
+ * Must not be <code>null</code>.
+ *
+ * @return a URL for reading the resource, or <code>null</code> if the
+ * resource could not be found or the caller doesn't have
+ * adequate privileges to get the resource.
+ */
+ public synchronized URL getResource(String name) {
+ // we need to search the components of the path to see if
+ // we can find the class we want.
+ URL url = null;
+ if (isParentFirst(name)) {
+ url = (parent == null) ? super.getResource(name)
+ : parent.getResource(name);
+ }
+
+ if (url != null) {
+ log.debug("Resource " + name + " loaded from parent loader");
+
+ } else {
+ // try and load from this loader if the parent either didn't find
+ // it or wasn't consulted.
+ for (Iterator itr = pathComponents.iterator(); itr.hasNext() && url==null;) {
+ File pathComponent = (File) itr.next();
+ url = getResourceURL(pathComponent, name);
+ if (url != null) {
+ log.debug("Resource " + name
+ + " loaded from ant loader");
+ }
+ }
+ }
+
+ if (url == null && !isParentFirst(name)) {
+ // this loader was first but it didn't find it - try the parent
+
+ url = (parent == null) ? super.getResource(name)
+ : parent.getResource(name);
+ if (url != null) {
+ log.debug("Resource " + name + " loaded from parent loader");
+ }
+ }
+
+ if (url == null) {
+ log.debug("Couldn't load Resource " + name);
+ }
+
+ return url;
+ }
+
+ /**
+ * Returns an enumeration of URLs representing all the resources with the
+ * given name by searching the class loader's classpath.
+ *
+ * @param name The resource name to search for.
+ * Must not be <code>null</code>.
+ * @return an enumeration of URLs for the resources
+ */
+ protected Enumeration findResources(String name) {
+ return new ResourceEnumeration(name);
+ }
+
+ /**
+ * Returns the URL of a given resource in the given file which may
+ * either be a directory or a zip file.
+ *
+ * @param file The file (directory or jar) in which to search for
+ * the resource. Must not be <code>null</code>.
+ * @param resourceName The name of the resource for which a stream
+ * is required. Must not be <code>null</code>.
+ *
+ * @return a stream to the required resource or <code>null</code> if the
+ * resource cannot be found in the given file object.
+ */
+ protected synchronized URL getResourceURL(File file, String resourceName) {
+ try {
+ if (!file.exists()) {
+ return null;
+ }
+
+ if (file.isDirectory()) {
+ File resource = new File(file, resourceName);
+
+ if (resource.exists()) {
+ try {
+ return resource.toURL();
+ } catch (MalformedURLException ex) {
+ return null;
+ }
+ }
+ } else {
+ ZipFile zipFile = (ZipFile) zipFiles.get(file);
+ if (zipFile == null) {
+ zipFile = new ZipFile(file);
+ zipFiles.put(file, zipFile);
+ }
+
+ ZipEntry entry = zipFile.getEntry(resourceName);
+ if (entry != null) {
+ try {
+ return new URL("jar:" + file.toURL()
+ + "!/" + entry);
+ } catch (MalformedURLException ex) {
+ return null;
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ /**
+ * Loads a class with this class loader.
+ *
+ * This class attempts to load the class in an order determined by whether
+ * or not the class matches the system/loader package lists, with the
+ * loader package list taking priority. If the classloader is in isolated
+ * mode, failure to load the class in this loader will result in a
+ * ClassNotFoundException.
+ *
+ * @param classname The name of the class to be loaded.
+ * Must not be <code>null</code>.
+ * @param resolve <code>true</code> if all classes upon which this class
+ * depends are to be loaded.
+ *
+ * @return the required Class object
+ *
+ * @exception ClassNotFoundException if the requested class does not exist
+ * on the system classpath (when not in isolated mode) or this loader's
+ * classpath.
+ */
+ protected synchronized Class loadClass(String classname, boolean resolve)
+ throws ClassNotFoundException {
+ // 'sync' is needed - otherwise 2 threads can load the same class
+ // twice, resulting in LinkageError: duplicated class definition.
+ // findLoadedClass avoids that, but without sync it won't work.
+
+ Class theClass = findLoadedClass(classname);
+ if (theClass != null) {
+ return theClass;
+ }
+
+ if (isParentFirst(classname)) {
+ try {
+ theClass = findBaseClass(classname);
+ log.debug("Class " + classname + " loaded from parent loader "
+ + "(parentFirst)");
+ } catch (ClassNotFoundException cnfe) {
+ theClass = findClass(classname);
+ log.debug("Class " + classname + " loaded from ant loader "
+ + "(parentFirst)");
+ }
+ } else {
+ try {
+ theClass = findClass(classname);
+ log.debug("Class " + classname + " loaded from ant loader");
+ } catch (ClassNotFoundException cnfe) {
+ if (ignoreBase) {
+ throw cnfe;
+ }
+ theClass = findBaseClass(classname);
+ log.debug("Class " + classname + " loaded from parent loader");
+ }
+ }
+
+ if (resolve) {
+ resolveClass(theClass);
+ }
+
+ return theClass;
+ }
+
+ /**
+ * Converts the class dot notation to a filesystem equivalent for
+ * searching purposes.
+ *
+ * @param classname The class name in dot format (eg java.lang.Integer).
+ * Must not be <code>null</code>.
+ *
+ * @return the classname in filesystem format (eg java/lang/Integer.class)
+ */
+ private String getClassFilename(String classname) {
+ return classname.replace('.', '/') + ".class";
+ }
+
+ /**
+ * Define a class given its bytes
+ *
+ * @param container the container from which the class data has been read
+ * may be a directory or a jar/zip file.
+ *
+ * @param classData the bytecode data for the class
+ * @param classname the name of the class
+ *
+ * @return the Class instance created from the given data
+ */
+ protected Class defineClassFromData(File container, byte[] classData, String classname) {
+ classData = transformer.transform(classData);
+ ProtectionDomain domain = this.getClass().getProtectionDomain();
+ return defineClass(classname,classData,0,classData.length,domain);
+ }
+
+ /**
+ * Reads a class definition from a stream.
+ *
+ * @param stream The stream from which the class is to be read.
+ * Must not be <code>null</code>.
+ * @param classname The name of the class in the stream.
+ * Must not be <code>null</code>.
+ * @param container the file or directory containing the class.
+ *
+ * @return the Class object read from the stream.
+ *
+ * @exception IOException if there is a problem reading the class from the
+ * stream.
+ * @exception SecurityException if there is a security problem while
+ * reading the class from the stream.
+ */
+ private Class getClassFromStream(InputStream stream, String classname,
+ File container)
+ throws IOException, SecurityException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int bytesRead;
+ byte[] buffer = new byte[BUFFER_SIZE];
+
+ while ((bytesRead = stream.read(buffer, 0, BUFFER_SIZE)) != -1) {
+ baos.write(buffer, 0, bytesRead);
+ }
+
+ byte[] classData = baos.toByteArray();
+ return defineClassFromData(container, classData, classname);
+ }
+
+ /**
+ * Searches for and load a class on the classpath of this class loader.
+ *
+ * @param name The name of the class to be loaded. Must not be
+ * <code>null</code>.
+ *
+ * @return the required Class object
+ *
+ * @exception ClassNotFoundException if the requested class does not exist
+ * on this loader's classpath.
+ */
+ public Class findClass(String name) throws ClassNotFoundException {
+ log.debug("Finding class " + name);
+
+ return findClassInComponents(name);
+ }
+
+ /**
+ * Indicate if the given file is in this loader's path
+ *
+ * @param component the file which is to be checked
+ *
+ * @return true if the file is in the class path
+ */
+ protected synchronized boolean isInPath(File component) {
+ for (Iterator itr = pathComponents.iterator(); itr.hasNext();) {
+ File pathComponent = (File) itr.next();
+ if (pathComponent.equals(component)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Finds a class on the given classpath.
+ *
+ * @param name The name of the class to be loaded. Must not be
+ * <code>null</code>.
+ *
+ * @return the required Class object
+ *
+ * @exception ClassNotFoundException if the requested class does not exist
+ * on this loader's classpath.
+ */
+ private synchronized Class findClassInComponents(String name)
+ throws ClassNotFoundException {
+ // we need to search the components of the path to see if
+ // we can find the class we want.
+ InputStream stream = null;
+ String classFilename = getClassFilename(name);
+ try {
+ for (Iterator itr = pathComponents.iterator(); itr.hasNext();) {
+ File pathComponent = (File) itr.next();
+ try {
+ stream = getResourceStream(pathComponent, classFilename);
+ if (stream != null) {
+ log.debug("Loaded from " + pathComponent + " " + classFilename);
+ return getClassFromStream(stream, name, pathComponent);
+ }
+ } catch (SecurityException se) {
+ throw se;
+ } catch (IOException ioe) {
+ // ioe.printStackTrace();
+ log.debug("Exception reading component " + pathComponent
+ + " (reason: " + ioe.getMessage() + ")");
+ }
+ }
+
+ throw new ClassNotFoundException(name);
+ } finally {
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException e) {
+ //ignore
+ }
+ }
+ }
+
+ /**
+ * Finds a system class (which should be loaded from the same classloader
+ * as the Ant core).
+ *
+ * For JDK 1.1 compatibility, this uses the findSystemClass method if
+ * no parent classloader has been specified.
+ *
+ * @param name The name of the class to be loaded.
+ * Must not be <code>null</code>.
+ *
+ * @return the required Class object
+ *
+ * @exception ClassNotFoundException if the requested class does not exist
+ * on this loader's classpath.
+ */
+ private Class findBaseClass(String name) throws ClassNotFoundException {
+ if (parent == null) {
+ return findSystemClass(name);
+ } else {
+ return parent.loadClass(name);
+ }
+ }
+
+ protected void finalize() {
+ cleanup();
+ }
+
+ /**
+ * Cleans up any resources held by this classloader. Any open archive
+ * files are closed.
+ */
+ public synchronized void cleanup() {
+ for (Iterator itr = zipFiles.values().iterator(); itr.hasNext();) {
+ ZipFile zipFile = (ZipFile) itr.next();
+ try {
+ zipFile.close();
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+ zipFiles = new HashMap();
+ }
+}
Modified: jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/bytecode/ClassTransformer.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/bytecode/ClassTransformer.java?rev=231405&r1=231404&r2=231405&view=diff
==============================================================================
--- jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/bytecode/ClassTransformer.java (original)
+++ jakarta/commons/sandbox/javaflow/trunk/src/java/org/apache/commons/javaflow/bytecode/ClassTransformer.java Wed Aug 10 22:55:01 2005
@@ -15,10 +15,21 @@
*/
package org.apache.commons.javaflow.bytecode;
+import org.apache.commons.javaflow.Continuation;
+import org.apache.commons.javaflow.bytecode.bcel.BcelClassTransformer;
+import org.apache.commons.javaflow.bytecode.asm.AsmClassTransformer;
+
/**
- * @author tcurdt
+ * Byte-code transformer that enhances the class files for javaflow.
*
+ * <p>
+ * When {@link Continuation#suspend()} is called, all the methods
+ * in the stack frame needs to be enhanced.
+ *
+ * @author tcurdt
+ * @see BcelClassTransformer
+ * @see AsmClassTransformer
*/
public interface ClassTransformer {
byte[] transform( final byte[] original );
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org