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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /*
  2. * Copyright 2000-2016 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.println("Invalid number of parameters\n"
  44. + "Usage: java -cp .. GenerateManifest <package.jar> <accepted package prefixes>\n"
  45. + "Use -Dvaadin.version to specify the version to be used for the packages\n"
  46. + "Use -DincludeNumberPackages=1 to include package names which start with a number (not 100% OSGi compatible)");
  47. System.exit(1);
  48. }
  49. // Open the JAR
  50. String jarFilename = args[0];
  51. JarFile jar = null;
  52. try {
  53. jar = new JarFile(jarFilename);
  54. } catch (IOException e) {
  55. System.err.println("Unable to open JAR '" + jarFilename + "'");
  56. System.exit(1);
  57. }
  58. // Accepted packages
  59. List<String> acceptedPackagePrefixes = new ArrayList<String>();
  60. for (int i = 1; i < args.length; i++) {
  61. acceptedPackagePrefixes.add(args[i]);
  62. }
  63. boolean includeNumberPackages = false;
  64. if ("1".equals(System.getProperty("includeNumberPackages"))) {
  65. includeNumberPackages = true;
  66. }
  67. // List the included Java packages
  68. HashSet<String> packages = getPackages(jar, acceptedPackagePrefixes,
  69. includeNumberPackages);
  70. // Avoid writing empty Export-Package attribute
  71. if (packages.isEmpty()) {
  72. return;
  73. }
  74. String exportPackage = sortAndJoinPackages(packages);
  75. // Read old manifest
  76. Manifest oldMF = null;
  77. try {
  78. oldMF = jar.getManifest();
  79. } catch (IOException e) {
  80. e.printStackTrace();
  81. }
  82. Attributes mainAttributes = oldMF.getMainAttributes();
  83. String existingExportPackage = mainAttributes
  84. .getValue(EXPORT_PACKAGE_ATTRIBUTE);
  85. if (existingExportPackage != null) {
  86. exportPackage = existingExportPackage + "," + exportPackage;
  87. }
  88. // Jar must be closed before updating it below, as it's
  89. // locked in Windows until closed. (#6045)
  90. try {
  91. jar.close();
  92. } catch (IOException e) {
  93. System.err.println("Unable to close JAR '" + jarFilename + "'");
  94. }
  95. // Create the modified manifest
  96. ManifestWriter manifest = new ManifestWriter();
  97. manifest.writeAttribute(EXPORT_PACKAGE_ATTRIBUTE, exportPackage);
  98. // Update the manifest in the Jar. The jar must be closed
  99. // before this is done.
  100. int status = manifest.updateJar(jarFilename);
  101. if (status != 0) {
  102. System.exit(status);
  103. }
  104. }
  105. private static String sortAndJoinPackages(HashSet<String> packages) {
  106. // Produce an ordered listing of the package names
  107. String packageArray[] = new String[packages.size()];
  108. packages.toArray(packageArray);
  109. Arrays.sort(packageArray);
  110. StringBuilder joinedPackages = new StringBuilder();
  111. for (int i = 0; i < packageArray.length; i++) {
  112. if (i != 0) {
  113. joinedPackages.append(",");
  114. }
  115. String version = getVersion(packageArray[i]);
  116. String packageAndVersion = packageArray[i];
  117. if (version != null) {
  118. packageAndVersion += ";version=\"" + version + "\"";
  119. } else {
  120. Logger.getLogger(GeneratePackageExports.class.getName())
  121. .severe("No version defined for " + packageArray[i]);
  122. }
  123. joinedPackages.append(packageAndVersion);
  124. }
  125. return joinedPackages.toString();
  126. }
  127. /**
  128. * Tries to find version specified using system properties of type
  129. * version.<java package>. Searches for the packge and then its parents
  130. * recursively. Falls back to the "vaadin.version" system property if no
  131. * other properties are found.
  132. *
  133. * @param javaPackage
  134. * The package to determine a version for
  135. * @return A version or null if no version has been defined
  136. */
  137. private static String getVersion(String javaPackage) {
  138. String packageVersion = System.getProperty("version." + javaPackage);
  139. if (packageVersion != null) {
  140. return packageVersion;
  141. }
  142. String parentPackage = null;
  143. if (javaPackage.contains(".")) {
  144. parentPackage = javaPackage.substring(0,
  145. javaPackage.lastIndexOf('.'));
  146. String parentVersion = getVersion(parentPackage);
  147. if (parentVersion != null) {
  148. return parentVersion;
  149. }
  150. }
  151. String vaadinVersion = System.getProperty("vaadin.version");
  152. if (vaadinVersion != null) {
  153. return vaadinVersion;
  154. }
  155. return null;
  156. }
  157. private static HashSet<String> getPackages(JarFile jar,
  158. List<String> acceptedPackagePrefixes,
  159. 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).replace('/',
  174. '.');
  175. if (!includeNumberPackages
  176. && startsWithNumber.matcher(pkg).find()) {
  177. continue;
  178. }
  179. packages.add(pkg);
  180. }
  181. return packages;
  182. }
  183. private static boolean acceptEntry(String name,
  184. List<String> acceptedPackagePrefixes) {
  185. for (String prefix : acceptedPackagePrefixes) {
  186. if (name.startsWith(prefix)) {
  187. return true;
  188. }
  189. }
  190. return false;
  191. }
  192. }