Java tutorial
/* * 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; } } }