Search code examples
javajavassist

Parse classes from jar using javassist


I'm noob in javassist. Anyone can give the sample how to load classes from jar and save them using javassist?

jar = new JarFile(fileName);
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = (JarEntry) entries.nextElement();
if(jarEntry == null)
    break;
if(jarEntry.getName().endsWith(".class")) {
    // ??????
} else {
    resources.add(new RResource(jarEntry.getName(), jar.getInputStream(jarEntry)));
}

Solution

  • You can load the bytes from the respective class inside the JAR file via the code below:

    JarFile jarFile = new JarFile(file);
    // lets get a reference to the .class-file contained in the JAR
    ZipEntry zipEntry = jarFile.getEntry(className.replace(".", "/")+".class");
    if (zipEntry == null) {
        jarFile.close();
        return null;
    }
    // with our valid reference, we are now able to get the bytes out of the jar-archive
    InputStream fis = jarFile.getInputStream(zipEntry);
    byte[] classBytes = new byte[fis.available()];
    fis.read(classBytes);
    

    To load the bytes in javassist you can do the following stuff:

    ClassPool cp = ClassPool.getDefault();
    cp.insertClassPath(new ClassClassPath(this.getClass()));
    ClassPath cp1 = null;
    ClassPath cp2 = null;
    
    // add the JAR file to the classpath
    try {
        cp1 = cp.insertClassPath(jarFile.getAbsolutePath());
    } catch (NotFoundException e1) {
        e1.printStackTrace();
        return null;
    }
    // add the class file we are going to modify to the classpath
    cp2 = cp.appendClassPath(new ByteArrayClassPath(className, classBytes));
    
    byte[] modifiedBytes;
    try {
        CtClass cc = cp.get(className);
        // skip instrumentation if the class is frozen and therefore
        // can't be modified
        if (!cc.isFrozen()) {
            // do your javassist stuff here
        }
        modifiedBytes = cc.toBytecode();
    } catch (NotFoundException | IOException | CannotCompileException | ClassNotFoundException e) {
        handleException(e);
    } finally {
        // free the locked resource files
        cp.removeClassPath(cp1);
        cp.removeClassPath(cp2);
    }
    
    // write your modified bytes somewhere
    if (modifiedBytes.length > 0) {
        try(FileOutputStream fos = new FileOutputStream("pathname")) {
            fos.write(modifiedBytes);
        }
    }
    

    Maybe some of the code can be reduced, but this is how I load bytes from a JAR file and load them into Javassist. The JAR file is loaded to the Javassist classpath due to eventual dependencies. Also the class which I instrument with Javassist needed to be added to the classpath for some reason.

    You might have a look at how I use them in a plugin-use-case:

    HTH