001package com.ganteater.ae; 002 003import java.util.Hashtable; 004 005/** 006 * A simple test class loader capable of loading from multiple sources, such as 007 * local files or a URL. 008 * 009 * This class is derived from an article by Chuck McManis 010 * http://www.javaworld.com/javaworld/jw-10-1996/indepth.src.html with large 011 * modifications. 012 * 013 * Note that this has been updated to use the non-deprecated version of 014 * defineClass() -- JDM. 015 * 016 * @author Jack Harich - 8/18/97 017 * @author John D. Mitchell - 99.03.04 018 */ 019public abstract class MultiClassLoader extends ClassLoader { 020 021//---------- Fields -------------------------------------- 022 private Hashtable<String, Class<?>> classes = new Hashtable<>(); 023 private char classNameReplacementChar; 024 025 protected boolean monitorOn = false; 026 protected boolean sourceMonitorOn = true; 027 028//---------- Initialization ------------------------------ 029 public MultiClassLoader() { 030 } 031 032//---------- Superclass Overrides ------------------------ 033 /** 034 * This is a simple version for external clients since they will always want the 035 * class resolved before it is returned to them. 036 */ 037 public Class<?> loadClass(String className) throws ClassNotFoundException { 038 return (loadClass(className, true)); 039 } 040 041//---------- Abstract Implementation --------------------- 042 public synchronized Class<?> loadClass(String className, boolean resolveIt) throws ClassNotFoundException { 043 044 Class<?> result; 045 byte[] classBytes; 046 monitor(">> MultiClassLoader.loadClass(" + className + ", " + resolveIt + ")"); 047 048 // ----- Check our local cache of classes 049 result = (Class<?>) classes.get(className); 050 if (result != null) { 051 monitor(">> returning cached activeTaskArray."); 052 return result; 053 } 054 055 // ----- Check with the primordial class loader 056 try { 057 result = super.findSystemClass(className); 058 monitor(">> returning system class (in CLASSPATH)."); 059 return result; 060 } catch (ClassNotFoundException e) { 061 monitor(">> Not a system class."); 062 } 063 064 // ----- Try to load it from preferred source 065 // Note loadClassBytes() is an abstract method 066 classBytes = loadClassBytes(className); 067 if (classBytes == null) { 068 throw new ClassNotFoundException(); 069 } 070 071 // ----- Define it (parse the class file) 072 result = defineClass(className, classBytes, 0, classBytes.length); 073 if (result == null) { 074 throw new ClassFormatError(); 075 } 076 077 // ----- Resolve if necessary 078 if (resolveIt) 079 resolveClass(result); 080 081 // Done 082 classes.put(className, result); 083 monitor(">> Returning newly loaded class."); 084 return result; 085 } 086 087//---------- Public Methods ------------------------------ 088 /** 089 * This optional call allows a class name such as "COM.test.Hello" to be changed 090 * to "COM_test_Hello", which is useful for storing classes from different 091 * packages in the same retrival directory. In the above example the char would 092 * be '_'. 093 */ 094 public void setClassNameReplacementChar(char replacement) { 095 classNameReplacementChar = replacement; 096 } 097 098//---------- Protected Methods --------------------------- 099 protected abstract byte[] loadClassBytes(String className); 100 101 protected String formatClassName(String className) { 102 if (classNameReplacementChar == '\u0000') { 103 // '/' is used to map the package to the path 104 return className.replace('.', '/') + ".class"; 105 } else { 106 // Replace '.' with custom char, such as '_' 107 return className.replace('.', classNameReplacementChar) + ".class"; 108 } 109 } 110 111 protected void monitor(String text) { 112 if (monitorOn) 113 print(text); 114 } 115 116//--- Std 117 protected static void print(String text) { 118 System.out.println(text); 119 } 120 121} // End class