KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > net > URLClassLoader


1 /*
2  * @(#)URLClassLoader.java 1.91 07/04/06
3  *
4  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.net;
9
10 import java.lang.reflect.Method JavaDoc;
11 import java.lang.reflect.Modifier JavaDoc;
12 import java.io.File JavaDoc;
13 import java.io.FilePermission JavaDoc;
14 import java.io.InputStream JavaDoc;
15 import java.io.IOException JavaDoc;
16 import java.net.URL JavaDoc;
17 import java.net.URLConnection JavaDoc;
18 import java.net.URLStreamHandlerFactory JavaDoc;
19 import java.util.Enumeration JavaDoc;
20 import java.util.NoSuchElementException JavaDoc;
21 import java.util.StringTokenizer JavaDoc;
22 import java.util.jar.Manifest JavaDoc;
23 import java.util.jar.Attributes JavaDoc;
24 import java.util.jar.Attributes.Name;
25 import java.security.CodeSigner JavaDoc;
26 import java.security.PrivilegedAction JavaDoc;
27 import java.security.PrivilegedExceptionAction JavaDoc;
28 import java.security.AccessController JavaDoc;
29 import java.security.AccessControlContext JavaDoc;
30 import java.security.SecureClassLoader JavaDoc;
31 import java.security.CodeSource JavaDoc;
32 import java.security.Permission JavaDoc;
33 import java.security.PermissionCollection JavaDoc;
34 import sun.misc.Resource;
35 import sun.misc.URLClassPath;
36 import sun.net.www.ParseUtil;
37 import sun.security.util.SecurityConstants;
38
39 /**
40  * This class loader is used to load classes and resources from a search
41  * path of URLs referring to both JAR files and directories. Any URL that
42  * ends with a '/' is assumed to refer to a directory. Otherwise, the URL
43  * is assumed to refer to a JAR file which will be opened as needed.
44  * <p>
45  * The AccessControlContext of the thread that created the instance of
46  * URLClassLoader will be used when subsequently loading classes and
47  * resources.
48  * <p>
49  * The classes that are loaded are by default granted permission only to
50  * access the URLs specified when the URLClassLoader was created.
51  *
52  * @author David Connelly
53  * @version 1.91, 04/06/07
54  * @since 1.2
55  */

