/Users/lyon/j4p/src/javagroup/util/StandardLoader.java
|
1 package javagroup.util;
2
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.IOException;
6 import java.util.StringTokenizer;
7 import java.util.zip.ZipEntry;
8 import java.util.zip.ZipFile;
9
10 /**
11 * This is a basic ClassLoader for loading files from directories on the
12 * local filesystem either in directories or zipfiles.
13 *
14 * @author Luke Gorrie
15 */
16 public class StandardLoader extends ClassLoader {
17
18 /**
19 * Classpath to search for class files.
20 */
21 protected Object[] _classPath;
22
23 /**
24 * Constructs a new StandardLoader with the specified classpath. The
25 * classpath can contain directories and/or zip-files.
26 *
27 * @param classPath The classpath to use for this loader.
28 */
29 public StandardLoader(String classPath) {
30
31 super();
32
33 // each element of the classpath is delimited by the current operating system's path separator
34 StringTokenizer st = new StringTokenizer(classPath,
35 System.getProperty("path.separator"));
36
37 // store the individual elements in an array to speed things up during loading
38 _classPath = new Object[st.countTokens()];
39 int index = 0;
40 while (st.hasMoreTokens())
41 _classPath[index++] = initPath(st.nextToken());
42 }
43
44 /**
45 * Initializes a path entry. Directories and zip files are supported.
46 *
47 * @param name Path name of the entry.
48 * @return An Object representing a classpath entry.
49 */
50 protected Object initPath(String name) {
51 // check if this entry is a zip-file
52 if (name.substring(name.length() - ".zip".length())
53 .equalsIgnoreCase(".zip"))
54 return initZipFile(name);
55 // if not, assume it is a directory
56 else
57 return initDirectory(name);
58 }
59
60 /**
61 * Initialize a directory accessible on the local filesystem.
62 *
63 * @param name Name of the directory.
64 * @return A File representing the directory.
65 */
66 protected File initDirectory(String name) {
67 try {
68 // open directory
69 File dir = new File(name);
70
71 // ensure a directory was opened.
72 if (!dir.isDirectory())
73 throw new IOException();
74
75 // return the directory
76 return dir;
77 } catch (IOException ioe) {
78 // return null, directory did not exist.
79 return null;
80 }
81 }
82
83 /**
84 * Initialize a zip-file accessible on the local filesystem.
85 *
86 * @param name Name of the Zip-file
87 * @return A ZipFile representing the Zip classpath entry.
88 */
89 public ZipFile initZipFile(String name) {
90
91 try {
92 // open and return the requested zipfile
93 ZipFile zip = new ZipFile(name);
94 return zip;
95 } catch (IOException ioe) {
96 // null if the file could not be found
97 return null;
98 }
99
100 }
101
102 /**
103 * Load a class from from available locations.
104 */
105 public Class loadClass(String name, boolean resolve)
106 throws ClassNotFoundException {
107
108 Class clazz = null;
109
110 // check if the class can be found in the system classpath
111 try {
112 clazz = findSystemClass(name);
113 } catch (ClassNotFoundException cnfe) {
114 }
115 // if not, check if it is preloaded by this classloader.
116 if (clazz == null) {
117 clazz = findLoadedClass(name);
118 }
119 // if the class is still not found, try to custom-load it
120 if (clazz == null) {
121
122 // array to store class data in
123 byte[] class_data = null;
124
125 // try each classpath entry
126 for (int i = 0; i < _classPath.length; i++) {
127
128 // if this entry failed to initialize, skip it
129 if (_classPath[i] != null) {
130 try {
131 // custom-load for this classpath entry
132 class_data = loadClassDef(_classPath[i], name);
133 } catch (IOException e) {
134 }
135 // if the load was successful, break out
136 if (class_data != null)
137 break;
138 }
139 }
140 // if the class data was loaded, define the class
141 if (class_data != null) {
142 clazz = defineClass(name,class_data, 0, class_data.length);
143 if (resolve)
144 resolveClass(clazz);
145
146 // success
147 return clazz;
148 }
149
150 }
151
152 // failure
153 throw new ClassNotFoundException("Cannot load class: " + name);
154 }
155
156 /**
157 * Reads a byte[] from the given path.
158 *
159 * @param from The path to load from.
160 * @param name The name of the class to load.
161 * @return A byte[] containing the class data.
162 */
163 public byte[] loadClassDef(Object from, String name)
164 throws IOException {
165
166 // byte array to store class data in
167 byte[] data = null;
168
169 // turn the classname into a filename
170 name = name.replace('.', File.separatorChar) + ".class";
171
172 // check if this entry is a Directory
173 if ((from instanceof File) && ((File) from).isDirectory()) {
174 File from_file = (File) from;
175
176 // open file
177 File class_file = new File(from_file, name);
178
179 // init array and read class data
180 data = new byte[(int) class_file.length()];
181 new FileInputStream(class_file).read(data);
182
183 // success
184 return data;
185 }
186
187 // check if this entry is a zip-file
188 if (from instanceof ZipFile) {
189 ZipFile zip = (ZipFile) from;
190
191 // find entry in zip for class
192 ZipEntry class_entry = zip.getEntry(name);
193
194 // ensure entry exists
195 if (class_entry == null)
196 throw new IOException("Entry doesn't exist");
197
198 // initalize and read data
199 data = new byte[(int) class_entry.getSize()];
200 zip.getInputStream(class_entry).read(data);
201
202 // success
203 return data;
204 }
205
206 // failure
207 return null;
208
209 }
210
211 }
212
213