blob: 8f8989d37d94cf281b387bdeea0d6e852ff2ecef [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2008 IBM Corporation.
* 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 Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.actf.util.jar;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.eclipse.actf.util.logging.LoggingUtil;
/**
* @author Barry Feigenbaum
*/
public class ZipImploder
{
protected int dirCount, fileCount;
private Logger logger = Logger.getLogger(LoggingUtil.ACTF_CORE_LOGGER_NAME);
/**
* @return Returns the dirCount.
*/
public int getDirCount () {
return dirCount;
}
/**
* @return Returns the fileCount.
*/
public int getFileCount () {
return fileCount;
}
/**
* create a new imploder with no verbosity
*
*/
public ZipImploder () {
this(false);
}
/**
* create a new imploder with the specified verbosity state
*
* @param verbose - verbosity state
*/
public ZipImploder (boolean verbose) {
setVerbose(verbose);
}
protected boolean verbose;
/**
* get the verbose mode
*
* @return verbosity mode
*/
public boolean getVerbose () {
return verbose;
}
/**
* set the verbosity mode
*
* @param f verbosity state
*/
public void setVerbose (boolean f) {
verbose = f;
}
protected String baseDir;
/**
* @return Returns the baseDir.
*/
public String getBaseDir () {
return baseDir;
}
/**
* @param baseDir The baseDir to set.
* @throws IOException
*/
public void setBaseDir (String baseDir) throws IOException {
if (baseDir != null) {
baseDir = new File(baseDir).getCanonicalPath();
baseDir = baseDir.replace('\\', '/');
}
this.baseDir = baseDir;
}
protected Manifest manifest;
/**
* @return Returns the manifest
*/
public Manifest getManifest () {
return manifest;
}
/**
* @param manifest The manifest to set.
*/
public void setManifest (Manifest manifest) {
this.manifest = manifest;
}
protected boolean includeDirs;
/**
* returns whether or not path information is included in .zip
*
* @return <code>true</code> if path information is included, <code>false</code> otherwise
*/
public boolean getIncludeDirs () {
return includeDirs;
}
/**
* set whether or not path information is included in .zip files
*
* @param includeDirs include path inforamtion in .zip file
*/
public void setIncludeDirs (boolean includeDirs) {
this.includeDirs = includeDirs;
}
/**
* implode source directory into .jar/.zip file
* @param zipName name of target file
* @param jarName name of target file
* @param sourceDir source directory name
* @exception IOException error creating a target file
*/
public void process (String zipName, String jarName, String sourceDir)
throws IOException {
dirCount = 0;
fileCount = 0;
if (zipName != null) {
processZip(zipName, sourceDir);
}
if (jarName != null) {
processJar(jarName, sourceDir);
}
}
/**
* Implode target JAR file from a source directory
* @param jarName name of target file
* @param sourceDir source directory name
* @exception IOException error creating a target file
*/
public void processJar (String jarName, String sourceDir)
throws IOException {
processJar(jarName, sourceDir, null);
}
/**
* Implode target JAR file from a source directory
* @param jarName name of target file
* @param sourceDir source directory name (
* @param comment
* @exception IOException error creating a target file
*/
public void processJar (String jarName, String sourceDir, String comment)
throws IOException {
processJar(jarName, sourceDir, comment, -1, -1);
}
/**
* Implode target JAR file from a source directory
*
* @param jarName - name of target .jar
* @param sourceDir - source directory
* @param comment - comment for .jar file
* @param method
* @param level
* @throws IOException
*/
public void processJar (String jarName, String sourceDir, String comment,
int method, int level) throws IOException {
String dest = setup(jarName, sourceDir);
Manifest man = getManifest();
JarOutputStream jos = man != null ? new JarOutputStream(new BufferedOutputStream(new FileOutputStream(dest)), man)
: new JarOutputStream(new BufferedOutputStream(new FileOutputStream(dest)));
configure(jos, comment, method, level);
process(jos, new File(sourceDir));
}
/**
* Implode target JAR file from a source directory
* @param zipName name of target file
* @param sourceDir source directory name (
* @exception IOException error creating a target file
*/
public void processZip (String zipName, String sourceDir)
throws IOException {
processZip(zipName, sourceDir, null);
}
/**
* Implode target zip file from a source directory
*
* @param zipName
* @param sourceDir
* @param comment
* @throws IOException
*/
public void processZip (String zipName, String sourceDir, String comment)
throws IOException {
processZip(zipName, sourceDir, comment, -1, -1);
}
/**
* Implode target zip file from a source directory
*
* @param zipName
* @param sourceDir
* @param comment
* @param method
* @param level
* @throws IOException
*/
public void processZip (String zipName, String sourceDir, String comment,
int method, int level) throws IOException {
String dest = setup(zipName, sourceDir);
ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(dest)));
configure(zos, comment, method, level);
process(zos, new File(sourceDir));
}
protected void configure (ZipOutputStream zos, String comment, int method,
int level) {
if (comment != null) {
zos.setComment(comment);
}
if (method >= 0) {
zos.setMethod(method);
}
if (level >= 0) {
zos.setLevel(level);
}
}
protected String setup (String zipName, String sourceDir)
throws IOException {
File dir = new File(sourceDir);
if (!dir.exists() && !dir.isDirectory()) { throw new IOException("source must exist and be a directory: "
+ dir); }
String source = dir.getCanonicalPath();
String dest = new File(zipName).getCanonicalPath();
if (verbose) {
logger.log(Level.FINE, "\n**** Imploding "
+ source + " to " + dest);
}
return dest;
}
protected void process (ZipOutputStream zos, File dir) throws IOException {
try {
processDir(zos, dir);
}finally {
zos.close();
}
}
protected String removeDrive (String path) {
return path.length() >= 2 && path.charAt(1) == ':' ? path.substring(2)
: path;
}
protected String removeLead (String path) {
if (baseDir != null && path.startsWith(baseDir)) {
path = path.substring(baseDir.length());
if (path.length() >= 1) {
if (path.charAt(0) == '/' || path.charAt(0) == '\\') {
path = path.substring(1); // drop leading /
}
}
}
return path;
}
public void processDir (ZipOutputStream zos, File dir) throws IOException {
String path = dir.getCanonicalPath();
path = path.replace('\\', '/');
if (includeDirs) {
if (baseDir == null || path.length() > baseDir.length()) {
String xpath = removeDrive(removeLead(path));
if (xpath.length() > 0) {
xpath += '/';
if (verbose) {
logger.log(Level.FINE, "\nProcessing directory "
+ path + " to " + xpath);
}
ZipEntry ze = new ZipEntry(xpath);
zos.putNextEntry(ze);
}else {
if (verbose) {
logger.log(Level.FINE, "\nSkipping empty path");
}
}
}else {
if (verbose) {
logger.log(Level.FINE, "\nDropping "
+ path);
}
}
}else {
if (verbose) {
logger.log(Level.FINE,"\nSkipping " + path);
}
}
dirCount++;
String[] files = dir.list();
for (int i = 0; i < files.length; i++) {
String file = files[i];
File f = new File(dir, file);
if (f.isDirectory()) {
processDir(zos, f);
}else {
processFile(zos, f);
}
}
}
/**
* process a single file for a .zip file
*
* @param zos
* @param f
* @throws IOException
*/
public void processFile (ZipOutputStream zos, File f) throws IOException {
String path = f.getCanonicalPath();
path = path.replace('\\', '/');
String xpath = removeDrive(removeLead(path));
if (verbose) {
logger.log(Level.FINE, "Processing file "
+ path + " to " + xpath);
}
ZipEntry ze = new ZipEntry(xpath);
ze.setTime(f.lastModified());
ze.setSize(f.length());
zos.putNextEntry(ze);
fileCount++;
try {
copyFileEntry(zos, f);
}finally {
zos.closeEntry();
}
}
protected void copyFileEntry (ZipOutputStream zos, File f)
throws IOException {
DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(f)));
try {
copyFileEntry(zos, dis);
}finally {
try {
dis.close();
}catch (IOException ioe) {
}
}
}
protected void copyFileEntry (ZipOutputStream zos, DataInputStream dis)
throws IOException {
byte[] bytes = readAllBytes(dis);
logger.log(Level.FINE, "Writing " + bytes.length
+ " bytes...");
zos.write(bytes, 0, bytes.length);
}
// *** below may be slow for large files ***
/** Read all the bytes in a stream */
protected byte[] readAllBytes (DataInputStream is) throws IOException {
byte[] bytes = new byte[0];
for (int len = is.available(); len > 0; len = is.available()) {
byte[] xbytes = new byte[len];
int count = is.read(xbytes);
logger.log(Level.FINE, "readAllBytes: " + len + " vs. "
+ count);
if (count > 0) {
byte[] nbytes = new byte[bytes.length + count];
System.arraycopy(bytes, 0, nbytes, 0, bytes.length);
System.arraycopy(xbytes, 0, nbytes, bytes.length, count);
bytes = nbytes;
}
}
return bytes;
}
protected void print (String s) {
System.out.print(s);
}
/** Print command help text. */
public static void printHelp () {
System.out.println();
System.out.println("Usage: java " + ZipImploder.class.getName());
System.out.println(" (-jar <jarName> {-manifest <manfile>} | -zip <zipName>)");
System.out.println(" -dir <sourceDir> {-lead <leadDir>} {-doDirs} {-verbose}");
System.out.println("Where:");
System.out.println(" <jarName> path to target jar");
System.out.println(" <zipName> path to target zip");
System.out.println(" <manfile> path to manifest file");
System.out.println(" <sourceDir> path to source directory; must exist");
System.out.println(" <leadDir> partial lead path to remove from stored entries; default: <sourceDir>");
System.out.println(" <noDirs> skip output of directory entries");
System.out.println(" <verbose> output progress information");
System.out.println("Note: switch case or order is not important");
}
protected static void reportError (String msg) {
System.err.println(msg);
//printHelp();
System.exit(1);
}
/**
* Main command line entry point.
* @param args
*/
public static void main (final String[] args) {
if (args.length == 0) {
printHelp();
System.exit(0);
}
String zipName = null;
String jarName = null;
String manName = null;
String sourceDir = null;
String leadDir = null;
boolean jarActive = false, manActive = false, zipActive = false, sourceDirActive = false, leadDirActive = false;
boolean verbose = false;
boolean noDirs = false;
// process arguments
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if (arg.charAt(0) == '-') { // switch
arg = arg.substring(1);
if (arg.equalsIgnoreCase("jar")) {
jarActive = true;
manActive = false;
zipActive = false;
sourceDirActive = false;
leadDirActive = false;
}else if (arg.equalsIgnoreCase("manifest")) {
jarActive = false;
manActive = true;
zipActive = false;
sourceDirActive = false;
leadDirActive = false;
}else if (arg.equalsIgnoreCase("zip")) {
zipActive = true;
manActive = false;
jarActive = false;
sourceDirActive = false;
leadDirActive = false;
}else if (arg.equalsIgnoreCase("dir")) {
jarActive = false;
manActive = false;
zipActive = false;
sourceDirActive = true;
leadDirActive = false;
}else if (arg.equalsIgnoreCase("lead")) {
jarActive = false;
manActive = false;
zipActive = false;
sourceDirActive = false;
leadDirActive = true;
}else if (arg.equalsIgnoreCase("noDirs")) {
noDirs = true;
jarActive = false;
manActive = false;
zipActive = false;
sourceDirActive = false;
leadDirActive = false;
}else if (arg.equalsIgnoreCase("verbose")) {
verbose = true;
jarActive = false;
manActive = false;
zipActive = false;
sourceDirActive = false;
leadDirActive = false;
}else {
reportError("Invalid switch - " + arg);
}
}else {
if (jarActive) {
if (jarName != null) {
reportError("Duplicate value - " + arg);
}
jarName = arg;
}else if (manActive) {
if (manName != null) {
reportError("Duplicate value - " + arg);
}
manName = arg;
}else if (zipActive) {
if (zipName != null) {
reportError("Duplicate value - " + arg);
}
zipName = arg;
}else if (sourceDirActive) {
if (sourceDir != null) {
reportError("Duplicate value - " + arg);
}
sourceDir = arg;
}else if (leadDirActive) {
if (leadDir != null) {
reportError("Duplicate value - " + arg);
}
leadDir = arg;
}else {
reportError("Too many parameters - " + arg);
}
}
}
if (sourceDir == null || (zipName == null && jarName == null)) {
reportError("Missing parameters");
}
if (manName != null && zipName != null) {
reportError("Manifests not supported on ZIP files");
}
if (leadDir == null) {
leadDir = new File(sourceDir).getAbsolutePath().replace('\\', '/') + '/';
}
if (verbose) {
System.out.println("Effective command: "
+ ZipImploder.class.getName()
+ (jarName != null ? " -jar " + jarName
+ (manName != null ? " -manifest " + manName : "")
: "") + (zipName != null ? " -zip " + zipName : "")
+ " -dir " + sourceDir + " -lead " + leadDir
+ (noDirs ? " -noDirs" : "") + (verbose ? " -verbose" : ""));
}
try {
ZipImploder zi = new ZipImploder(verbose);
if (leadDir != null) {
zi.setBaseDir(leadDir);
}
if (manName != null) {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(manName));
try {
zi.setManifest(new Manifest(bis));
}finally {
bis.close();
}
}
zi.setIncludeDirs(!noDirs);
zi.process(zipName, jarName, sourceDir);
if (verbose) {
System.out.println("\nDone Directories=" + zi.getDirCount()
+ " Files=" + zi.getFileCount());
}
}catch (IOException ioe) {
System.err.println("Exception - " + ioe.getMessage());
//ioe.printStackTrace(); // *** debug ***
System.exit(2);
}
}
}