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