56 public class URLClassLoader extends SecureClassLoader JavaDoc {
57     /* The search path for classes and resources */
58     URLClassPath ucp;
59
60     /* The context to be used when loading classes and resources */
61     private AccessControlContext JavaDoc acc;
62
63     /**
64      * Constructs a new URLClassLoader for the given URLs. The URLs will be
65      * searched in the order specified for classes and resources after first
66      * searching in the specified parent class loader. Any URL that ends with
67      * a '/' is assumed to refer to a directory. Otherwise, the URL is assumed
68      * to refer to a JAR file which will be downloaded and opened as needed.
69      *
70      * <p>If there is a security manager, this method first
71      * calls the security manager's <code>checkCreateClassLoader</code> method
72      * to ensure creation of a class loader is allowed.
73      *
74      * @param urls the URLs from which to load classes and resources
75      * @param parent the parent class loader for delegation
76      * @exception SecurityException if a security manager exists and its
77      * <code>checkCreateClassLoader</code> method doesn't allow
78      * creation of a class loader.
79      * @see SecurityManager#checkCreateClassLoader
80      */

81     public URLClassLoader(URL JavaDoc[] urls, ClassLoader JavaDoc parent) {
82     super(parent);
83     // this is to make the stack depth consistent with 1.1
84
SecurityManager JavaDoc security = System.getSecurityManager();
85     if (security != null) {
86         security.checkCreateClassLoader();
87     }
88     ucp = new URLClassPath(urls);
89     acc = AccessController.getContext();
90     }
91
92     /**
93      * Constructs a new URLClassLoader for the specified URLs using the
94      * default delegation parent <code>ClassLoader</code>. The URLs will
95      * be searched in the order specified for classes and resources after
96      * first searching in the parent class loader. Any URL that ends with
97      * a '/' is assumed to refer to a directory. Otherwise, the URL is
98      * assumed to refer to a JAR file which will be downloaded and opened
99      * as needed.
100      *
101      * <p>If there is a security manager, this method first
102      * calls the security manager's <code>checkCreateClassLoader</code> method
103      * to ensure creation of a class loader is allowed.
104      *
105      * @param urls the URLs from which to load classes and resources
106      *
107      * @exception SecurityException if a security manager exists and its
108      * <code>checkCreateClassLoader</code> method doesn't allow
109      * creation of a class loader.
110      * @see SecurityManager#checkCreateClassLoader
111      */

112     public URLClassLoader(URL JavaDoc[] urls) {
113     super();
114     // this is to make the stack depth consistent with 1.1
115
SecurityManager JavaDoc security = System.getSecurityManager();
116     if (security != null) {
117         security.checkCreateClassLoader();
118     }
119     ucp = new URLClassPath(urls);
120     acc = AccessController.getContext();
121     }
122
123     /**
124      * Constructs a new URLClassLoader for the specified URLs, parent
125      * class loader, and URLStreamHandlerFactory. The parent argument
126      * will be used as the parent class loader for delegation. The
127      * factory argument will be used as the stream handler factory to
128      * obtain protocol handlers when creating new jar URLs.
129      *
130      * <p>If there is a security manager, this method first
131      * calls the security manager's <code>checkCreateClassLoader</code> method
132      * to ensure creation of a class loader is allowed.
133      *
134      * @param urls the URLs from which to load classes and resources
135      * @param parent the parent class loader for delegation
136      * @param factory the URLStreamHandlerFactory to use when creating URLs
137      *
138      * @exception SecurityException if a security manager exists and its
139      * <code>checkCreateClassLoader</code> method doesn't allow
140      * creation of a class loader.
141      * @see SecurityManager#checkCreateClassLoader
142      */

143     public URLClassLoader(URL JavaDoc[] urls, ClassLoader JavaDoc parent,
144               URLStreamHandlerFactory JavaDoc factory) {
145     super(parent);
146     // this is to make the stack depth consistent with 1.1
147
SecurityManager JavaDoc security = System.getSecurityManager();
148     if (security != null) {
149         security.checkCreateClassLoader();
150     }
151     ucp = new URLClassPath(urls, factory);
152     acc = AccessController.getContext();
153     }
154
155     /**
156      * Appends the specified URL to the list of URLs to search for
157      * classes and resources.
158      *
159      * @param url the URL to be added to the search path of URLs
160      */

161     protected void addURL(URL JavaDoc url) {
162     ucp.addURL(url);
163     }
164
165     /**
166      * Returns the search path of URLs for loading classes and resources.
167      * This includes the original list of URLs specified to the constructor,
168      * along with any URLs subsequently appended by the addURL() method.
169      * @return the search path of URLs for loading classes and resources.
170      */

171     public URL JavaDoc[] getURLs() {
172     return ucp.getURLs();
173     }
174
175     /**
176      * Finds and loads the class with the specified name from the URL search
177      * path. Any URLs referring to JAR files are loaded and opened as needed
178      * until the class is found.
179      *
180      * @param name the name of the class
181      * @return the resulting class
182      * @exception ClassNotFoundException if the class could not be found
183      */

184     protected Class JavaDoc<?> findClass(final String JavaDoc name)
185      throws ClassNotFoundException JavaDoc
186     {
187     try {
188         return (Class JavaDoc)
189         AccessController.doPrivileged(new PrivilegedExceptionAction JavaDoc() {
190             public Object JavaDoc run() throws ClassNotFoundException JavaDoc {
191             String JavaDoc path = name.replace('.', '/').concat(".class");
192             Resource res = ucp.getResource(path, false);
193             if (res != null) {
194                 try {
195                 return defineClass(name, res);
196                 } catch (IOException JavaDoc e) {
197                 throw new ClassNotFoundException JavaDoc(name, e);
198                 }
199             } else {
200                 throw new ClassNotFoundException JavaDoc(name);
201             }
202             }
203         }, acc);
204     } catch (java.security.PrivilegedActionException JavaDoc pae) {
205         throw (ClassNotFoundException JavaDoc) pae.getException();
206     }
207     }
208
209     /*
210      * Defines a Class using the class bytes obtained from the specified
211      * Resource. The resulting Class must be resolved before it can be
212      * used.
213      */

214     private Class JavaDoc defineClass(String JavaDoc name, Resource res) throws IOException JavaDoc {
215     int i = name.lastIndexOf('.');
216     URL JavaDoc url = res.getCodeSourceURL();
217     if (i != -1) {
218         String JavaDoc pkgname = name.substring(0, i);
219         // Check if package already loaded.
220
Package JavaDoc pkg = getPackage(pkgname);
221         Manifest JavaDoc man = res.getManifest();
222         if (pkg != null) {
223         // Package found, so check package sealing.
224
if (pkg.isSealed()) {
225             // Verify that code source URL is the same.
226
if (!pkg.isSealed(url)) {
227             throw new SecurityException JavaDoc(
228                 "sealing violation: package " + pkgname + " is sealed");
229             }
230
231         } else {
232             // Make sure we are not attempting to seal the package
233
// at this code source URL.
234
if ((man != null) && isSealed(pkgname, man)) {
235             throw new SecurityException JavaDoc(
236                 "sealing violation: can't seal package " + pkgname +
237                 ": already loaded");
238             }
239         }
240         } else {
241         if (man != null) {
242             definePackage(pkgname, man, url);
243         } else {
244                     definePackage(pkgname, null, null, null, null, null, null, null);
245                 }
246         }
247     }
248     // Now read the class bytes and define the class
249
java.nio.ByteBuffer JavaDoc bb = res.getByteBuffer();
250     if (bb != null) {
251         // Use (direct) ByteBuffer:
252
CodeSigner JavaDoc[] signers = res.getCodeSigners();
253         CodeSource JavaDoc cs = new CodeSource JavaDoc(url, signers);
254         return defineClass(name, bb, cs);
255     } else {
256         byte[] b = res.getBytes();
257         // must read certificates AFTER reading bytes.
258
CodeSigner JavaDoc[] signers = res.getCodeSigners();
259         CodeSource JavaDoc cs = new CodeSource JavaDoc(url, signers);
260         return defineClass(name, b, 0, b.length, cs);
261     }
262     }
263
264     /**
265      * Defines a new package by name in this ClassLoader. The attributes
266      * contained in the specified Manifest will be used to obtain package
267      * version and sealing information. For sealed packages, the additional
268      * URL specifies the code source URL from which the package was loaded.
269      *
270      * @param name the package name
271      * @param man the Manifest containing package version and sealing
272      * information
273      * @param url the code source url for the package, or null if none
274      * @exception IllegalArgumentException if the package name duplicates
275      * an existing package either in this class loader or one
276      * of its ancestors
277      * @return the newly defined Package object
278      */

279     protected Package JavaDoc definePackage(String JavaDoc name, Manifest JavaDoc man, URL JavaDoc url)
280     throws IllegalArgumentException JavaDoc
281     {
282     String JavaDoc path = name.replace('.', '/').concat("/");
283     String JavaDoc specTitle = null, specVersion = null, specVendor = null;
284     String JavaDoc implTitle = null, implVersion = null, implVendor = null;
285     String JavaDoc sealed = null;
286     URL JavaDoc sealBase = null;
287
288     Attributes JavaDoc attr = man.getAttributes(path);
289     if (attr != null) {
290         specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
291         specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
292         specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
293         implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
294         implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
295         implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
296         sealed = attr.getValue(Name.SEALED);
297     }
298     attr = man.getMainAttributes();
299     if (attr != null) {
300         if (specTitle == null) {
301         specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
302         }
303         if (specVersion == null) {
304         specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
305         }
306         if (specVendor == null) {
307         specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
308         }
309         if (implTitle == null) {
310         implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
311         }
312         if (implVersion == null) {
313         implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
314         }
315         if (implVendor == null) {
316         implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
317         }
318         if (sealed == null) {
319         sealed = attr.getValue(Name.SEALED);
320         }
321     }
322     if ("true".equalsIgnoreCase(sealed)) {
323         sealBase = url;
324     }
325     return definePackage(name, specTitle, specVersion, specVendor,
326                  implTitle, implVersion, implVendor, sealBase);
327     }
328
329     /*
330      * Returns true if the specified package name is sealed according to the
331      * given manifest.
332      */

333     private boolean isSealed(String JavaDoc name, Manifest JavaDoc man) {
334     String JavaDoc path = name.replace('.', '/').concat("/");
335     Attributes JavaDoc attr = man.getAttributes(path);
336     String JavaDoc sealed = null;
337     if (attr != null) {
338         sealed = attr.getValue(Name.SEALED);
339     }
340     if (sealed == null) {
341         if ((attr = man.getMainAttributes()) != null) {
342         sealed = attr.getValue(Name.SEALED);
343         }
344     }
345     return "true".equalsIgnoreCase(sealed);
346     }
347
348     /**
349      * Finds the resource with the specified name on the URL search path.
350      *
351      * @param name the name of the resource
352      * @return a <code>URL</code> for the resource, or <code>null</code>
353      * if the resource could not be found.
354      */

355     public URL JavaDoc findResource(final String JavaDoc name) {
356     /*
357      * The same restriction to finding classes applies to resources
358      */

359     URL JavaDoc url =
360         (URL JavaDoc) AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
361                 public Object JavaDoc run() {
362                     return ucp.findResource(name, true);
363                 }
364             }, acc);
365
366     return url != null ? ucp.checkURL(url) : null;
367     }
368
369     /**
370      * Returns an Enumeration of URLs representing all of the resources
371      * on the URL search path having the specified name.
372      *
373      * @param name the resource name
374      * @exception IOException if an I/O exception occurs
375      * @return an <code>Enumeration</code> of <code>URL</code>s
376      */

