Manipulating Java artifacts

Author
Damian
Terlecki
5 minutes read

Sometimes you encounter this one bug in an external library, and after many hours you find out that it's practically impossible to make any workaround. The components might be very tightly coupled and impossible to extend (hello static methods). Java, however, is a very mature language and there are some methods to cope with such situations.

Overview

The basic overview of our situation is that Java code (.java files) is compiled (javac) to bytecode (.class files) and then packaged into an archive ([J|W|E]AR) usually with some meta-information. The classes of such an artifact are later loaded at runtime into JVM (memory) by a ClassLoader, verified and compiled into native code by a JIT Compiler. Within this process, you can find multiple ways to solve the problem with problematic classes. This is a pretty broad topic, but you could consider:

  • reordering the classpath so that the correct class is loaded first;
  • implementing your own ClassLoader which will load the correct class (beware of loading by multiple ClassLoaders);
  • using instrumentation API and implementing your own Java Agent which will redefine or retransform classes (with some limitations);
  • removing the class from the artifact archive and implementing your own one.
Bytecode

If you've ever checked how a bytecode looks, you will probably agree that manipulating it might be pretty complex. Fortunately, there are many libraries that can help with this like ByteBuddy, Javassist or ASM. I've been successful with using them to fix some specific bugs in external library static methods in wait for a proper bugfix. Often though, bytecode manipulation or introducing custom ClassLoaders might not be that feasible, especially in enterprise environments where you may have limited viability to modify the startup/deployment scripts or redefine the order of precedence for a classpath.

Manipulating Java artifacts

In some specific cases, though, you may get by with removing the bugged class from the artifact archive and implement your own one. A perfect example would be an MDB (Message Driven Bean) component in JEE. Usually, such components are loosely-coupled and can be removed without much side effects. Prior to that, it's recommended to check any deployment descriptions and the application server to retain any specific configuration. This solution is not very elegant but get's the job done until the component in the dependent library is fixed.

To remove the class you can unpack the archive remove it and package it again. With Maven it's possible to automate it into the build process with some plugin like truezip-maven-plugin:

<plugins>
  <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>truezip-maven-plugin</artifactId>
    <version>1.2</version>
    <executions>
      <execution>
        <id>remove-a-file</id>
        <goals>
          <goal>remove</goal>
        </goals>
        <phase>package</phase>
        <configuration>
          <fileset>
            <directory>${project.build.directory}/com.example.project.ear/lib/com.example.library.jar/</directory>
            <includes>
              <include>com/example/library/Broken.class</include>
            </includes>
          </fileset>
        </configuration>
      </execution>
    </executions>
  </plugin>
</plugins>

Depending on whether you're deploying an exploded archive or not, you might want to edit the plugin configuration. It's quite a dirty way and not always applicable, so use it as a last resort when hotfixing stuff.