001: // THIS SOFTWARE IS PROVIDED BY SOFTARIS PTY.LTD. AND OTHER METABOSS
002: // CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
003: // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
004: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTARIS PTY.LTD.
005: // OR OTHER METABOSS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
006: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
007: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
008: // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
009: // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
010: // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
011: // EVEN IF SOFTARIS PTY.LTD. OR OTHER METABOSS CONTRIBUTORS ARE ADVISED OF THE
012: // POSSIBILITY OF SUCH DAMAGE.
013: //
014: // Copyright 2000-2005 © Softaris Pty.Ltd. All Rights Reserved.
015: package com.metaboss.util;
016:
017: import java.io.ByteArrayInputStream;
018: import java.io.File;
019: import java.io.FileFilter;
020: import java.io.FileInputStream;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.net.MalformedURLException;
024: import java.net.URL;
025: import java.util.ArrayList;
026: import java.util.Collections;
027: import java.util.Enumeration;
028: import java.util.HashMap;
029: import java.util.Iterator;
030: import java.util.List;
031: import java.util.jar.JarEntry;
032: import java.util.jar.JarInputStream;
033:
034: /** This utility class loader allows to load and execute classes from the directory. During initialisation
035: * it will load all jar files in the directory and than it will be able to load these classes on demand */
036: public class DirClassLoader extends ClassLoader {
037: // All details about resource
038: private class ResourceDescriptor {
039: public String mDirPath; // Full path to the base directory which has this resource
040: public String mJarPath; // Full path to the jar which has this resource
041: public String mContentPath; // Path to the resource inside jar or just a path to it (if it was in the directory and not in the jar)
042: public URL mResourceURL; // The URL we use to uniquely identify the resource
043: public byte[] mBinaryContent;
044: }
045:
046: private HashMap mResourceNamesMap = new HashMap(); // Contains information about all resources. Key - resource name, value Set with Resource Descriptors
047: private HashMap mClassesMap = new HashMap(); // Contains information about resources which represent classes. Key - class name, ResourceDescriptor
048: private HashMap mAlreadyLoadedClasses = new HashMap(); // Key - class name. Contents class
049:
050: // Default constructor is hidden. This class loader can not be created without valid directory */
051: private DirClassLoader() {
052: }
053:
054: /** Constructor. Constructs directory class loader without parent classloader */
055: public DirClassLoader(String pHomeDirectory) throws IOException {
056: readDirectory(new File(pHomeDirectory).getAbsoluteFile());
057: }
058:
059: /** Constructor. Constructs directory class loader with parent classloader */
060: public DirClassLoader(String pHomeDirectory,
061: ClassLoader pParentClassLoader) throws IOException {
062: super (pParentClassLoader);
063: readDirectory(new File(pHomeDirectory).getAbsoluteFile());
064: }
065:
066: /** Constructor. Constructs directory class loader without parent classloader */
067: public void addDirectory(String pAdditionalDirectory)
068: throws IOException {
069: readDirectory(new File(pAdditionalDirectory).getAbsoluteFile());
070: }
071:
072: /** Iterates through the directory and reads all jars */
073: private void readDirectory(File pFromDirectory) throws IOException {
074: if (!pFromDirectory.exists())
075: throw new IllegalArgumentException("Directory "
076: + pFromDirectory.getAbsolutePath()
077: + " does not exists");
078: if (!pFromDirectory.isDirectory())
079: throw new IllegalArgumentException(pFromDirectory
080: .getAbsolutePath()
081: + " is not a directory");
082: File[] lFiles = pFromDirectory.listFiles(new FileFilter() {
083: public boolean accept(File pPathname) {
084: return pPathname.isFile();
085: }
086: });
087: // Load all files one by one
088: for (int i = 0; i < lFiles.length; i++) {
089: File lFile = lFiles[i];
090: if (lFile.getName().toLowerCase().endsWith(".jar"))
091: addJar(lFile);
092: else
093: addResource(lFile, pFromDirectory.getAbsolutePath());
094: }
095: }
096:
097: /** This method will take passed in-memory jar file and add it to its library */
098: private void addResource(File pResource, String pRootDirectoryPath)
099: throws java.io.IOException {
100: if (!pResource.getAbsolutePath().startsWith(pRootDirectoryPath))
101: throw new java.io.IOException("File "
102: + pResource.getAbsolutePath() + " is not in the "
103: + pRootDirectoryPath + " directory.");
104: // Chop off the root directory path and force file separators
105: String lResourceName = StringUtils.replace(pResource
106: .getAbsolutePath().substring(
107: pRootDirectoryPath.length() + 1), "\\", "/");
108:
109: List lResourceDescriptors = (List) mResourceNamesMap
110: .get(lResourceName);
111: if (lResourceDescriptors == null)
112: mResourceNamesMap.put(lResourceName,
113: lResourceDescriptors = new ArrayList());
114: ResourceDescriptor lResourceDescriptor = new ResourceDescriptor();
115: lResourceDescriptor.mDirPath = StringUtils.replace(
116: pRootDirectoryPath, "\\", "/");
117: lResourceDescriptor.mContentPath = lResourceName;
118: lResourceDescriptor.mResourceURL = pResource.toURL();
119: lResourceDescriptor.mBinaryContent = ByteUtils
120: .readByteStreamFully(new FileInputStream(pResource));
121: lResourceDescriptors.add(lResourceDescriptor);
122: // See if we need to store the resource in the java class maps
123: if (lResourceName.toLowerCase().endsWith(".class")) {
124: // Prepare java style class name - strip .class from the end
125: String lClassName = lResourceName.substring(0,
126: lResourceName.length() - 6);
127: // Ensure that all slashes (back and forward) are converted to dots
128: lClassName = StringUtils.replace(lClassName, "/", ".");
129: mClassesMap.put(lClassName, lResourceDescriptor);
130: }
131: }
132:
133: /** This method will take passed in-memory jar file and add it to its library */
134: private void addJar(File pJar) throws java.io.IOException {
135: JarInputStream lJarInputStream = new JarInputStream(
136: new FileInputStream(pJar));
137: JarEntry lEntry = null;
138: String lJarPath = pJar.getAbsolutePath();
139: URL lJarURL = pJar.toURL();
140: while ((lEntry = lJarInputStream.getNextJarEntry()) != null) {
141: byte[] lBinaryContent = ByteUtils
142: .readByteStreamFully(lJarInputStream);
143: if (lBinaryContent != null && lBinaryContent.length > 0) {
144: // Ensure that all back slashes are converted to forward slashes (just for standartisation)
145: String lResourceName = StringUtils.replace(lEntry
146: .getName(), "\\", "/");
147: List lResourceDescriptors = (List) mResourceNamesMap
148: .get(lResourceName);
149: if (lResourceDescriptors == null)
150: mResourceNamesMap.put(lResourceName,
151: lResourceDescriptors = new ArrayList());
152: ResourceDescriptor lResourceDescriptor = new ResourceDescriptor();
153: lResourceDescriptor.mJarPath = lJarPath;
154: lResourceDescriptor.mContentPath = lResourceName;
155: try {
156: lResourceDescriptor.mResourceURL = new URL(lJarURL
157: .toString()
158: + "#JarEntry="
159: + ByteUtils.bytesToText(lEntry.getName()
160: .getBytes()));
161: } catch (MalformedURLException e) {
162: // Ignore as we are trying to fake it
163: }
164: lResourceDescriptor.mBinaryContent = lBinaryContent;
165: lResourceDescriptors.add(lResourceDescriptor);
166: // See if we need to store the resource in the java class maps
167: if (lResourceName.toLowerCase().endsWith(".class")) {
168: // Prepare java style class name - strip .class from the end
169: String lClassName = lResourceName.substring(0,
170: lResourceName.length() - 6);
171: // Ensure that all slashes (back and forward) are converted to dots
172: lClassName = StringUtils.replace(lClassName, "/",
173: ".");
174: mClassesMap.put(lClassName, lResourceDescriptor);
175: }
176: }
177: }
178: }
179:
180: /** Returns true if this classloader has the specified class */
181: public boolean hasClass(String pClassName) {
182: return mClassesMap.containsKey(pClassName);
183: }
184:
185: /** This method is a part of ClassLoader mechanism.
186: * Overridden here to load classes from stored ones */
187: public Class findClass(String pClassName)
188: throws ClassNotFoundException {
189: // construct the name of the class file to look for
190: ResourceDescriptor lResourceDescriptor = (ResourceDescriptor) mClassesMap
191: .get(pClassName);
192: if (lResourceDescriptor != null)
193: return defineClass(pClassName,
194: lResourceDescriptor.mBinaryContent, 0,
195: lResourceDescriptor.mBinaryContent.length);
196: throw new ClassNotFoundException(pClassName);
197: }
198:
199: protected URL findResource(String pResourceName) {
200: // Supply first resource
201: List lResourceDescriptors = (List) mResourceNamesMap
202: .get(pResourceName);
203: if (lResourceDescriptors != null
204: && lResourceDescriptors.size() > 0) {
205: ResourceDescriptor lResourceDescriptor = (ResourceDescriptor) lResourceDescriptors
206: .get(0);
207: return lResourceDescriptor.mResourceURL;
208: }
209: return null;
210: }
211:
212: protected Enumeration findResources(String pResourceName)
213: throws IOException {
214: // Supply all resources
215: List lURLs = new ArrayList();
216: List lResourceDescriptors = (List) mResourceNamesMap
217: .get(pResourceName);
218: if (lResourceDescriptors != null
219: && lResourceDescriptors.size() > 0) {
220: Iterator lDescriptorsIter = lResourceDescriptors.iterator();
221: while (lDescriptorsIter.hasNext()) {
222: ResourceDescriptor lResourceDescriptor = (ResourceDescriptor) lDescriptorsIter
223: .next();
224: lURLs.add(lResourceDescriptor.mResourceURL);
225: }
226: }
227: Enumeration lOtherResourcePaths = super
228: .findResources(pResourceName);
229: while (lOtherResourcePaths.hasMoreElements())
230: lURLs.add(lOtherResourcePaths.nextElement());
231: return Collections.enumeration(lURLs);
232: }
233:
234: public InputStream getResourceAsStream(String pResourceName) {
235: List lResourceDescriptors = (List) mResourceNamesMap
236: .get(pResourceName);
237: if (lResourceDescriptors != null
238: && lResourceDescriptors.size() > 0) {
239: ResourceDescriptor lResourceDescriptor = (ResourceDescriptor) lResourceDescriptors
240: .get(0);
241: return new ByteArrayInputStream(
242: lResourceDescriptor.mBinaryContent);
243: }
244: ClassLoader lParent = getParent();
245: if (lParent == null)
246: lParent = getSystemClassLoader();
247: return lParent.getResourceAsStream(pResourceName);
248: }
249:
250: /**
251: * Load the class with the specified name, searching using the following
252: * algorithm until it finds and returns the class. If the class cannot
253: * be found, returns <code>ClassNotFoundException</code>.
254: * <ul>
255: * <li>Call <code>findLoadedClass(String)</code> to check if the
256: * class has already been loaded. If it has, the same
257: * <code>Class</code> object is returned.</li>
258: * <li>If the <code>delegate</code> property is set to <code>true</code>,
259: * call the <code>loadClass()</code> method of the parent class
260: * loader, if any.</li>
261: * <li>Call <code>findClass()</code> to find this class in our locally
262: * defined repositories.</li>
263: * <li>Call the <code>loadClass()</code> method of our parent
264: * class loader, if any.</li>
265: * </ul>
266: * If the class was found using the above steps, and the
267: * <code>resolve</code> flag is <code>true</code>, this method will then
268: * call <code>resolveClass(Class)</code> on the resulting Class object.
269: *
270: * @param name Name of the class to be loaded
271: * @param resolve If <code>true</code> then resolve the class
272: *
273: * @exception ClassNotFoundException if the class was not found
274: */
275: // public Class loadClass(String pClassName) throws ClassNotFoundException
276: // {
277: // Class lClass = (Class)mAlreadyLoadedClasses.get(pClassName);
278: // if (lClass != null)
279: // return lClass;
280: // // Try loading the class with the system class loader, to prevent from overriding J2SE classes
281: // try
282: // {
283: // if ((lClass = getSystemClassLoader().loadClass(pClassName)) != null)
284: // {
285: // mAlreadyLoadedClasses.put(pClassName,lClass);
286: // return lClass;
287: // }
288: // }
289: // catch (ClassNotFoundException e)
290: // {
291: // // Ignore
292: // }
293: //
294: //// // (1) Delegate to our parent if requested
295: //// if (delegate) {
296: //// if (debug >= 3)
297: //// log(" Delegating to parent classloader");
298: //// ClassLoader loader = parent;
299: //// if (loader == null)
300: //// loader = system;
301: //// try {
302: //// clazz = loader.loadClass(name);
303: //// if (clazz != null) {
304: //// if (debug >= 3)
305: //// log(" Loading class from parent");
306: //// if (resolve)
307: //// resolveClass(clazz);
308: //// return (clazz);
309: //// }
310: //// } catch (ClassNotFoundException e) {
311: //// ;
312: //// }
313: //// }
314: //
315: // // (2) Search local repositories
316: // try
317: // {
318: // if ((lClass = findClass(pClassName)) != null)
319: // {
320: // mAlreadyLoadedClasses.put(pClassName,lClass);
321: // return lClass;
322: // }
323: // }
324: // catch (ClassNotFoundException e)
325: // {
326: // // Ignore
327: // }
328: //
329: // // (3) Delegate to parent
330: // ClassLoader lParentClassLoader = getParent();
331: // if (lParentClassLoader == null)
332: // lParentClassLoader = getSystemClassLoader();
333: // try
334: // {
335: // if ((lClass = lParentClassLoader.loadClass(pClassName)) != null)
336: // {
337: // mAlreadyLoadedClasses.put(pClassName,lClass);
338: // return lClass;
339: // }
340: // }
341: // catch (ClassNotFoundException e)
342: // {
343: // // Ignore
344: // }
345: // // This class was not found
346: // throw new ClassNotFoundException(pClassName);
347: // }
348: }
|