377     public Enumeration JavaDoc<URL JavaDoc> findResources(final String JavaDoc name)
378     throws IOException JavaDoc
379     {
380         final Enumeration JavaDoc e = ucp.findResources(name, true);
381
382     return new Enumeration JavaDoc<URL JavaDoc>() {
383         private URL JavaDoc url = null;
384
385         private boolean next() {
386         if (url != null) {
387             return true;
388         }
389         do {
390             URL JavaDoc u = (URL JavaDoc)
391             AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
392                 public Object JavaDoc run() {
393                 if (!e.hasMoreElements())
394                                     return null;
395                                 return e.nextElement();
396                 }
397             }, acc);
398             if (u == null)
399             break;
400             url = ucp.checkURL(u);
401         } while (url == null);
402         return url != null;
403         }
404
405         public URL JavaDoc nextElement() {
406         if (!next()) {
407             throw new NoSuchElementException JavaDoc();
408         }
409         URL JavaDoc u = url;
410         url = null;
411         return u;
412         }
413
414         public boolean hasMoreElements() {
415         return next();
416         }
417     };
418     }
419
420     /**
421      * Returns the permissions for the given codesource object.
422      * The implementation of this method first calls super.getPermissions
423      * and then adds permissions based on the URL of the codesource.
424      * <p>
425      * If the protocol of this URL is "jar", then the permission granted
426      * is based on the permission that is required by the URL of the Jar
427      * file.
428      * <p>
429      * If the protocol is "file"
430      * and the path specifies a file, then permission to read that
431      * file is granted. If protocol is "file" and the path is
432      * a directory, permission is granted to read all files
433      * and (recursively) all files and subdirectories contained in
434      * that directory.
435      * <p>
436      * If the protocol is not "file", then
437      * to connect to and accept connections from the URL's host is granted.
438      * @param codesource the codesource
439      * @return the permissions granted to the codesource
440      */

