7/05/2011

How to programmatically copy jar files

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.

6 comments:

Anonymous said...

Thank you, it is good to know about copying all jar entries individually, but if we just want to copy the full jar as is - why not just read the whole file in binary mode and do?

jdtangney said...

Thanks so much! This bug has been in our code forever, and we have only just started seeing the exception. It's great to know there is a well-documented cause and that the fix is so easy.

faruk arshad said...

wat if my source destination is a database table how can i do tat ?? means wat wshould be my path to the file output stream plz help

Anonymous said...

Using your method, when i try to delete the source jar file immediately afterwards, the file appears to be locked and won't delete. Any comment?

Anonymous said...

Having investigated further, your code is missing a final jarFile.close(); in order to release the appropriate resources.

Patrick Meppe said...

Thanks thanks thanks.