FileDocCategorySizeDatePackage
EnhancerClassLoader.javaAPI DocGlassfish v2 API25192Fri May 04 22:34:26 BST 2007com.sun.jdo.api.persistence.enhancer

EnhancerClassLoader

public class EnhancerClassLoader extends URLClassLoader
Implements a ClassLoader which automatically enchances the .class files according to the JDOMetaData information in the jar archive.
author
Yury Kamen
version

Fields Summary
public static final String
DO_SIMPLE_TIMING
public static final String
VERBOSE_LEVEL
public static final String
VERBOSE_LEVEL_QUIET
public static final String
VERBOSE_LEVEL_WARN
public static final String
VERBOSE_LEVEL_VERBOSE
public static final String
VERBOSE_LEVEL_DEBUG
private boolean
debug
private boolean
doTiming
private PrintWriter
out
private ByteCodeEnhancer
enhancer
private com.sun.jdo.api.persistence.enhancer.meta.JDOMetaData
metaData
private Properties
settings
private WeakReference
outByteCodeRef
private final sun.misc.URLClassPath
ucp
private final AccessControlContext
acc
Constructors Summary
public EnhancerClassLoader(URL[] urls, com.sun.jdo.api.persistence.enhancer.meta.JDOMetaData metaData, Properties settings, PrintWriter out)
Creates a new EnhancerClassLoader for the specified url.

param
urls the classpath to search

        this(urls);
        init(metaData, settings, out);
    
protected EnhancerClassLoader(URL[] urls)
Creates a new EnhancerClassLoader for the specified url.

param
urls the classpath to search

        super(urls);
        acc = AccessController.getContext();
        ucp = new URLClassPath(urls);
        checkUCP(urls);
    
protected EnhancerClassLoader(URL[] urls, ClassLoader loader)
Creates a new EnhancerClassLoader for the specified url.

param
urls the classpath to search

        super(urls, loader);
        acc = AccessController.getContext();
        ucp = new URLClassPath(urls);
        checkUCP(urls);
    
public EnhancerClassLoader(String classpath, Properties settings, PrintWriter out)
Creates a new EnhancerClassLoader for the specified url.

param
classpath the classpath to search

        this(pathToURLs(classpath));
        JDOMetaData metaData = new JDOMetaDataModelImpl(Model.ENHANCER, out);
        init(metaData, settings, out);
    
public EnhancerClassLoader(URL[] urls, Properties settings, PrintWriter out)
Creates a new EnhancerClassLoader for the specified url.

param
urls the classpath to search

        this(urls);
        JDOMetaData metaData = new JDOMetaDataModelImpl(Model.ENHANCER, out);
        init(metaData, settings, out);
    
public EnhancerClassLoader(String classpath, com.sun.jdo.api.persistence.enhancer.meta.JDOMetaData metaData, Properties settings, PrintWriter out)
Creates a new EnhancerClassLoader for the specified url.

param
classpath the classpath to search

        this(pathToURLs(classpath));
        init(metaData, settings, out);
    
Methods Summary
protected voidaddURL(java.net.URL url)
Appends the specified URL to the list of URLs to search for classes and resources.

param
url the URL to be added to the search path of URLs

        throw new UnsupportedOperationException("Not implemented yet: EnhancerClassLoader.addURL(URL)");//NOI18N
        //super.addURL(url);
        //ucp.addURL(url);
    
private voidcheckUCP(java.net.URL[] urls)

        // ensure classpath is not empty
        if (null == urls) {
            throw new IllegalArgumentException("urls == null");//NOI18N
        }
        if (urls.length == 0) {
            throw new IllegalArgumentException("urls.length == 0");//NOI18N
        }

        for (int i = 0; i < urls.length; i++) {
            super.addURL(urls[i]);
        }
    
