1. /*
  2. * @(#)URLClassLoader.java 1.74 01/04/21
  3. *
  4. * Copyright 1997-2001 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.net;
  11. import java.lang.reflect.Method;
  12. import java.lang.reflect.Modifier;
  13. import java.io.File;
  14. import java.io.FilePermission;
  15. import java.io.InputStream;
  16. import java.io.IOException;
  17. import java.net.URL;
  18. import java.net.URLConnection;
  19. import java.net.URLStreamHandlerFactory;
  20. import java.util.Enumeration;
  21. import java.util.NoSuchElementException;
  22. import java.util.StringTokenizer;
  23. import java.util.jar.Manifest;
  24. import java.util.jar.Attributes;
  25. import java.util.jar.Attributes.Name;
  26. import java.security.PrivilegedAction;
  27. import java.security.PrivilegedExceptionAction;
  28. import java.security.AccessController;
  29. import java.security.AccessControlContext;
  30. import java.security.SecureClassLoader;
  31. import java.security.CodeSource;
  32. import java.security.Permission;
  33. import java.security.PermissionCollection;
  34. import sun.misc.Resource;
  35. import sun.misc.URLClassPath;
  36. /**
  37. * This class loader is used to load classes and resources from a search
  38. * path of URLs referring to both JAR files and directories. Any URL that
  39. * ends with a '/' is assumed to refer to a directory. Otherwise, the URL
  40. * is assumed to refer to a JAR file which will be opened as needed.
  41. * <p>
  42. * The AccessControlContext of the thread that created the instance of
  43. * URLClassLoader will be used when subsequently loading classes and
  44. * resources.
  45. * <p>
  46. * The classes that are loaded are by default granted permission only to
  47. * access the URLs specified when the URLClassLoader was created.
  48. *
  49. * @author David Connelly
  50. * @version 1.74, 04/21/01
  51. * @since 1.2
  52. */
  53. public class URLClassLoader extends SecureClassLoader {
  54. /* The search path for classes and resources */
  55. private URLClassPath ucp;
  56. /* The context to be used when loading classes and resources */
  57. private AccessControlContext acc;
  58. /**
  59. * Constructs a new URLClassLoader for the given URLs. The URLs will be
  60. * searched in the order specified for classes and resources after first
  61. * searching in the specified parent class loader. Any URL that ends with
  62. * a '/' is assumed to refer to a directory. Otherwise, the URL is assumed
  63. * to refer to a JAR file which will be downloaded and opened as needed.
  64. *
  65. * <p>If there is a security manager, this method first
  66. * calls the security manager's <code>checkCreateClassLoader</code> method
  67. * to ensure creation of a class loader is allowed.
  68. *
  69. * @param urls the URLs from which to load classes and resources
  70. * @param parent the parent class loader for delegation
  71. * @exception SecurityException if a security manager exists and its
  72. * <code>checkCreateClassLoader</code> method doesn't allow
  73. * creation of a class loader.
  74. * @see SecurityManager#checkCreateClassLoader
  75. */
  76. public URLClassLoader(URL[] urls, ClassLoader parent) {
  77. super(parent);
  78. // this is to make the stack depth consistent with 1.1
  79. SecurityManager security = System.getSecurityManager();
  80. if (security != null) {
  81. security.checkCreateClassLoader();
  82. }
  83. ucp = new URLClassPath(urls);
  84. acc = AccessController.getContext();
  85. }
  86. /**
  87. * Constructs a new URLClassLoader for the specified URLs using the
  88. * default delegation parent <code>ClassLoader</code>. The URLs will
  89. * be searched in the order specified for classes and resources after
  90. * first searching in the parent class loader. Any URL that ends with
  91. * a '/' is assumed to refer to a directory. Otherwise, the URL is
  92. * assumed to refer to a JAR file which will be downloaded and opened
  93. * as needed.
  94. *
  95. * <p>If there is a security manager, this method first
  96. * calls the security manager's <code>checkCreateClassLoader</code> method
  97. * to ensure creation of a class loader is allowed.
  98. *
  99. * @param urls the URLs from which to load classes and resources
  100. *
  101. * @exception SecurityException if a security manager exists and its
  102. * <code>checkCreateClassLoader</code> method doesn't allow
  103. * creation of a class loader.
  104. * @see SecurityManager#checkCreateClassLoader
  105. */
  106. public URLClassLoader(URL[] urls) {
  107. super();
  108. // this is to make the stack depth consistent with 1.1
  109. SecurityManager security = System.getSecurityManager();
  110. if (security != null) {
  111. security.checkCreateClassLoader();
  112. }
  113. ucp = new URLClassPath(urls);
  114. acc = AccessController.getContext();
  115. }
  116. /**
  117. * Constructs a new URLClassLoader for the specified URLs, parent
  118. * class loader, and URLStreamHandlerFactory. The parent argument
  119. * will be used as the parent class loader for delegation. The
  120. * factory argument will be used as the stream handler factory to
  121. * obtain protocol handlers when creating new URLs.
  122. *
  123. * <p>If there is a security manager, this method first
  124. * calls the security manager's <code>checkCreateClassLoader</code> method
  125. * to ensure creation of a class loader is allowed.
  126. *
  127. * @param urls the URLs from which to load classes and resources
  128. * @param parent the parent class loader for delegation
  129. * @param factory the URLStreamHandlerFactory to use when creating URLs
  130. *
  131. * @exception SecurityException if a security manager exists and its
  132. * <code>checkCreateClassLoader</code> method doesn't allow
  133. * creation of a class loader.
  134. * @see SecurityManager#checkCreateClassLoader
  135. */
  136. public URLClassLoader(URL[] urls, ClassLoader parent,
  137. URLStreamHandlerFactory factory) {
  138. super(parent);
  139. // this is to make the stack depth consistent with 1.1
  140. SecurityManager security = System.getSecurityManager();
  141. if (security != null) {
  142. security.checkCreateClassLoader();
  143. }
  144. ucp = new URLClassPath(urls, factory);
  145. acc = AccessController.getContext();
  146. }
  147. /**
  148. * Appends the specified URL to the list of URLs to search for
  149. * classes and resources.
  150. *
  151. * @param url the URL to be added to the search path of URLs
  152. */
  153. protected void addURL(URL url) {
  154. ucp.addURL(url);
  155. }
  156. /**
  157. * Returns the search path of URLs for loading classes and resources.
  158. * This includes the original list of URLs specified to the constructor,
  159. * along with any URLs subsequently appended by the addURL() method.
  160. * @return the search path of URLs for loading classes and resources.
  161. */
  162. public URL[] getURLs() {
  163. return ucp.getURLs();
  164. }
  165. /**
  166. * Finds and loads the class with the specified name from the URL search
  167. * path. Any URLs referring to JAR files are loaded and opened as needed
  168. * until the class is found.
  169. *
  170. * @param name the name of the class
  171. * @return the resulting class
  172. * @exception ClassNotFoundException if the class could not be found
  173. */
  174. protected Class findClass(final String name)
  175. throws ClassNotFoundException
  176. {
  177. try {
  178. return (Class)
  179. AccessController.doPrivileged(new PrivilegedExceptionAction() {
  180. public Object run() throws ClassNotFoundException {
  181. String path = name.replace('.', '/').concat(".class");
  182. Resource res = ucp.getResource(path, false);
  183. if (res != null) {
  184. try {
  185. return defineClass(name, res);
  186. } catch (IOException e) {
  187. throw new ClassNotFoundException(name, e);
  188. }
  189. } else {
  190. throw new ClassNotFoundException(name);
  191. }
  192. }
  193. }, acc);
  194. } catch (java.security.PrivilegedActionException pae) {
  195. throw (ClassNotFoundException) pae.getException();
  196. }
  197. }
  198. /*
  199. * Defines a Class using the class bytes obtained from the specified
  200. * Resource. The resulting Class must be resolved before it can be
  201. * used.
  202. */
  203. private Class defineClass(String name, Resource res) throws IOException {
  204. int i = name.lastIndexOf('.');
  205. URL url = res.getCodeSourceURL();
  206. if (i != -1) {
  207. String pkgname = name.substring(0, i);
  208. // Check if package already loaded.
  209. Package pkg = getPackage(pkgname);
  210. Manifest man = res.getManifest();
  211. if (pkg != null) {
  212. // Package found, so check package sealing.
  213. boolean ok;
  214. if (pkg.isSealed()) {
  215. // Verify that code source URL is the same.
  216. ok = pkg.isSealed(url);
  217. } else {
  218. // Make sure we are not attempting to seal the package
  219. // at this code source URL.
  220. ok = (man == null) || !isSealed(pkgname, man);
  221. }
  222. if (!ok) {
  223. throw new SecurityException("sealing violation");
  224. }
  225. } else {
  226. if (man != null) {
  227. definePackage(pkgname, man, url);
  228. } else {
  229. definePackage(pkgname, null, null, null, null, null, null, null);
  230. }
  231. }
  232. }
  233. // Now read the class bytes and define the class
  234. byte[] b = res.getBytes();
  235. java.security.cert.Certificate[] certs = res.getCertificates();
  236. CodeSource cs = new CodeSource(url, certs);
  237. return defineClass(name, b, 0, b.length, cs);
  238. }
  239. /**
  240. * Defines a new package by name in this ClassLoader. The attributes
  241. * contained in the specified Manifest will be used to obtain package
  242. * version and sealing information. For sealed packages, the additional
  243. * URL specifies the code source URL from which the package was loaded.
  244. *
  245. * @param name the package name
  246. * @param man the Manifest containing package version and sealing
  247. * information
  248. * @param url the code source url for the package, or null if none
  249. * @exception IllegalArgumentException if the package name duplicates
  250. * an existing package either in this class loader or one
  251. * of its ancestors
  252. * @return the newly defined Package object
  253. */
  254. protected Package definePackage(String name, Manifest man, URL url)
  255. throws IllegalArgumentException
  256. {
  257. String path = name.replace('.', '/').concat("/");
  258. String specTitle = null, specVersion = null, specVendor = null;
  259. String implTitle = null, implVersion = null, implVendor = null;
  260. String sealed = null;
  261. URL sealBase = null;
  262. Attributes attr = man.getAttributes(path);
  263. if (attr != null) {
  264. specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
  265. specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
  266. specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
  267. implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
  268. implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
  269. implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
  270. sealed = attr.getValue(Name.SEALED);
  271. }
  272. attr = man.getMainAttributes();
  273. if (attr != null) {
  274. if (specTitle == null) {
  275. specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
  276. }
  277. if (specVersion == null) {
  278. specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
  279. }
  280. if (specVendor == null) {
  281. specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
  282. }
  283. if (implTitle == null) {
  284. implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
  285. }
  286. if (implVersion == null) {
  287. implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
  288. }
  289. if (implVendor == null) {
  290. implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
  291. }
  292. if (sealed == null) {
  293. sealed = attr.getValue(Name.SEALED);
  294. }
  295. }
  296. if ("true".equalsIgnoreCase(sealed)) {
  297. sealBase = url;
  298. }
  299. return definePackage(name, specTitle, specVersion, specVendor,
  300. implTitle, implVersion, implVendor, sealBase);
  301. }
  302. /*
  303. * Returns true if the specified package name is sealed according to the
  304. * given manifest.
  305. */
  306. private boolean isSealed(String name, Manifest man) {
  307. String path = name.replace('.', '/').concat("/");
  308. Attributes attr = man.getAttributes(path);
  309. String sealed = null;
  310. if (attr != null) {
  311. sealed = attr.getValue(Name.SEALED);
  312. }
  313. if (sealed == null) {
  314. if ((attr = man.getMainAttributes()) != null) {
  315. sealed = attr.getValue(Name.SEALED);
  316. }
  317. }
  318. return "true".equalsIgnoreCase(sealed);
  319. }
  320. /**
  321. * Finds the resource with the specified name on the URL search path.
  322. *
  323. * @param name the name of the resource
  324. * @return a <code>URL</code> for the resource, or <code>null</code>
  325. * if the resource could not be found.
  326. */
  327. public URL findResource(final String name) {
  328. /*
  329. * The same restriction to finding classes applies to resources
  330. */
  331. Resource res =
  332. (Resource) AccessController.doPrivileged(new PrivilegedAction() {
  333. public Object run() {
  334. return ucp.getResource(name, true);
  335. }
  336. }, acc);
  337. return res != null ? ucp.checkURL(res.getURL()) : null;
  338. }
  339. /**
  340. * Returns an Enumeration of URLs representing all of the resources
  341. * on the URL search path having the specified name.
  342. *
  343. * @param name the resource name
  344. * @exception if an I/O exception occurs
  345. * @return an <code>Enumeration</code> of <code>URL</code>s
  346. */
  347. public Enumeration findResources(final String name) throws IOException {
  348. final Enumeration e = ucp.getResources(name, true);
  349. return new Enumeration() {
  350. private URL res;
  351. public Object nextElement() {
  352. if (res == null)
  353. throw new NoSuchElementException();
  354. URL url = res;
  355. res = null;
  356. return url;
  357. }
  358. public boolean hasMoreElements() {
  359. if (res != null)
  360. return true;
  361. do {
  362. Resource r = (Resource)
  363. AccessController.doPrivileged(new PrivilegedAction() {
  364. public Object run() {
  365. if (!e.hasMoreElements())
  366. return null;
  367. return e.nextElement();
  368. }
  369. }, acc);
  370. if (r == null)
  371. break;
  372. res = ucp.checkURL(r.getURL());
  373. } while (res == null);
  374. return res != null;
  375. }
  376. };
  377. }
  378. /**
  379. * Returns the permissions for the given codesource object.
  380. * The implementation of this method first calls super.getPermissions,
  381. * to get the permissions
  382. * granted by the policy, and then adds additional permissions
  383. * based on the URL of the codesource.
  384. * <p>
  385. * If the protocol is "file"
  386. * and the path specifies a file, then permission to read that
  387. * file is granted. If protocol is "file" and the path is
  388. * a directory, permission is granted to read all files
  389. * and (recursively) all files and subdirectories contained in
  390. * that directory.
  391. * <p>
  392. * If the protocol is not "file", then
  393. * to connect to and accept connections from the URL's host is granted.
  394. * @param codesource the codesource
  395. * @return the permissions granted to the codesource
  396. */
  397. protected PermissionCollection getPermissions(CodeSource codesource)
  398. {
  399. PermissionCollection perms = super.getPermissions(codesource);
  400. URL url = codesource.getLocation();
  401. Permission p;
  402. try {
  403. p = url.openConnection().getPermission();
  404. } catch (java.io.IOException ioe) {
  405. p = null;
  406. }
  407. if (p instanceof FilePermission) {
  408. // if the permission has a separator char on the end,
  409. // it means the codebase is a directory, and we need
  410. // to add an additional permission to read recursively
  411. String path = p.getName();
  412. if (path.endsWith(File.separator)) {
  413. path += "-";
  414. p = new FilePermission(path, "read");
  415. }
  416. } else if ((p == null) && (url.getProtocol().equals("file"))) {
  417. String path = url.getFile().replace('/', File.separatorChar);
  418. if (path.endsWith(File.separator))
  419. path += "-";
  420. p = new FilePermission(path, "read");
  421. } else {
  422. String host = url.getHost();
  423. if (host == null)
  424. host = "localhost";
  425. p = new SocketPermission(host,"connect, accept");
  426. }
  427. // make sure the person that created this class loader
  428. // would have this permission
  429. if (p != null) {
  430. final SecurityManager sm = System.getSecurityManager();
  431. if (sm != null) {
  432. final Permission fp = p;
  433. AccessController.doPrivileged(new PrivilegedAction() {
  434. public Object run() throws SecurityException {
  435. sm.checkPermission(fp);
  436. return null;
  437. }
  438. }, acc);
  439. }
  440. perms.add(p);
  441. }
  442. return perms;
  443. }
  444. /**
  445. * Creates a new instance of URLClassLoader for the specified
  446. * URLs and parent class loader. If a security manager is
  447. * installed, the <code>loadClass</code> method of the URLClassLoader
  448. * returned by this method will invoke the
  449. * <code>SecurityManager.checkPackageAccess</code> method before
  450. * loading the class.
  451. *
  452. * @param urls the URLs to search for classes and resources
  453. * @param parent the parent class loader for delegation
  454. * @return the resulting class loader
  455. */
  456. public static URLClassLoader newInstance(final URL[] urls,
  457. final ClassLoader parent) {
  458. // Save the caller's context
  459. AccessControlContext acc = AccessController.getContext();
  460. // Need a privileged block to create the class loader
  461. URLClassLoader ucl =
  462. (URLClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
  463. public Object run() {
  464. return new FactoryURLClassLoader(urls, parent);
  465. }
  466. });
  467. // Now set the context on the loader using the one we saved,
  468. // not the one inside the privileged block...
  469. ucl.acc = acc;
  470. return ucl;
  471. }
  472. /**
  473. * Creates a new instance of URLClassLoader for the specified
  474. * URLs and default parent class loader. If a security manager is
  475. * installed, the <code>loadClass</code> method of the URLClassLoader
  476. * returned by this method will invoke the
  477. * <code>SecurityManager.checkPackageAccess</code> before
  478. * loading the class.
  479. *
  480. * @param urls the URLs to search for classes and resources
  481. * @return the resulting class loader
  482. */
  483. public static URLClassLoader newInstance(final URL[] urls) {
  484. // Save the caller's context
  485. AccessControlContext acc = AccessController.getContext();
  486. // Need a privileged block to create the class loader
  487. URLClassLoader ucl = (URLClassLoader)
  488. AccessController.doPrivileged(new PrivilegedAction() {
  489. public Object run() {
  490. return new FactoryURLClassLoader(urls);
  491. }
  492. });
  493. // Now set the context on the loader using the one we saved,
  494. // not the one inside the privileged block...
  495. ucl.acc = acc;
  496. return ucl;
  497. }
  498. }
  499. final class FactoryURLClassLoader extends URLClassLoader {
  500. FactoryURLClassLoader(URL[] urls, ClassLoader parent) {
  501. super(urls, parent);
  502. }
  503. FactoryURLClassLoader(URL[] urls) {
  504. super(urls);
  505. }
  506. public final synchronized Class loadClass(String name, boolean resolve)
  507. throws ClassNotFoundException
  508. {
  509. // First check if we have permission to access the package. This
  510. // should go away once we've added support for exported packages.
  511. SecurityManager sm = System.getSecurityManager();
  512. if (sm != null) {
  513. int i = name.lastIndexOf('.');
  514. if (i != -1) {
  515. sm.checkPackageAccess(name.substring(0, i));
  516. }
  517. }
  518. return super.loadClass(name, resolve);
  519. }
  520. }