2

I'm working on a project that uses Maven for dependency / building / whatever (project life cycle management or sth), and I'm using Eclipse to develop and test.

The project uses Vert.x (latest) and I'm trying to use Hazelcast for some cluster management, but I encountered a bug with the Hazelcast version that Vert.x declares as a dependency (3.6.3) and the solution apparently is to upgrade to a more recent version.

I've added an updated Hazelcast dependency in my pom.xml as such:

<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast</artifactId>
    <version>[3.7,)</version>
</dependency>

And maven indeed updates the dependency (actually Eclipse called maven to update as soon as I save the pom.xml file - pretty neat), and so I get the dependency tree:

$ mvn dependency:tree
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building project 3.8.4
[INFO] ------------------------------------------------------------------------
[WARNING] The POM for jfree:jfreechart:jar:1.0.8 is missing, no dependency information available
[INFO] 
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ project ---
[INFO] my.group:project:jar:3.8.4
[INFO] +- some.private.dep ...
[INFO] |  +- org.junit:junit4-engine:jar:5.0.0-ALPHA:compile (version selected from constraint [4,))
[INFO] |  |  \- org.junit:junit-engine-api:jar:5.0.0-ALPHA:compile
[INFO] |  |     +- org.junit:junit-commons:jar:5.0.0-ALPHA:compile
[INFO] |  |     \- org.opentest4j:opentest4j:jar:1.0.0-ALPHA:compile
[INFO] |  +- io.vertx:vertx-hazelcast:jar:3.3.3:compile (version selected from constraint [3.0.0,))
[INFO] |  \- io.vertx:vertx-web:jar:3.4.0.Beta1:compile (version selected from constraint [3.0.0,))
[INFO] |     \- io.vertx:vertx-auth-common:jar:3.4.0.Beta1:compile
[INFO] +- io.vertx:vertx-core:jar:3.4.0.Beta1:compile
[INFO] |  +- io.netty:netty-common:jar:4.1.8.Final:compile
[INFO] |  +- io.netty:netty-buffer:jar:4.1.8.Final:compile
[INFO] |  +- io.netty:netty-transport:jar:4.1.8.Final:compile
[INFO] |  +- io.netty:netty-handler:jar:4.1.8.Final:compile
[INFO] |  |  \- io.netty:netty-codec:jar:4.1.8.Final:compile
[INFO] |  +- io.netty:netty-handler-proxy:jar:4.1.8.Final:compile
[INFO] |  |  \- io.netty:netty-codec-socks:jar:4.1.8.Final:compile
[INFO] |  +- io.netty:netty-codec-http:jar:4.1.8.Final:compile
[INFO] |  +- io.netty:netty-codec-http2:jar:4.1.8.Final:compile
[INFO] |  +- io.netty:netty-resolver:jar:4.1.8.Final:compile
[INFO] |  \- io.netty:netty-resolver-dns:jar:4.1.8.Final:compile
[INFO] |     \- io.netty:netty-codec-dns:jar:4.1.8.Final:compile
[INFO] +- junit:junit:jar:4.12:test
...

[INFO] +- com.hazelcast:hazelcast:jar:3.8-EA:compile
[INFO] \- org.slf4j:slf4j-jdk14:jar:1.7.22:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.785 s
[INFO] Finished at: 2017-02-06T09:52:46+02:00
[INFO] Final Memory: 25M/435M
[INFO] ------------------------------------------------------------------------

When I run mvn package to create the shaded Jar, I get the correct version of Hazelcast.

The problem is that if I create an Eclipse launch configuration for running the project or its unit tests, it inserts both the old version of Hazelcast as well as the new version of Hazelcase into the classpath - here's an example command line from a unit test being run:

/usr/lib/jvm/java-8-openjdk-amd64/bin/java -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:33123 -ea -Dfile.encoding=UTF-8
  -classpath ...:
  $HOME/.m2/repository/io/vertx/vertx-hazelcast/3.3.3/vertx-hazelcast-3.3.3.jar:
  $HOME/.m2/repository/com/hazelcast/hazelcast/3.6.3/hazelcast-3.6.3.jar:
  ...
  $HOME/.m2/repository/com/hazelcast/hazelcast/3.8-EA/hazelcast-3.8-EA.jar:
  ...
  -version 3 -port 38387 -testLoaderClass org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader 
  -loaderpluginname org.eclipse.jdt.junit4.runtime 
  -classNames my.group.project.SomeTest

Now because both versions are loaded into the classpath, the first one (the older) "wins" and I get the bug instead of getting the newer fixed version.

The launch configuration "Classpath" tab looks very standard:

+ Bootstrap Entries
 \- JRE System Library
+ User Enties
 \- project
 \- Maven Dependencies

And the "Maven Dependencies" "folder" in the "Project Explorer" view shows only the newer Hazelcast version.

What is going on?

Update:

As per the discussion in the comments, I added exclusions to the pom.xml file to prevent vertx-hazelcast from adding the old hazelcast dependency. Because vertx-hazelcast is loaded from yet another (private) dependency, the new setup is a bit more involved and looks like this:

<dependency>
    <groupId>some.private</groupId>
    <artifactId>dependency</artifactId>
    <version>[1.3,)</version>
    <exclusions>
        <exclusion>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-hazelcast</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.vertx</groupId>
    <artifactId>vertx-core</artifactId>
    <version>[3.0.0,)</version>
</dependency>
<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast</artifactId>
    <version>[3.7,)</version>
