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