org.apache.openejb.core.TempClassLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.openejb.core.TempClassLoader.java

Source

/*
 * 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.openejb.core;

import org.apache.openejb.loader.Options;
import org.apache.openejb.loader.SystemInstance;
import org.apache.xbean.asm.ClassReader;
import org.apache.xbean.asm.Opcodes;
import org.apache.xbean.asm.commons.EmptyVisitor;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Set;

/**
 * ClassLoader implementation that allows classes to be temporarily
 * loaded and then thrown away. Useful for verifying and inspecting
 * a class without first loading(and thus polluting) the parent
 * ClassLoader.
 * </p>
 * This class is a proper subclass of URLClassLoader.  This class
 * will locally load any class except for those defined in the
 * java.*, javax.* and sun.* packages and annotations all of which
 * are loaded by with
 * <code>Class.forName(name, resolve, getClass().getClassLoader())</code>
 */
// Note: this class is a fork from OpenJPA
public class TempClassLoader extends URLClassLoader {

    private Set<Skip> skip;

    public TempClassLoader(ClassLoader parent) {
        super(new URL[0], parent);

        Options options = SystemInstance.get().getOptions();
        skip = options.getAll("openejb.tempclassloader.skip", Skip.NONE);
    }

    /*
     * Needed for testing
     */
    public void skip(Skip s) {
        this.skip.add(s);
    }

    public Class loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

    protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
        if (name == null)
            throw new NullPointerException("name cannot be null");

        // see if we've already loaded it
        Class c = findLoadedClass(name);
        if (c != null) {
            return c;
        }

        // bug #283. defer to system if the name is a protected name.
        // "sun." is required for JDK 1.4, which has an access check for
        // sun.reflect.GeneratedSerializationConstructorAccessor1
        /*
         * FIX for openejb-tomcat JSF support . Added the following to the if statement below: !name.startsWith("javax.faces")
         *We want to use this TempClassLoader to also load the classes in the javax.faces package. 
         *If not, then our AnnotationDeployer will not be able to load the javax.faces.FacesServlet class if this class is in a jar which 
         *is in the WEB-INF/lib directory of a web app. 
         * see AnnotationDeployer$ProcessAnnotatedBeans.deploy(WebModule webModule) 
         * Here is what happened  before this fix was applied:
         * 1. The AnnotationDeployer tries to load the javax.faces.FacesServlet using this classloader (TempClassLoader)
         * 2. Since this class loader uses Class.forName to load classes starting with java, javax or sun, it cannot load javax.faces.FacesServlet
         * 3. Result is , AnnotationDeployer throws a ClassNotFoundException
         */
        if (skip(name)) {
            return Class.forName(name, resolve, getClass().getClassLoader());
        }
        //        ( && !name.startsWith("javax.faces.") )||
        String resourceName = name.replace('.', '/') + ".class";
        InputStream in = getResourceAsStream(resourceName);
        if (in == null) {
            throw new ClassNotFoundException(name);
        }

        // 80% of class files are smaller then 6k
        ByteArrayOutputStream bout = new ByteArrayOutputStream(8 * 1024);

        // copy the input stream into a byte array
        byte[] bytes;
        try {
            byte[] buf = new byte[4 * 1024];
            for (int count; (count = in.read(buf)) >= 0;) {
                bout.write(buf, 0, count);
            }
            bytes = bout.toByteArray();
        } catch (IOException e) {
            throw new ClassNotFoundException(name, e);
        }

        // Annotation classes must be loaded by the normal classloader
        // So must Enum classes to prevent problems with the sun jdk.
        if (skip.contains(Skip.ANNOTATIONS) && isAnnotationClass(bytes)) {
            return Class.forName(name, resolve, getClass().getClassLoader());
        }

        if (skip.contains(Skip.ENUMS) && isEnum(bytes)) {
            return Class.forName(name, resolve, getClass().getClassLoader());
        }

        // define the package
        int packageEndIndex = name.lastIndexOf('.');
        if (packageEndIndex != -1) {
            String packageName = name.substring(0, packageEndIndex);
            if (getPackage(packageName) == null) {
                definePackage(packageName, null, null, null, null, null, null, null);
            }
        }

