Search code examples
javamavenxpathclasspathclassloader

Why does the behaviour of my program change just based on maven dependency?


So this is the code I have:

package biz.tugay.deleteNow;
/* User: koray@tugay.biz Date: 19/12/15 Time: 12:57 */

import com.sun.org.apache.xml.internal.dtm.ref.DTMNodeList;
import org.xml.sax.InputSource;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;

public class TestClass {
    public static void main(String[] args) throws XPathExpressionException, FileNotFoundException {
        XPath xpath = XPathFactory.newInstance().newXPath();
        String expression = "/library/catalog/book";
        InputSource inputSource = new InputSource();
        inputSource.setCharacterStream(new FileReader(new File("/Users/koraytugay/Desktop/widgets.xml")));
        Object evaluate = xpath.evaluate(expression, inputSource, XPathConstants.NODESET);
        DTMNodeList dtmNodeList = (DTMNodeList) evaluate;
        System.out.println(dtmNodeList.getLength());
    }
}

and here is my pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>biz.tugay</groupId>
    <artifactId>deleteNow</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>deleteNow</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
    </properties>


    <build>
        <finalName>deleteNow</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

and lets give it a try:

Korays-MacBook-Pro:deleteNow koraytugay$ mvn clean install
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
Korays-MacBook-Pro:deleteNow koraytugay$ cd target
Korays-MacBook-Pro:target koraytugay$ java -cp deleteNow-jar-with-dependencies.jar biz.tugay.deleteNow.TestClass
12

Ok, all seems fine, now I will add a dependency in my pom.xml and repeat the whole process..

<dependencies>
    <dependency>
        <groupId>xalan</groupId>
        <artifactId>xalan</artifactId>
        <version>2.7.2</version>
    </dependency>
</dependencies>

build successful, try to execute:

Korays-MacBook-Pro:target koraytugay$ java -cp deleteNow-jar-with-dependencies.jar biz.tugay.deleteNow.TestClass
Exception in thread "main" java.lang.ClassCastException: org.apache.xml.dtm.ref.DTMNodeList cannot be cast to com.sun.org.apache.xml.internal.dtm.ref.DTMNodeList
    at biz.tugay.deleteNow.TestClass.main(TestClass.java:22)

Why is this happening? How can a dependency break a working software?


Solution

  • Because of your dependency to a specific implementation of an Interface.

     import com.sun.org.apache.xml.internal.dtm.ref.DTMNodeList;
    

    This line causes the error. By importing Xalan, you activate a different XPath provider wich in turn creates org.apache.xml.dtm.ref.DTMNodeList instead of com.sun.org.apache.xml.internal.dtm.ref.DTMNodeList

    If you would change your code to use org.w3c.dom.NodeList instead, both XPath implementations would work with both:

        Object evaluate = xpath.evaluate(expression, inputSource, XPathConstants.NODESET);
        NodeList dtmNodeList = (NodeList) evaluate;
        System.out.println(dtmNodeList.getLength());