diff options
Diffstat (limited to 'buildhelpers/src')
-rw-r--r-- | buildhelpers/src/com/vaadin/buildhelpers/GeneratePackageExports.java | 152 | ||||
-rw-r--r-- | buildhelpers/src/com/vaadin/buildhelpers/ManifestWriter.java | 130 |
2 files changed, 282 insertions, 0 deletions
diff --git a/buildhelpers/src/com/vaadin/buildhelpers/GeneratePackageExports.java b/buildhelpers/src/com/vaadin/buildhelpers/GeneratePackageExports.java new file mode 100644 index 0000000000..0deebdc9a1 --- /dev/null +++ b/buildhelpers/src/com/vaadin/buildhelpers/GeneratePackageExports.java @@ -0,0 +1,152 @@ +package com.vaadin.buildhelpers; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Vector; +import java.util.jar.Attributes; +import java.util.jar.Attributes.Name; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +/** + * Generates Export-Packages attribute for OSGi compatible manifest. + * + * Reads the included Java packages in Vaadin JAR, generates a corresponding + * MANIFEST.MF file, and replaces the dummy one in the JAR with the generated + * one. + * + * See #3521 for details. + * + * @author magi + */ +public class GeneratePackageExports { + public static final String VAADIN_PACKAGE_PATH_PREFIX = "com/vaadin/"; + public static final String GOOGLE_PACKAGE_PATH_PREFIX = "com/google/"; + + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("Invalid number of parameters\n" + + "Usage: java -cp .. GenerateManifest <package.jar>"); + System.exit(1); + } + + // Open the JAR + String jarFilename = args[0]; + JarFile jar = null; + try { + jar = new JarFile(jarFilename); + } catch (IOException e) { + System.err.println("Unable to open JAR '" + jarFilename + "'"); + System.exit(1); + } + + // List the included Java packages + HashSet<String> packages = new HashSet<String>(); + for (Enumeration<JarEntry> it = jar.entries(); it.hasMoreElements();) { + JarEntry entry = it.nextElement(); + if ((entry.getName().startsWith(VAADIN_PACKAGE_PATH_PREFIX) || entry + .getName().startsWith(GOOGLE_PACKAGE_PATH_PREFIX)) + && entry.getName().endsWith(".class")) { + int lastSlash = entry.getName().lastIndexOf('/'); + String pkg = entry.getName().substring(0, lastSlash) + .replace('/', '.'); + packages.add(pkg); + } + } + + // List theme packages + for (Enumeration<JarEntry> it = jar.entries(); it.hasMoreElements();) { + JarEntry entry = it.nextElement(); + if (entry.isDirectory() + && entry.getName().startsWith("VAADIN/themes")) { + // Strip ending slash + int lastSlash = entry.getName().lastIndexOf('/'); + String pkg = entry.getName().substring(0, lastSlash) + .replace('/', '.'); + packages.add(pkg); + } + } + + // Replacement for the "Export-Package" attribute in the manifest + String exportPackage = ""; + + // Produce an ordered listing of the package names + String packageArray[] = new String[packages.size()]; + packages.toArray(packageArray); + Arrays.sort(packageArray); + for (int i = 0; i < packageArray.length; i++) { + if (i == 0) { + exportPackage = packageArray[i]; + } else { + exportPackage += ", " + packageArray[i]; + } + } + + // Read old manifest + Manifest oldMF = null; + try { + oldMF = jar.getManifest(); + } catch (IOException e) { + e.printStackTrace(); + } + + // Read main attributes + Attributes mainAtts = oldMF.getMainAttributes(); + Vector<String> keys = new Vector<String>(mainAtts.size()); + for (Iterator<Object> attrit = mainAtts.keySet().iterator(); attrit + .hasNext();) { + Name name = (Name) attrit.next(); + keys.add(name.toString()); + } + + // Jar must be closed before updating it below, as it's + // locked in Windows until closed. (#6045) + try { + jar.close(); + } catch (IOException e) { + System.err.println("Unable to close JAR '" + jarFilename + "'"); + } + + // Put the manifest version as the first line + String orderedKeys[] = new String[keys.size()]; + keys.toArray(orderedKeys); + Arrays.sort(orderedKeys); // Must sort to be able to search + int mvPos = Arrays.binarySearch(orderedKeys, "Manifest-Version"); + orderedKeys[mvPos] = orderedKeys[0]; // Swap + orderedKeys[0] = "Manifest-Version"; + + // This final ordering is just for esthetic reasons and + // in practice unnecessary and will actually be messed up + // when the 'jar' command reads the manifest + Arrays.sort(orderedKeys, 1, orderedKeys.length - 1); + + // Create the modified manifest + ManifestWriter manifest = new ManifestWriter(); + for (int i = 0; i < orderedKeys.length; i++) { + // Skip an existing Export-Package attribute + if (orderedKeys[i].equals("Export-Package")) { + // Copy the attribute to the modified manifest + manifest.writeAttribute(orderedKeys[i], + mainAtts.getValue(orderedKeys[i])); + } + } + + // Add the Export-Package attribute at the end of the manifest. + // The alternative would be replacing an existing attribute in + // the loop above, but it's not guaranteed that it exists. + manifest.writeAttribute("Export-Package", exportPackage); + + // Update the manifest in the Jar. The jar must be closed + // before this is done. + int status = manifest.updateJar(jarFilename); + + if (status != 0) { + System.exit(status); + } + } + +} diff --git a/buildhelpers/src/com/vaadin/buildhelpers/ManifestWriter.java b/buildhelpers/src/com/vaadin/buildhelpers/ManifestWriter.java new file mode 100644 index 0000000000..a6130e2a46 --- /dev/null +++ b/buildhelpers/src/com/vaadin/buildhelpers/ManifestWriter.java @@ -0,0 +1,130 @@ +/** + * + */ +package com.vaadin.buildhelpers; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Date; +import java.util.jar.Manifest; + +public class ManifestWriter { + StringBuffer buffer = new StringBuffer(); + + public ManifestWriter() { + } + + /** + * Writes a manifest attribute to a temporary buffer. + * + * @param name Attribute name + * @param value Attribute value + */ + public void writeAttribute(String name, String value) { + int linelen = name.length() + 2; + buffer.append(name); + buffer.append(": "); + + String remainingValue = value; + while (linelen + remainingValue.length() > 72) { + int fitsLine = 72 - linelen; + buffer.append(remainingValue.substring(0, fitsLine) + "\n "); + remainingValue = remainingValue.substring(fitsLine); + linelen = 1; + } + buffer.append(remainingValue + "\n"); + } + + /** + * Writes the manifest to given JAR file. + * + * The manifest must be created with {@code #writeAttribute(String, String)} + * before calling this write. + * + * @param jarFilename File name of the JAR in which the manifest is written + * @return 0 on success, nonzero value on error + */ + int updateJar(String jarFilename) { + int status = 0; + + // Determine a temporary file name + String newMfPrefix = "vaadin-manifest-" + (new Date()).getTime(); + File newMfFile = null; + try { + newMfFile = File.createTempFile(newMfPrefix, ".mf"); + } catch (IOException e) { + System.err.println("Creating temp file failed"); + status = 1; + } + + // Write the manifest to the temporary file + if (status == 0) { + FileOutputStream fos = null; + try { + fos = new FileOutputStream(newMfFile); + fos.write(getBytes()); + fos.close(); + } catch (IOException e) { + System.err.println("Writing to file '"+newMfFile.getAbsolutePath() +"' failed because: " + e.getMessage()); + status = 1; + } + } + + // Check that the manifest is OK + if (status == 0) { + Manifest checkMf = new Manifest(); + FileInputStream is; + try { + is = new FileInputStream(newMfFile); + checkMf.read(is); + } catch (IOException e) { + System.err.println("Reading from file '"+newMfFile.getAbsolutePath() +"' failed because: " + e.getMessage()); + status = 1; + } + } + + // Update the manifest in the Jar + if (status == 0) { + System.out.println("Updating manifest in JAR " + jarFilename); + try { + // The "mf" order must correspond with manifest-jarfile order + Process process = Runtime.getRuntime().exec(new String[]{"jar", "umf", newMfFile.getAbsolutePath(), jarFilename}); + int exitValue = process.waitFor(); + if (exitValue != 0) { + InputStream jarErr = process.getErrorStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(jarErr)); + while (reader.ready()) { + System.err.println("jar: " + reader.readLine()); + } + System.err.println("The 'jar' command returned with exit value " + exitValue); + status = 1; + } + } catch (IOException e) { + System.err.println("Failed to execute 'jar' command. " + e.getMessage()); + status = 1; + } catch (InterruptedException e) { + System.err.println("Execution of 'jar' command was interrupted. " + e.getMessage()); + status = 1; + } + } + + // Remove the temporary file + if (newMfFile != null) + newMfFile.delete(); + + return status; + } + + public String toString() { + return buffer.toString(); + } + + public byte[] getBytes() { + return buffer.toString().getBytes(); + } +}
\ No newline at end of file |