12

Background

I have a project where I parse some XML documents and I happened to need the xerces dependency:

<dependency>
    <groupId>xerces</groupId>
    <artifactId>xerces</artifactId>
    <version>2.4.0</version>
</dependency>

While writing the unit tests with junit4, I had an issue every time I was running a unit test, which was the following and occurring every time I was compiling with mvn clean install:

[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.346 s <<< FAILURE! - in ConversionTest
[ERROR] ConversionTest.initializationError  Time elapsed: 0.054 s  <<< ERROR!
java.lang.NoClassDefFoundError: org/w3c/dom/ls/DocumentLS
        at ConversionTest.fromDirectory(ConversionTest.java:92)
        at ConversionTest.data(ConversionTest.java:65)
Caused by: java.lang.ClassNotFoundException: org.w3c.dom.ls.DocumentLS
        at ConversionTest.fromDirectory(ConversionTest.java:92)
        at ConversionTest.data(ConversionTest.java:65)

Compile-time solution

Searching the web, I have realized that I needed to add a new dependency to my pom.xml:

<dependency>
    <groupId>xerces</groupId>
    <artifactId>xercesImpl</artifactId>
    <version>2.11.0</version>
</dependency>

After doing this, the tests compiled fine and I could produce my .jar which was packaged with the following build plug-in:

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>lib/</classpathPrefix>
                        <mainClass>com.company.tools.Application</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin> 

... and compiled with the following settings:

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${maven.compiler.plugin.version}</version>
            <configuration>
                <encoding>cp1252</encoding>
                <release>11</release>
                <fork>true</fork>
                <meminitial>128m</meminitial>
                <maxmem>512m</maxmem>
                <compilerArgs>
                    <arg>-Xpkginfo:always</arg>
                </compilerArgs>
            </configuration>
        </plugin>

This produced a .jar which contains all the required dependencies, here including the famous org/w3c/dom/ls/DocumentLS:

enter image description here

Deployment

Now I move this .jar into my server and try to run it with the following command:

java -jar myJar.jar <inputs>

When I do that, I get the following exception, again!

Exception in thread "main" java.lang.NoClassDefFoundError: org/w3c/dom/ls/DocumentLS
        at java.base/java.lang.ClassLoader.defineClass1(Native Method)
        at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
        at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
        at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:801)
        at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:699)
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:622)
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        at org.apache.xerces.jaxp.DocumentBuilderImpl.<init>(Unknown Source)
        at org.apache.xerces.jaxp.DocumentBuilderFactoryImpl.newDocumentBuilder(Unknown Source)
        at com.company.tools.impl.FileProviderImpl.getXmlFile(FileProviderImpl.java:68)
        at com.company.tools.impl.FileProviderImpl.<init>(FileProviderImpl.java:38)
        at com.company.tools.impl.FileProviderImpl$Builder.build(FileProviderImpl.java:91)
        at com.company.tools.Application.main(Application.java:50)
Caused by: java.lang.ClassNotFoundException: org.w3c.dom.ls.DocumentLS
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        ... 15 more

My question and some details about the machines

I am a bit lost here. I've added the dependency to my pom.xml, the class is well packaged inside the .jar, but still I have the same issue. What am I doing wrong?

If it can help:

My machine:

Java version: 11.0.2-BellSoft, vendor: BellSoft, runtime: C:\jdk-11.0.2
Default locale: fr_FR, platform encoding: Cp1252
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

My server:

openjdk version "11" 2018-09-25
OpenJDK Runtime Environment 18.9 (build 11+28)
OpenJDK 64-Bit Server VM 18.9 (build 11+28, mixed mode)
OS: Linux myServerAddress 3.10.0-327.el7.x86_64 #1 SMP Thu Oct 29 17:29:29 EDT 2015 x86_64 x86_64 x86_64 GNU/Linux

Thanks in advance!

Sambit
  • 7,625
  • 7
  • 34
  • 65
Matteo NNZ
  • 11,930
  • 12
  • 52
  • 89
  • Can you try with this version `2.12.0` ? – Sambit May 25 '20 at 16:10
  • @Sambit same issue, unfortunately – Matteo NNZ May 25 '20 at 16:16
  • Check this link. https://github.com/capstone-coal/coal-sds/issues/32. – Sambit May 25 '20 at 16:21
  • @Sambit if I check the fix made, I understand that he was having some of his dependencies referencing a different version of the xercesImpl which was conflicting with the wanted one. But it’s not my case, I basically have no dependencies in Pom except for xerces and junit with scope test... – Matteo NNZ May 25 '20 at 20:33
  • 1
    @Sambit you were finally right, the answer was in the issue on Github you suggested, I just didn't see it coming as I couldn't imagine the interface was pulling a transitive dependency on the implementation (+ the dependency tree in maven wasn't seeing it). Thanks a lot! – Matteo NNZ May 28 '20 at 13:04

1 Answers1

15

I ended up finding the solution thanks to @Sambit second comment about this GitHub issue. Posting the answer here hoping it can save someone else days of headache!

Basically, I had this in my pom.xml:

<dependencies>
    <dependency>
        <groupId>xerces</groupId>
        <artifactId>xerces</artifactId>
        <version>2.4.0</version>
    </dependency>
    <dependency>
        <groupId>xerces</groupId>
        <artifactId>xercesImpl</artifactId>
        <version>2.11.0</version>
    </dependency>
</dependencies>

In both cases (unit test and main code), the exception was raised by this code:

try(InputStream is = new FileInputStream(file)) {
    documents.put(file.getName(), DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is));
}

... specifically, by the call to newDocumentBuilder() which was looking for an implementation of type DocumentImpl.

Issue explanation

The first dependency xerces is pulling a transitive dependency on a deprecated version of xercesImpl. Hence, when I was running my tests, the code was compiling (because the dependency was there) but when the newDocumentBuilder() was looking for the DocumentImpl, the implementation was returned by the bad dependency which was looking for org/w3c/dom/ls/DocumentLS without success, so raising the NoClassDefFoundError.

Once I added the explicit dependency to xercesImpl in my pom, the junit runner understood that instead of searching the DocumentImpl in the deprecated version of xercesImpl, it should have been looked for in the explicit dependency so the issue was solved.

However, the JVM running the program on the server wasn't taking the same assumption: the newDocumentBuilder() was still looking for DocumentImpl inside the transitive dependency, so the issue was still there.

Resolution

Get rid of the transitive dependency:

<dependencies>
    <dependency>
        <groupId>xerces</groupId>
        <artifactId>xerces</artifactId>
        <version>2.4.0</version>
        <exclusions>
            <exclusion>
                <groupId>xerces</groupId>
                <artifactId>xercesImpl</artifactId>
            <exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>xerces</groupId>
        <artifactId>xercesImpl</artifactId>
        <version>2.11.0</version>
    </dependency>
</dependencies>    
Matteo NNZ
  • 11,930
  • 12
  • 52
  • 89
  • You can also just revert the order of these two dependencies : xerces xercesImpl 2.11.0 xerces xerces 2.4.0 Maven takes the first call so you don't need to exclude xercesImpl in xerces if you call it before. – pacataque Nov 27 '20 at 14:50
  • Great.. solved similar issue to my spring boot project as well – Garry Dias May 08 '21 at 04:17