1. /*
  2. * @(#)Package.java 1.29 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.lang;
  8. import java.io.InputStream;
  9. import java.util.Enumeration;
  10. import java.util.StringTokenizer;
  11. import java.io.File;
  12. import java.io.FileInputStream;
  13. import java.io.FileNotFoundException;
  14. import java.io.IOException;
  15. import java.net.URL;
  16. import java.net.MalformedURLException;
  17. import java.security.AccessController;
  18. import java.security.PrivilegedAction;
  19. import java.util.jar.JarInputStream;
  20. import java.util.jar.Manifest;
  21. import java.util.jar.Attributes;
  22. import java.util.jar.Attributes.Name;
  23. import java.util.jar.JarException;
  24. import java.util.Map;
  25. import java.util.HashMap;
  26. import java.util.Iterator;
  27. /**
  28. * <code>Package</code> objects contain version information
  29. * about the implementation and specification of a Java package.
  30. * This versioning information is retrieved and made available
  31. * by the classloader that loaded the class(es). Typically, it is
  32. * stored in the manifest that is distributed with the classes.<p>
  33. *
  34. * The set of classes that make up the package may implement a
  35. * particular specification and if
  36. * so the specification title, version number, and vendor strings
  37. * identify that specification.
  38. * An application can ask if the package is
  39. * compatible with a particular version, see the <code>isCompatibleWith</code>
  40. * method for details. <p>
  41. *
  42. * Specification version numbers use a "Dewey Decimal"
  43. * syntax that consists of positive decimal integers
  44. * separated by periods ".", for example, "2.0" or "1.2.3.4.5.6.7".
  45. * This allows an extensible number to be used to
  46. * represent major, minor, micro, etc versions.
  47. * The version number must begin with a number. <p>
  48. *
  49. * The implementation title, version, and vendor strings identify an
  50. * implementation and are made available conveniently to enable accurate
  51. * reporting of the packages involved when a problem occurs. The contents
  52. * all three implementation strings are vendor specific. The
  53. * implementation version strings have no specified syntax and should
  54. * only be compared for equality with desired version identifers.
  55. *
  56. * Within each classloader all classes from the same java package have
  57. * the same Package object. The static methods allow a package
  58. * to be found by name or the set of all packages known
  59. * to the current class loader to be found.<p>
  60. *
  61. * @see ClassLoader#definePackage
  62. */
  63. public class Package {
  64. /**
  65. * Return the name of this package.
  66. *
  67. * @return The name of this package using the Java language dot notation
  68. * for the package. i.e java.lang
  69. */
  70. public String getName() {
  71. return pkgName;
  72. }
  73. /**
  74. * Return the title of the specification that this package implements.
  75. * @return the specification title, null is returned if it is not known.
  76. */
  77. public String getSpecificationTitle() {
  78. return specTitle;
  79. }
  80. /**
  81. * Returns the version number of the specification
  82. * that this package implements.
  83. * This version string must be a sequence of positive decimal
  84. * integers separated by "."'s and may have leading zeros.
  85. * When version strings are compared the most significant
  86. * numbers are compared.
  87. * @return the specification version, null is returned if it is not known.
  88. */
  89. public String getSpecificationVersion() {
  90. return specVersion;
  91. }
  92. /**
  93. * Return the name of the organization, vendor,
  94. * or company that owns and maintains the specification
  95. * of the classes that implement this package.
  96. * @return the specification vendor, null is returned if it is not known.
  97. */
  98. public String getSpecificationVendor() {
  99. return specVendor;
  100. }
  101. /**
  102. * Return the title of this package.
  103. * @return the title of the implementation, null is returned if it is not known.
  104. */
  105. public String getImplementationTitle() {
  106. return implTitle;
  107. }
  108. /**
  109. * Return the version of this implementation. It consists of any string
  110. * assigned by the vendor of this implementation and does
  111. * not have any particular syntax specified or expected by the Java
  112. * runtime. It may be compared for equality with other
  113. * package version strings used for this implementation
  114. * by this vendor for this package.
  115. * @return the version of the implementation, null is returned if it is not known.
  116. */
  117. public String getImplementationVersion() {
  118. return implVersion;
  119. }
  120. /**
  121. * Returns the name of the organization,
  122. * vendor or company that provided this implementation.
  123. * @return the vendor that implemented this package..
  124. */
  125. public String getImplementationVendor() {
  126. return implVendor;
  127. }
  128. /**
  129. * Returns true if this package is sealed.
  130. *
  131. * @return true if the package is sealed, false otherwise
  132. */
  133. public boolean isSealed() {
  134. return sealBase != null;
  135. }
  136. /**
  137. * Returns true if this package is sealed with respect to the specified
  138. * code source url.
  139. *
  140. * @param url the code source url
  141. */
  142. public boolean isSealed(URL url) {
  143. return url.equals(sealBase);
  144. }
  145. /**
  146. * Compare this package's specification version with a
  147. * desired version. It returns true if
  148. * this packages specification version number is greater than or equal
  149. * to the desired version number. <p>
  150. *
  151. * Version numbers are compared by sequentially comparing corresponding
  152. * components of the desired and specification strings.
  153. * Each component is converted as a decimal integer and the values
  154. * compared.
  155. * If the specification value is greater than the desired
  156. * value true is returned. If the value is less false is returned.
  157. * If the values are equal the period is skipped and the next pair of
  158. * components is compared.
  159. *
  160. * @param desired the version string of the desired version.
  161. * @return true if this package's version number is greater
  162. * than or equal to the desired version number<br>
  163. *
  164. * @exception NumberFormatException if the desired or current version
  165. * is not of the correct dotted form.
  166. */
  167. public boolean isCompatibleWith(String desired)
  168. throws NumberFormatException
  169. {
  170. if (specVersion == null || specVersion.length() < 1) {
  171. throw new NumberFormatException("Empty version string");
  172. }
  173. // Until it matches scan and compare numbers
  174. StringTokenizer dtok = new StringTokenizer(desired, ".", true);
  175. StringTokenizer stok = new StringTokenizer(specVersion, ".", true);
  176. while (dtok.hasMoreTokens() || stok.hasMoreTokens()) {
  177. int dver;
  178. int sver;
  179. if (dtok.hasMoreTokens()) {
  180. dver = Integer.parseInt(dtok.nextToken());
  181. } else
  182. dver = 0;
  183. if (stok.hasMoreTokens()) {
  184. sver = Integer.parseInt(stok.nextToken());
  185. } else
  186. sver = 0;
  187. if (sver < dver)
  188. return false; // Known to be incompatible
  189. if (sver > dver)
  190. return true; // Known to be compatible
  191. // Check for and absorb seperators
  192. if (dtok.hasMoreTokens())
  193. dtok.nextToken();
  194. if (stok.hasMoreTokens())
  195. stok.nextToken();
  196. // Compare next component
  197. }
  198. // All components numerically equal
  199. return true;
  200. }
  201. /**
  202. * Find a package by name in the callers classloader.
  203. * The callers classloader is used to find the package instance
  204. * corresponding to the named class. If the callers classloader
  205. * is null then the set of packages loaded by the system
  206. * classloader is searched to find the named package. <p>
  207. *
  208. * Packages have attributes for versions and specifications only
  209. * if the class loader created the package
  210. * instance with the appropriate attributes. Typically, those
  211. * attributes are defined in the manifests that accompany
  212. * the classes.
  213. *
  214. * @param packageName a package name, for example, java.lang.
  215. * @return the package of the requested name. It may be null if no package
  216. * information is available from the archive or codebase.
  217. */
  218. public static Package getPackage(String name) {
  219. ClassLoader l = ClassLoader.getCallerClassLoader();
  220. if (l != null) {
  221. return l.getPackage(name);
  222. } else {
  223. return getSystemPackage(name);
  224. }
  225. }
  226. /**
  227. * Get all the packages currently known for the caller's class loader.
  228. * Those packages correspond to classes loaded via or accessible
  229. * by name to that class loader. If the caller's class loader is
  230. * the bootstrap classloader, which may be represented by
  231. * <code>null</code> in some implementations, only packages corresponding
  232. * to classes loaded by the bootstrap class loader will be returned.
  233. *
  234. * @return a new array of packages known to the callers classloader.
  235. * An zero length array is returned if none are known.
  236. */
  237. public static Package[] getPackages() {
  238. ClassLoader l = ClassLoader.getCallerClassLoader();
  239. if (l != null) {
  240. return l.getPackages();
  241. } else {
  242. return getSystemPackages();
  243. }
  244. }
  245. /**
  246. * Get the package for the specified class.
  247. * The class's class loader is used to find the package instance
  248. * corresponding to the specified class. If the class loader
  249. * is the bootstrap class loader, which may be represented by
  250. * <code>null</code> in some implementations, then the set of packages
  251. * loaded by the bootstrap class loader is searched to find the package.
  252. * <p>
  253. * Packages have attributes for versions and specifications only
  254. * if the class loader created the package
  255. * instance with the appropriate attributes. Typically those
  256. * attributes are defined in the manifests that accompany
  257. * the classes.
  258. *
  259. * @param class the class to get the package of.
  260. * @return the package of the class. It may be null if no package
  261. * information is available from the archive or codebase. */
  262. static Package getPackage(Class c) {
  263. String name = c.getName();
  264. int i = name.lastIndexOf('.');
  265. if (i != -1) {
  266. name = name.substring(0, i);
  267. ClassLoader cl = c.getClassLoader();
  268. if (cl != null) {
  269. return cl.getPackage(name);
  270. } else {
  271. return getSystemPackage(name);
  272. }
  273. } else {
  274. return null;
  275. }
  276. }
  277. /**
  278. * Return the hashcode computed from the package name.
  279. * @return the hodecode computed from the package name.
  280. */
  281. public int hashCode(){
  282. return pkgName.hashCode();
  283. }
  284. /**
  285. * Returns the string representation of this Package.
  286. * Its value is the string "package " and the package name.
  287. * If the package title is defined it is appended.
  288. * If the package version is defined it is appended.
  289. * @return the string representation of the package.
  290. */
  291. public String toString() {
  292. String spec = specTitle;
  293. String ver = specVersion;
  294. if (spec != null && spec.length() > 0)
  295. spec = ", " + spec;
  296. else
  297. spec = "";
  298. if (ver != null && ver.length() > 0)
  299. ver = ", version " + ver;
  300. else
  301. ver = "";
  302. return "package " + pkgName + spec + ver;
  303. }
  304. /**
  305. * Construct a package instance with the specified version
  306. * information.
  307. * @param pkgName the name of the package
  308. * @param spectitle the title of the specification
  309. * @param specversion the version of the specification
  310. * @param specvendor the organization that maintains the specification
  311. * @param impltitle the title of the implementation
  312. * @param implversion the version of the implementation
  313. * @param implvendor the organization that maintains the implementation
  314. * @return a new package for containing the specified information.
  315. */
  316. Package(String name,
  317. String spectitle, String specversion, String specvendor,
  318. String impltitle, String implversion, String implvendor,
  319. URL sealbase)
  320. {
  321. pkgName = name;
  322. implTitle = impltitle;
  323. implVersion = implversion;
  324. implVendor = implvendor;
  325. specTitle = spectitle;
  326. specVersion = specversion;
  327. specVendor = specvendor;
  328. sealBase = sealbase;
  329. }
  330. /*
  331. * Construct a package using the attributes from the specified manifest.
  332. *
  333. * @param name the package name
  334. * @param man the optional manifest for the package
  335. * @param url the optional code source url for the package
  336. */
  337. private Package(String name, Manifest man, URL url) {
  338. String path = name.replace('.', '/').concat("/");
  339. String sealed = null;
  340. Attributes attr = man.getAttributes(path);
  341. if (attr != null) {
  342. specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
  343. specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
  344. specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
  345. implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
  346. implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
  347. implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
  348. sealed = attr.getValue(Name.SEALED);
  349. }
  350. attr = man.getMainAttributes();
  351. if (attr != null) {
  352. if (specTitle == null) {
  353. specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
  354. }
  355. if (specVersion == null) {
  356. specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
  357. }
  358. if (specVendor == null) {
  359. specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
  360. }
  361. if (implTitle == null) {
  362. implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
  363. }
  364. if (implVersion == null) {
  365. implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
  366. }
  367. if (implVendor == null) {
  368. implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
  369. }
  370. if (sealed == null) {
  371. sealed = attr.getValue(Name.SEALED);
  372. }
  373. }
  374. if ("true".equalsIgnoreCase(sealed)) {
  375. sealBase = url;
  376. }
  377. pkgName = name;
  378. }
  379. /*
  380. * Returns the loaded system package for the specified name.
  381. */
  382. static Package getSystemPackage(String name) {
  383. synchronized (pkgs) {
  384. Package pkg = (Package)pkgs.get(name);
  385. if (pkg == null) {
  386. name = name.replace('.', '/').concat("/");
  387. String fn = getSystemPackage0(name);
  388. if (fn != null) {
  389. pkg = defineSystemPackage(name, fn);
  390. }
  391. }
  392. return pkg;
  393. }
  394. }
  395. /*
  396. * Return an array of loaded system packages.
  397. */
  398. static Package[] getSystemPackages() {
  399. // First, update the system package map with new package names
  400. String[] names = getSystemPackages0();
  401. synchronized (pkgs) {
  402. for (int i = 0; i < names.length; i++) {
  403. defineSystemPackage(names[i], getSystemPackage0(names[i]));
  404. }
  405. return (Package[])pkgs.values().toArray(new Package[pkgs.size()]);
  406. }
  407. }
  408. private static Package defineSystemPackage(final String iname,
  409. final String fn)
  410. {
  411. return (Package) AccessController.doPrivileged(new PrivilegedAction() {
  412. public Object run() {
  413. String name = iname;
  414. // Get the cached code source url for the file name
  415. URL url = (URL)urls.get(fn);
  416. if (url == null) {
  417. // URL not found, so create one
  418. File file = new File(fn);
  419. try {
  420. url = file.toURL();
  421. } catch (MalformedURLException e) {
  422. }
  423. if (url != null) {
  424. urls.put(fn, url);
  425. // If loading a JAR file, then also cache the manifest
  426. if (file.isFile()) {
  427. mans.put(fn, loadManifest(fn));
  428. }
  429. }
  430. }
  431. // Convert to "."-separated package name
  432. name = name.substring(0, name.length() - 1).replace('/', '.');
  433. Package pkg;
  434. Manifest man = (Manifest)mans.get(fn);
  435. if (man != null) {
  436. pkg = new Package(name, man, url);
  437. } else {
  438. pkg = new Package(name, null, null, null,
  439. null, null, null, null);
  440. }
  441. pkgs.put(name, pkg);
  442. return pkg;
  443. }
  444. });
  445. }
  446. /*
  447. * Returns the Manifest for the specified JAR file name.
  448. */
  449. private static Manifest loadManifest(String fn) {
  450. try {
  451. FileInputStream fis = new FileInputStream(fn);
  452. JarInputStream jis = new JarInputStream(fis, false);
  453. Manifest man = jis.getManifest();
  454. jis.close();
  455. return man;
  456. } catch (IOException e) {
  457. return null;
  458. }
  459. }
  460. // The map of loaded system packages
  461. private static Map pkgs = new HashMap(31);
  462. // Maps each directory or zip file name to its corresponding url
  463. private static Map urls = new HashMap(10);
  464. // Maps each code source url for a jar file to its manifest
  465. private static Map mans = new HashMap(10);
  466. private static native String getSystemPackage0(String name);
  467. private static native String[] getSystemPackages0();
  468. /*
  469. * Private storage for the package name and attributes.
  470. */
  471. private String pkgName;
  472. private String specTitle;
  473. private String specVersion;
  474. private String specVendor;
  475. private String implTitle;
  476. private String implVersion;
  477. private String implVendor;
  478. private URL sealBase;
  479. }