1 package com.explosion.utilities.classes;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import java.io.DataInputStream;
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.security.SecureClassLoader;
28 import java.util.Enumeration;
29 import java.util.HashMap;
30 import java.util.jar.JarEntry;
31 import java.util.jar.JarFile;
32 import java.util.jar.Manifest;
33
34 import org.apache.log4j.LogManager;
35 import org.apache.log4j.Logger;
36
37 import com.explosion.utilities.exception.ExceptionManagerFactory;
38
39 public class JarFileClassLoader extends SecureClassLoader
40 {
41
42 private JarFile jarFile;
43
44 private Manifest manifest;
45
46 private HashMap classNameIndex = new HashMap();
47
48 private HashMap loadedClasses = new HashMap();
49
50 private static Logger log = LogManager.getLogger(JarFileClassLoader.class);
51
52 public JarFileClassLoader(ClassLoader parent, String jarFilePath) throws IllegalArgumentException, IOException
53 {
54 super(parent);
55 log.debug("Constructing new JarFileCLassLoader");
56 if (jarFilePath == null)
57 throw new IllegalArgumentException("Null jarfile path");
58
59 File file = new File(jarFilePath);
60 if (!file.exists())
61 throw new IllegalArgumentException("File '" + jarFilePath + "' does not exist.");
62
63 if (!file.exists())
64 throw new IllegalArgumentException("Path '" + jarFilePath + "' points to a directory and not a jar file.");
65
66 this.jarFile = new JarFile(jarFilePath);
67
68 manifest = jarFile.getManifest();
69
70 buildIndex(jarFilePath);
71 }
72
73 /***
74 * This method builds an index of entries in the jarfile
75 *
76 * @param jarFile
77 * @throws Exception
78 */
79 private void buildIndex(String jarFile) throws IOException
80 {
81 log.debug("Building the entry name index");
82 if (jarFile != null)
83 {
84 JarFile file = new JarFile(jarFile);
85 Enumeration en = file.entries();
86 while (en.hasMoreElements())
87 {
88 JarEntry entry = (JarEntry) en.nextElement();
89 String name = entry.getName();
90
91 int index = name.lastIndexOf(".");
92
93 if (index < 0)
94 index = name.length();
95
96 String key = name.substring(0, index);
97 log.debug("Indexing: Entry " + name + " Key derived " + key);
98
99 if (classNameIndex.get(key) != null)
100 throw new IOException("Duplicate key '" + key + "' found in jar file " + jarFile);
101 else
102 classNameIndex.put(key, entry.getName());
103 }
104
105 }
106 }
107
108 /***
109 * This method loads a class
110 *
111 */
112 public Class findClass(String name) throws ClassNotFoundException
113 {
114 try
115 {
116 log.debug("Looking for "+name+" in the store");
117
118 Class c = (Class) loadedClasses.get(name);
119 if (c != null)
120 return c;
121
122 log.debug("Not found in the store, .loading from disk");
123
124 byte data[] = null;
125 data = loadClassData(name);
126
127 if (data == null)
128 throw new ClassNotFoundException(name);
129
130 log.debug("Found on disk");
131
132 try
133 {
134
135 c = defineClass(name, data, 0, data.length);
136 log.debug("Defined "+name+".");
137
138
139 log.debug("Inserting "+name+" into store.");
140 loadedClasses.put(name, c);
141
142 } catch (NoClassDefFoundError cnfe)
143 {
144 c = null;
145 }
146
147
148 if (c == null) { throw new ClassNotFoundException(name); }
149
150 return c;
151 } catch (IOException e)
152 {
153 throw new ClassNotFoundException("Error reading file: " + name);
154 }
155
156 }
157
158 /***
159 * Looks up the entryname in the index and then loads the class
160 *
161 * @param name
162 * @param jarFile
163 * @return @throws IOException
164 */
165 private byte[] loadClassData(String name) throws IOException
166 {
167 log.debug("loadClassData() Loading: " + name);
168 JarEntry entry = getEntry(name, jarFile);
169 if (entry != null)
170 {
171 byte buff[] = new byte[(int) entry.getSize()];
172 DataInputStream dis = new DataInputStream(jarFile.getInputStream(entry));
173 try
174 {
175 dis.readFully(buff);
176 } finally
177 {
178 try
179 {
180 dis.close();
181 } catch (Exception e)
182 {
183 }
184 }
185 return buff;
186 }
187
188 return null;
189 }
190
191 /***
192 * @see java.lang.ClassLoader#getResourceAsStream(java.lang.String)
193 * @param name
194 * @return
195 */
196 public InputStream getResourceAsStream(String name)
197 {
198 log.debug("getResourceAsStream() getting: " + name);
199 JarEntry entry = getEntry(name, jarFile);
200 if (entry != null)
201 {
202 try
203 {
204 return jarFile.getInputStream(entry);
205 } catch (IOException e)
206 {
207 ExceptionManagerFactory.getExceptionManager().manageException(e, "Exception caught while obtaingin resource.");
208 }
209 }
210
211 return null;
212 }
213
214 /***
215 * This method looks up a particular entry in the jar file. It performs
216 * string manipulation on the name to convert "." to "/" and other things
217 *
218 * @param name
219 * @param file
220 * @return
221 */
222 private JarEntry getEntry(String name, JarFile file)
223 {
224 log.debug("getEntry getting entry " + name);
225 String entryName = (String) classNameIndex.get(name.replace('.', '/'));
226 if (entryName == null && File.separatorChar != '/')
227 entryName = (String) classNameIndex.get(name.replace('.', File.separatorChar));
228
229 if (entryName == null)
230 log.debug("Name " + name + " not found in index.");
231 else
232 log.debug("Name " + name + " was found in index. Using corresponding lookup entry '" + entryName + "'.");
233
234 return file.getJarEntry(entryName);
235 }
236 }