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