1

I want to patch a java class of a library I use in a war project.

I has overriden the class with a new class with same canonical name, but in a Web application the original class is still loaded first. How can I control the class loading order?

I've started from this post, and readed about JAR Hell: Override class in java

And used the maven example found here: http://owenou.com/2010/07/20/patching-with-class-shadowing-and-maven.html

Basically is an auxiliar project that depends on a new patched jar with the overriden class and the original library, with a MANIFEST.MF with these libraries in the correct order in classpath. So other projects could use this auxiliar project.

This works fine in a standalone application, but in a Web project the original jar is still loaded first.

Any help?

Community
  • 1
  • 1
Aníbal
  • 785
  • 8
  • 24

3 Answers3

5

I had the exact same issue and here is what I did to make it work. My project was including a "ThatProject" library containing a ServiceFactory.java class causing a bug.

<dependencies>
    <dependency>
        <groupId>com.example.ThatGroup</groupId>
        <artifactId>ThatProject</artifactId>
        <version>${ThatProject.version}</version>
    </dependency>
</dependencies>

In my own project I created a same class to shadow the one of "ThatProject" (the path must match). → src\main\java\com\example\ThatGroup\ThatProject\ServiceFactory.java

In my pom.xml I used the maven-shade-plugin plugin as follows. Please note how I am excluding the original ServiceFactory.java from the "ThatProject" library.

<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${maven-compiler-plugin.version}</version>
            <configuration>
                <source>${java.version}</source>
                <target>${java.version}</target>
            </configuration>
        </plugin>
        <!-- Include all dependencies in JAR -->
        <plugin>
          <artifactId>maven-shade-plugin</artifactId>
          <version>2.2</version>
          <executions>
            <execution>
              <id>shade</id>
              <goals>
                <goal>shade</goal>
              </goals>
              <phase>package</phase>
              <configuration>
                <createDependencyReducedPom>false</createDependencyReducedPom>
                <filters>
                  <filter>
                    <artifact>com.example.ThatGroup:ThatClient</artifact>
                    <excludes>
                      <exclude>com/example/ThatGroup/ThatProject/ServiceFactory.class</exclude>
                    </excludes>
                  </filter>
                </filters>
              </configuration>
            </execution>
          </executions>
        </plugin>
    </plugins>
</build>

And that's it, it finally worked for me. In my case I was including all the files of my project with this configuration of pom.xml (including my shadowing version of ServiceFactory.java). As mentioned, the original ServiceFactory.java was excluded during the maven build.

Hope this helps!

TranceVibes
  • 126
  • 2
  • 8
2

A dirty solution is to remove that class from the jar containing the version that you don't like to load.

Another solution is to reorder the classpath so your version comes before the old one. From java documentation:

Specification Order

The order in which you specify multiple class path entries is important. The Java interpreter will look for classes in the directories in the order they appear in the class path variable. In the example above, the Java interpreter will first look for a needed class in the directory C:\java\MyClasses. Only if it doesn't find a class with the proper name in that directory will the interpreter look in the C:\java\OtherClasses directory.

Please note that both solution are very dirty and future modification of your code can broke your functionalities (for example changing classpath or updating a jar).

The best way will be one of the following:

  • extends the class and use only the new class
  • implements the same interface in a new class and use it
Davide Lorenzo MARINO
  • 26,420
  • 4
  • 39
  • 56
  • Well, it's an utility class used into that external jar not controlled by me, I can't extend the class. I've replaced directly the class into the jar, but as you say this is not very correct. The classpath reordering is what is precisely not working in the web project. – Aníbal Nov 20 '15 at 12:24
1

If you control the application client, you could try to play with jar at runtime:

See this for loading How to load a jar file at runtime

If you want also unloading: Dynamic loading and unloading of jar

Or, you could try to launch in your web app, at "boot" , the precedent trick. Then you be sure of what classes you get.

Community
  • 1
  • 1