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