001 /*
002 * $Id: XrayClassLoader.java,v 1.4 2011/07/09 21:43:23 oboehm Exp $
003 *
004 * Copyright (c) 2010 by Oliver Boehm
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * 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 orimplied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 *
018 * (c)reated 26.12.2010 by oliver (ob@oasd.de)
019 */
020
021 package patterntesting.runtime.experimental;
022
023 import java.io.*;
024 import java.net.URI;
025 import java.util.*;
026 import java.util.jar.*;
027
028 import org.apache.commons.io.*;
029 import org.apache.commons.lang.StringUtils;
030 import org.slf4j.*;
031
032 import patterntesting.runtime.monitor.ClasspathMonitor;
033 import patterntesting.runtime.util.Converter;
034
035 /**
036 * If we want to load a class and see what happens if another class (needed by
037 * the original class) is missing we need a class loader where we can control
038 * the classpath and other things.
039 *
040 * @author oliver
041 * @since 1.1 (26.12.2010)
042 */
043 public final class XrayClassLoader extends ClassLoader {
044
045 private static final ClasspathMonitor classpathMonitor = ClasspathMonitor.getInstance();
046 private static final Logger log = LoggerFactory.getLogger(XrayClassLoader.class);
047 private final Map<String, Class<?>> loadedClassMap = new HashMap<String, Class<?>>();
048
049 /**
050 * Load class.
051 *
052 * @param classname the classname
053 * @return the class
054 * @throws ClassNotFoundException the class not found exception
055 * @see java.lang.ClassLoader#loadClass(java.lang.String)
056 */
057 @Override
058 public Class<?> loadClass(final String classname) throws ClassNotFoundException {
059 Class<?> loaded = loadedClassMap.get(classname);
060 if (loaded == null) {
061 try {
062 loaded = findClass(classname);
063 } catch (SecurityException ce) {
064 log.debug(ce.getLocalizedMessage() + " - using parent to load " + classname);
065 loaded = super.loadClass(classname);
066 }
067 loadedClassMap.put(classname, loaded);
068 }
069 return loaded;
070 }
071
072 /**
073 * Gets the loaded classed of this classloader here.
074 *
075 * @return the loaded classed
076 */
077 public Set<Class<?>> getLoadedClasses() {
078 return new HashSet<Class<?>>(this.loadedClassMap.values());
079 }
080
081 /**
082 * Find class.
083 *
084 * @param classname the classname
085 * @return the class
086 * @throws ClassNotFoundException the class not found exception
087 * @see java.lang.ClassLoader#findClass(java.lang.String)
088 */
089 @Override
090 protected Class<?> findClass(final String classname) throws ClassNotFoundException {
091 URI classUri = classpathMonitor.whichClass(classname);
092 try {
093 byte[] data = read(classUri);
094 return defineClass(classname, data, 0, data.length);
095 } catch (IOException ioe) {
096 throw new ClassNotFoundException("can't load class " + classname, ioe);
097 }
098 }
099
100 private static byte[] read(final URI uri) throws IOException {
101 log.trace("loading " + uri + "...");
102 try {
103 File file = new File(uri);
104 return FileUtils.readFileToByteArray(file);
105 } catch (IllegalArgumentException iae) {
106 String scheme = uri.getScheme();
107 if ("jar".equals(scheme)) {
108 return readJar(uri);
109 } else {
110 throw new IllegalArgumentException("don't know how to load " + uri);
111 }
112 }
113 }
114
115 private static byte[] readJar(final URI uri) throws IOException {
116 File file = Converter.toFile(StringUtils.substringBefore(uri.toString(), "!"));
117 String classpath = StringUtils.substringAfterLast(uri.toString(), "!");
118 JarFile jarFile = new JarFile(file);
119 JarEntry entry = getEntry(classpath, jarFile);
120 InputStream istream = jarFile.getInputStream(entry);
121 try {
122 return IOUtils.toByteArray(istream);
123 } finally {
124 istream.close();
125 }
126 }
127
128 private static JarEntry getEntry(final String classpath, final JarFile jarFile) {
129 JarEntry entry = jarFile.getJarEntry(classpath);
130 if (entry == null) {
131 entry = jarFile.getJarEntry(classpath.substring(1));
132 }
133 return entry;
134 }
135
136 }
137