diff options
20 files changed, 2085 insertions, 0 deletions
diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/.classpath b/bundles/org.eclipse.equinox.p2.jarprocessor/.classpath new file mode 100644 index 000000000..7cdeb7319 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/CDC-1.1%Foundation-1.1"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/.project b/bundles/org.eclipse.equinox.p2.jarprocessor/.project new file mode 100644 index 000000000..b3a3e9e0c --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.equinox.p2.jarprocessor</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.equinox.p2.jarprocessor/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..186d5a85d --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +#Tue Aug 28 11:20:14 EDT 2007 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2 +org.eclipse.jdt.core.compiler.compliance=1.4 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning +org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning +org.eclipse.jdt.core.compiler.source=1.3 diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/.settings/org.eclipse.pde.core.prefs b/bundles/org.eclipse.equinox.p2.jarprocessor/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 000000000..69a27179c --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,4 @@ +#Tue Aug 28 11:19:59 EDT 2007 +eclipse.preferences.version=1 +pluginProject.extensions=false +resolve.requirebundle=false diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.jarprocessor/META-INF/MANIFEST.MF new file mode 100644 index 000000000..1d8bca319 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/META-INF/MANIFEST.MF @@ -0,0 +1,9 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Jarprocessor Bundle +Bundle-SymbolicName: org.eclipse.equinox.p2.jarprocessor +Bundle-Version: 0.1.0.qualifier +Bundle-RequiredExecutionEnvironment: CDC-1.1/Foundation-1.1, + J2SE-1.4 +Export-Package: org.eclipse.equinox.internal.p2.jarprocessor;x-internal:=true, + org.eclipse.equinox.p2.jarprocessor diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/about.html b/bundles/org.eclipse.equinox.p2.jarprocessor/about.html new file mode 100644 index 000000000..460233046 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/about.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> +<title>About</title> +</head> +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>June 2, 2006</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p> + +</body> +</html>
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/build.properties b/bundles/org.eclipse.equinox.p2.jarprocessor/build.properties new file mode 100644 index 000000000..34d2e4d2d --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/CommandStep.java b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/CommandStep.java new file mode 100644 index 000000000..2570a4f51 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/CommandStep.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM - Initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.jarprocessor; + +import java.io.File; +import java.util.List; +import java.util.Properties; +import org.eclipse.equinox.p2.jarprocessor.IProcessStep; + +/** + * @author aniefer@ca.ibm.com + * + */ +public abstract class CommandStep implements IProcessStep { + protected String command = null; + protected String extension = null; + private Properties options = null; + protected boolean verbose = false; + + public CommandStep(Properties options, String command, String extension, boolean verbose) { + this.command = command; + this.extension = extension; + this.options = options; + this.verbose = verbose; + } + + protected static int execute(String[] cmd) { + return execute(cmd, false); + } + + protected static int execute(String[] cmd, boolean verbose) { + Runtime runtime = Runtime.getRuntime(); + Process proc = null; + try { + proc = runtime.exec(cmd); + StreamProcessor errorStreamProcessor = new StreamProcessor(proc.getErrorStream(), StreamProcessor.STDERR, verbose); //$NON-NLS-1$ + StreamProcessor outputStreamProcessor = new StreamProcessor(proc.getInputStream(), StreamProcessor.STDOUT, verbose); //$NON-NLS-1$ + errorStreamProcessor.start(); + outputStreamProcessor.start(); + } catch (Exception e) { + if(verbose) { + System.out.println("Error executing command " + Utils.concat(cmd)); //$NON-NLS-1$ + e.printStackTrace(); + } + return -1; + } + try { + int result = proc.waitFor(); + return result; + } catch (InterruptedException e) { + if(verbose) + e.printStackTrace(); + } + return -1; + } + + public Properties getOptions() { + if(options == null) + options = new Properties(); + return options; + } + + public void adjustInf(File input, Properties inf, List containers) { + //nothing + } +} diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/Main.java b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/Main.java new file mode 100644 index 000000000..d06022a1f --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/Main.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2006-2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM - Initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.jarprocessor; + +import java.io.File; +import org.eclipse.equinox.p2.jarprocessor.JarProcessor; +import org.eclipse.equinox.p2.jarprocessor.JarProcessorExecutor; + +public class Main { + + public static class Options { + public String outputDir = "."; //$NON-NLS-1$ + public String signCommand = null; + public boolean pack = false; + public boolean repack = false; + public boolean unpack = false; + public boolean verbose = false; + public boolean processAll = false; + public File input = null; + } + + private static void printUsage() { + System.out.println("[-option ...]... input"); //$NON-NLS-1$ + System.out.println("The following options are supported:"); //$NON-NLS-1$ + System.out.println("-processAll process all jars, regardless of whether they were previously normalized"); //$NON-NLS-1$ + System.out.println(" By default only normalized jars will be processed."); //$NON-NLS-1$ + System.out.println("-repack normalize jars "); //$NON-NLS-1$ + System.out.println("-sign <command> sign jars using <command>"); //$NON-NLS-1$ + System.out.println("-pack pack the jars. pack and repack are redundant unless"); //$NON-NLS-1$ + System.out.println(" sign is also specified."); //$NON-NLS-1$ + System.out.println("-unpack unpack pack.gz files. Unpack is mutually exclusive"); //$NON-NLS-1$ + System.out.println(" with repack, sign and pack."); //$NON-NLS-1$ + System.out.println(); + System.out.println("-outputDir <dir> the output directory"); //$NON-NLS-1$ + System.out.println("-verbose verbose mode "); //$NON-NLS-1$ + } + + public static Options processArguments(String[] args) { + if (args.length == 0) { + printUsage(); + return null; + } + + Options options = new Options(); + int i = 0; + for (; i < args.length - 1; i++) { + if (args[i].equals("-pack")) {//$NON-NLS-1$ + options.pack = true; + } else if (args[i].equals("-unpack")) { //$NON-NLS-1$ + options.unpack = true; + } else if (args[i].equals("-sign") && i < args.length - 2) { //$NON-NLS-1$ + if (args[i + 1].startsWith("-")) { //$NON-NLS-1$ + printUsage(); + return null; + } + options.signCommand = args[++i]; + } else if (args[i].equals("-repack")) { //$NON-NLS-1$ + options.repack = true; + } else if (args[i].equals("-outputDir") && i < args.length - 2) { //$NON-NLS-1$ + if (args[i + 1].startsWith("-")) { //$NON-NLS-1$ + printUsage(); + return null; + } + options.outputDir = args[++i]; + } else if (args[i].equals("-verbose")) { //$NON-NLS-1$ + options.verbose = true; + } else if (args[i].equals("-processAll")) { //$NON-NLS-1$ + options.processAll = true; + } + } + + options.input = new File(args[i]); + + String problemMessage = null; + String inputName = options.input.getName(); + if (options.unpack) { + if (!JarProcessor.canPerformUnpack()) { + problemMessage = "The unpack200 command cannot be found."; //$NON-NLS-1$ + } else if (options.input.isFile() && !inputName.endsWith(".zip") && !inputName.endsWith(".pack.gz")) { //$NON-NLS-1$ //$NON-NLS-2$ + problemMessage = "Input file is not a pack.gz file."; //$NON-NLS-1$ + } else if (options.pack || options.repack || options.signCommand != null) { + problemMessage = "Pack, repack or sign cannot be specified with unpack."; //$NON-NLS-1$ + } + } else { + if (options.input.isFile() && !inputName.endsWith(".zip") && !inputName.endsWith(".jar")) { //$NON-NLS-1$ //$NON-NLS-2$ + problemMessage = "Input file is not a jar file."; //$NON-NLS-1$ + } else if ((options.pack || options.repack) && !JarProcessor.canPerformPack()) { + problemMessage = "The pack200 command can not be found."; //$NON-NLS-1$ + } + } + if(problemMessage != null){ + System.out.println(problemMessage); + System.out.println(); + printUsage(); + return null; + } + + return options; + } + + /** + * @param args + */ + public static void main(String[] args) { + Options options = processArguments(args); + if (options == null) + return; + new JarProcessorExecutor().runJarProcessor(options); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/PackStep.java b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/PackStep.java new file mode 100644 index 000000000..1006dbe42 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/PackStep.java @@ -0,0 +1,200 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM - Initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.jarprocessor; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class PackStep extends CommandStep { + + protected static String packCommand = null; + private static Boolean canPack = null; + + private String arguments = null; + private Set exclusions = Collections.EMPTY_SET; + + public static boolean canPack() { + if (canPack != null) + return canPack.booleanValue(); + + String[] locations = Utils.getPack200Commands("pack200"); //$NON-NLS-1$ + if (locations == null) { + canPack = Boolean.FALSE; + packCommand = null; + return false; + } + + int result; + for (int i = 0; i < locations.length; i++) { + if (locations[i] == null) + continue; + result = execute(new String[] {locations[i], "-V"}); //$NON-NLS-1$ + if (result == 0) { + packCommand = locations[i]; + canPack = Boolean.TRUE; + return true; + } + } + + canPack = Boolean.FALSE; + return false; + } + + public PackStep(Properties options) { + super(options, null, null, false); + exclusions = Utils.getPackExclusions(options); + } + + public PackStep(Properties options, boolean verbose) { + super(options, null, null, verbose); + exclusions = Utils.getPackExclusions(options); + } + + public String recursionEffect(String entryName) { + if (canPack() && entryName.endsWith(".jar") && !exclusions.contains(entryName)) { //$NON-NLS-1$ + return entryName + Utils.PACKED_SUFFIX; + } + return null; + } + + public File preProcess(File input, File workingDirectory, List containers) { + return null; + } + + public File postProcess(File input, File workingDirectory, List containers) { + if (canPack() && packCommand != null) { + Properties inf = Utils.getEclipseInf(input, verbose); + if (!shouldPack(input, containers, inf)) + return null; + File outputFile = new File(workingDirectory, input.getName() + Utils.PACKED_SUFFIX); + try { + String[] cmd = getCommand(input, outputFile, inf, containers); + int result = execute(cmd, verbose); + if (result != 0 && verbose) + System.out.println("Error: " + result + " was returned from command: " + Utils.concat(cmd)); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (IOException e) { + if (verbose) + e.printStackTrace(); + return null; + } + return outputFile; + } + return null; + } + + protected boolean shouldPack(File input, List containers, Properties inf) { + //1: exclude by containers + // innermost jar is first on the list, it can override outer jars + for (Iterator iterator = containers.iterator(); iterator.hasNext();) { + Properties container = (Properties) iterator.next(); + if (container.containsKey(Utils.MARK_EXCLUDE_CHILDREN_PACK)) { + if (Boolean.valueOf(container.getProperty(Utils.MARK_EXCLUDE_CHILDREN_PACK)).booleanValue()) { + if (verbose) + System.out.println(input.getName() + " is excluded from pack200 by its containers."); + return false; + } + break; + } + } + + //2: excluded by self + if (inf != null && inf.containsKey(Utils.MARK_EXCLUDE_PACK) && Boolean.valueOf(inf.getProperty(Utils.MARK_EXCLUDE_PACK)).booleanValue()) { + if (verbose) + System.out.println("Excluding " + input.getName() + " from " + getStepName()); //$NON-NLS-1$ //$NON-NLS-2$ + return false; + } + + return true; + } + + protected String[] getCommand(File input, File outputFile, Properties inf, List containers) throws IOException { + String[] cmd = null; + String arguments = getArguments(input, inf, containers); + if (arguments != null && arguments.length() > 0) { + String[] args = Utils.toStringArray(arguments, ","); //$NON-NLS-1$ + cmd = new String[3 + args.length]; + cmd[0] = packCommand; + System.arraycopy(args, 0, cmd, 1, args.length); + cmd[cmd.length - 2] = outputFile.getCanonicalPath(); + cmd[cmd.length - 1] = input.getCanonicalPath(); + } else { + cmd = new String[] {packCommand, outputFile.getCanonicalPath(), input.getCanonicalPath()}; + } + return cmd; + } + + protected String getArguments(File input, Properties inf, List containers) { + if (arguments != null) + return arguments; + //1: Explicitly marked in our .inf file + if (inf != null && inf.containsKey(Utils.PACK_ARGS)) { + arguments = inf.getProperty(Utils.PACK_ARGS); + return arguments; + } + + //2: Defaults set in one of our containing jars + for (Iterator iterator = containers.iterator(); iterator.hasNext();) { + Properties container = (Properties) iterator.next(); + if (container.containsKey(Utils.DEFAULT_PACK_ARGS)) { + arguments = container.getProperty(Utils.DEFAULT_PACK_ARGS); + return arguments; + } + } + + //3: Set by name in outside pack.properties file + Properties options = getOptions(); + String argsKey = input.getName() + Utils.PACK_ARGS_SUFFIX; + if (options.containsKey(argsKey)) { + arguments = options.getProperty(argsKey); + return arguments; + } + + //4: Set by default in outside pack.properties file + if (options.containsKey(Utils.DEFAULT_PACK_ARGS)) { + arguments = options.getProperty(Utils.DEFAULT_PACK_ARGS); + return arguments; + } + + if (arguments == null) + arguments = ""; + return arguments; + } + + public String getStepName() { + return "Pack"; //$NON-NLS-1$ + } + + public void adjustInf(File input, Properties inf, List containers) { + if (input == null || inf == null) + return; + + //don't be verbose to check if we should mark the inf + boolean v = verbose; + verbose = false; + if (!shouldPack(input, containers, inf)) { + verbose = v; + return; + } + verbose = v; + + //mark as conditioned + inf.put(Utils.MARK_PROPERTY, "true"); //$NON-NLS-1$ + + //record arguments used + String arguments = inf.getProperty(Utils.PACK_ARGS); + if (arguments == null) { + arguments = getArguments(input, inf, containers); + if (arguments != null && arguments.length() > 0) + inf.put(Utils.PACK_ARGS, arguments); + } + } +} diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/PackUnpackStep.java b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/PackUnpackStep.java new file mode 100644 index 000000000..79707a69f --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/PackUnpackStep.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM - Initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.jarprocessor; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +/** + * @author aniefer@ca.ibm.com + * + */ +public class PackUnpackStep extends PackStep { + private Set exclusions = null; + + public PackUnpackStep(Properties options) { + super(options); + exclusions = Utils.getPackExclusions(options); + } + + public PackUnpackStep(Properties options, boolean verbose) { + super(options, verbose); + exclusions = Utils.getPackExclusions(options); + } + + public String recursionEffect(String entryName) { + if (canPack() && entryName.endsWith(".jar") && !exclusions.contains(entryName)) { //$NON-NLS-1$ + return entryName; + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.update.internal.jarprocessor.PackStep#postProcess(java.io.File, java.io.File, java.util.LinkedList) + */ + public File postProcess(File input, File workingDirectory, List containers) { + if (canPack() && packCommand != null && input != null) { + Properties inf = Utils.getEclipseInf(input, verbose); + if (!shouldPack(input, containers, inf)) + return null; + File tempFile = new File(workingDirectory, "temp_" + input.getName()); //$NON-NLS-1$ + try { + String[] tmp = getCommand(input, tempFile, inf, containers); + String[] cmd = new String[tmp.length + 1]; + cmd[0] = tmp[0]; + cmd[1] = "-r"; //$NON-NLS-1$ + System.arraycopy(tmp, 1, cmd, 2, tmp.length - 1); + + int result = execute(cmd, verbose); + if (result == 0 && tempFile.exists()) { + File finalFile = new File(workingDirectory, input.getName()); + if (finalFile.exists()) + finalFile.delete(); + tempFile.renameTo(finalFile); + return finalFile; + } else if (verbose) { + System.out.println("Error: " + result + " was returned from command: " + Utils.concat(cmd)); //$NON-NLS-1$ //$NON-NLS-2$ + } + } catch (IOException e) { + if (verbose) + e.printStackTrace(); + return null; + } + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.update.internal.jarprocessor.PackStep#preProcess(java.io.File, java.io.File, java.util.LinkedList) + */ + public File preProcess(File input, File workingDirectory, List containers) { + return null; + } + + public String getStepName() { + return "Repack"; //$NON-NLS-1$ + } +} diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/SignCommandStep.java b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/SignCommandStep.java new file mode 100644 index 000000000..b8c6ec091 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/SignCommandStep.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2006-2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM - Initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.jarprocessor; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class SignCommandStep extends CommandStep { + private Set exclusions = null; + + public SignCommandStep(Properties options, String command) { + super(options, command, ".jar", false); //$NON-NLS-1$ + exclusions = Utils.getSignExclusions(options); + } + + public SignCommandStep(Properties options, String command, boolean verbose) { + super(options, command, ".jar", verbose); //$NON-NLS-1$ + exclusions = Utils.getSignExclusions(options); + } + + /* (non-Javadoc) + * @see org.eclipse.update.jarprocessor.IProcessStep#recursionEffect(java.lang.String) + */ + public String recursionEffect(String entryName) { + if (entryName.endsWith(extension) && !exclusions.contains(entryName)) + return entryName; + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.update.jarprocessor.IProcessStep#preProcess(java.io.File, java.io.File) + */ + public File preProcess(File input, File workingDirectory, List containers) { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.update.jarprocessor.IProcessStep#postProcess(java.io.File, java.io.File) + */ + public File postProcess(File input, File workingDirectory, List containers) { + if (command != null && input != null && shouldSign(input, containers)) { + try { + String[] cmd = new String[] {command, input.getCanonicalPath()}; + int result = execute(cmd, verbose); + if (result == 0) { + return input; + } else if (verbose) { + System.out.println("Error: " + result + " was returned from command: " + Utils.concat(cmd)); //$NON-NLS-1$ //$NON-NLS-2$ + } + } catch (IOException e) { + if (verbose) { + e.printStackTrace(); + } + } + } + return null; + } + + public boolean shouldSign(File input, List containers) { + Properties inf = null; + + //1: Are we excluded from signing by our parents? + //innermost jar is first on the list, it overrides outer jars + for (Iterator iterator = containers.iterator(); iterator.hasNext();) { + inf = (Properties) iterator.next(); + if (inf.containsKey(Utils.MARK_EXCLUDE_CHILDREN_SIGN)){ + if(Boolean.valueOf(inf.getProperty(Utils.MARK_EXCLUDE_CHILDREN_SIGN)).booleanValue()) { + if (verbose) + System.out.println(input.getName() + "is excluded from signing by its containers."); //$NON-NLS-1$ //$NON-NLS-2$ + return false; + } + break; + } + } + + //2: Is this jar itself marked as exclude? + inf = Utils.getEclipseInf(input, verbose); + if (inf != null && inf.containsKey(Utils.MARK_EXCLUDE_SIGN) && Boolean.valueOf(inf.getProperty(Utils.MARK_EXCLUDE_SIGN)).booleanValue()) { + if (verbose) + System.out.println("Excluding " + input.getName() + " from signing."); //$NON-NLS-1$ //$NON-NLS-2$ + return false; + } + return true; + } + + public String getStepName() { + return "Sign"; //$NON-NLS-1$ + } +} diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/StreamProcessor.java b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/StreamProcessor.java new file mode 100644 index 000000000..cdab81291 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/StreamProcessor.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM - Initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.jarprocessor; + +import java.io.*; + +public class StreamProcessor extends Thread { + public static final String STDERR = "STDERR"; //$NON-NLS-1$ + public static final String STDOUT = "STDOUT"; //$NON-NLS-1$ + + private InputStream inputStream; + private String name; + private boolean verbose; + + public StreamProcessor(InputStream is, String name, boolean verbose) { + this.inputStream = is; + this.name = name; + this.verbose = verbose; + } + + public void run() { + try { + InputStreamReader isr = new InputStreamReader(inputStream); + BufferedReader br = new BufferedReader(isr); + while (true) { + String s = br.readLine(); + if (s == null) { + break; + } + if (verbose) { + if (STDERR.equals(name)) + System.err.println(name + ": " + s); //$NON-NLS-1$ + else + System.out.println(name + ": " + s); //$NON-NLS-1$ + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/UnpackStep.java b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/UnpackStep.java new file mode 100644 index 000000000..276d78d59 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/UnpackStep.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM - Initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.jarprocessor; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Properties; + +/** + * @author aniefer + * + */ +public class UnpackStep extends CommandStep { + public static final String UNPACKER_PROPERTY = "org.eclipse.update.jarprocessor.Unpacker"; //$NON-NLS-1$ + private static Boolean canUnpack = null; + private static String unpackCommand = null; + + public static boolean canUnpack() { + if (canUnpack != null) + return canUnpack.booleanValue(); + + String[] locations = Utils.getPack200Commands("unpack200"); //$NON-NLS-1$ + if (locations == null) { + canUnpack = Boolean.FALSE; + unpackCommand = null; + return false; + } + + int result; + for (int i = 0; i < locations.length; i++) { + if (locations[i] == null) + continue; + result = execute(new String[] {locations[i], "-V"}); //$NON-NLS-1$ + if (result == 0) { + unpackCommand = locations[i]; + canUnpack = Boolean.TRUE; + return true; + } + } + + canUnpack = Boolean.FALSE; + return false; + } + + public UnpackStep(Properties options) { + super(options, null, null, false); + } + + public UnpackStep(Properties options, boolean verbose) { + super(options, null, null, verbose); + } + + /* (non-Javadoc) + * @see org.eclipse.update.jarprocessor.IProcessStep#recursionEffect(java.lang.String) + */ + public String recursionEffect(String entryName) { + if (canUnpack() && entryName.endsWith(Utils.PACKED_SUFFIX)) { + return entryName.substring(0, entryName.length() - Utils.PACKED_SUFFIX.length()); + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.update.jarprocessor.IProcessStep#preProcess(java.io.File, java.io.File) + */ + public File preProcess(File input, File workingDirectory, List containers) { + if (canUnpack() && unpackCommand != null) { + String name = input.getName(); + if (name.endsWith(Utils.PACKED_SUFFIX)) { + name = name.substring(0, name.length() - Utils.PACKED_SUFFIX.length()); + + File unpacked = new File(workingDirectory, name); + File parent = unpacked.getParentFile(); + if (!parent.exists()) + parent.mkdirs(); + try { + String options = getOptions().getProperty(input.getName() + ".unpack.args"); //$NON-NLS-1$ + String[] cmd = null; + if (options != null) { + cmd = new String[] {unpackCommand, options, input.getCanonicalPath(), unpacked.getCanonicalPath()}; + } else { + cmd = new String[] {unpackCommand, input.getCanonicalPath(), unpacked.getCanonicalPath()}; + } + int result = execute(cmd, verbose); + if (result != 0 && verbose) + System.out.println("Error: " + result + " was returned from command: " + Utils.concat(cmd)); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (IOException e) { + if (verbose) + e.printStackTrace(); + return null; + } + return unpacked; + } + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.update.jarprocessor.IProcessStep#postProcess(java.io.File, java.io.File) + */ + public File postProcess(File input, File workingDirectory, List containers) { + return null; + } + + public String getStepName() { + return "Unpack"; //$NON-NLS-1$ + } +} diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/Utils.java b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/Utils.java new file mode 100644 index 000000000..744e92f6d --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/Utils.java @@ -0,0 +1,327 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM - Initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.jarprocessor; + +import java.io.*; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.zip.ZipException; + +/** + * @author aniefer@ca.ibm.com + * + */ +public class Utils { + public static final String MARK_FILE_NAME = "META-INF/eclipse.inf"; //$NON-NLS-1$ + + /* + * Properties found in outer pack.properties file + */ + //comma separated list of jars to exclude from sigining + public static final String SIGN_EXCLUDES = "sign.excludes"; //$NON-NLS-1$ + //comma separated list of jars to exlclude from packing + public static final String PACK_EXCLUDES = "pack.excludes"; //$NON-NLS-1$ + //Suffix used when specifying arguments to use when running pack200 on a jar + public static final String PACK_ARGS_SUFFIX = ".pack.args"; //$NON-NLS-1$ + + /* + * Properties found in both pack.properties and eclipse.inf + */ + // Default arguments to use when running pack200. + // Affects all jars when specified in pack.properties, affects children when specified in eclipse.inf + public static final String DEFAULT_PACK_ARGS = "pack200.default.args"; //$NON-NLS-1$ + + /* + * Properties found in eclipse.inf file + */ + //This jar has been conditioned with pack200 + public static final String MARK_PROPERTY = "pack200.conditioned"; //$NON-NLS-1$ + //Exclude this jar from processing + public static final String MARK_EXCLUDE = "jarprocessor.exclude"; //$NON-NLS-1$ + //Exclude this jar from pack200 + public static final String MARK_EXCLUDE_PACK = "jarprocessor.exclude.pack"; //$NON-NLS-1$ + //Exclude this jar from signing + public static final String MARK_EXCLUDE_SIGN = "jarprocessor.exclude.sign"; //$NON-NLS-1$ + //Exclude this jar's children from processing + public static final String MARK_EXCLUDE_CHILDREN = "jarprocessor.exclude.children"; + //Exclude this jar's children from pack200 + public static final String MARK_EXCLUDE_CHILDREN_PACK = "jarprocessor.exclude.children.pack"; + //Exclude this jar's children from signing + public static final String MARK_EXCLUDE_CHILDREN_SIGN = "jarprocessor.exclude.children.sign"; + //Arguments used in pack200 for this jar + public static final String PACK_ARGS = "pack200.args"; //$NON-NLS-1$ + + public static final String PACK200_PROPERTY = "org.eclipse.update.jarprocessor.pack200"; //$NON-NLS-1$ + public static final String JRE = "@jre"; //$NON-NLS-1$ + public static final String PATH = "@path"; //$NON-NLS-1$ + public static final String NONE = "@none"; //$NON-NLS-1$ + + public static final String PACKED_SUFFIX = ".pack.gz"; //$NON-NLS-1$ + public static final String JAR_SUFFIX = ".jar"; //$NON-NLS-1$ + + public static final FileFilter JAR_FILTER = new FileFilter() { + public boolean accept(File pathname) { + return pathname.isFile() && pathname.getName().endsWith(".jar"); //$NON-NLS-1$ + } + }; + + public static final FileFilter PACK_GZ_FILTER = new FileFilter() { + public boolean accept(File pathname) { + return pathname.isFile() && pathname.getName().endsWith(PACKED_SUFFIX); + } + }; + + public static void close(Object stream) { + if (stream != null) { + try { + if (stream instanceof InputStream) + ((InputStream) stream).close(); + else if (stream instanceof OutputStream) + ((OutputStream) stream).close(); + else if (stream instanceof JarFile) + ((JarFile) stream).close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * get the set of commands to try to execute pack/unpack + * @param cmd the command, either "pack200" or "unpack200" + * @return String [] or null + */ + public static String[] getPack200Commands(String cmd) { + String[] locations = null; + String prop = System.getProperty(PACK200_PROPERTY); + String javaHome = System.getProperty("java.home"); //$NON-NLS-1$ + if (NONE.equals(prop)) { + return null; + } else if (JRE.equals(prop)) { + locations = new String[] {javaHome + "/bin/" + cmd}; //$NON-NLS-1$ + } else if (PATH.equals(prop)) { + locations = new String[] {cmd}; + } else if (prop == null) { + locations = new String[] {javaHome + "/bin/" + cmd, cmd}; //$NON-NLS-1$ + } else { + locations = new String[] {prop + "/" + cmd}; //$NON-NLS-1$ + } + return locations; + } + + /** + * Transfers all available bytes from the given input stream to the given + * output stream. Closes both streams if close == true, regardless of failure. + * Flushes the destination stream if close == false + * + * @param source + * @param destination + * @param close + * @throws IOException + */ + public static void transferStreams(InputStream source, OutputStream destination, boolean close) throws IOException { + source = new BufferedInputStream(source); + destination = new BufferedOutputStream(destination); + try { + byte[] buffer = new byte[8192]; + while (true) { + int bytesRead = -1; + if ((bytesRead = source.read(buffer)) == -1) + break; + destination.write(buffer, 0, bytesRead); + } + } finally { + if (close) { + close(source); + close(destination); + } else { + destination.flush(); + } + } + } + + /** + * Deletes all the files and directories from the given root down (inclusive). + * Returns false if we could not delete some file or an exception occurred + * at any point in the deletion. + * Even if an exception occurs, a best effort is made to continue deleting. + */ + public static boolean clear(java.io.File root) { + boolean result = clearChildren(root); + try { + if (root.exists()) + result &= root.delete(); + } catch (Exception e) { + result = false; + } + return result; + } + + /** + * Deletes all the files and directories from the given root down, except for + * the root itself. + * Returns false if we could not delete some file or an exception occurred + * at any point in the deletion. + * Even if an exception occurs, a best effort is made to continue deleting. + */ + public static boolean clearChildren(java.io.File root) { + boolean result = true; + if (root.isDirectory()) { + String[] list = root.list(); + // for some unknown reason, list() can return null. + // Just skip the children If it does. + if (list != null) + for (int i = 0; i < list.length; i++) + result &= clear(new java.io.File(root, list[i])); + } + return result; + } + + public static Set getPackExclusions(Properties properties) { + if (properties == null) + return Collections.EMPTY_SET; + + String packExcludes = properties.getProperty(PACK_EXCLUDES); + if (packExcludes != null) { + String[] excludes = toStringArray(packExcludes, ","); //$NON-NLS-1$ + Set packExclusions = new HashSet(); + for (int i = 0; i < excludes.length; i++) { + packExclusions.add(excludes[i]); + } + return packExclusions; + } + return Collections.EMPTY_SET; + } + + public static Set getSignExclusions(Properties properties) { + if (properties == null) + return Collections.EMPTY_SET; + String signExcludes = properties.getProperty(SIGN_EXCLUDES); + if (signExcludes != null) { + String[] excludes = toStringArray(signExcludes, ","); //$NON-NLS-1$ + Set signExclusions = new HashSet(); + for (int i = 0; i < excludes.length; i++) { + signExclusions.add(excludes[i]); + } + return signExclusions; + } + return Collections.EMPTY_SET; + } + + public static String concat(String[] array) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < array.length; i++) { + if (i > 0) + buffer.append(' '); + buffer.append(array[i]); + } + return buffer.toString(); + } + + public static String[] toStringArray(String input, String separator) { + StringTokenizer tokenizer = new StringTokenizer(input, separator); + int count = tokenizer.countTokens(); + String[] result = new String[count]; + for (int i = 0; i < count; i++) { + result[i] = tokenizer.nextToken().trim(); + } + return result; + } + + /** + * Get the properties from the eclipse.inf file from the given jar. If the file is not a jar, null is returned. + * If the file is a jar, but does not contain an eclipse.inf file, an empty Properties object is returned. + * @param jarFile + * @return The eclipse.inf properties for the given jar file + */ + public static Properties getEclipseInf(File jarFile, boolean verbose) { + if (jarFile == null || !jarFile.exists()) { + if (verbose) + System.out.println("Failed to obtain eclipse.inf due to missing jar file: " + jarFile); + return null; + } + JarFile jar = null; + try { + jar = new JarFile(jarFile, false); + } catch (ZipException e) { + //not a jar, don't bother logging this. + return null; + } catch (IOException e) { + if (verbose) { + System.out.println("Failed to obtain eclipse.inf due to IOException: " + jarFile); + e.printStackTrace(); + } + return null; + } + try { + JarEntry mark = jar.getJarEntry(MARK_FILE_NAME); + if (mark != null) { + InputStream in = jar.getInputStream(mark); + Properties props = new Properties(); + props.load(in); + in.close(); + return props; + } + return new Properties(); + } catch (IOException e) { + if (verbose) { + System.out.println("Failed to obtain eclipse.inf due to IOException: " + jarFile); + e.printStackTrace(); + } + return null; + } finally { + close(jar); + } + } + + public static boolean shouldSkipJar(File input, boolean processAll, boolean verbose) { + Properties inf = getEclipseInf(input, verbose); + if (inf == null) { + //not a jar, could be a pack.gz + return false; + } + String exclude = inf.getProperty(MARK_EXCLUDE); + + //was marked as exclude, we should skip + if (exclude != null && Boolean.valueOf(exclude).booleanValue()) + return true; + + //process all was set, don't skip + if (processAll) + return false; + + //otherwise, we skip if not marked marked + String marked = inf.getProperty(MARK_PROPERTY); + return !Boolean.valueOf(marked).booleanValue(); + } + + /** + * Stores the given properties in the output stream. We store the properties + * in sorted order so that the signing hash doesn't change if the properties didn't change. + * @param props + * @param stream + */ + public static void storeProperties(Properties props, OutputStream stream) { + PrintStream printStream = new PrintStream(stream); + printStream.print("#Processed using Jarprocessor\n"); //$NON-NLS-1$ + SortedMap sorted = new TreeMap(props); + for (Iterator iter = sorted.keySet().iterator(); iter.hasNext();) { + String key = (String) iter.next(); + printStream.print(key); + printStream.print(" = "); //$NON-NLS-1$ + printStream.print(sorted.get(key)); + printStream.print("\n"); + + } + printStream.flush(); + } +} diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/ZipProcessor.java b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/ZipProcessor.java new file mode 100644 index 000000000..6278a7775 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/ZipProcessor.java @@ -0,0 +1,245 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM - Initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.jarprocessor; + +import java.io.*; +import java.util.*; +import java.util.zip.*; +import org.eclipse.equinox.p2.jarprocessor.IProcessStep; +import org.eclipse.equinox.p2.jarprocessor.JarProcessor; + +/** + * @author aniefer@ca.ibm.com + * + */ +public class ZipProcessor { + + private IProcessStep signStep = null; + private IProcessStep packStep = null; + private IProcessStep packUnpackStep = null; + private IProcessStep unpackStep = null; + + private String workingDirectory = null; + private Properties properties = null; + private Set packExclusions = null; + private Set signExclusions = null; + private String command = null; + private boolean packing = false; + private boolean signing = false; + private boolean repacking = false; + private boolean unpacking = false; + private boolean verbose = false; + private boolean processAll = false; + + public void setWorkingDirectory(String dir) { + workingDirectory = dir; + } + + public String getWorkingDirectory() { + if (workingDirectory == null) + workingDirectory = "."; //$NON-NLS-1$ + return workingDirectory; + } + + public void setSignCommand(String command) { + this.command = command; + this.signing = (command != null); + } + + public void setPack(boolean pack) { + this.packing = pack; + } + + public void setRepack(boolean repack) { + this.repacking = repack; + } + + public void setUnpack(boolean unpack) { + this.unpacking = unpack; + } + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + public void setProcessAll(boolean all) { + this.processAll = all; + } + + public void processZip(File zipFile) throws ZipException, IOException { + if (verbose) + System.out.println("Processing " + zipFile.getPath()); //$NON-NLS-1$ + ZipFile zip = new ZipFile(zipFile); + initialize(zip); + + String extension = unpacking ? "pack.gz" : ".jar"; //$NON-NLS-1$ //$NON-NLS-2$ + File tempDir = new File(getWorkingDirectory(), "temp_" + zipFile.getName()); //$NON-NLS-1$ + JarProcessor processor = new JarProcessor(); + processor.setVerbose(verbose); + processor.setProcessAll(processAll); + processor.setWorkingDirectory(tempDir.getCanonicalPath()); + if (unpacking) { + processor.addProcessStep(unpackStep); + } + + File outputFile = new File(getWorkingDirectory(), zipFile.getName() + ".temp"); //$NON-NLS-1$ + File parent = outputFile.getParentFile(); + if (!parent.exists()) + parent.mkdirs(); + ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(outputFile)); + Enumeration entries = zip.entries(); + if (entries.hasMoreElements()) { + for (ZipEntry entry = (ZipEntry) entries.nextElement(); entry != null; entry = entries.hasMoreElements() ? (ZipEntry) entries.nextElement() : null) { + String name = entry.getName(); + + InputStream entryStream = zip.getInputStream(entry); + + boolean pack = packing && !packExclusions.contains(name); + boolean sign = signing && !signExclusions.contains(name); + boolean repack = repacking && !packExclusions.contains(name); + + File extractedFile = null; + + if (entry.getName().endsWith(extension) && (pack || sign || repack || unpacking)) { + extractedFile = new File(tempDir, name); + parent = extractedFile.getParentFile(); + if (!parent.exists()) + parent.mkdirs(); + if (verbose) + System.out.println("Extracting " + entry.getName()); //$NON-NLS-1$ + FileOutputStream extracted = new FileOutputStream(extractedFile); + Utils.transferStreams(entryStream, extracted, true); // this will close the stream + entryStream = null; + + boolean skip = Utils.shouldSkipJar(extractedFile, processAll, verbose); + if (skip) { + //skipping this file + entryStream = new FileInputStream(extractedFile); + if (verbose) + System.out.println(entry.getName() + " is not marked, skipping."); //$NON-NLS-1$ + } else { + if (unpacking) { + File result = processor.processJar(extractedFile); + name = name.substring(0, name.length() - extractedFile.getName().length()) + result.getName(); + extractedFile = result; + } else { + if (repack || sign) { + processor.clearProcessSteps(); + if (repack) + processor.addProcessStep(packUnpackStep); + if (sign) + processor.addProcessStep(signStep); + extractedFile = processor.processJar(extractedFile); + } + if (pack) { + processor.clearProcessSteps(); + processor.addProcessStep(packStep); + File modifiedFile = processor.processJar(extractedFile); + if (modifiedFile.exists()) { + try { + String newName = name.substring(0, name.length() - extractedFile.getName().length()) + modifiedFile.getName(); + if (verbose) { + System.out.println("Adding " + newName + " to " + outputFile.getPath()); //$NON-NLS-1$ //$NON-NLS-2$ + System.out.println(); + } + ZipEntry zipEntry = new ZipEntry(newName); + entryStream = new FileInputStream(modifiedFile); + zipOut.putNextEntry(zipEntry); + Utils.transferStreams(entryStream, zipOut, false); //we want to keep zipOut open + entryStream.close(); + Utils.clear(modifiedFile); + } catch (IOException e) { + Utils.close(entryStream); + if (verbose) { + e.printStackTrace(); + System.out.println("Warning: Problem reading " + modifiedFile.getPath() + "."); + } + } + entryStream = null; + } else if (verbose) { + System.out.println("Warning: " + modifiedFile.getPath() + " not found."); + } + } + } + if (extractedFile.exists()) { + try { + entryStream = new FileInputStream(extractedFile); + } catch (IOException e) { + if (verbose) { + e.printStackTrace(); + System.out.println("Warning: Problem reading " + extractedFile.getPath() + "."); + } + } + } + + if (verbose && entryStream != null) { + System.out.println("Adding " + name + " to " + outputFile.getPath()); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } + if (entryStream != null) { + ZipEntry newEntry = new ZipEntry(name); + try { + zipOut.putNextEntry(newEntry); + Utils.transferStreams(entryStream, zipOut, false); + zipOut.closeEntry(); + } catch (ZipException e) { + if(verbose) { + System.out.println("Warning: " + name + " already exists in " + outputFile.getName() + ". Skipping."); + } + } + entryStream.close(); + } + + if (extractedFile != null) + Utils.clear(extractedFile); + + if (verbose) { + System.out.println(); + System.out.println("Processing " + zipFile.getPath()); //$NON-NLS-1$ + } + } + } + zipOut.close(); + zip.close(); + + File finalFile = new File(getWorkingDirectory(), zipFile.getName()); + if (finalFile.exists()) + finalFile.delete(); + outputFile.renameTo(finalFile); + Utils.clear(tempDir); + } + + private void initialize(ZipFile zip) { + ZipEntry entry = zip.getEntry("pack.properties"); //$NON-NLS-1$ + properties = new Properties(); + if (entry != null) { + InputStream stream = null; + try { + stream = zip.getInputStream(entry); + properties.load(stream); + } catch (IOException e) { + if (verbose) + e.printStackTrace(); + } finally { + Utils.close(stream); + } + } + + packExclusions = Utils.getPackExclusions(properties); + signExclusions = Utils.getSignExclusions(properties); + + packUnpackStep = new PackUnpackStep(properties, verbose); + packStep = new PackStep(properties, verbose); + signStep = new SignCommandStep(properties, command, verbose); + unpackStep = new UnpackStep(properties, verbose); + } +} diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/pack-readme.html b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/pack-readme.html new file mode 100644 index 000000000..ef5fe5e0b --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/internal/p2/jarprocessor/pack-readme.html @@ -0,0 +1,82 @@ +<!DOCTYPE html PUBLIC "-//w3c//dtd html 4.0 transitional//en"> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>Eclipse update packing tool readme</title> +</head> +<body> +<h1>Eclipse update packing tool</h1> + +<h3>Overview</h3> +The update packing tool processes a hierarchy of arbitrarily nested +JARs and ZIP files. It is a generic utility that performs a depth first traversal of +a nested hierarchy of ZIPs and JARs, performs various commands on +each of the JARs in the hierarchy, and then rebuilds the same hierarchy +of ZIPs and JARs again. Currently its main functions are: +<ul> + <li>Packing JARs using the Java 1.5 <a href="http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/pack200.html">pack200</a> + command.</li> + <li>Unpacking PACK.GZs using the Java 1.5 <a href="http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/unpack200.html">unpack200</a> + command.</li> + <li>Normalizing JARs for future compression by pack200. This is accomplished + by running the pack200 command with the <tt>--repack</tt> command line argument.</li> + <li>Signing JARs to allow for authentication of the origin of JARs. This is accomplished by + running a supplied command (typically the command will just be a wrapper around + the Java <a href="http://java.sun.com/j2se/1.3/docs/tooldocs/win32/jarsigner.html">jarsigner</a> tool).</li> +</ul> +The packing tool is used in the following contexts: +<ul> + <li>During a PDE build, to prepare JARs for uploading to an Eclipse + update site. In this usage, it is used to both nomalize JAR contents + (pack200 -repack), and sign JARs.</li> + <li>On an update site, to convert traditional JAR content into the + compressed pack200 format.</li> + <li>From an Eclipse client application during update, to convert + compressed pack200 format content into executable JAR files.</li> +</ul> +<h3>Tool usage</h3> +To run the packing tool, you need a 1.5 JRE installed. The tool is run +by invoking Java as follows: + +<pre> + java jarprocessor.jar [options] input +</pre> + +Where <tt>input</tt> is either a zip file, a directory, or a JAR (or a pack.gz file). All files ending +in ".jar" or ".pack.gz" in the provided zip or directory hierarchy +will be processed. +The following additional command line arguments are supported: +<ul> +<li>-repack : Normalize the jars using pack200 <tt>--repack</tt></li> +<li>-sign <cmd> : signs the jars by executing the provided command. +The command will be provided a single argument that will be the full path of the JAR to process. +</li> +<li>-pack : for each input in JAR form, produce a corresponding output +in packed form. For an input "a.jar", the output is a.jar.pack.gz. +</li> +<li>-unpack : for each input in packed form, produce a corresponding output +in unpacked form. For an input "a.jar.pack.gz", the output is "a.jar". -unpack is mutually exclusive with -repack, -pack and -sign.</li> +<li>-outputDir <dir> : The directory to put the tool's output into. If the input was a zip file, then an output zip file will be +created containg all the output files. If the input was a directory, for each input file there is a corresponding output file in the output directory. By default the current working directory is used. If the input is in the same +directory as the output, the input files may be overwritten.</li> +</ul> + +Additionally, when the input is a zip file, it may contain a file called +<tt>pack.properties</tt>. The pack.properties file supports the following values: +<ul> +<li>pack.excludes = jarName[, jarName]* : A comma-delimited list of JARs that should not be packed or repacked. +</li> +<li>sign.excludes = jarName[, jarName]* : A comma-delimited list of JARs that should not be signed. +</li> +<li><jarname>.pack.args = option[, option]* : A comma-delimited list of additional arguments that should +be passed to pack200 when packing any jar with name <jarname>. +</ul> +</p> +<p> +<font size=-1> +Copyright (c) IBM Corporation and others 2006. All rights reserved. This program and the accompanying materials +are made available under the terms of the +<a href="http://www.eclipse.org/legal/epl-v10.html">Eclipse Public License v1.0</a>. +</font> +</body> +</html> diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/p2/jarprocessor/IProcessStep.java b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/p2/jarprocessor/IProcessStep.java new file mode 100644 index 000000000..d5d0ba7ef --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/p2/jarprocessor/IProcessStep.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM - Initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.jarprocessor; + +import java.io.File; +import java.util.List; +import java.util.Properties; + +/** + * @author aniefer@ca.ibm.com + * + */ +public interface IProcessStep { + + /** + * The effect of this processing step if the JarProcessor was to recurse on this entry. + * Return null if this step will not do anything with this entry. + * Return the new entryName if this step will modify this entry on recursion. + * @param entryName + * @return + */ + public String recursionEffect(String entryName); + + /** + * Perform some processing on the input file before the JarProcessor considers the entries for recursion. + * return the file containing the result of the processing + * @param input + * @param workingDirectory + * @param containers inf properties for containing jars, innermost jar is first on the list + * @return + */ + public File preProcess(File input, File workingDirectory, List containers); + + /** + * Perform some processing on the input file after the JarProcessor returns from recursion + * return the file containing the result of the processing + * @param input + * @param workingDirectory + * @param containers inf properties for containing jars, innermost jar is first on the list + * @return + */ + public File postProcess(File input, File workingDirectory, List containers); + + /** + * Return the name of this process step + * @return + */ + public String getStepName(); + + /** + * Adjust any properties in the eclipse.inf as appropriate for this step + * @param input + * @param inf + * @param containers inf properties for containing jars, innermost jar is first on the list + */ + public void adjustInf(File input, Properties inf, List containers); +} diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/p2/jarprocessor/JarProcessor.java b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/p2/jarprocessor/JarProcessor.java new file mode 100644 index 000000000..fbaa7c7f9 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/p2/jarprocessor/JarProcessor.java @@ -0,0 +1,394 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM - Initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.jarprocessor; + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import org.eclipse.equinox.internal.p2.jarprocessor.*; + +/** + * @author aniefer@ca.ibm.com + * + */ +public class JarProcessor { + private List steps = new ArrayList(); + private String workingDirectory = ""; //$NON-NLS-1$ + private int depth = -1; + private boolean verbose = false; + private boolean processAll = false; + private LinkedList containingInfs = new LinkedList(); + + static public JarProcessor getUnpackProcessor(Properties properties) { + if (!canPerformUnpack()) + throw new UnsupportedOperationException(); + JarProcessor processor = new JarProcessor(); + processor.addProcessStep(new UnpackStep(properties)); + return processor; + } + + static public JarProcessor getPackProcessor(Properties properties) { + if (!canPerformPack()) + throw new UnsupportedOperationException(); + JarProcessor processor = new JarProcessor(); + processor.addProcessStep(new PackStep(properties)); + return processor; + } + + static public boolean canPerformPack() { + return PackStep.canPack(); + } + + static public boolean canPerformUnpack() { + return UnpackStep.canUnpack(); + } + + public String getWorkingDirectory() { + return workingDirectory; + } + + public void setWorkingDirectory(String dir) { + if(dir != null) + workingDirectory = dir; + } + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + public void setProcessAll(boolean all){ + this.processAll = all; + } + + public void addProcessStep(IProcessStep step) { + steps.add(step); + } + + public void clearProcessSteps() { + steps.clear(); + } + + /** + * Recreate a jar file. The replacements map specifies entry names to be replaced, the replacements are + * expected to be found in directory. + * + * @param jar - The input jar + * @param outputJar - the output + * @param replacements - map of entryName -> new entryName + * @param directory - location to find file for new entryName + * @throws IOException + */ + private void recreateJar(JarFile jar, JarOutputStream outputJar, Map replacements, File directory, Properties inf) throws IOException { + InputStream in = null; + boolean marked = false; + try { + Enumeration entries = jar.entries(); + for (JarEntry entry = (JarEntry) entries.nextElement(); entry != null; entry = entries.hasMoreElements() ? (JarEntry) entries.nextElement() : null) { + File replacement = null; + JarEntry newEntry = null; + if (replacements.containsKey(entry.getName())) { + String name = (String) replacements.get(entry.getName()); + replacement = new File(directory, name); + if (name != null) { + if (replacement.exists()) { + try { + in = new BufferedInputStream(new FileInputStream(replacement)); + newEntry = new JarEntry(name); + } catch (Exception e) { + if (verbose) { + e.printStackTrace(); + System.out.println("Warning: Problem reading " +replacement.getPath() + ", using " + jar.getName() + File.separator + entry.getName() + " instead."); + } + } + } else if (verbose) { + System.out.println("Warning: " + replacement.getPath() + " not found, using " + jar.getName() + File.separator + entry.getName() + " instead."); + } + } + } + if (newEntry == null) { + try { + in = new BufferedInputStream(jar.getInputStream(entry)); + newEntry = new JarEntry(entry.getName()); + } catch( Exception e ) { + if(verbose) { + e.printStackTrace(); + System.out.println("ERROR: problem reading " + entry.getName() + " from " + jar.getName()); + } + continue; + } + } + newEntry.setTime(entry.getTime()); + outputJar.putNextEntry(newEntry); + if (entry.getName().equals(Utils.MARK_FILE_NAME)) { + //The eclipse.inf file was read in earlier, don't need to reread it, just write it out now + Utils.storeProperties(inf, outputJar); + marked = true; + } else { + Utils.transferStreams(in, outputJar, false); + } + outputJar.closeEntry(); + in.close(); + + //delete the nested jar file + if (replacement != null) { + replacement.delete(); + } + } + if (!marked) { + JarEntry entry = new JarEntry(Utils.MARK_FILE_NAME); + outputJar.putNextEntry(entry); + Utils.storeProperties(inf, outputJar); + outputJar.closeEntry(); + } + } finally { + Utils.close(outputJar); + Utils.close(jar); + Utils.close(in); + } + } + + private String recursionEffect(String entryName) { + String result = null; + for (Iterator iter = steps.iterator(); iter.hasNext();) { + IProcessStep step = (IProcessStep) iter.next(); + + result = step.recursionEffect(entryName); + if (result != null) + entryName = result; + } + return result; + } + + private void extractEntries(JarFile jar, File tempDir, Map data, Properties inf) throws IOException { + if(inf != null ) { + //skip if excluding children + if(inf.containsKey(Utils.MARK_EXCLUDE_CHILDREN)){ + String excludeChildren = inf.getProperty(Utils.MARK_EXCLUDE_CHILDREN); + if( Boolean.valueOf(excludeChildren).booleanValue() ) + if(verbose){ + for(int i = 0; i <= depth; i++) + System.out.print(" "); //$NON-NLS-1$ + System.out.println("Children of " + jar.getName() + "are excluded from processing."); + } + return; + } + } + + Enumeration entries = jar.entries(); + if (entries.hasMoreElements()) { + for (JarEntry entry = (JarEntry) entries.nextElement(); entry != null; entry = entries.hasMoreElements() ? (JarEntry) entries.nextElement() : null) { + String name = entry.getName(); + String newName = recursionEffect(name); + if (newName != null) { + if(verbose){ + for(int i = 0; i <= depth; i++) + System.out.print(" "); //$NON-NLS-1$ + System.out.println("Processing nested file: " + name); //$NON-NLS-1$ + } + //extract entry to temp directory + File extracted = new File(tempDir, name); + File parentDir = extracted.getParentFile(); + if (!parentDir.exists()) + parentDir.mkdirs(); + + InputStream in = null; + OutputStream out = null; + try { + in = jar.getInputStream(entry); + out = new BufferedOutputStream(new FileOutputStream(extracted)); + Utils.transferStreams(in, out, true); //this will close both streams + } finally { + Utils.close(in); + Utils.close(out); + } + extracted.setLastModified(entry.getTime()); + + //recurse + containingInfs.addFirst(inf); + String dir = getWorkingDirectory(); + setWorkingDirectory(parentDir.getCanonicalPath()); + File result = processJar(extracted); + + newName = name.substring(0, name.length() - extracted.getName().length()) + result.getName(); + data.put(name, newName); + + setWorkingDirectory(dir); + containingInfs.removeFirst(); + + //delete the extracted item leaving the recursion result + if (!name.equals(newName)) + extracted.delete(); + } + } + } + } + + private File preProcess(File input, File tempDir) { + File result = null; + for (Iterator iter = steps.iterator(); iter.hasNext();) { + IProcessStep step = (IProcessStep) iter.next(); + result = step.preProcess(input, tempDir, containingInfs); + if (result != null) + input = result; + } + return input; + } + + private File postProcess(File input, File tempDir) { + File result = null; + for (Iterator iter = steps.iterator(); iter.hasNext();) { + IProcessStep step = (IProcessStep) iter.next(); + result = step.postProcess(input, tempDir, containingInfs); + if (result != null) + input = result; + } + return input; + } + + private void adjustInf(File input, Properties inf) { + for (Iterator iter = steps.iterator(); iter.hasNext();) { + IProcessStep step = (IProcessStep) iter.next(); + step.adjustInf(input, inf, containingInfs); + } + } + + public File processJar(File input) throws IOException { + ++depth; + long lastModified = input.lastModified(); + File workingDir = new File(getWorkingDirectory()); + if (!workingDir.exists()) + workingDir.mkdirs(); + + boolean skip = Utils.shouldSkipJar(input, processAll, verbose); + if (depth == 0 && verbose) { + if (skip) + System.out.println("Skipping " + input.getPath()); //$NON-NLS-1$ + else { + System.out.print("Running "); //$NON-NLS-1$ + for (Iterator iter = steps.iterator(); iter.hasNext();) { + IProcessStep step = (IProcessStep) iter.next(); + System.out.print(step.getStepName() + " "); //$NON-NLS-1$ + } + System.out.println("on " + input.getPath()); //$NON-NLS-1$ + } + } + + if (skip) { + //This jar was not marked as conditioned, and we are only processing conditioned jars, so do nothing + --depth; + return input; + } + + //pre + File workingFile = preProcess(input, workingDir); + + //Extract entries from jar and recurse on them + File tempDir = null; + if (depth == 0) { + tempDir = new File(workingDir, "temp." + workingFile.getName()); //$NON-NLS-1$ + } else { + File parent = workingDir.getParentFile(); + tempDir = new File(parent, "temp_" + depth + '_' + workingFile.getName()); //$NON-NLS-1$ + } + + JarFile jar = new JarFile(workingFile, false); + Map replacements = new HashMap(); + Properties inf = Utils.getEclipseInf(workingFile, verbose); + extractEntries(jar, tempDir, replacements, inf); + + if (inf != null) + adjustInf(workingFile, inf); + + //Recreate the jar with replacements. + //TODO: This is not strictly necessary if we didn't change the inf file and didn't change any content + File tempJar = null; + tempJar = new File(tempDir, workingFile.getName()); + File parent = tempJar.getParentFile(); + if (!parent.exists()) + parent.mkdirs(); + JarOutputStream jarOut = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(tempJar))); + recreateJar(jar, jarOut, replacements, tempDir, inf); + + jar.close(); + if (tempJar != null) { + if (!workingFile.equals(input)) { + workingFile.delete(); + } + workingFile = tempJar; + } + + //post + File result = postProcess(workingFile, workingDir); + + //have to normalize after the post steps + normalize(result, workingDir); + + if (!result.equals(workingFile) && !workingFile.equals(input)) + workingFile.delete(); + if (!result.getParentFile().equals(workingDir)) { + File finalFile = new File(workingDir, result.getName()); + if (finalFile.exists()) + finalFile.delete(); + result.renameTo(finalFile); + result = finalFile; + } + + if (tempDir.exists()) + Utils.clear(tempDir); + + result.setLastModified(lastModified); + --depth; + return result; + } + + private void normalize(File input, File workingDirectory) { + if(input.getName().endsWith(Utils.PACKED_SUFFIX)) { + //not a jar + return; + } + try { + File tempJar = new File(workingDirectory, "temp_" + input.getName()); //$NON-NLS-1$ + JarFile jar = null; + try { + jar = new JarFile(input, false); + } catch (JarException e) { + //not a jar + return ; + } + JarOutputStream jarOut = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(tempJar))); + InputStream in = null; + try { + Enumeration entries = jar.entries(); + for (JarEntry entry = (JarEntry) entries.nextElement(); entry != null; entry = entries.hasMoreElements() ? (JarEntry) entries.nextElement() : null) { + JarEntry newEntry = new JarEntry(entry.getName()); + newEntry.setTime(entry.getTime()); + in = new BufferedInputStream(jar.getInputStream(entry)); + jarOut.putNextEntry(newEntry); + Utils.transferStreams(in, jarOut, false); + jarOut.closeEntry(); + in.close(); + } + } finally { + Utils.close(jarOut); + Utils.close(jar); + Utils.close(in); + } + tempJar.setLastModified(input.lastModified()); + input.delete(); + tempJar.renameTo(input); + } catch (IOException e) { + if (verbose) { + System.out.println("Error normalizing jar " + input.getName()); //$NON-NLS-1$ + e.printStackTrace(); + } + } + } +} diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/p2/jarprocessor/JarProcessorExecutor.java b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/p2/jarprocessor/JarProcessorExecutor.java new file mode 100644 index 000000000..92c762562 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/src/org/eclipse/equinox/p2/jarprocessor/JarProcessorExecutor.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.equinox.p2.jarprocessor; + +import java.io.*; +import java.util.Properties; +import java.util.zip.ZipException; +import org.eclipse.equinox.internal.p2.jarprocessor.*; +import org.eclipse.equinox.internal.p2.jarprocessor.Main.Options; + +public class JarProcessorExecutor { + public void runJarProcessor(Options options) { + if (options.input.isFile() && options.input.getName().endsWith(".zip")) { //$NON-NLS-1$ + ZipProcessor processor = new ZipProcessor(); + processor.setWorkingDirectory(options.outputDir); + processor.setSignCommand(options.signCommand); + processor.setPack(options.pack); + processor.setRepack(options.repack || (options.pack && options.signCommand != null)); + processor.setUnpack(options.unpack); + processor.setVerbose(options.verbose); + processor.setProcessAll(options.processAll); + try { + processor.processZip(options.input); + } catch (ZipException e) { + if (options.verbose) + e.printStackTrace(); + } catch (IOException e) { + if (options.verbose) + e.printStackTrace(); + } + } else { + JarProcessor processor = new JarProcessor(); + JarProcessor packProcessor = null; + + processor.setWorkingDirectory(options.outputDir); + processor.setProcessAll(options.processAll); + processor.setVerbose(options.verbose); + + //load options file + Properties properties = new Properties(); + if (options.input.isDirectory()) { + File packProperties = new File(options.input, "pack.properties"); + if (packProperties.exists() && packProperties.isFile()) { + InputStream in = null; + try { + in = new BufferedInputStream(new FileInputStream(packProperties)); + properties.load(in); + } catch (IOException e) { + if (options.verbose) + e.printStackTrace(); + } finally { + Utils.close(in); + } + } + } + + if (options.unpack) + addUnpackStep(processor, properties, options); + + if (options.repack || (options.pack && options.signCommand != null)) + addPackUnpackStep(processor, properties, options); + + if (options.signCommand != null) + addSignStep(processor, properties, options); + + if (options.pack) { + packProcessor = new JarProcessor(); + packProcessor.setWorkingDirectory(options.outputDir); + packProcessor.setProcessAll(options.processAll); + packProcessor.setVerbose(options.verbose); + addPackStep(packProcessor, properties, options); + } + + try { + process(options.input, options.unpack ? Utils.PACK_GZ_FILTER : Utils.JAR_FILTER, options.verbose, processor, packProcessor); + } catch (FileNotFoundException e) { + if (options.verbose) + e.printStackTrace(); + } + } + } + + protected void process(File input, FileFilter filter, boolean verbose, JarProcessor processor, JarProcessor packProcessor) throws FileNotFoundException { + if (!input.exists()) + throw new FileNotFoundException(); + + File[] files = null; + if (input.isDirectory()) { + files = input.listFiles(); + } else if (filter.accept(input)) { + files = new File[] {input}; + } + for (int i = 0; i < files.length; i++) { + if (files[i].isDirectory()) { + String dir = processor.getWorkingDirectory(); + processor.setWorkingDirectory(dir + "/" + files[i].getName()); //$NON-NLS-1$ + if (packProcessor != null) + packProcessor.setWorkingDirectory(dir + "/" + files[i].getName()); + process(files[i], filter, verbose, processor, packProcessor); + processor.setWorkingDirectory(dir); + if (packProcessor != null) + packProcessor.setWorkingDirectory(dir); + } else if (filter.accept(files[i])) { + try { + File result = processor.processJar(files[i]); + if (packProcessor != null && result != null && result.exists()) { + packProcessor.processJar(result); + } + } catch (IOException e) { + if (verbose) + e.printStackTrace(); + } + } + } + } + + public void addPackUnpackStep(JarProcessor processor, Properties properties, Options options) { + processor.addProcessStep(new PackUnpackStep(properties, options.verbose)); + } + + public void addSignStep(JarProcessor processor, Properties properties, Options options) { + processor.addProcessStep(new SignCommandStep(properties, options.signCommand, options.verbose)); + } + + public void addPackStep(JarProcessor processor, Properties properties, Options options) { + processor.addProcessStep(new PackStep(properties, options.verbose)); + } + + public void addUnpackStep(JarProcessor processor, Properties properties, Options options) { + processor.addProcessStep(new UnpackStep(properties, options.verbose)); + } +} |