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