441     protected PermissionCollection JavaDoc getPermissions(CodeSource JavaDoc codesource)
442     {
443     PermissionCollection JavaDoc perms = super.getPermissions(codesource);
444
445     URL JavaDoc url = codesource.getLocation();
446
447     Permission JavaDoc p;
448     URLConnection JavaDoc urlConnection;
449
450     try {
451         urlConnection = url.openConnection();
452         p = urlConnection.getPermission();
453     } catch (java.io.IOException JavaDoc ioe) {
454         p = null;
455         urlConnection = null;
456     }
457
458     if (p instanceof FilePermission JavaDoc) {
459         // if the permission has a separator char on the end,
460
// it means the codebase is a directory, and we need
461
// to add an additional permission to read recursively
462
String JavaDoc path = p.getName();
463         if (path.endsWith(File.separator)) {
464         path += "-";
465         p = new FilePermission JavaDoc(path, SecurityConstants.FILE_READ_ACTION);
466         }
467     } else if ((p == null) && (url.getProtocol().equals("file"))) {
468         String JavaDoc path = url.getFile().replace('/', File.separatorChar);
469             path = ParseUtil.decode(path);
470         if (path.endsWith(File.separator))
471         path += "-";
472         p = new FilePermission JavaDoc(path, SecurityConstants.FILE_READ_ACTION);
473     } else {
474         URL JavaDoc locUrl = url;
475         if (urlConnection instanceof JarURLConnection JavaDoc) {
476         locUrl = ((JarURLConnection JavaDoc)urlConnection).getJarFileURL();
477         }
478         String JavaDoc host = locUrl.getHost();
479         if (host != null && (host.length() > 0))
480         p = new SocketPermission JavaDoc(host,
481                      SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION);
482     }
483     // make sure the person that created this class loader
484
// would have this permission
485

486     if (p != null) {
487         final SecurityManager JavaDoc sm = System.getSecurityManager();
488         if (sm != null) {
489         final Permission JavaDoc fp = p;
490         AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
491             public Object JavaDoc run() throws SecurityException JavaDoc {
492             sm.checkPermission(fp);
493             return null;
494             }
495         }, acc);
496         }
497         perms.add(p);
498     }
499     return perms;
500     }
501
502     /**
503      * Creates a new instance of URLClassLoader for the specified
504      * URLs and parent class loader. If a security manager is
505      * installed, the <code>loadClass</code> method of the URLClassLoader
506      * returned by this method will invoke the
507      * <code>SecurityManager.checkPackageAccess</code> method before
508      * loading the class.
509      *
510      * @param urls the URLs to search for classes and resources
511      * @param parent the parent class loader for delegation
512      * @return the resulting class loader
513      */

