You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GeneratePackageExports.java 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.buildhelpers;
  17. import java.io.IOException;
  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Enumeration;
  21. import java.util.HashSet;
  22. import java.util.List;
  23. import java.util.jar.Attributes;
  24. import java.util.jar.JarEntry;
  25. import java.util.jar.JarFile;
  26. import java.util.jar.Manifest;
  27. import java.util.logging.Logger;
  28. import java.util.regex.Pattern;
  29. /**
  30. * Generates Export-Packages attribute for OSGi compatible manifest.
  31. * <p>
  32. * Reads the included Java packages in a jar file, generates a corresponding
  33. * Export-Package attribute, and appends it to the jar's MANIFEST.MF.
  34. * <p>
  35. * See #3521 for details.
  36. *
  37. * @author magi
  38. */
  39. public class GeneratePackageExports {
  40. private static final String EXPORT_PACKAGE_ATTRIBUTE = "Export-Package";
  41. public static void main(String[] args) {
  42. if (args.length < 2) {
  43. System.err
  44. .println("Invalid number of parameters\n"
  45. + "Usage: java -cp .. GenerateManifest <package.jar> <accepted package prefixes>\n"
  46. + "Use -Dvaadin.version to specify the version to be used for the packages\n"
  47. + "Use -DincludeNumberPackages=1 to include package names which start with a number (not 100% OSGi compatible)");
  48. System.exit(1);
  49. }
  50. // Open the JAR
  51. String jarFilename = args[0];
  52. JarFile jar = null;
  53. try {
  54. jar = new JarFile(jarFilename);
  55. } catch (IOException e) {
  56. System.err.println("Unable to open JAR '" + jarFilename + "'");
  57. System.exit(1);
  58. }
  59. // Accepted packages
  60. List<String> acceptedPackagePrefixes = new ArrayList<String>();
  61. for (int i = 1; i < args.length; i++) {
  62. acceptedPackagePrefixes.add(args[i]);
  63. }
  64. boolean includeNumberPackages = false;
  65. if ("1".equals(System.getProperty("includeNumberPackages"))) {
  66. includeNumberPackages = true;
  67. }
  68. // List the included Java packages
  69. HashSet<String> packages = getPackages(jar, acceptedPackagePrefixes,
  70. includeNumberPackages);
  71. // Avoid writing empty Export-Package attribute
  72. if (packages.isEmpty()) {
  73. return;
  74. }
  75. String exportPackage = sortAndJoinPackages(packages);
  76. // Read old manifest
  77. Manifest oldMF = null;
  78. try {
  79. oldMF = jar.getManifest();
  80. } catch (IOException e) {
  81. e.printStackTrace();
  82. }
  83. Attributes mainAttributes = oldMF.getMainAttributes();
  84. String existingExportPackage = mainAttributes
  85. .getValue(EXPORT_PACKAGE_ATTRIBUTE);
  86. if (existingExportPackage != null) {
  87. exportPackage = existingExportPackage + "," + exportPackage;
  88. }
  89. // Jar must be closed before updating it below, as it's
  90. // locked in Windows until closed. (#6045)
  91. try {
  92. jar.close();
  93. } catch (IOException e) {
  94. System.err.println("Unable to close JAR '" + jarFilename + "'");
  95. }
  96. // Create the modified manifest
  97. ManifestWriter manifest = new ManifestWriter();
  98. manifest.writeAttribute(EXPORT_PACKAGE_ATTRIBUTE, exportPackage);
  99. // Update the manifest in the Jar. The jar must be closed
  100. // before this is done.
  101. int status = manifest.updateJar(jarFilename);
  102. if (status != 0) {
  103. System.exit(status);
  104. }
  105. }
  106. private static String sortAndJoinPackages(HashSet<String> packages) {
  107. // Produce an ordered listing of the package names
  108. String packageArray[] = new String[packages.size()];
  109. packages.toArray(packageArray);
  110. Arrays.sort(packageArray);
  111. StringBuilder joinedPackages = new StringBuilder();
  112. for (int i = 0; i < packageArray.length; i++) {
  113. if (i != 0) {
  114. joinedPackages.append(",");
  115. }
  116. String version = getVersion(packageArray[i]);
  117. String packageAndVersion = packageArray[i];
  118. if (version != null) {
  119. packageAndVersion += ";version=\"" + version + "\"";
  120. } else {
  121. Logger.getLogger(GeneratePackageExports.class.getName())
  122. .severe("No version defined for " + packageArray[i]);
  123. }
  124. joinedPackages.append(packageAndVersion);
  125. }
  126. return joinedPackages.toString();
  127. }
  128. /**
  129. * Tries to find version specified using system properties of type
  130. * version.<java package>. Searches for the packge and then its parents
  131. * recursively. Falls back to the "vaadin.version" system property if no
  132. * other properties are found.
  133. *
  134. * @param javaPackage
  135. * The package to determine a version for
  136. * @return A version or null if no version has been defined
  137. */
  138. private static String getVersion(String javaPackage) {
  139. String packageVersion = System.getProperty("version." + javaPackage);
  140. if (packageVersion != null) {
  141. return packageVersion;
  142. }
  143. String parentPackage = null;
  144. if (javaPackage.contains(".")) {
  145. parentPackage = javaPackage.substring(0,
  146. javaPackage.lastIndexOf('.'));
  147. String parentVersion = getVersion(parentPackage);
  148. if (parentVersion != null) {
  149. return parentVersion;
  150. }
  151. }
  152. String vaadinVersion = System.getProperty("vaadin.version");
  153. if (vaadinVersion != null) {
  154. return vaadinVersion;
  155. }
  156. return null;
  157. }
  158. private static HashSet<String> getPackages(JarFile jar,
  159. List<String> acceptedPackagePrefixes, boolean includeNumberPackages) {
  160. HashSet<String> packages = new HashSet<String>();
  161. Pattern startsWithNumber = Pattern.compile("\\.\\d");
  162. for (Enumeration<JarEntry> it = jar.entries(); it.hasMoreElements();) {
  163. JarEntry entry = it.nextElement();
  164. boolean classFile = entry.getName().endsWith(".class");
  165. boolean directory = entry.isDirectory();
  166. if (!classFile && !directory) {
  167. continue;
  168. }
  169. if (!acceptEntry(entry.getName(), acceptedPackagePrefixes)) {
  170. continue;
  171. }
  172. int lastSlash = entry.getName().lastIndexOf('/');
  173. String pkg = entry.getName().substring(0, lastSlash)
  174. .replace('/', '.');
  175. if (!includeNumberPackages && startsWithNumber.matcher(pkg).find()) {
  176. continue;
  177. }
  178. packages.add(pkg);
  179. }
  180. return packages;
  181. }
  182. private static boolean acceptEntry(String name,
  183. List<String> acceptedPackagePrefixes) {
  184. for (String prefix : acceptedPackagePrefixes) {
  185. if (name.startsWith(prefix)) {
  186. return true;
  187. }
  188. }
  189. return false;
  190. }
  191. }