001: //
002: // Modified version of jdk1.4.0 java.net.URLClassLoader
003: // without the implied RMI baggage.
004: // Modified only by changing the package and
005: // global replacing URLClassLoader with XURLClassLoader
006: // except for the sun copyright notice.
007: //
008:
009: /*
010: * @(#)URLClassLoader.java 1.74 01/12/03
011: *
012: * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
013: * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
014: */
015:
016: package org.cougaar.bootstrap; // BBN
017:
018: import java.io.File;
019: import java.io.FilePermission;
020: import java.io.IOException;
021: import java.net.SocketPermission;
022: import java.net.URL;
023: import java.net.URLStreamHandlerFactory;
024: import java.security.AccessControlContext;
025: import java.security.AccessController;
026: import java.security.CodeSource;
027: import java.security.Permission;
028: import java.security.PermissionCollection;
029: import java.security.PrivilegedAction;
030: import java.security.PrivilegedExceptionAction;
031: import java.security.SecureClassLoader;
032: import java.util.Enumeration;
033: import java.util.NoSuchElementException;
034: import java.util.jar.Attributes;
035: import java.util.jar.Manifest;
036: import java.util.jar.Attributes.Name;
037:
038: import sun.misc.Resource;
039: import sun.misc.URLClassPath;
040: import sun.net.www.ParseUtil;
041:
042: /**
043: * This class loader is used to load classes and resources from a search
044: * path of URLs referring to both JAR files and directories. Any URL that
045: * ends with a '/' is assumed to refer to a directory. Otherwise, the URL
046: * is assumed to refer to a JAR file which will be opened as needed.
047: * <p>
048: * The AccessControlContext of the thread that created the instance of
049: * XURLClassLoader will be used when subsequently loading classes and
050: * resources.
051: * <p>
052: * The classes that are loaded are by default granted permission only to
053: * access the URLs specified when the XURLClassLoader was created.
054: *
055: * @since 1.2
056: */
057: public class XURLClassLoader extends SecureClassLoader {
058: /* The search path for classes and resources */
059: private URLClassPath ucp;
060:
061: /* The context to be used when loading classes and resources */
062: private AccessControlContext acc;
063:
064: /**
065: * Constructs a new XURLClassLoader for the given URLs. The URLs will be
066: * searched in the order specified for classes and resources after first
067: * searching in the specified parent class loader. Any URL that ends with
068: * a '/' is assumed to refer to a directory. Otherwise, the URL is assumed
069: * to refer to a JAR file which will be downloaded and opened as needed.
070: *
071: * <p>If there is a security manager, this method first
072: * calls the security manager's <code>checkCreateClassLoader</code> method
073: * to ensure creation of a class loader is allowed.
074: *
075: * @param urls the URLs from which to load classes and resources
076: * @param parent the parent class loader for delegation
077: * @exception SecurityException if a security manager exists and its
078: * <code>checkCreateClassLoader</code> method doesn't allow
079: * creation of a class loader.
080: * @see SecurityManager#checkCreateClassLoader
081: */
082: public XURLClassLoader(URL[] urls, ClassLoader parent) {
083: super (parent);
084: // this is to make the stack depth consistent with 1.1
085: SecurityManager security = System.getSecurityManager();
086: if (security != null) {
087: security.checkCreateClassLoader();
088: }
089: ucp = new URLClassPath(urls);
090: acc = AccessController.getContext();
091: }
092:
093: /**
094: * Constructs a new XURLClassLoader for the specified URLs using the
095: * default delegation parent <code>ClassLoader</code>. The URLs will
096: * be searched in the order specified for classes and resources after
097: * first searching in the parent class loader. Any URL that ends with
098: * a '/' is assumed to refer to a directory. Otherwise, the URL is
099: * assumed to refer to a JAR file which will be downloaded and opened
100: * as needed.
101: *
102: * <p>If there is a security manager, this method first
103: * calls the security manager's <code>checkCreateClassLoader</code> method
104: * to ensure creation of a class loader is allowed.
105: *
106: * @param urls the URLs from which to load classes and resources
107: *
108: * @exception SecurityException if a security manager exists and its
109: * <code>checkCreateClassLoader</code> method doesn't allow
110: * creation of a class loader.
111: * @see SecurityManager#checkCreateClassLoader
112: */
113: public XURLClassLoader(URL[] urls) {
114: super ();
115: // this is to make the stack depth consistent with 1.1
116: SecurityManager security = System.getSecurityManager();
117: if (security != null) {
118: security.checkCreateClassLoader();
119: }
120: ucp = new URLClassPath(urls);
121: acc = AccessController.getContext();
122: }
123:
124: /**
125: * Constructs a new XURLClassLoader for the specified URLs, parent
126: * class loader, and URLStreamHandlerFactory. The parent argument
127: * will be used as the parent class loader for delegation. The
128: * factory argument will be used as the stream handler factory to
129: * obtain protocol handlers when creating new URLs.
130: *
131: * <p>If there is a security manager, this method first
132: * calls the security manager's <code>checkCreateClassLoader</code> method
133: * to ensure creation of a class loader is allowed.
134: *
135: * @param urls the URLs from which to load classes and resources
136: * @param parent the parent class loader for delegation
137: * @param factory the URLStreamHandlerFactory to use when creating URLs
138: *
139: * @exception SecurityException if a security manager exists and its
140: * <code>checkCreateClassLoader</code> method doesn't allow
141: * creation of a class loader.
142: * @see SecurityManager#checkCreateClassLoader
143: */
144: public XURLClassLoader(URL[] urls, ClassLoader parent,
145: URLStreamHandlerFactory factory) {
146: super (parent);
147: // this is to make the stack depth consistent with 1.1
148: SecurityManager security = System.getSecurityManager();
149: if (security != null) {
150: security.checkCreateClassLoader();
151: }
152: ucp = new URLClassPath(urls, factory);
153: acc = AccessController.getContext();
154: }
155:
156: /**
157: * Appends the specified URL to the list of URLs to search for
158: * classes and resources.
159: *
160: * @param url the URL to be added to the search path of URLs
161: */
162: protected void addURL(URL url) {
163: ucp.addURL(url);
164: }
165:
166: /**
167: * Returns the search path of URLs for loading classes and resources.
168: * This includes the original list of URLs specified to the constructor,
169: * along with any URLs subsequently appended by the addURL() method.
170: * @return the search path of URLs for loading classes and resources.
171: */
172: public URL[] getURLs() {
173: return ucp.getURLs();
174: }
175:
176: /**
177: * Finds and loads the class with the specified name from the URL search
178: * path. Any URLs referring to JAR files are loaded and opened as needed
179: * until the class is found.
180: *
181: * @param name the name of the class
182: * @return the resulting class
183: * @exception ClassNotFoundException if the class could not be found
184: */
185: protected Class findClass(final String name)
186: throws ClassNotFoundException {
187: try {
188: return (Class) AccessController.doPrivileged(
189: new PrivilegedExceptionAction() {
190: public Object run()
191: throws ClassNotFoundException {
192: String path = name.replace('.', '/')
193: .concat(".class");
194: Resource res = ucp.getResource(path, false);
195: if (res != null) {
196: try {
197: return defineClass(name, res);
198: } catch (IOException e) {
199: throw new ClassNotFoundException(
200: name, e);
201: }
202: } else {
203: throw new ClassNotFoundException(name);
204: }
205: }
206: }, acc);
207: } catch (java.security.PrivilegedActionException pae) {
208: throw (ClassNotFoundException) pae.getException();
209: }
210: }
211:
212: /*
213: * Defines a Class using the class bytes obtained from the specified
214: * Resource. The resulting Class must be resolved before it can be
215: * used.
216: */
217: protected Class defineClass(String name, Resource res)
218: throws IOException {
219: int i = name.lastIndexOf('.');
220: URL url = res.getCodeSourceURL();
221: if (i != -1) {
222: String pkgname = name.substring(0, i);
223: // Check if package already loaded.
224: Package pkg = getPackage(pkgname);
225: Manifest man = res.getManifest();
226: if (pkg != null) {
227: // Package found, so check package sealing.
228: boolean ok;
229: if (pkg.isSealed()) {
230: // Verify that code source URL is the same.
231: ok = pkg.isSealed(url);
232: } else {
233: // Make sure we are not attempting to seal the package
234: // at this code source URL.
235: ok = (man == null) || !isSealed(pkgname, man);
236: }
237: if (!ok) {
238: throw new SecurityException("sealing violation");
239: }
240: } else {
241: if (man != null) {
242: definePackage(pkgname, man, url);
243: } else {
244: definePackage(pkgname, null, null, null, null,
245: null, null, null);
246: }
247: }
248: }
249: // Now read the class bytes and define the class
250: byte[] b = res.getBytes();
251: java.security.cert.Certificate[] certs = res.getCertificates();
252: CodeSource cs = new CodeSource(url, certs);
253: if (Bootstrapper.getLoudness() > 1)
254: System.out.println("XURLClassLoader: Loaded " + name
255: + " from " + url);
256: return defineClass(name, b, 0, b.length, cs);
257: }
258:
259: /**
260: * Defines a new package by name in this ClassLoader. The attributes
261: * contained in the specified Manifest will be used to obtain package
262: * version and sealing information. For sealed packages, the additional
263: * URL specifies the code source URL from which the package was loaded.
264: *
265: * @param name the package name
266: * @param man the Manifest containing package version and sealing
267: * information
268: * @param url the code source url for the package, or null if none
269: * @exception IllegalArgumentException if the package name duplicates
270: * an existing package either in this class loader or one
271: * of its ancestors
272: * @return the newly defined Package object
273: */
274: protected Package definePackage(String name, Manifest man, URL url)
275: throws IllegalArgumentException {
276: String path = name.replace('.', '/').concat("/");
277: String specTitle = null, specVersion = null, specVendor = null;
278: String implTitle = null, implVersion = null, implVendor = null;
279: String sealed = null;
280: URL sealBase = null;
281:
282: Attributes attr = man.getAttributes(path);
283: if (attr != null) {
284: specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
285: specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
286: specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
287: implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
288: implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
289: implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
290: sealed = attr.getValue(Name.SEALED);
291: }
292: attr = man.getMainAttributes();
293: if (attr != null) {
294: if (specTitle == null) {
295: specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
296: }
297: if (specVersion == null) {
298: specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
299: }
300: if (specVendor == null) {
301: specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
302: }
303: if (implTitle == null) {
304: implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
305: }
306: if (implVersion == null) {
307: implVersion = attr
308: .getValue(Name.IMPLEMENTATION_VERSION);
309: }
310: if (implVendor == null) {
311: implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
312: }
313: if (sealed == null) {
314: sealed = attr.getValue(Name.SEALED);
315: }
316: }
317: if ("true".equalsIgnoreCase(sealed)) {
318: sealBase = url;
319: }
320: return definePackage(name, specTitle, specVersion, specVendor,
321: implTitle, implVersion, implVendor, sealBase);
322: }
323:
324: /*
325: * Returns true if the specified package name is sealed according to the
326: * given manifest.
327: */
328: private boolean isSealed(String name, Manifest man) {
329: String path = name.replace('.', '/').concat("/");
330: Attributes attr = man.getAttributes(path);
331: String sealed = null;
332: if (attr != null) {
333: sealed = attr.getValue(Name.SEALED);
334: }
335: if (sealed == null) {
336: if ((attr = man.getMainAttributes()) != null) {
337: sealed = attr.getValue(Name.SEALED);
338: }
339: }
340: return "true".equalsIgnoreCase(sealed);
341: }
342:
343: /**
344: * Finds the resource with the specified name on the URL search path.
345: *
346: * @param name the name of the resource
347: * @return a <code>URL</code> for the resource, or <code>null</code>
348: * if the resource could not be found.
349: */
350: public URL findResource(final String name) {
351: /*
352: * The same restriction to finding classes applies to resources
353: */
354: Resource res = (Resource) AccessController.doPrivileged(
355: new PrivilegedAction() {
356: public Object run() {
357: return ucp.getResource(name, true);
358: }
359: }, acc);
360:
361: return res != null ? ucp.checkURL(res.getURL()) : null;
362: }
363:
364: /**
365: * Returns an Enumeration of URLs representing all of the resources
366: * on the URL search path having the specified name.
367: *
368: * @param name the resource name
369: * @exception IOException if an I/O exception occurs
370: * @return an <code>Enumeration</code> of <code>URL</code>s
371: */
372: public Enumeration findResources(final String name)
373: throws IOException {
374: final Enumeration e = ucp.getResources(name, true);
375:
376: return new Enumeration() {
377: private URL res;
378:
379: public Object nextElement() {
380: if (res == null)
381: throw new NoSuchElementException();
382: URL url = res;
383: res = null;
384: return url;
385: }
386:
387: public boolean hasMoreElements() {
388: if (res != null)
389: return true;
390: do {
391: Resource r = (Resource) AccessController
392: .doPrivileged(new PrivilegedAction() {
393: public Object run() {
394: if (!e.hasMoreElements())
395: return null;
396: return e.nextElement();
397: }
398: }, acc);
399: if (r == null)
400: break;
401: res = ucp.checkURL(r.getURL());
402: } while (res == null);
403: return res != null;
404: }
405: };
406: }
407:
408: /**
409: * Returns the permissions for the given codesource object.
410: * The implementation of this method first calls super.getPermissions
411: * and then adds permissions based on the URL of the codesource.
412: * <p>
413: * If the protocol is "file"
414: * and the path specifies a file, then permission to read that
415: * file is granted. If protocol is "file" and the path is
416: * a directory, permission is granted to read all files
417: * and (recursively) all files and subdirectories contained in
418: * that directory.
419: * <p>
420: * If the protocol is not "file", then
421: * to connect to and accept connections from the URL's host is granted.
422: * @param codesource the codesource
423: * @return the permissions granted to the codesource
424: */
425: protected PermissionCollection getPermissions(CodeSource codesource) {
426: PermissionCollection perms = super .getPermissions(codesource);
427:
428: URL url = codesource.getLocation();
429:
430: Permission p;
431:
432: try {
433: p = url.openConnection().getPermission();
434: } catch (java.io.IOException ioe) {
435:
436: p = null;
437: }
438:
439: if (p instanceof FilePermission) {
440: // if the permission has a separator char on the end,
441: // it means the codebase is a directory, and we need
442: // to add an additional permission to read recursively
443: String path = p.getName();
444: if (path.endsWith(File.separator)) {
445: path += "-";
446: p = new FilePermission(path, "read");
447: }
448: } else if ((p == null) && (url.getProtocol().equals("file"))) {
449: String path = url.getFile()
450: .replace('/', File.separatorChar);
451: path = ParseUtil.decode(path);
452: if (path.endsWith(File.separator))
453: path += "-";
454: p = new FilePermission(path, "read");
455: } else {
456: String host = url.getHost();
457: if (host == null)
458: host = "localhost";
459: p = new SocketPermission(host, "connect, accept");
460: }
461:
462: // make sure the person that created this class loader
463: // would have this permission
464:
465: if (p != null) {
466: final SecurityManager sm = System.getSecurityManager();
467: if (sm != null) {
468: final Permission fp = p;
469: AccessController.doPrivileged(new PrivilegedAction() {
470: public Object run() throws SecurityException {
471: sm.checkPermission(fp);
472: return null;
473: }
474: }, acc);
475: }
476: perms.add(p);
477: }
478: return perms;
479: }
480:
481: /**
482: * Creates a new instance of XURLClassLoader for the specified
483: * URLs and parent class loader. If a security manager is
484: * installed, the <code>loadClass</code> method of the XURLClassLoader
485: * returned by this method will invoke the
486: * <code>SecurityManager.checkPackageAccess</code> method before
487: * loading the class.
488: *
489: * @param urls the URLs to search for classes and resources
490: * @param parent the parent class loader for delegation
491: * @return the resulting class loader
492: */
493: public static XURLClassLoader newInstance(final URL[] urls,
494: final ClassLoader parent) {
495: // Save the caller's context
496: AccessControlContext acc = AccessController.getContext();
497: // Need a privileged block to create the class loader
498: XURLClassLoader ucl = (XURLClassLoader) AccessController
499: .doPrivileged(new PrivilegedAction() {
500: public Object run() {
501: return new FactoryXURLClassLoader(urls, parent);
502: }
503: });
504: // Now set the context on the loader using the one we saved,
505: // not the one inside the privileged block...
506: ucl.acc = acc;
507: return ucl;
508: }
509:
510: /**
511: * Creates a new instance of XURLClassLoader for the specified
512: * URLs and default parent class loader. If a security manager is
513: * installed, the <code>loadClass</code> method of the XURLClassLoader
514: * returned by this method will invoke the
515: * <code>SecurityManager.checkPackageAccess</code> before
516: * loading the class.
517: *
518: * @param urls the URLs to search for classes and resources
519: * @return the resulting class loader
520: */
521: public static XURLClassLoader newInstance(final URL[] urls) {
522: // Save the caller's context
523: AccessControlContext acc = AccessController.getContext();
524: // Need a privileged block to create the class loader
525: XURLClassLoader ucl = (XURLClassLoader) AccessController
526: .doPrivileged(new PrivilegedAction() {
527: public Object run() {
528: return new FactoryXURLClassLoader(urls);
529: }
530: });
531:
532: // Now set the context on the loader using the one we saved,
533: // not the one inside the privileged block...
534: ucl.acc = acc;
535: return ucl;
536: }
537: }
538:
539: final class FactoryXURLClassLoader extends XURLClassLoader {
540:
541: FactoryXURLClassLoader(URL[] urls, ClassLoader parent) {
542: super (urls, parent);
543: }
544:
545: FactoryXURLClassLoader(URL[] urls) {
546: super (urls);
547: }
548:
549: public final synchronized Class loadClass(String name,
550: boolean resolve) throws ClassNotFoundException {
551: // First check if we have permission to access the package. This
552: // should go away once we've added support for exported packages.
553: SecurityManager sm = System.getSecurityManager();
554: if (sm != null) {
555: int i = name.lastIndexOf('.');
556: if (i != -1) {
557: sm.checkPackageAccess(name.substring(0, i));
558: }
559: }
560: return super.loadClass(name, resolve);
561: }
562: }
|