1. /*
  2. * @(#)URLClassLoader.java 1.85 04/08/02
  3. *
  4. * Copyright 2004 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.CodeSigner;
  24. import java.security.PrivilegedAction;
  25. import java.security.PrivilegedExceptionAction;
  26. import java.security.AccessController;
  27. import java.security.AccessControlContext;
  28. import java.security.SecureClassLoader;
  29. import java.security.CodeSource;
  30. import java.security.Permission;
  31. import java.security.PermissionCollection;
  32. import sun.misc.Resource;
  33. import sun.misc.URLClassPath;
  34. import sun.net.www.ParseUtil;
  35. import sun.security.util.SecurityConstants;
  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.85, 08/02/04
  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. if (pkg.isSealed()) {
  214. // Verify that code source URL is the same.
  215. if (!pkg.isSealed(url)) {
  216. throw new SecurityException(
  217. "sealing violation: package " + pkgname + " is sealed");
  218. }
  219. } else {
  220. // Make sure we are not attempting to seal the package
  221. // at this code source URL.
  222. if ((man != null) && isSealed(pkgname, man)) {
  223. throw new SecurityException(
  224. "sealing violation: can't seal package " + pkgname +
  225. ": already loaded");
  226. }
  227. }
  228. } else {
  229. if (man != null) {
  230. definePackage(pkgname, man, url);
  231. } else {
  232. definePackage(pkgname, null, null, null, null, null, null, null);
  233. }
  234. }
  235. }
  236. // Now read the class bytes and define the class
  237. java.nio.ByteBuffer bb = res.getByteBuffer();
  238. if (bb != null) {
  239. // Use (direct) ByteBuffer:
  240. CodeSigner[] signers = res.getCodeSigners();
  241. CodeSource cs = new CodeSource(url, signers);
  242. return defineClass(name, bb, cs);
  243. } else {
  244. byte[] b = res.getBytes();
  245. // must read certificates AFTER reading bytes.
  246. CodeSigner[] signers = res.getCodeSigners();
  247. CodeSource cs = new CodeSource(url, signers);
  248. return defineClass(name, b, 0, b.length, cs);
  249. }
  250. }
  251. /**
  252. * Defines a new package by name in this ClassLoader. The attributes
  253. * contained in the specified Manifest will be used to obtain package
  254. * version and sealing information. For sealed packages, the additional
  255. * URL specifies the code source URL from which the package was loaded.
  256. *
  257. * @param name the package name
  258. * @param man the Manifest containing package version and sealing
  259. * information
  260. * @param url the code source url for the package, or null if none
  261. * @exception IllegalArgumentException if the package name duplicates
  262. * an existing package either in this class loader or one
  263. * of its ancestors
  264. * @return the newly defined Package object
  265. */
  266. protected Package definePackage(String name, Manifest man, URL url)
  267. throws IllegalArgumentException
  268. {
  269. String path = name.replace('.', '/').concat("/");
  270. String specTitle = null, specVersion = null, specVendor = null;
  271. String implTitle = null, implVersion = null, implVendor = null;
  272. String sealed = null;
  273. URL sealBase = null;
  274. Attributes attr = man.getAttributes(path);
  275. if (attr != null) {
  276. specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
  277. specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
  278. specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
  279. implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
  280. implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
  281. implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
  282. sealed = attr.getValue(Name.SEALED);
  283. }
  284. attr = man.getMainAttributes();
  285. if (attr != null) {
  286. if (specTitle == null) {
  287. specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
  288. }
  289. if (specVersion == null) {
  290. specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
  291. }
  292. if (specVendor == null) {
  293. specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
  294. }
  295. if (implTitle == null) {
  296. implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
  297. }
  298. if (implVersion == null) {
  299. implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
  300. }
  301. if (implVendor == null) {
  302. implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
  303. }
  304. if (sealed == null) {
  305. sealed = attr.getValue(Name.SEALED);
  306. }
  307. }
  308. if ("true".equalsIgnoreCase(sealed)) {
  309. sealBase = url;
  310. }
  311. return definePackage(name, specTitle, specVersion, specVendor,
  312. implTitle, implVersion, implVendor, sealBase);
  313. }
  314. /*
  315. * Returns true if the specified package name is sealed according to the
  316. * given manifest.
  317. */
  318. private boolean isSealed(String name, Manifest man) {
  319. String path = name.replace('.', '/').concat("/");
  320. Attributes attr = man.getAttributes(path);
  321. String sealed = null;
  322. if (attr != null) {
  323. sealed = attr.getValue(Name.SEALED);
  324. }
  325. if (sealed == null) {
  326. if ((attr = man.getMainAttributes()) != null) {
  327. sealed = attr.getValue(Name.SEALED);
  328. }
  329. }
  330. return "true".equalsIgnoreCase(sealed);
  331. }
  332. /**
  333. * Finds the resource with the specified name on the URL search path.
  334. *
  335. * @param name the name of the resource
  336. * @return a <code>URL</code> for the resource, or <code>null</code>
  337. * if the resource could not be found.
  338. */
  339. public URL findResource(final String name) {
  340. /*
  341. * The same restriction to finding classes applies to resources
  342. */
  343. URL url =
  344. (URL) AccessController.doPrivileged(new PrivilegedAction() {
  345. public Object run() {
  346. return ucp.findResource(name, true);
  347. }
  348. }, acc);
  349. return url != null ? ucp.checkURL(url) : null;
  350. }
  351. /**
  352. * Returns an Enumeration of URLs representing all of the resources
  353. * on the URL search path having the specified name.
  354. *
  355. * @param name the resource name
  356. * @exception IOException if an I/O exception occurs
  357. * @return an <code>Enumeration</code> of <code>URL</code>s
  358. */
  359. public Enumeration<URL> findResources(final String name)
  360. throws IOException
  361. {
  362. final Enumeration e = ucp.findResources(name, true);
  363. return new Enumeration<URL>() {
  364. private URL url = null;
  365. private boolean next() {
  366. if (url != null) {
  367. return true;
  368. }
  369. do {
  370. URL u = (URL)
  371. AccessController.doPrivileged(new PrivilegedAction() {
  372. public Object run() {
  373. if (!e.hasMoreElements())
  374. return null;
  375. return e.nextElement();
  376. }
  377. }, acc);
  378. if (u == null)
  379. break;
  380. url = ucp.checkURL(u);
  381. } while (url == null);
  382. return url != null;
  383. }
  384. public URL nextElement() {
  385. if (!next()) {
  386. throw new NoSuchElementException();
  387. }
  388. URL u = url;
  389. url = null;
  390. return u;
  391. }
  392. public boolean hasMoreElements() {
  393. return next();
  394. }
  395. };
  396. }
  397. /**
  398. * Returns the permissions for the given codesource object.
  399. * The implementation of this method first calls super.getPermissions
  400. * and then adds permissions based on the URL of the codesource.
  401. * <p>
  402. * If the protocol is "file"
  403. * and the path specifies a file, then permission to read that
  404. * file is granted. If protocol is "file" and the path is
  405. * a directory, permission is granted to read all files
  406. * and (recursively) all files and subdirectories contained in
  407. * that directory.
  408. * <p>
  409. * If the protocol is not "file", then
  410. * to connect to and accept connections from the URL's host is granted.
  411. * @param codesource the codesource
  412. * @return the permissions granted to the codesource
  413. */
  414. protected PermissionCollection getPermissions(CodeSource codesource)
  415. {
  416. PermissionCollection perms = super.getPermissions(codesource);
  417. URL url = codesource.getLocation();
  418. Permission p;
  419. URLConnection urlConnection;
  420. try {
  421. urlConnection = url.openConnection();
  422. p = urlConnection.getPermission();
  423. } catch (java.io.IOException ioe) {
  424. p = null;
  425. urlConnection = null;
  426. }
  427. if (p instanceof FilePermission) {
  428. // if the permission has a separator char on the end,
  429. // it means the codebase is a directory, and we need
  430. // to add an additional permission to read recursively
  431. String path = p.getName();
  432. if (path.endsWith(File.separator)) {
  433. path += "-";
  434. p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
  435. }
  436. } else if ((p == null) && (url.getProtocol().equals("file"))) {
  437. String path = url.getFile().replace('/', File.separatorChar);
  438. path = ParseUtil.decode(path);
  439. if (path.endsWith(File.separator))
  440. path += "-";
  441. p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
  442. } else {
  443. URL locUrl = url;
  444. if (urlConnection instanceof JarURLConnection) {
  445. locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
  446. }
  447. String host = locUrl.getHost();
  448. if (host == null)
  449. host = "localhost";
  450. p = new SocketPermission(host,
  451. SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION);
  452. }
  453. // make sure the person that created this class loader
  454. // would have this permission
  455. if (p != null) {
  456. final SecurityManager sm = System.getSecurityManager();
  457. if (sm != null) {
  458. final Permission fp = p;
  459. AccessController.doPrivileged(new PrivilegedAction() {
  460. public Object run() throws SecurityException {
  461. sm.checkPermission(fp);
  462. return null;
  463. }
  464. }, acc);
  465. }
  466. perms.add(p);
  467. }
  468. return perms;
  469. }
  470. /**
  471. * Creates a new instance of URLClassLoader for the specified
  472. * URLs and parent class loader. If a security manager is
  473. * installed, the <code>loadClass</code> method of the URLClassLoader
  474. * returned by this method will invoke the
  475. * <code>SecurityManager.checkPackageAccess</code> method before
  476. * loading the class.
  477. *
  478. * @param urls the URLs to search for classes and resources
  479. * @param parent the parent class loader for delegation
  480. * @return the resulting class loader
  481. */
  482. public static URLClassLoader newInstance(final URL[] urls,
  483. final ClassLoader parent) {
  484. // Save the caller's context
  485. AccessControlContext acc = AccessController.getContext();
  486. // Need a privileged block to create the class loader
  487. URLClassLoader ucl =
  488. (URLClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
  489. public Object run() {
  490. return new FactoryURLClassLoader(urls, parent);
  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. * Creates a new instance of URLClassLoader for the specified
  500. * URLs and default parent class loader. If a security manager is
  501. * installed, the <code>loadClass</code> method of the URLClassLoader
  502. * returned by this method will invoke the
  503. * <code>SecurityManager.checkPackageAccess</code> before
  504. * loading the class.
  505. *
  506. * @param urls the URLs to search for classes and resources
  507. * @return the resulting class loader
  508. */
  509. public static URLClassLoader newInstance(final URL[] urls) {
  510. // Save the caller's context
  511. AccessControlContext acc = AccessController.getContext();
  512. // Need a privileged block to create the class loader
  513. URLClassLoader ucl = (URLClassLoader)
  514. AccessController.doPrivileged(new PrivilegedAction() {
  515. public Object run() {
  516. return new FactoryURLClassLoader(urls);
  517. }
  518. });
  519. // Now set the context on the loader using the one we saved,
  520. // not the one inside the privileged block...
  521. ucl.acc = acc;
  522. return ucl;
  523. }
  524. }
  525. final class FactoryURLClassLoader extends URLClassLoader {
  526. FactoryURLClassLoader(URL[] urls, ClassLoader parent) {
  527. super(urls, parent);
  528. }
  529. FactoryURLClassLoader(URL[] urls) {
  530. super(urls);
  531. }
  532. public final synchronized Class loadClass(String name, boolean resolve)
  533. throws ClassNotFoundException
  534. {
  535. // First check if we have permission to access the package. This
  536. // should go away once we've added support for exported packages.
  537. SecurityManager sm = System.getSecurityManager();
  538. if (sm != null) {
  539. int i = name.lastIndexOf('.');
  540. if (i != -1) {
  541. sm.checkPackageAccess(name.substring(0, i));
  542. }
  543. }
  544. return super.loadClass(name, resolve);
  545. }
  546. }