514     public static URLClassLoader JavaDoc newInstance(final URL JavaDoc[] urls,
515                          final ClassLoader JavaDoc parent) {
516     // Save the caller's context
517
AccessControlContext JavaDoc acc = AccessController.getContext();
518     // Need a privileged block to create the class loader
519
URLClassLoader JavaDoc ucl =
520         (URLClassLoader JavaDoc) AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
521         public Object JavaDoc run() {
522             return new FactoryURLClassLoader(urls, parent);
523         }
524         });
525     // Now set the context on the loader using the one we saved,
526
// not the one inside the privileged block...
527
ucl.acc = acc;
528     return ucl;
529     }
530
531     /**
532      * Creates a new instance of URLClassLoader for the specified
533      * URLs and default parent class loader. If a security manager is
534      * installed, the <code>loadClass</code> method of the URLClassLoader
535      * returned by this method will invoke the
536      * <code>SecurityManager.checkPackageAccess</code> before
537      * loading the class.
538      *
539      * @param urls the URLs to search for classes and resources
540      * @return the resulting class loader
541      */

542     public static URLClassLoader JavaDoc newInstance(final URL JavaDoc[] urls) {
543     // Save the caller's context
544
AccessControlContext JavaDoc acc = AccessController.getContext();
545     // Need a privileged block to create the class loader
546
URLClassLoader JavaDoc ucl = (URLClassLoader JavaDoc)
547         AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
548         public Object JavaDoc run() {
549             return new FactoryURLClassLoader(urls);
550         }
551         });
552
553     // Now set the context on the loader using the one we saved,
554
// not the one inside the privileged block...
555
ucl.acc = acc;
556     return ucl;
557     }
558
559     static {
560         sun.misc.SharedSecrets.setJavaNetAccess (
561         new sun.misc.JavaNetAccess() {
562         public URLClassPath getURLClassPath (URLClassLoader JavaDoc u) {
563             return u.ucp;
564         }
565         }
566     );
567     }
568 }
569
570 final class FactoryURLClassLoader extends URLClassLoader JavaDoc {
571
572     FactoryURLClassLoader(URL JavaDoc[] urls, ClassLoader JavaDoc parent) {
573     super(urls, parent);
574     }
575
576     FactoryURLClassLoader(URL JavaDoc[] urls) {
577     super(urls);
578     }
579
580     public final synchronized Class JavaDoc loadClass(String JavaDoc name, boolean resolve)
581     throws ClassNotFoundException JavaDoc
582     {
583     // First check if we have permission to access the package. This
584
// should go away once we've added support for exported packages.
585
SecurityManager JavaDoc sm = System.getSecurityManager();
586     if (sm != null) {
587         int i = name.lastIndexOf('.');
588         if (i != -1) {
589         sm.checkPackageAccess(name.substring(0, i));
590         }
591     }
592     return super.loadClass(name, resolve);
593     }
594 }
595
Popular Tags