001    /**
002    * Licensed to the Apache Software Foundation (ASF) under one or more
003    * contributor license agreements.  See the NOTICE file distributed with
004    * this work for additional information regarding copyright ownership.
005    * The ASF licenses this file to You under the Apache License, Version 2.0
006    * (the "License"); you may not use this file except in compliance with
007    * the License.  You may obtain a copy of the License at
008    *
009    *     http://www.apache.org/licenses/LICENSE-2.0
010    *
011    *  Unless required by applicable law or agreed to in writing, software
012    *  distributed under the License is distributed on an "AS IS" BASIS,
013    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014    *  See the License for the specific language governing permissions and
015    *  limitations under the License.
016    */
017    package org.apache.geronimo.kernel.classloader;
018    
019    import java.io.ByteArrayOutputStream;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.net.URL;
023    import java.net.URLClassLoader;
024    import java.net.URLStreamHandlerFactory;
025    
026    import org.objectweb.asm.ClassReader;
027    import org.objectweb.asm.Opcodes;
028    import org.objectweb.asm.commons.EmptyVisitor;
029    
030    /**
031    * ClassLoader implementation that allows classes to be temporarily
032    * loaded and then thrown away. Useful for verifying and inspecting
033    * a class without first loading(and thus polluting) the parent
034    * ClassLoader.
035    * </p>
036    * This class is a proper subclass of URLClassLoader.  This class
037    * will locally load any class except for those defined in the
038    * java.*, javax.* and sun.* packages and annotations all of which
039    * are loaded by with
040    * <code>Class.forName(name, resolve, getClass().getClassLoader())</code>
041    * @author Marc Prud'hommeaux
042    */
043    //Note: this class is a fork from OpenEJB which was a fork from OpenJPA
044    public class TemporaryClassLoader extends URLClassLoader {
045       public TemporaryClassLoader(ClassLoader parent) {
046           super(new URL[0], parent);
047       }
048    
049       public TemporaryClassLoader(URL[] urls, ClassLoader parent) {
050           super(urls, parent);
051       }
052    
053       public TemporaryClassLoader(URL[] urls) {
054           super(urls);
055       }
056    
057       public TemporaryClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
058           super(urls, parent, factory);
059       }
060    
061       public Class loadClass(String name) throws ClassNotFoundException {
062           return loadClass(name, false);
063       }
064    
065       protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
066           // see if we've already loaded it
067           Class c = findLoadedClass(name);
068           if (c != null) {
069               return c;
070           }
071    
072           // bug #283. defer to system if the name is a protected name.
073           // "sun." is required for JDK 1.4, which has an access check for
074           // sun.reflect.GeneratedSerializationConstructorAccessor1
075           if (name.startsWith("java.") ||
076                   name.startsWith("javax.") ||
077                   name.startsWith("sun.")) {
078               return getParent().loadClass(name);
079           }
080    
081           String resourceName = name.replace('.', '/') + ".class";
082           InputStream in = getResourceAsStream(resourceName);
083           if (in == null) {
084               throw new ClassNotFoundException(name);
085           }
086    
087           // 80% of class files are smaller then 6k
088           ByteArrayOutputStream bout = new ByteArrayOutputStream(8 * 1024);
089    
090           // copy the input stream into a byte array
091           byte[] bytes = new byte[0];
092           try {
093               byte[] buf = new byte[4 * 1024];
094               for (int count = -1; (count = in.read(buf)) >= 0;) {
095                   bout.write(buf, 0, count);
096               }
097               bytes = bout.toByteArray();
098           } catch (IOException e) {
099               throw new ClassNotFoundException(name, e);
100           }
101    
102           // Annotation classes must be loaded by the normal classloader
103           if (isAnnotationClass(bytes)) {
104               return Class.forName(name, resolve, getClass().getClassLoader());
105           }
106    
107           // define the package
108           int packageEndIndex = name.lastIndexOf('.');
109           if (packageEndIndex != -1) {
110               String packageName = name.substring(0, packageEndIndex);
111               if (getPackage(packageName) == null) {
112                   definePackage(packageName, null, null, null, null, null, null, null);
113               }
114           }
115    
116           // define the class
117           try {
118               return defineClass(name, bytes, 0, bytes.length);
119           } catch (SecurityException e) {
120               // possible prohibited package: defer to the parent
121               return super.loadClass(name, resolve);
122           }
123       }
124    
125       /**
126        * Fast-parse the given class bytecode to determine if it is an
127        * annotation class.
128        */
129       private static boolean isAnnotationClass(byte[] bytes) {
130           IsAnnotationVisitor isAnnotationVisitor = new IsAnnotationVisitor();
131           ClassReader classReader = new ClassReader(bytes);
132           classReader.accept(isAnnotationVisitor, true);
133           return isAnnotationVisitor.isAnnotation;
134       }
135    
136       public static class IsAnnotationVisitor extends EmptyVisitor {
137           public boolean isAnnotation = false;
138    
139           public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
140               isAnnotation = (access & Opcodes.ACC_ANNOTATION) != 0;
141           }
142    
143       }
144    }
145