private java.lang.ClassdefineClass(java.lang.String name, sun.misc.Resource res)
Defines a Class using the class bytes obtained from the specified Resource. The resulting Class must be resolved before it can be used.

        int i = name.lastIndexOf('.");
        URL url = res.getCodeSourceURL();
        if (i != -1) {
            String pkgname = name.substring(0, i);
            // Check if package already loaded.
            Package pkg = getPackage(pkgname);
            Manifest man = res.getManifest();
            if (pkg != null) {
                // Package found, so check package sealing.
                boolean ok;
                if (pkg.isSealed()) {
                    // Verify that code source URL is the same.
                    ok = pkg.isSealed(url);
                } else {
                    // Make sure we are not attempting to seal the package
                    // at this code source URL.
                    ok = (man == null) || !isSealed(pkgname, man);
                }
                if (!ok) {
                    throw new SecurityException("sealing violation");//NOI18N
                }
            } else {
                if (man != null) {
                    definePackage(pkgname, man, url);
                } else {
                    definePackage(pkgname, null, null, null, null, null, null, null);
                }
            }
        }
        // Now read the class bytes and define the class
        byte[] b = res.getBytes();
        Certificate[] certs = res.getCertificates();
        CodeSource cs = new CodeSource(url, certs);

        //@olsen: performance bug 4457471: circumvent enhancer for
        // non-enhancable classes
        final String classPath = name.replace('.", '/");
        if (!metaData.isTransientClass(classPath)) {
            // Add enhancement here
            b = enhance(name, b, 0, b.length);
        }

        return defineClass(name, b, 0, b.length, cs);
    
private byte[]enhance(java.lang.String name, byte[] data, int off, int len)

        //message("EnhancerClassLoader: enhance class: " + name);

        final byte[] result;
        try {
            // create enhancer if not done yet
            if (null == enhancer) {
                enhancer = new FilterEnhancer(metaData, settings, out, null);
                if (doTiming) {
                    // wrap with timing filter enhancer object
                    enhancer = new ByteCodeEnhancerTimer(enhancer);
                }
            }

            // create input and output byte streams
            ByteArrayInputStream inByteCode
                = new ByteArrayInputStream(data, off, len);
            ByteArrayOutputStream outByteCode
                = ((null == outByteCodeRef)
                   ? null : (ByteArrayOutputStream)outByteCodeRef.get());
            if (null == outByteCode) {
                outByteCode = new ByteArrayOutputStream(10000);
                outByteCodeRef = new WeakReference(outByteCode);
            }
            outByteCode.reset();

            // enhance class
            boolean changed
                = enhancer.enhanceClassFile(inByteCode, outByteCode);

            // check whether class has been enhanced
            result = (changed ? outByteCode.toByteArray() : data);
        } catch (EnhancerUserException e) {
            //@olsen: 4370739
            message(e);
            final String msg = ("Exception caught while loading class '"//NOI18N
                                + name + "' : " + e);//NOI18N
            throw new ClassNotFoundException(msg, e);
        } catch(EnhancerFatalError e) {
            //@olsen: 4370739
            message(e);
            final String msg = ("Exception caught while loading class '"//NOI18N
                                + name + "' : " + e);//NOI18N
            // discard enhancer since it might have become inconsistent
            enhancer = null;
            throw new ClassNotFoundException(msg, e);
        }
        return result;
    
private java.lang.ClassfindAndEnhanceClass(java.lang.String name)
Finds and loads the class with the specified name from the URL search path. Any URLs referring to JAR files are loaded and opened as needed until the class is found.

param
name the name of the class
return
the resulting class
exception
ClassNotFoundException if the class could not be found

        try {
            if (doTiming) {
                Support.timer.push("EnhancerClassLoader.findAndEnhanceClass(String)",//NOI18N
                                   "EnhancerClassLoader.findAndEnhanceClass(" + name + ")");//NOI18N
            }
            return (Class)
            AccessController.doPrivileged(new PrivilegedExceptionAction() {
                public Object run() throws ClassNotFoundException {
                    String path = name.replace('.", '/").concat(".class");//NOI18N
                    //message("path=" + path);
                    Resource res = ucp.getResource(path, false);
                    if (res != null) {
                        try {
                            return defineClass(name, res);
                        } catch (IOException e) {
                            final String msg
                                = ("Exception caught while loading class '"//NOI18N
                                   + name + "' : " + e);//NOI18N
                            throw new ClassNotFoundException(msg, e);
                        }
                    } else {
                        // ok if class resource not found (e.g. java.*)
                        //throw new ClassNotFoundException(name);
                        return null;
                    }
                }
            }, acc);
        } catch (PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        } finally {
            if (doTiming) {
                Support.timer.pop();
            }
        }
    
private voidinit(com.sun.jdo.api.persistence.enhancer.meta.JDOMetaData metaData, java.util.Properties settings, java.io.PrintWriter out)
Initialize the EnhancingClassLoader

        this.out = out;
        final String verboseLevel
            = (settings == null ? null
               : settings.getProperty(FilterEnhancer.VERBOSE_LEVEL));
        this.debug = FilterEnhancer.VERBOSE_LEVEL_DEBUG.equals(verboseLevel);
        this.settings = settings;
        this.metaData = metaData;
        this.enhancer = null;

        if (settings != null) {
            final String timing
                = settings.getProperty(FilterEnhancer.DO_SIMPLE_TIMING);
            this.doTiming = Boolean.valueOf(timing).booleanValue();
        }
        if (this.doTiming) {
            // wrap with timing meta data object
            this.metaData = new JDOMetaDataTimer(metaData);
        }

        message("EnhancerClassLoader: UCP = {");//NOI18N
        final URL[] urls = getURLs();
        for (int i = 0; i < urls.length; i++) {
            message("    " + urls[i]);//NOI18N
        }
        message("}");//NOI18N

        message("EnhancerClassLoader: jdoMetaData = " + metaData);//NOI18N
    
private booleanisSealed(java.lang.String name, java.util.jar.Manifest man)
Returns true if the specified package name is sealed according to the given manifest.

        String path = name.replace('.", '/").concat("/");//NOI18N
        Attributes attr = man.getAttributes(path);
        String sealed = null;
        if (attr != null) {
            sealed = attr.getValue(Name.SEALED);
        }
        if (sealed == null) {
            if ((attr = man.getMainAttributes()) != null) {
                sealed = attr.getValue(Name.SEALED);
            }
        }
        return "true".equalsIgnoreCase(sealed);//NOI18N
    
public synchronized java.lang.ClassloadClass(java.lang.String name, boolean resolve)

        message();
        message("EnhancerClassLoader: loading class: " + name);//NOI18N

        try {
            Class c = null;

            final String classPath = name.replace('.", '/");
            // At least these packages must be delegated to parent class
            // loader:
            //    java/lang,	     (Object, ...)
            //    java/util,         (Collection)
            //    java/io,           (PrintWriter)
            //    javax/sql,         (PMF->javax.sql.DataSource)
            //    javax/transaction  (Tx->javax.transaction.Synchronization)
            //
            //@olsen: delegate loading of "safe" classes to parent
            //if (metaData.isTransientClass(classPath)) {
            //
            //@olsen: only delegate loading of bootstrap classes to parent
            //if (classPath.startsWith("java/lang/")) {
            //
            //@olsen: performance bug 4457471: delegate loading of F4J
            // persistence classes to parent tp prevent passing these and
            // other IDE classes plus database drivers etc. to the enhancer!
            //if (classPath.startsWith("java/lang/")
            //    || classPath.startsWith("com/sun/jdo/")) {
            //
            //@olsen: bug 4480618: delegate loading of javax.{sql,transaction}
            // classes to parent class loader to support user-defined
            // DataSource and Synchronization objects to be passed to the
            // TP runtime.  By the same argument, java.{util,io} classes need
            // also be loaded by the parent class loader.  This has been
            // the case since the EnhancerClassLoader will never find these
            // bootstrap classes in the passed Classpath.  However, for
            // efficiency and clarity, this delegation should be expressed
            // by testing for entire "java/" package in the check here.
            if (classPath.startsWith("java/")//NOI18N
                || classPath.startsWith("javax/sql/")//NOI18N
                || classPath.startsWith("javax/transaction/")//NOI18N
                || classPath.startsWith("com/sun/jdo/")) {//NOI18N
                message("EnhancerClassLoader: bootstrap class, using parent loader for class: " + name);//NOI18N
                return super.loadClass(name, resolve);

//@olsen: dropped alternative approach
/*
                message("EnhancerClassLoader: transient class, skipping enhancing: " + name);//NOI18N

                // get a byte array output stream to collect byte code
                ByteArrayOutputStream outByteCode
                    = ((null == outByteCodeRef)
                       ? null : (ByteArrayOutputStream)outByteCodeRef.get());
                if (null == outByteCode) {
                    outByteCode = new ByteArrayOutputStream(10000);
                    outByteCodeRef = new WeakReference(outByteCode);
                }
                outByteCode.reset();

                // find byte code of class
                final InputStream is = getSystemResourceAsStream(name);
                //@olsen: (is == null) ?!

                // copy byte code of class into byte array
                final byte[] data;
                try {
                    int b;
                    while ((b = is.read()) >= 0) {
                        outByteCode.write(b);
                    }
                    data = outByteCode.toByteArray();
                } catch (IOException e) {
                    final String msg
                        = ("Exception caught while loading class '"//NOI18N
                           + name + "' : " + e);//NOI18N
                    throw new ClassNotFoundException(msg, e);
                }

                // convert the byte code into class object
                c = defineClass(name, data, 0, data.length);
*/
            }

            //@olsen: check if class has been loaded already
            if (c == null) {
                c = findLoadedClass(name);
                if (c != null) {                
                    message("EnhancerClassLoader: class already loaded: " + name);//NOI18N
                }
            }

            if (c == null) {
                c = findAndEnhanceClass(name);
            }

            // as a last resort, if the class couldn't be found, try
            // loading class by parent class loader
            if (c == null) {
                message("EnhancerClassLoader: class not found, using parent loader for class: " + name);//NOI18N
                return super.loadClass(name, resolve);
            }

            message();
            message("EnhancerClassLoader: loaded class: " + name);//NOI18N
            if (resolve) {
                resolveClass(c);
            }

            message();
            message("EnhancerClassLoader: loaded+resolved class: " + name);//NOI18N
            return c;
        } catch (RuntimeException e) {
            // log exception only
            message();
            message("EnhancerClassLoader: EXCEPTION SEEN: " + e);//NOI18N
            //e.printStackTrace(out);
            throw e;
        } catch (ClassNotFoundException e) {
            // log exception only
            message();
            message("EnhancerClassLoader: EXCEPTION SEEN: " + e);//NOI18N
            //e.printStackTrace(out);
            throw e;
        }
    
private final voidmessage()


    //@olsen: 4370739
        
        if (debug) {
            out.println();
        }
    
private final voidmessage(java.lang.String s)

        if (debug) {
            out.println(s);
        }
    
private final voidmessage(java.lang.Exception e)

        if (debug) {
            final String msg = ("Exception caught: " + e);//NOI18N
            out.println(msg);
            e.printStackTrace(out);
        }
    
public static java.net.URL[]pathToURLs(java.lang.String classpath)


         
        return URLClassPath.pathToURLs(classpath);