/Users/lyon/j4p/src/javagroup/util/URLClassLoader.java

1    /* 
2     * Copyright (C) 1997, 1998 Luke Gorrie 
3     * 
4     * This library is free software; you can redistribute it and/or 
5     * modify it under the terms of the GNU Library General Public 
6     * License as published by the Free Software Foundation; either 
7     * version 2 of the License, or (at your option) any later version. 
8     * 
9     * This library is distributed in the hope that it will be useful, 
10    * but WITHOUT ANY WARRANTY; without even the implied warranty of 
11    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
12    * Library General Public License for more details. 
13    * 
14    * You should have received a copy of the GNU Library General Public 
15    * License along with this library; if not, write to the 
16    * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
17    * MA 02139, USA. 
18    */ 
19    
20   package javagroup.util; 
21    
22   import java.io.*; 
23   import java.net.MalformedURLException; 
24   import java.net.URL; 
25   import java.util.Enumeration; 
26   import java.util.Hashtable; 
27   import java.util.StringTokenizer; 
28   import java.util.Vector; 
29    
30   /** 
31    * Generic classloader for URL-based classpaths. 
32    * <p/> 
33    * TODO: add archive support, and rewrite as this is currently a hack 
34    * 
35    * @author Luke Gorrie 
36    */ 
37   public class URLClassLoader extends ClassLoader { 
38    
39       protected Vector _urlClassPath; 
40       protected Hashtable _classCache; 
41    
42       /** 
43        * Creates a ClassLoader with no classpath. 
44        */ 
45       public URLClassLoader() { 
46           _urlClassPath = new Vector(); 
47           _classCache = new Hashtable(); 
48       } 
49    
50       /** 
51        * Creates a ClassLoader with a single URL for a classpath. 
52        * 
53        * @param classpath The base URL to search for classes relative to. 
54        */ 
55       public URLClassLoader(URL classpath) { 
56           this(); 
57           addClassPath(classpath); 
58       } 
59    
60       /** 
61        * Creates a ClassLoader with a list of classpath URLs. 
62        * 
63        * @param classpath The URLs to search for classes relative to. 
64        */ 
65       public URLClassLoader(URL[] classpath) { 
66           this(); 
67           addClassPath(classpath); 
68       } 
69    
70       public void addClassPath(URL[] classpath) { 
71           for (int i = 0; i < classpath.length; i++) 
72               addClassPath(classpath[i]); 
73       } 
74    
75       /** 
76        * Add a URL to the classpath.  This URL is searched for for classes. 
77        * 
78        * @param classpath The base URL to search. 
79        */ 
80       public void addClassPath(URL classpath) { 
81           _urlClassPath.addElement(classpath); 
82       } 
83    
84       public Class loadMainClass(String name) 
85               throws ClassNotFoundException { 
86           return loadClass(name, true, true); 
87       } 
88    
89       public Class loadClass(String name, boolean resolve) 
90               throws ClassNotFoundException { 
91    
92           return loadClass(name, resolve, false); 
93       } 
94    
95       public Class loadClass(String name, 
96                              boolean resolve, 
97                              boolean mainClass) 
98               throws ClassNotFoundException { 
99    
100          Class klass = null; 
101   
102          try { 
103   
104              // give the vm a chance to find it in the classpath 
105              try { 
106                  klass = findSystemClass(name); 
107                  if (klass != null) 
108                      return klass; 
109              } catch (ClassNotFoundException e) { 
110              } 
111   
112              // check cache 
113              klass = (Class) _classCache.get(name); 
114              if (klass != null) { 
115                  return klass; 
116              } 
117   
118              // read from custom classpath 
119              byte[] data = readClassFile(name); 
120              if (data != null) { 
121                  if (mainClass) { 
122                      forcePublic(data); 
123                  } 
124                  klass = defineClass(name, data, 0, data.length); 
125              } 
126   
127              if (klass == null) 
128                  throw new ClassNotFoundException( 
129                          "Class not found: " + name); 
130   
131              _classCache.put(name, klass); 
132   
133              return klass; 
134   
135          } finally { 
136              // if the class was found, and is to be resolved, resolve 
137              if ((klass != null) && resolve) 
138                  resolveClass(klass); 
139          } 
140      } 
141   
142      /** 
143       * Try to read the byte[] data for a class file from the classpath. 
144       * 
145       * @param classname The fully-qualified name of the class. 
146       * @return A byte[] containing the classfile data, or null if not 
147       *         found. 
148       */ 
149      protected byte[] readClassFile(String classname) { 
150          classname = classname.replace('.', '/') + ".class"; 
151          return readFile(classname); 
152      } 
153   
154      public InputStream getResourceAsStream(String name) { 
155          byte[] data = readFile(name); 
156          return data == null ? null : new ByteArrayInputStream(data); 
157      } 
158   
159      // bit of a hack. :-) 
160      public URL getResource(String name) { 
161          URL path = null; 
162          Enumeration classpath = _urlClassPath.elements(); 
163          while (classpath.hasMoreElements()) { 
164              URL base_path = (URL) classpath.nextElement(); 
165              try { 
166                  path = new URL(base_path, name); 
167                  return path; 
168              } catch (IOException e) { 
169                  // file not accessible or doesn't exist 
170              } 
171          } 
172          return null; 
173      } 
174   
175      protected byte[] readFile(String name) { 
176   
177          // enumeration of base-urls to try 
178          Enumeration classpath = _urlClassPath.elements(); 
179          byte[] data = null; 
180   
181          // loop until a class is read, or there are no more paths to try 
182          while ((data == null) && (classpath.hasMoreElements())) { 
183   
184              // pop a path to try 
185              URL base_path = (URL) classpath.nextElement(); 
186   
187              try { 
188   
189                  // create a fully qualified class url 
190                  URL path = new URL(base_path, name); 
191   
192                  // io streams 
193                  ByteArrayOutputStream out_buffer = new ByteArrayOutputStream(); 
194                  InputStream in = new BufferedInputStream( 
195                          path.openStream()); 
196   
197                  // read into buffer 
198                  int octet; 
199                  while ((octet = in.read()) != -1) 
200                      out_buffer.write(octet); 
201   
202                  // pull class data out of buffer 
203                  data = out_buffer.toByteArray(); 
204   
205              } catch (IOException e) { 
206                  // class not found in that path 
207              } 
208   
209          } 
210   
211          // null if class not found 
212          return data; 
213   
214      } 
215   
216      /** 
217       * Converts a path string into an array of URLs. eg. "foo:http://bar/" 
218       * would become a 2-URL array, with "file:///foo/" and "http://bar/". 
219       * 
220       * @param classpath The path to decode, entries delimited by the 
221       *                  appropriate charactor for the platform. 
222       */ 
223      public static URL[] decodePathString(String classpath) { 
224   
225          // create a file:/// as a base URL.  This was "foo" will be seen as 
226          // file:///foo 
227          URL base_url = null; 
228          try { 
229              base_url = new URL("file:/"); 
230          } catch (MalformedURLException e) { 
231          } 
232   
233          // vector to store URLs in temporarily 
234          Vector classpath_urls = new Vector(); 
235   
236          // if a base url is provided and the classpath is non-null 
237          if ((base_url != null) && (classpath != null)) { 
238   
239              // tokenize path 
240              StringTokenizer tok = new StringTokenizer(classpath, ","); 
241              while (tok.hasMoreTokens()) { 
242                  String path = tok.nextToken(); 
243                  // create URL for path entry 
244   
245                  URL path_url = null; 
246                  try { 
247                      path_url = new URL(path); 
248                  } catch (MalformedURLException e) { 
249                      try { 
250                          path_url = new URL(base_url, path); 
251                      } catch (Exception e2) { 
252                      } 
253                  } 
254   
255                  if (path_url != null) 
256                      classpath_urls.addElement(path_url); 
257              } 
258          } 
259   
260          URL[] paths = null; 
261   
262          // if there are any valid classpath entries 
263          if (!classpath_urls.isEmpty()) { 
264              // convert the vector into a URL array 
265              paths = new URL[classpath_urls.size()]; 
266              for (int i = 0; i < paths.length; i++) 
267                  paths[i] = (URL) classpath_urls.elementAt(i); 
268          } 
269   
270          // return path url array 
271          return paths; 
272   
273      } 
274   
275      /** 
276       * Take a byte array of class data and modify it to ensure that the 
277       * class is public. This is used for the "main" classes of 
278       * applications. 
279       */ 
280      public void forcePublic(byte[] theClass) { 
281          int constant_pool_count = ((theClass[8] & 0xff) << 8) 
282                  | (theClass[9] & 0xff); 
283   
284          int currOffset = 10; 
285   
286          // seek through everything in the way of the access modifiers 
287          for (int i = 1; i < constant_pool_count; i++) { 
288              switch (theClass[currOffset] & 0xff) { 
289                  case 7: 
290                  case 8:  // CONSTANT_Class, CONSTANT_String 
291                      currOffset += 3; 
292                      break; 
293                  case 9: 
294                  case 10: 
295                  case 11: 
296                  case 12: 
297                  case 3: 
298                  case 4:  // CONSTANT_Fieldref, CONSTANT_Methodref 
299                      currOffset += 5;       // CONSTANT_InterfaceMethodref, CONSTANT_NameAndType 
300                      break;                 // CONSTANT_Integer, CONSTANT_Float 
301                  case 5: 
302                  case 6:  // CONSTANT_Long, CONSTANT_Double 
303                      currOffset += 9; 
304                      i++; 
305                      break; 
306                  case 1: 
307                      int length = ((theClass[++currOffset] & 0xff) << 8) 
308                              | (theClass[++currOffset] & 0xff); 
309                      currOffset += length + 1; 
310                      break; 
311                  default: 
312                      return; 
313              } 
314          } 
315   
316          // add PUBLIC flag 
317          theClass[currOffset + 1] |= 1; 
318      } 
319   
320  } 
321   
322