1

I have a fairly large legacy project that I'm adding a component to. This component uses HtmlUnit. I can compile it ok with Maven but when I run it I get:

java.lang.NoSuchMethodError:
  org.apache.http.conn.ssl.SSLConnectionSocketFactory.<init>
    (Ljavax/net/ssl/SSLContext;[Ljava/lang/String;[Ljava/lang/String;Ljavax/net/ssl/HostnameVerifier;)

So it's missing the correct constructor. I think this is almost certainly a version conflict in httpclient but I'm not sure how to resolve it. Here's the relevant part of my pom.xml (note all the games I've been trying to play with exclusions and dependency management):

<dependencies>
        <dependency>
            <groupId>com.mycompany.mine</groupId>
            <artifactId>my-base-project</artifactId>
            <version>${project.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.httpcomponents</groupId>
                    <artifactId>httpclient</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.mycompany</groupId>
            <artifactId>base-project</artifactId>
            <version>${project.version}</version>
            <scope>provided</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.httpcomponents</groupId>
                    <artifactId>httpclient</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
                <version>4.5.2</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

Any ideas?

Edit: it's been suggested that this question is a duplicate of this one, but it's not since the dependency type in this case is not war.

Community
  • 1
  • 1
Johnny
  • 7,073
  • 9
  • 46
  • 72
  • 3
    Have you tried `mvn dependency:tree` to make sure nothing uses another version of `org.apache.httpcomponents`? – StephaneM Jun 02 '16 at 14:40
  • Did you check that version 4.5.2 contains the correct constructor? I checked the sources for 4.5 online and those say the last parameter is of type `org.apache.http.conn.ssl.X509HostnameVerifier` and not `javax.net.ssl.HostnameVerifier`, so that might be a problem (or 4.5.2 had a change here). - Which version of Maven are you using? I have the impression that Maven 2.x has some difficulties in resolving version conflicts while Maven 3.x seems to do the job just fine. – Thomas Jun 02 '16 at 14:41
  • @Thomas It looks like it does have that constructor: https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/ssl/SSLConnectionSocketFactory.html. I'm using maven version 3.3.9. – Johnny Jun 02 '16 at 14:47
  • As StephaneM suggested, try using maven dependency plugin; anyway, just by specifying the dependency in the project you're running it should use the right one, since Maven resolves by the nearest to root – dtortola Jun 02 '16 at 14:52
  • @StephaneM this is the dependency tree filtered for http: http://imgur.com/oe22qDB – Johnny Jun 02 '16 at 14:52
  • My bad, was looking at the wrong class. Yes the constructor should be present. – Thomas Jun 02 '16 at 14:53
  • @dtortola According to the [dep tree](http://imgur.com/oe22qDB) there is one `httpclient:4.5.2` and one `commons-httpclient:3.1`. These are different artifacts, so both of them are used and if the one missing the desired constructor is first in the classpath... – Gerold Broser Jun 02 '16 at 15:41
  • @GeroldBroser so how can I resolve that? I think both are need since one is a dependency of hadoop and the other of HtmlUnit. – Johnny Jun 02 '16 at 16:11
  • The latest [`hadoop-client`](http://search.maven.org/#search|ga|1|a%3A%22hadoop-client%22) is v2.7.2. Version 2.5.0 is almost 2 years old and uses `commons-httpclient` which reached its [end of life](https://hc.apache.org/httpclient-3.x/) 5 years ago and refers to its successor `httpclient` there. If you can't refactor this legacy project accordingly one solution I can think of is to use the old `commons-httpclient` in `-core:0.5.0-SNAPSHOT` instead of `httpclient`. – Gerold Broser Jun 02 '16 at 19:20
  • @GeroldBroser, but wouldn't that just mean I'm exactly at the point I'm at now? Hadoop requires the old `commons-httpclient` and `HtmlUnit` requires the new client. – Johnny Jun 03 '16 at 08:49
  • You're right, sorry. I overlooked that `HtmlUnit`. I was just referring to your screenshot. – Gerold Broser Jun 03 '16 at 09:02
  • 2
    Possible duplicate of [Maven dependency exclusion doesn't seem to work](http://stackoverflow.com/questions/15030317/maven-dependency-exclusion-doesnt-seem-to-work) – Gerold Broser Jun 03 '16 at 10:04
  • Nope, the dependency type is not `war`. – Johnny Jun 03 '16 at 12:01

1 Answers1

3

In order to identify conflicting dependecies, use mvn dependency:tree. I like to pipe it to a text file for ease of use:

mvn dependency:tree > tree.txt

Then, use your favorite text editor to look for multiple versions of a depedency.

Alternatively, if you are looking for a specific groupId or artifactId, use the -Dincludes flag:

mvn dependency:tree -Dincludes=<groupId>:<artifactId>:<version>:<packaging>
mvn dependency:tree -Dincludes=org.springframework <<< get all dependencies with by groupId
mvn dependency:tree -Dincludes=:spring-web <<< get all dependencies by artifactId

You might also want to add the -Dverbose flag here.

To resolve dependency conflicts, there are two ways:

1) Exclude the one you don't want

<depdency>
    <groupId>some.stuff</groupId>
    <artifactId>with.transitive.depdency</artifactId>
    <exclusions>
        <exclusion>
            <groupId>something</groupId>
            <artifactId>unwanted</artifactId>
        <exclusion>
    <exclusions>
<depdency>

With this way, you will have to exclude on every dependency that brings in a transitive one. For this reason I like the other one better.

2) Explicitly add the version you want

<dependency>
    <groupId>something</groupId>
    <artifactId>with.version.conflict</artifactId>
    <version>what I want</version>
</dependency>

This will make sure that any transitive dependency will be swapped with this exact version. This might also lead to errors though, if some framework actually needs an older version. For using this strategy safely, your dependencies will need to be fairly close to the newest available version (or versions released at the same time).

Tobb
  • 11,850
  • 6
  • 52
  • 77
  • Note - If you do `mvn dependency:tree > tree.txt`, the text file that you get shows dependencies, *comma separated by package*. Look for different version numbers of the same sub-dependency across 2 or more different packages. – Chris Halcrow Nov 23 '18 at 00:45