        // define the class
        try {
            return defineClass(name, bytes, 0, bytes.length);
        } catch (SecurityException e) {
            // possible prohibited package: defer to the parent
            return super.loadClass(name, resolve);
        } catch (LinkageError le) {
            // fallback
            return super.loadClass(name, resolve);
        }
    }

    // TODO: for jsf it can be useful to include commons-logging and openwebbeans...
    private boolean skip(String name) {
        if (skip.equals(Skip.ALL)) {
            return true;
        }

        if (name.startsWith("javax.faces."))
            return false;
        if (name.startsWith("javax.servlet.jsp.jstl"))
            return false;
        if (name.equals("org.apache.commons.logging.impl.LogFactoryImpl"))
            return false;
        if (name.startsWith("org.apache.webbeans.jsf"))
            return false;

        if (name.startsWith("java."))
            return true;
        if (name.startsWith("javax."))
            return true;
        if (name.startsWith("sun."))
            return true;
        if (name.startsWith("org.apache.openjpa."))
            return true;
        if (name.startsWith("org.apache.derby."))
            return true;
        if (name.startsWith("org.apache.xbean."))
            return true;
        if (name.startsWith("org.eclipse.jdt."))
            return true;
        if (name.startsWith("org.apache.openejb.jee."))
            return true;
        if (name.startsWith("org.apache.openejb.api."))
            return true;
        if (name.startsWith("org.apache.commons."))
            return true;
        if (name.startsWith("javassist"))
            return true;
        if (name.startsWith("org.codehaus.swizzle"))
            return true;
        if (name.startsWith("org.w3c.dom"))
            return true;
        if (name.startsWith("org.apache.geronimo."))
            return true;
        if (name.startsWith("com.sun.org.apache."))
            return true;
        if (name.startsWith("org.apache.coyote"))
            return true;
        if (name.startsWith("org.quartz"))
            return true;
        if (name.startsWith("serp.bytecode"))
            return true;
        if (name.startsWith("org.apache.webbeans."))
            return true;

        //        if (name.startsWith("org.apache.myfaces.")) return true;
        //        if (name.startsWith("org.apache.taglibs.")) return true;
        //        if (name.startsWith("org.apache.tomcat.")) return true;
        //        if (name.startsWith("org.apache.el.")) return true;
        //        if (name.startsWith("org.apache.jasper.")) return true;
        //        if (name.startsWith("org.apache.catalina")) return true;
        //        if (name.startsWith("org.apache.jsp")) return true;
        //        if (name.startsWith("org.apache.log4j")) return true;
        //        if (name.startsWith("org.apache.naming")) return true;
        //        if (name.startsWith("org.apache.openejb")) return true;

        return false;
    }

    public static enum Skip {
        NONE, ANNOTATIONS, ENUMS, ALL
    }

    /**
     * Fast-parse the given class bytecode to determine if it is an
     * enum class.
     */
    private static boolean isEnum(byte[] bytes) {
        IsEnumVisitor isEnumVisitor = new IsEnumVisitor();
        ClassReader classReader = new ClassReader(bytes);
        classReader.accept(isEnumVisitor, ClassReader.SKIP_DEBUG);
        return isEnumVisitor.isEnum;
    }

    /**
     * Fast-parse the given class bytecode to determine if it is an
     * annotation class.
     */
    private static boolean isAnnotationClass(byte[] bytes) {
        IsAnnotationVisitor isAnnotationVisitor = new IsAnnotationVisitor();
        ClassReader classReader = new ClassReader(bytes);
        classReader.accept(isAnnotationVisitor, ClassReader.SKIP_DEBUG);
        return isAnnotationVisitor.isAnnotation;
    }

    public static class IsAnnotationVisitor extends EmptyVisitor {
        public boolean isAnnotation = false;

        public void visit(int version, int access, String name, String signature, String superName,
                String[] interfaces) {
            isAnnotation = (access & Opcodes.ACC_ANNOTATION) != 0;
        }

    }

    public static class IsEnumVisitor extends EmptyVisitor {
        public boolean isEnum = false;

        public void visit(int version, int access, String name, String signature, String superName,
                String[] interfaces) {
            isEnum = (access & Opcodes.ACC_ENUM) != 0;
        }

    }
}