To copy a jar file programmatically in java involves the following steps:

1, create a JarOutputStream based on the destination jar file;

2, loop through all entries in the source jar file, and get the InputStream from each entry;

3, create a new jar entry with the same name as the source jar entry, and put the new entry to the JarOutputStream;

4, now we have InputStream, OutputStream, and the new jar entry in place, we are ready to transfer the bits, by reading bytes from InputStream into a buffer and writing buffer content to OutputStream.

5, close the InputStream, flush the JarOutputStream, and close the jar entry. This completes the copying of one jar entry.

6, after the loop ends, all entries are copied from source jar file to destination jar file. Finally, close the JarOutputStream.
package test;
import java.io.*;
import java.util.Enumeration;
import java.util.jar.*;

//java -cp . test.CopyZip xxx/lib/sac-1.3.jar /tmp
//
public class CopyZip {
public static void main(String[] args) throws Exception {
File sourceFileOrDir = new File(args[0]);
File destDir = new File(args[1]);
if (sourceFileOrDir.isFile()) {
copyJarFile(new JarFile(sourceFileOrDir), destDir);
} else if (sourceFileOrDir.isDirectory()) {
File[] files = sourceFileOrDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".jar");
}
});
for (File f : files) {
copyJarFile(new JarFile(f), destDir);
}
}
}

public static void copyJarFile(JarFile jarFile, File destDir) throws IOException {
String fileName = jarFile.getName();
String fileNameLastPart = fileName.substring(fileName.lastIndexOf(File.separator));
File destFile = new File(destDir, fileNameLastPart);

JarOutputStream jos = new JarOutputStream(new FileOutputStream(destFile));
Enumeration<JarEntry> entries = jarFile.entries();

while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
InputStream is = jarFile.getInputStream(entry);

//jos.putNextEntry(entry);
//create a new entry to avoid ZipException: invalid entry compressed size
jos.putNextEntry(new JarEntry(entry.getName()));
byte[] buffer = new byte[4096];
int bytesRead = 0;
while ((bytesRead = is.read(buffer)) != -1) {
jos.write(buffer, 0, bytesRead);
}
is.close();
jos.flush();
jos.closeEntry();
}
jos.close();
}
}
A common error is:
Exception in thread "main" java.util.zip.ZipException: invalid entry compressed size (expected 1665 but got 1680 bytes)
at java.util.zip.ZipOutputStream.closeEntry(ZipOutputStream.java:206)
at test.CopyZip.copyJarFile(CopyZip.java:63)
at test.CopyZip.main(CopyZip.java:37)
It usually occurs when text file entries in the source jar file contain some non-ASCII characters such as ^I ^Z ^D ^C. Some common files are META-INF/COPYRIGHT.html, META-INF/LICENSE.txt, etc, probably because these files were created in a non-ASCII editor but saved as text files. Open them in vi or vim to see these offending characters.

To avoid this type of ZipException, always create a new JarEntry with the same name, and pass it to putNextEntry() method. Do not pass the existing jar entry from the source jar file to putNextEntry() method.
2

View comments

Labels
Archive
Popular Posts
Popular Posts
  • Two JVM options are often used to tune JVM heap size: -Xmx for maximum heap size, and -Xms for initial heap size. Here are some common mi...
  • Simple enum . The ; after the last element is optional, when this is the end of enum definition. public enum Color { WHITE, BLACK, RED, ...
  • How to set project classpath in Eclipse and NetBeans are similar: just right-click the project name, choose Properties to bring up the Prope...
  • Let's say I need to spawn multiple threads to do the work, and continue to the next step only after all of them complete. I will need t...
  • This is a sample web.xml based on Servlet 2.5 (part of Java EE 5) that declares common elements. All top-level elements are optional, and c...
  • The default string value for java enum is its face value, or the element name. However, you can customize the string value by overriding toS...
  • Prior to JDK 6, we can check if a string is empty in 2 ways: if(s != null && s.length() == 0) if(("").equals(s)) Checking ...
  • When writing javadocs, IntelliJ automatically adds a closing tag for html elements. For instance, after typing <lt>, it automaticaly a...
  • StringBuilder was introduced in JDK 1.5. What's the difference between StringBuilder and StringBuffer? According to javadoc , StringBu...
  • With array, we can easily declare and initialize it at the same time: String[] favorites = new String[] {"EJB", "JPA", ...
Loading