</dependency>
<dependency>
    <groupId>io.vertx</groupId>
    <artifactId>vertx-hazelcast</artifactId>
    <version>[3.0.0,)</version>
    <exclusions>
        <exclusion>
            <groupId>com.hazelcast</groupId>
            <artifactId>hazelcast</artifactId>
        </exclusion>
    </exclusions>
</dependency>

I then removed the old launch configuration and recreated it (by right clicking the JUnit test case and choosing "Debug as Junit") - But that does not change the Eclipse behavior - some jars in the classpath have moved around a bit, but the result is still that vertx-hazelcast and its hazelcast-3.6.3.jar dependency are loaded before hazelcast-3.8.jar.

Guss
  • 30,470
  • 17
  • 104
  • 128
  • What happens if you delete the cached version from disc `$HOME/.m2/repository/com/hazelcast/hazelcast/3.6.3*`? – Nic Feb 06 '17 at 08:10
  • @Nic - That's not the jar that I'm having trouble with, and if I delete it then the launch fails to fine required classes, but if I delete the `hazelcast-3.6.3.jar` from the `.m2` repo, then the launch continue and uses the correct jar file (presumably this is a feature of the JVM that ignores missing files on the classpath). But whenever I run maven (whether manually or automatically by Eclipse), the older jar gets downloaded again and I'm back to square - so I don't think that is a solution. – Guss Feb 06 '17 at 08:16
  • try to declare the new before the old dependency in your pom.xml, maybe... – Ehcnalb Feb 06 '17 at 08:30
  • @Ehcnalb - I don't declare the old dependency in my `pom.xml` file - it comes from the Vert.x `pom.xml` file. If I could have removed it, I would have. – Guss Feb 06 '17 at 08:31
  • ok so, declare hazelcast (3.8) before Vert.x, and try to add an exclusion – Ehcnalb Feb 06 '17 at 08:37
  • I don't understand what Eclipse is doing here, but if you only want to replace the version of hazelcast you can use for that. Furthermore, many Maven people seem to dislike the old "version range" feature and consider it slightly deprecated, so that I would not be surprised to find a bug in m2e that only shows up if you use those ranges. – J Fabian Meier Feb 06 '17 at 08:53
  • @Ehcnalb I wasn't aware of exclusions, so I've added some - see the update in the OP - and that didn't change anything. I'm not sure exactly that's going on, but I think Eclipse implements its own parser for `pom.xml` and it might not understand/care about exclusions. – Guss Feb 06 '17 at 08:53
  • @JFMeier - I think that dropping version ranges would be stupid and the solution should be to properly support semantic version limits like npm and gem (where you can specify that upgrades are allowed only up to a certain semantic level) or just let people who want reproduceable builds to hardcode their versions, as they can do now, without removing a feature a lot of people find useful... but that is besides the point - I tried hardcoding the `hazelcast` version to `3.7.5` and the `vertx-hazelcast` version `3.3.3` and Eclipse honored them but kept the old `hazelcast` version in front. – Guss Feb 06 '17 at 09:04
  • It [looks like](https://github.com/vert-x3/vertx-hazelcast/blob/master/pom.xml) vertex-hazelcast declares both compile and test scoped dependencies on hazelcast. These scopes are not transitive so it's my understanding that that ver. will be included in the classpath by design. @Echnalb exclusion is the [recommended](https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html#Dependency_Exclusions) way to resolve this. As that hasn't worked, it could be an ancestor is also declaring a dependency which would also need to be excluded? – Nic Feb 06 '17 at 10:55
  • @Nic how can I detect which ancestor is pushing the old version no me? `mvn depenency:tree` doesn't show anything interesting, and from a cursory review of my dependencies nothing stands out as some that would need `hazelcast` – Guss Feb 06 '17 at 11:03
  • I'm not sure, I'm looking through source poms. I'm guessing that hazelcast-client is a good bet. But I could be way off the mark – Nic Feb 06 '17 at 11:07
  • And it does (well [this version](https://github.com/hazelcast/hazelcast/blob/master/hazelcast-client/pom.xml) does). Try also add an exclusion for hazelcast-client maybe? – Nic Feb 06 '17 at 11:10
  • Won't excluding `hazelcast` from `vertx-hazelcast` also exclude it if `vertx-hazelcast -> hazelcast-client` declares the dependency? – Guss Feb 06 '17 at 11:10
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/134962/discussion-between-nic-and-guss). – Nic Feb 06 '17 at 11:17

1 Answers1

0

Note This answer is a summary from discussion with the OP

It looks like vertex-hazelcast declares both compile and test scoped dependencies on hazelcast. These scopes are not transitive so it's my understanding that this version will be included in the classpath by design. @Echnalb suggestion to use an exclusion is the recommended way to resolve this.

As that hasn't worked, it could be an ancestor is also declaring a dependency which would also need to be excluded.

After looking through some other dependencies that were declared in vertex-hazelcast, I checked hazelcast-client to see if it also declared a dependency on the earlier version of hazelcast the assumption being that the ancestral dependency was being added to the classpath despite the exclusion from the parent.

It did include such a dependency:

<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast</artifactId>
    <scope>test</scope>
    <version>${project.parent.version}</version>
    <classifier>tests</classifier>
</dependency>

I suggest(ed) adding an exclusion for hazelcast-client which seems to have solved the problem.


@Guss comments

  • Prior to the exclusion, running dependency:tree, hazelcast-client is not listed at all
  • It is interesting that dependency:tree does not detect test scoped dependencies. Is there's a way to force it to switch scopes?
Nic
  • 253
  • 1
  • 3
  • 9