package org.eclipse.buckminster.jarprocessor; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import java.util.jar.Pack200; import java.util.jar.Pack200.Packer; import java.util.jar.Pack200.Unpacker; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import org.eclipse.buckminster.runtime.IOUtils; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; abstract class RecursivePack200 implements IConstants { /** * An input stream that ignores the call to close(). */ static class NonClosingInputStream extends FilterInputStream { public NonClosingInputStream(InputStream wrapped) throws IOException { super(wrapped); } @Override public void close() throws IOException { } } private final List<String> defaultArgs; static final Pattern EFFORT_PATTERN = Pattern.compile("^-(?:E|-effort=)([0-9])$"); //$NON-NLS-1$ private static final Pattern stripDebugPattern = Pattern.compile("^-(?:G|-strip-debug)$"); //$NON-NLS-1$ private static final Pattern noKeepFileOrderPattern = Pattern.compile("^-(?:O|-no-keep-file-order)$"); //$NON-NLS-1$ private static final Pattern keepFileOrderPattern = Pattern.compile("^--keep-file-order$"); //$NON-NLS-1$ private static final Pattern segmentLimitPattern = Pattern.compile("^-(?:S|-segment-limit=)([0-9]+)$"); //$NON-NLS-1$ private static final Pattern deflateHintPattern = Pattern.compile("^-(?:H|-deflate-hint=)(true|false|keep)$"); //$NON-NLS-1$ private static final Pattern modificationTimePatter = Pattern.compile("^-(?:m|-modification-time=)(latest|keep)$"); //$NON-NLS-1$ private static final Pattern passFilePatter = Pattern.compile("^-(?:P|-pass-file=)(.+)$"); //$NON-NLS-1$ static ZipEntry createEntry(ZipEntry original) { ZipEntry copy = createEntry(original, original.getName()); copy.setExtra(original.getExtra()); return copy; } static ZipEntry createEntry(ZipEntry original, String name) { ZipEntry copy = new ZipEntry(name); copy.setComment(original.getComment()); copy.setTime(original.getTime()); return copy; } static Unpacker getUnpacker() { return Pack200.newUnpacker(); } final File tempDir; RecursivePack200(File tempDir, List<String> defaultArgs) { if (defaultArgs == null || defaultArgs.isEmpty()) this.defaultArgs = Collections.emptyList(); else this.defaultArgs = new ArrayList<String>(defaultArgs); this.tempDir = tempDir; } Packer getPacker(JarInfo jarInfo) { int passFileSuffix = 1; List<String> args = new ArrayList<String>(); jarInfo.appendArgs(args); args.addAll(defaultArgs); // Default arguments are last in the list. This HashSet // guarantees that only the first occurrence is used for // each option // HashSet<String> propsAdded = new HashSet<String>(); Packer packer = Pack200.newPacker(); Map<String, String> properties = packer.properties(); // Use this to set debug verbosity // properties.put("com.sun.java.util.jar.pack.verbose", "1"); //$NON-NLS-2$ if (!jarInfo.hasClasses()) { // This is a parent of a nested pack. No need to pack again. propsAdded.add(Packer.EFFORT); properties.put(Packer.EFFORT, "0"); //$NON-NLS-1$ } for (String arg : args) { Matcher m = EFFORT_PATTERN.matcher(arg); if (m.matches()) { if (propsAdded.add(Packer.EFFORT)) properties.put(Packer.EFFORT, m.group(1)); continue; } m = segmentLimitPattern.matcher(arg); if (m.matches()) { if (propsAdded.add(Packer.SEGMENT_LIMIT)) properties.put(Packer.SEGMENT_LIMIT, m.group(1)); continue; } m = noKeepFileOrderPattern.matcher(arg); if (m.matches()) { if (propsAdded.add(Packer.KEEP_FILE_ORDER)) properties.put(Packer.KEEP_FILE_ORDER, Packer.FALSE); continue; } m = keepFileOrderPattern.matcher(arg); if (m.matches()) { if (propsAdded.add(Packer.KEEP_FILE_ORDER)) properties.put(Packer.KEEP_FILE_ORDER, Packer.TRUE); continue; } m = stripDebugPattern.matcher(arg); if (m.matches()) { if (propsAdded.add("strip.debug")) //$NON-NLS-1$ { properties.put(Packer.CODE_ATTRIBUTE_PFX + "LineNumberTable", Packer.STRIP); //$NON-NLS-1$ properties.put(Packer.CODE_ATTRIBUTE_PFX + "LocalVariableTable", Packer.STRIP); //$NON-NLS-1$ properties.put(Packer.CLASS_ATTRIBUTE_PFX + "SourceFile", Packer.STRIP); //$NON-NLS-1$ } continue; } m = deflateHintPattern.matcher(arg); if (m.matches()) { if (propsAdded.add(Packer.DEFLATE_HINT)) properties.put(Packer.DEFLATE_HINT, m.group(1)); continue; } m = modificationTimePatter.matcher(arg); if (m.matches()) { if (propsAdded.add(Packer.MODIFICATION_TIME)) properties.put(Packer.MODIFICATION_TIME, m.group(1)); continue; } m = passFilePatter.matcher(arg); if (m.matches()) { IPath path = Path.fromOSString(m.group(1)); properties.put(Packer.PASS_FILE_PFX + '.' + passFileSuffix++, path.toPortableString()); continue; } } return packer; } void pack(JarInfo jarInfo, InputStream in, OutputStream out) throws IOException { File packInputFile = null; try { tempDir.mkdirs(); OutputStream tempOut = null; try { packInputFile = File.createTempFile("conditioned_", ".jar", tempDir); //$NON-NLS-1$//$NON-NLS-2$ tempOut = new BufferedOutputStream(new FileOutputStream(packInputFile)); IOUtils.copy(in, tempOut, new NullProgressMonitor()); } finally { IOUtils.close(tempOut); } Packer packer = getPacker(jarInfo); packer.pack(new JarFile(packInputFile), out); } finally { if (packInputFile != null) packInputFile.delete(); } } void unpack(InputStream in, OutputStream out) throws IOException { File unpackInputFile = null; try { tempDir.mkdirs(); OutputStream tempOut = null; try { unpackInputFile = File.createTempFile("packed_", ".jar.pack.gz", tempDir); //$NON-NLS-1$//$NON-NLS-2$ tempOut = new BufferedOutputStream(new FileOutputStream(unpackInputFile)); IOUtils.copy(in, tempOut, new NullProgressMonitor()); } finally { IOUtils.close(tempOut); } Unpacker unpacker = Pack200.newUnpacker(); JarOutputStream jarOut = new JarOutputStream(out); unpacker.unpack(unpackInputFile, jarOut); jarOut.finish(); } finally { if (unpackInputFile != null) unpackInputFile.delete(); } } }