Source code: edu/emory/mathcs/util/classloader/GenericClassLoader.java
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is the Emory Utilities.
15 *
16 * The Initial Developer of the Original Code is
17 * The Distributed Computing Laboratory, Emory University.
18 * Portions created by the Initial Developer are Copyright (C) 2002
19 * the Initial Developer. All Rights Reserved.
20 *
21 * Alternatively, the contents of this file may be used under the terms of
22 * either the GNU General Public License Version 2 or later (the "GPL"), or
23 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
24 * in which case the provisions of the GPL or the LGPL are applicable instead
25 * of those above. If you wish to allow use of your version of this file only
26 * under the terms of either the GPL or the LGPL, and not to allow others to
27 * use your version of this file under the terms of the MPL, indicate your
28 * decision by deleting the provisions above and replace them with the notice
29 * and other provisions required by the GPL or the LGPL. If you do not delete
30 * the provisions above, a recipient may use your version of this file under
31 * the terms of any one of the MPL, the GPL or the LGPL.
32 *
33 * ***** END LICENSE BLOCK ***** */
34
35 package edu.emory.mathcs.util.classloader;
36
37 import java.io.*;
38 import java.net.*;
39 import java.security.*;
40 import java.util.*;
41 import java.util.jar.*;
42 import java.util.jar.Attributes.Name;
43 import edu.emory.mathcs.util.security.action.*;
44
45 /**
46 * This class loader can be used to find class, resource and library
47 * {@link ResourceHandle handles}
48 * as well as load classes, resources and libraries using abstract
49 * {@link ResourceFinder} entity encapsulating the searching approach.
50 * Resource handles allow accessing meta-information (like Attributes,
51 * Certificates etc.) related to classes, resources and libraries prior to
52 * loading them.
53 * <p>
54 * GenericClassLoader is intended to be used as a base for custom class
55 * loaders. In most applications, GenericClassLoader can be used directly --
56 * the application-specific functionality of resource searching can often be
57 * completely delegated to the resource finder. See {@link URIClassLoader}
58 * for a concrete implementation using a simple resource finder.
59 *
60 * @see ResourceFinder
61 * @see ResourceLoader
62 * @see ResourceHandle
63 *
64 * @author Dawid Kurzyniec
65 * @version 1.0
66 */
67 public class GenericClassLoader extends SecureClassLoader {
68
69 protected ResourceFinder finder;
70 private AccessControlContext acc;
71
72 /**
73 * Creates new GenericClassLoader instance using specified
74 * {@link ResourceFinder} to find resources and having specified
75 * parent class loader.
76 */
77 public GenericClassLoader(ResourceFinder finder, ClassLoader parent) {
78 super(parent);
79 SecurityManager security = System.getSecurityManager();
80 if (security != null) {
81 security.checkCreateClassLoader();
82 }
83 this.finder = finder;
84 acc = AccessController.getContext();
85 }
86
87 /**
88 * Creates new GenericClassLoader instance using specified
89 * {@link ResourceFinder} to find resources and with default
90 * parent class loader.
91 */
92 public GenericClassLoader(ResourceFinder finder) {
93 super();
94 // this is to make the stack depth consistent with 1.1
95 SecurityManager security = System.getSecurityManager();
96 if (security != null) {
97 security.checkCreateClassLoader();
98 }
99 this.finder = finder;
100 //acc = AccessController.getContext();
101 }
102
103 /**
104 * Finds and loads the class with the specified name.
105 *
106 * @param name the name of the class
107 * @return the resulting class
108 * @exception ClassNotFoundException if the class could not be found
109 */
110 protected Class findClass(final String name)
111 throws ClassNotFoundException
112 {
113 try {
114 return (Class)
115 AccessController.doPrivileged(new PrivilegedExceptionAction() {
116 public Object run() throws ClassNotFoundException {
117 String path = name.replace('.', '/').concat(".class");
118 ResourceHandle h = finder.getResource(path);
119 if (h != null) {
120 try {
121 return defineClass(name, h);
122 } catch (IOException e) {
123 throw new ClassNotFoundException(name, e);
124 }
125 } else {
126 throw new ClassNotFoundException(name);
127 }
128 }
129 }, acc);
130 } catch (java.security.PrivilegedActionException pae) {
131 throw (ClassNotFoundException)pae.getException();
132 }
133 }
134
135 protected Class defineClass(String name, ResourceHandle h) throws IOException {
136 int i = name.lastIndexOf('.');
137 URL url = h.getCodeSourceURL();
138 if (i != -1) { // check package
139 String pkgname = name.substring(0, i);
140 // check if package already loaded
141 Package pkg = getPackage(pkgname);
142 Manifest man = h.getManifest();
143 if (pkg != null) {
144 // package found, so check package sealing
145 boolean ok;
146 if (pkg.isSealed()) {
147 // verify that code source URLs are the same
148 ok = pkg.isSealed(url);
149 } else {
150 // make sure we are not attempting to seal the package
151 // at this code source URL
152 ok = (man == null) || !isSealed(pkgname, man);
153 }
154 if (!ok) {
155 throw new SecurityException("sealing violation: " + name);
156 }
157 } else { // package not yet defined
158 if (man != null) {
159 definePackage(pkgname, man, url);
160 } else {
161 definePackage(pkgname, null, null, null, null, null, null, null);
162 }
163 }
164 }
165
166 // now read the class bytes and define the class
167 byte[] b = h.getBytes();
168 java.security.cert.Certificate[] certs = h.getCertificates();
169 CodeSource cs = new CodeSource(url, certs);
170 return defineClass(name, b, 0, b.length, cs);
171 }
172
173 /**
174 * returns true if the specified package name is sealed according to the
175 * given manifest.
176 */
177 private boolean isSealed(String name, Manifest man) {
178 String path = name.replace('.', '/').concat("/");
179 Attributes attr = man.getAttributes(path);
180 String sealed = null;
181 if (attr != null) {
182 sealed = attr.getValue(Name.SEALED);
183 }
184 if (sealed == null) {
185 if ((attr = man.getMainAttributes()) != null) {
186 sealed = attr.getValue(Name.SEALED);
187 }
188 }
189 return "true".equalsIgnoreCase(sealed);
190 }
191
192 /**
193 * Finds the resource with the specified name.
194 *
195 * @param name the name of the resource
196 * @return a <code>URL</code> for the resource, or <code>null</code>
197 * if the resource could not be found.
198 */
199 protected URL findResource(final String name) {
200 return
201 (URL) AccessController.doPrivileged(new PrivilegedAction() {
202 public Object run() {
203 return finder.findResource(name);
204 }
205 }, acc);
206 }
207
208 /**
209 * Returns an Enumeration of URLs representing all of the resources
210 * having the specified name.
211 *
212 * @param name the resource name
213 * @exception IOException if an I/O exception occurs
214 * @return an <code>Enumeration</code> of <code>URL</code>s
215 */
216 protected Enumeration findResources(final String name) throws IOException {
217 return
218 (Enumeration) AccessController.doPrivileged(new PrivilegedAction() {
219 public Object run() {
220 return finder.findResources(name);
221 }
222 }, acc);
223 }
224
225 /**
226 * Returns the absolute path name of a native library. The VM
227 * invokes this method to locate the native libraries that belong
228 * to classes loaded with this class loader. If this method returns
229 * <code>null</code>, the VM searches the library along the path
230 * specified as the <code>java.library.path</code> property.
231 * This method invoke {@link #getLibraryHandle} method to find handle
232 * of this library. If the handle is found and its URL protocol is "file",
233 * the system-dependent absolute library file path is returned.
234 * Otherwise this method returns null. <p>
235 *
236 * Subclasses can override this method to provide specific approaches
237 * in library searching.
238 *
239 * @param libname the library name
240 * @return the absolute path of the native library
241 * @see java.lang.System#loadLibrary(java.lang.String)
242 * @see java.lang.System#mapLibraryName(java.lang.String)
243 */
244 protected String findLibrary(String libname) {
245 ResourceHandle md = getLibraryHandle(libname);
246 if (md == null) return null;
247 URL url = md.getURL();
248 if (!"file".equals(url.getProtocol())) return null;
249 return new File(URI.create(url.toString())).getPath();
250 }
251 //
252 // /**
253 // * Gets the ResourceHandle object for the specified loaded class.
254 // *
255 // * @param clazz the Class
256 // * @return the ResourceHandle of the Class
257 // */
258 // protected ResourceHandle getClassHandle(Class clazz)
259 // {
260 // return null;
261 // }
262
263 /**
264 * Finds the ResourceHandle object for the class with the specified name.
265 * Unlike <code>findClass()</code>, this method does not load the class.
266 *
267 * @param name the name of the class
268 * @return the ResourceHandle of the class
269 */
270 protected ResourceHandle getClassHandle(final String name) {
271 String path = name.replace('.', '/').concat(".class");
272 return getResourceHandle(path);
273 }
274
275 /**
276 * Finds the ResourceHandle object for the resource with the specified name.
277 *
278 * @param name the name of the resource
279 * @return the ResourceHandle of the resource
280 */
281 protected ResourceHandle getResourceHandle(final String name)
282 {
283 return
284 (ResourceHandle) AccessController.doPrivileged(new PrivilegedAction() {
285 public Object run() {
286 return finder.getResource(name);
287 }
288 }, acc);
289 }
290
291 /**
292 * Finds the ResourceHandle object for the native library with the specified
293 * name.
294 * The library name must be '/'-separated path. The last part of this
295 * path is substituted by its system-dependent mapping (using
296 * {@link System#mapLibraryName(String)} method). Next, the
297 * <code>ResourceFinder</code> is used to look for the library as it
298 * was ordinary resource. <p>
299 *
300 * Subclasses can override this method to provide specific approaches
301 * in library searching.
302 *
303 * @param name the name of the library
304 * @return the ResourceHandle of the library
305 */
306 protected ResourceHandle getLibraryHandle(final String name) {
307 int idx = name.lastIndexOf('/');
308 String path;
309 String simplename;
310 if (idx == -1) {
311 path = "";
312 simplename = name;
313 } else if (idx == name.length()-1) { // name.endsWith('/')
314 throw new IllegalArgumentException(name);
315 } else {
316 path = name.substring(0, idx+1); // including '/'
317 simplename = name.substring(idx+1);
318 }
319 return getResourceHandle(path + System.mapLibraryName(simplename));
320 }
321
322 /**
323 * Returns an Enumeration of ResourceHandle objects representing all of the
324 * resources having the specified name.
325 *
326 * @param name the name of the resource
327 * @return the ResourceHandle of the resource
328 */
329 protected Enumeration getResourceHandles(final String name)
330 {
331 return
332 (Enumeration) AccessController.doPrivileged(new PrivilegedAction() {
333 public Object run() {
334 return finder.getResources(name);
335 }
336 }, acc);
337 }
338
339 /**
340 * Defines a new package by name in this ClassLoader. The attributes
341 * contained in the specified Manifest will be used to obtain package
342 * version and sealing information. For sealed packages, the additional
343 * URL specifies the code source URL from which the package was loaded.
344 *
345 * @param name the package name
346 * @param man the Manifest containing package version and sealing
347 * information
348 * @param url the code source url for the package, or null if none
349 * @exception IllegalArgumentException if the package name duplicates
350 * an existing package either in this class loader or one
351 * of its ancestors
352 * @return the newly defined Package object
353 */
354 protected Package definePackage(String name, Manifest man, URL url)
355 throws IllegalArgumentException
356 {
357 String path = name.replace('.', '/').concat("/");
358 URL sealBase = null;
359
360 Attributes entryAttr = man.getAttributes(path);
361 Attributes mainAttr = man.getMainAttributes();
362
363 String specTitle = getManifestVal(entryAttr, mainAttr, Name.SPECIFICATION_TITLE);
364 String specVersion = getManifestVal(entryAttr, mainAttr, Name.SPECIFICATION_VERSION);
365 String specVendor = getManifestVal(entryAttr, mainAttr, Name.SPECIFICATION_VENDOR);
366 String implTitle = getManifestVal(entryAttr, mainAttr, Name.IMPLEMENTATION_TITLE);
367 String implVersion = getManifestVal(entryAttr, mainAttr, Name.IMPLEMENTATION_VERSION);
368 String implVendor = getManifestVal(entryAttr, mainAttr, Name.IMPLEMENTATION_VENDOR);
369 String sealed = getManifestVal(entryAttr, mainAttr, Name.SEALED);
370
371 if ("true".equalsIgnoreCase(sealed)) sealBase = url;
372
373 return definePackage(name, specTitle, specVersion, specVendor,
374 implTitle, implVersion, implVendor, sealBase);
375 }
376
377 private static String getManifestVal(Attributes entryAttr, Attributes mainAttr, Name key) {
378 String val = null;
379 if (entryAttr != null) {
380 val = entryAttr.getValue(key);
381 }
382 if (val == null && mainAttr != null) {
383 val = mainAttr.getValue(key);
384 }
385 return val;
386 }
387
388
389 public static URLStreamHandler getDefaultURLStreamHandler(String protocol) {
390
391 String pkgList = (String)java.security.AccessController.doPrivileged(
392 new GetPropertyAction("java.protocol.handler.pkgs", ""));
393
394 if (pkgList.length() > 0) pkgList += "|";
395 pkgList += "sun.net.www.protocol";
396
397 StringTokenizer tokenizer = new StringTokenizer(pkgList, "|");
398
399 while (tokenizer.hasMoreTokens()) {
400 String pkg = tokenizer.nextToken().trim();
401 try {
402 String clname = pkg + "." + protocol + ".Handler";
403 Class cls;
404 try {
405 cls = Class.forName(clname);
406 } catch (ClassNotFoundException e) {
407 cls = Class.forName(clname, false, null);
408 }
409 return (URLStreamHandler)cls.newInstance();
410 } catch (Exception e) {
411 // ignore and try next one
412 }
413 }
414 return null;
415 }
416
417 }