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