001: package com.reeltwo.jumble.mutation;
002:
003: //import org.apache.bcel.util.ClassPath;
004: import java.io.IOException;
005: import java.io.InputStream;
006: import java.net.URL;
007: import java.util.Enumeration;
008: import java.util.Hashtable;
009: import org.apache.bcel.classfile.JavaClass;
010: import org.apache.bcel.util.ClassPath;
011: import org.apache.bcel.util.Repository;
012: import org.apache.bcel.util.SyntheticRepository;
013:
014: /**
015: * A <code>ClassLoader</code> which embeds a <code>Mutater</code> so
016: * that applications can be run with a single class undergoing
017: * mutation.
018: *
019: * @author Tin Pavlinic
020: * @version $Revision: 516 $
021: */
022: public class MutatingClassLoader extends ClassLoader {
023:
024: /** Used to perform the actual mutation */
025: private final Mutater mMutater;
026:
027: /** The name of the class being mutated */
028: private final String mTarget;
029:
030: private final String[] mIgnoredPackages = new String[] { "java.",
031: //"javax.",
032: "sun.reflect", "junit.",
033: //"org.apache",
034: //"org.xml",
035: //"org.w3c"
036: };
037:
038: private final Hashtable<String, Class> mClasses = new Hashtable<String, Class>();
039: private final ClassLoader mDeferTo = ClassLoader
040: .getSystemClassLoader();
041: private final Repository mRepository;
042:
043: private final ClassPath mClassPath;
044:
045: /** Textual description of the modification made. */
046: private String mModification;
047:
048: /**
049: * Creates a new <code>MutatingClassLoader</code> instance.
050: *
051: * @param target the class name to be mutated. Other classes will
052: * not be mutated.
053: * @param mutater a <code>Mutater</code> value that will carry out
054: * mutations.
055: * @param classpath a <code>String</code> value supplying the
056: * classes visible to the classloader.
057: */
058: public MutatingClassLoader(final String target,
059: final Mutater mutater, final String classpath) {
060: // Add these ignored classes to work around jakarta commons logging stupidity with class loaders.
061: mTarget = target;
062: mMutater = mutater;
063: mClassPath = new ClassPath(classpath);
064: //mRepository = SyntheticRepository.getInstance();
065: mRepository = SyntheticRepository.getInstance(mClassPath);
066: mMutater.setRepository(mRepository);
067: }
068:
069: /**
070: * Gets a string description of the modification produced.
071: *
072: * @return the modification
073: */
074: public String getModification() {
075: return mModification;
076: }
077:
078: public int countMutationPoints(String className)
079: throws ClassNotFoundException {
080: loadClass(className);
081: return mMutater.countMutationPoints(className);
082: }
083:
084: protected Class loadClass(String className, boolean resolve)
085: throws ClassNotFoundException {
086: Class cl = null;
087:
088: if ((cl = (Class) mClasses.get(className)) == null) {
089: // Classes we're forcing to be loaded by mDeferTo
090: for (int i = 0; i < mIgnoredPackages.length; i++) {
091: if (className.startsWith(mIgnoredPackages[i])) {
092: //System.err.println("Parent forced loading of class: " + className);
093: cl = mDeferTo.loadClass(className);
094: break;
095: }
096: }
097:
098: if (cl == null) {
099: JavaClass clazz = null;
100:
101: // Try loading from our repository
102: try {
103: if ((clazz = mRepository.loadClass(className)) != null) {
104: clazz = modifyClass(clazz);
105: }
106: } catch (ClassNotFoundException e) {
107: ; // OK, because we'll let Class.forName handle it
108: }
109:
110: if (clazz != null) {
111: //System.err.println("MCL loading class: " + className);
112: byte[] bytes = clazz.getBytes();
113: cl = defineClass(className, bytes, 0, bytes.length);
114: } else {
115: //cl = Class.forName(className);
116: //System.err.println("Parent loading of class: " + className);
117: cl = mDeferTo.loadClass(className);
118: }
119: }
120:
121: if (resolve) {
122: resolveClass(cl);
123: }
124: }
125:
126: mClasses.put(className, cl);
127:
128: return cl;
129: }
130:
131: /**
132: * If the class matches the target then it is mutated, otherwise the class if
133: * returned unmodified. Overrides the corresponding method in the superclass.
134: * Classes are cached so that we always load a fresh version.
135: *
136: * This method is public so we can test it
137: *
138: * @param clazz modification target
139: * @return possibly modified class
140: */
141: public JavaClass modifyClass(JavaClass clazz) {
142: if (clazz.getClassName().equals(mTarget)) {
143: synchronized (mMutater) {
144: clazz = mMutater.jumbler(clazz);
145: mModification = mMutater.getModification();
146: }
147: }
148: return clazz;
149: }
150:
151: @SuppressWarnings("unchecked")
152: public Enumeration<URL> getResources(String name)
153: throws IOException {
154: Enumeration<URL> resources = mClassPath.getResources(name);
155: if (!resources.hasMoreElements()) {
156: resources = mDeferTo.getResources(name);
157: //System.err.println("Parent getting resources: " + name + " " + resources);
158: //} else {
159: //System.err.println("MCL getting resources: " + name + " " + resources);
160: }
161: return resources;
162: }
163:
164: public URL getResource(String name) {
165: URL resource = mClassPath.getResource(name);
166: if (resource == null) {
167: resource = mDeferTo.getResource(name);
168: //System.err.println("Parent getting resource: " + name + " " + resource);
169: //} else {
170: //System.err.println("MCL getting resource: " + name + " " + resource);
171: }
172: return resource;
173: }
174:
175: public InputStream getResourceAsStream(String name) {
176: InputStream resource = mClassPath.getResourceAsStream(name);
177: if (resource == null) {
178: resource = mDeferTo.getResourceAsStream(name);
179: //System.err.println("Parent getting resource as stream: " + name + " " + resource);
180: //} else {
181: //System.err.println("MCL getting resource as stream: " + name + " " + resource);
182: }
183: return resource;
184: }
185: }
|