I'm using a bytecode library known as ASM to alter classfiles, then I want to write each classfile back into a jar file rather than a folder filled with class files. I do this by running this code:
My problem occurs when a ZipException is throw for not being the expected size, i.e.
java.util.zip.ZipException: invalid entry compressed size (expected 695 but got 693 bytes)
at java.util.zip.ZipOutputStream.closeEntry(Unknown Source)
at org.steinburg.client.accessor.Accessor.accessJar(Accessor.java:64)
at org.steinburg.client.accessor.Accessor.<init>(Accessor.java:41)
at Loader.main(Loader.java:5)
package org.steinburg.client.accessor;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
public class Accessor {
private String input;
private File inFile;
private JarEntry jarEntry;
private JarFile jarFile;
private Enumeration<JarEntry> entries;
private InputStream is;
private String out;
private File outFile;
private FileOutputStream fos;
private JarOutputStream jos;
private byte[] bytes;
public Accessor(){
try{
input = new String("Input Jar.jar");
inFile = new File(input);
jarFile = new JarFile(inFile);
entries = jarFile.entries();
out = new String("Output Jar.jar");
outFile = new File(out);
fos = new FileOutputStream(outFile);
jos = new JarOutputStream(fos);
accessJar();
} catch (Exception e) {
e.printStackTrace();
}
}
protected final void accessJar(){
try {
while (entries.hasMoreElements()){
jarEntry = entries.nextElement();
is = jarFile.getInputStream(jarEntry);
if (jarEntry.getName().endsWith(".class")){
ClassReader cr = new ClassReader(is);
ClassWriter cw = new ClassWriter(cr, 0);
FieldAdapter fa = new FieldAdapter(cw);
cr.accept(fa, 0);
bytes = cw.toByteArray();
} else {
bytes = readBytes(is);
}
JarEntry je = new JarEntry(jarEntry);
jos.putNextEntry(je);
jos.write(bytes);
jos.closeEntry();
}
jos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
protected byte[] readBytes(InputStream inputStream){
try{
DataInputStream reader = new DataInputStream(inputStream);
byte[] toReturn = new byte[reader.available()];
reader.readFully(toReturn);
reader.close();
return toReturn;
} catch (Exception e){
e.printStackTrace();
return null;
}
}
}
The ClassReader, ClassWriter (each part of library), and FieldAdapter (made by myself) just altar the code, and to get the bytes of the entire class I use cw.toByteArray(). There is no problem as for the bytecode manipulation itself, it's just the writing to a new jar file through a JarOutputStream. Anyone known how to solve this problem?
The problematic line is this:
JarEntry je = new JarEntry(jarEntry);
as it will also copy the compressed size.
Unless you care about preserving other fields than name, you should use this constructor and provide only the file name, as this:
JarEntry je = new JarEntry(jarEntry.getName ());
The compressed size will be then automatically computed.
doing in this way it will not copy Jar Attributes and other meta information. The issue is caused when the jar/zip file is modified after it is initially packaged.
You can do this:
JarEntry je = new JarEntry(jarEntry);
je.setCompressedSize(-1);
This will cause the size of entry to be recalculated and you'll get copied all other attributes from the original jarEntry
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With