001: /*--
002:
003: Copyright (C) 2000-2003 Anthony Eden.
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009:
010: 1. Redistributions of source code must retain the above copyright
011: notice, this list of conditions, and the following disclaimer.
012:
013: 2. Redistributions in binary form must reproduce the above copyright
014: notice, this list of conditions, and the disclaimer that follows
015: these conditions in the documentation and/or other materials
016: provided with the distribution.
017:
018: 3. The name "EdenLib" must not be used to endorse or promote products
019: derived from this software without prior written permission. For
020: written permission, please contact me@anthonyeden.com.
021:
022: 4. Products derived from this software may not be called "EdenLib", nor
023: may "EdenLib" appear in their name, without prior written permission
024: from Anthony Eden (me@anthonyeden.com).
025:
026: In addition, I request (but do not require) that you include in the
027: end-user documentation provided with the redistribution and/or in the
028: software itself an acknowledgement equivalent to the following:
029: "This product includes software developed by
030: Anthony Eden (http://www.anthonyeden.com/)."
031:
032: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
033: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
034: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
035: DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
036: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
037: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
038: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
039: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
040: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
041: IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
042: POSSIBILITY OF SUCH DAMAGE.
043:
044: For more information on EdenLib, please see <http://edenlib.sf.net/>.
045:
046: */
047:
048: package com.anthonyeden.lib.util;
049:
050: import java.io.File;
051: import java.io.InputStream;
052: import java.io.IOException;
053: import java.net.URL;
054: import java.net.MalformedURLException;
055: import java.util.ArrayList;
056: import java.util.zip.ZipFile;
057: import java.util.zip.ZipEntry;
058:
059: import org.apache.commons.logging.Log;
060: import org.apache.commons.logging.LogFactory;
061:
062: public class CustomClassLoader extends ClassLoader {
063:
064: private static final Log log = LogFactory
065: .getLog(CustomClassLoader.class);
066:
067: private File searchDirectory;
068:
069: /** Construct a new CustomClassLoader which searches for classes in the
070: given directory.
071:
072: @param searchDirectory The root directory where classes may be found
073: */
074:
075: public CustomClassLoader(String searchDirectory) {
076: this (new File(searchDirectory));
077: }
078:
079: /** Construct a new CustomClassLoader which searches for classes in the parent
080: ClassLoader first and then in the given directory.
081:
082: @param searchDirectory The root directory where classes may be found
083: @param parent The parent ClassLoader
084: */
085:
086: public CustomClassLoader(String searchDirectory, ClassLoader parent) {
087: this (new File(searchDirectory), parent);
088: }
089:
090: /** Construct a new CustomClassLoader which searches for classes in the
091: given directory.
092:
093: @param searchDirectory The root directory where classes may be found
094: */
095:
096: public CustomClassLoader(File searchDirectory) {
097: super ();
098: this .searchDirectory = searchDirectory;
099: }
100:
101: /** Construct a new CustomClassLoader which searches for classes in the parent
102: ClassLoader first and then in the given directory.
103:
104: @param searchDirectory The root directory where classes may be found
105: @param parent The parent ClassLoader
106: */
107:
108: public CustomClassLoader(File searchDirectory, ClassLoader parent) {
109: super (parent);
110: this .searchDirectory = searchDirectory;
111: }
112:
113: /** Get the directory which is searched for class files.
114:
115: @return The search directory
116: */
117:
118: public File getSearchDirectory() {
119: return searchDirectory;
120: }
121:
122: /** Set the directory which is searched for class files.
123:
124: @param searchDirectory The new search directory
125: */
126:
127: public void setSearchDirectory(File searchDirectory) {
128: this .searchDirectory = searchDirectory;
129: }
130:
131: /** Find the class with the given class name.
132:
133: @param name The class name
134: @return the Class object
135: @throws ClassNotFoundException
136: */
137:
138: public Class findClass(String name) throws ClassNotFoundException {
139: byte[] b = loadClassData(name);
140: return defineClass(name, b, 0, b.length);
141: }
142:
143: /** Find the resource URL with the given name. If the resource URL
144: cannot be found then this method returns null.
145:
146: @param name The resource name
147: @return The URL
148: */
149:
150: public URL findResource(String name) {
151: File file = findFile(name);
152: if (file == null) {
153: return null;
154: }
155:
156: try {
157: String urlString = "jar:" + file.toURL() + "!/" + name;
158: log.debug("URL: " + urlString);
159:
160: return new URL(urlString);
161: } catch (MalformedURLException e) {
162: log.error("Malformed URL error: " + e.getMessage());
163: return null;
164: }
165: }
166:
167: /** Load the class data for the given name.
168:
169: @param name The class name
170: @return The class data as a byte array
171: @throws ClassNotFoundException
172: */
173:
174: private byte[] loadClassData(String name)
175: throws ClassNotFoundException {
176: log.debug("Loading class data for " + name);
177:
178: String path = name.replace('.', '/') + ".class";
179:
180: byte[] data = loadData(path);
181: if (data == null) {
182: throw new ClassNotFoundException("Cannot find class: "
183: + name);
184: }
185:
186: return data;
187: }
188:
189: /** Find the ZIP or JAR file which contains the given path.
190:
191: @param path The path
192: @return The FIle
193: */
194:
195: private File findFile(String path) {
196: File[] files = searchDirectory.listFiles();
197: for (int i = 0; i < files.length; i++) {
198: if (files[i].getName().endsWith(".jar")
199: || files[i].getName().endsWith(".zip")) {
200: //log.debug("Looking for data in " + files[i]);
201:
202: try {
203: ZipFile zipFile = new ZipFile(files[i]);
204: ZipEntry zipEntry = zipFile.getEntry(path);
205:
206: if (zipEntry != null) {
207: return files[i];
208: }
209: } catch (IOException e) {
210: log.error("IO error: " + e.getMessage());
211: }
212: }
213: }
214: return null;
215: }
216:
217: /** Load the data from the given path. This method first tries to find
218: the path in any JAR or ZIP files in the specified directory. If a JAR
219: or ZIP file is found then the data is read from the file and returned
220: as a byte array.
221:
222: @param path The path
223: @return The byte array of data
224: */
225:
226: private byte[] loadData(String path) {
227: File file = findFile(path);
228: if (file == null) {
229: return null;
230: }
231:
232: try {
233: ZipFile zipFile = new ZipFile(file);
234: ZipEntry zipEntry = zipFile.getEntry(path);
235:
236: long dataSize = zipEntry.getSize();
237: log.debug("ZIP entry size: " + dataSize);
238:
239: ArrayList dataList = new ArrayList();
240: InputStream in = zipFile.getInputStream(zipEntry);
241: int b = -1;
242: while ((b = in.read()) != -1) {
243: dataList.add(new Byte((byte) b));
244: }
245:
246: byte[] data = new byte[dataList.size()];
247: for (int j = 0; j < data.length; j++) {
248: data[j] = ((Byte) dataList.get(j)).byteValue();
249: }
250:
251: return data;
252: } catch (IOException e) {
253: log.error("IO error: " + e.getMessage());
254: }
255:
256: return null;
257: }
258:
259: }
|