blob: aef37044f478a75c97649d141882446857a2c12e [file] [log] [blame]
/*******************************************************************************
* 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.update.internal.jarprocessor;
import java.io.*;
import java.util.*;
import java.util.jar.*;
/**
* @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();
}
}
}
}