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