New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release Version 2.1 On Maven #224

Closed
ankurpathak opened this Issue Nov 5, 2018 · 61 comments

Comments

Projects
None yet
10 participants
@ankurpathak
Copy link

ankurpathak commented Nov 5, 2018

I can see the last artificat of Hamcrest released on maven central was back in 2015.
But the latest master has many updates which are not availabe on that maven build.
For example empty matcher for Map. So please release the binaries for latest master
on maven central. So we can consume it in our projects. Also the latest master and
binaries on maven central should be in sync.

@tumbarumba tumbarumba self-assigned this Nov 8, 2018

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 8, 2018

I think this is a great idea. It's been far too long since releases. I plan to try and do this myself over the next couple of weeks.

@hamcrest/hamcrest-java-devs: looking at the shiny new Hamcrest Distributables page, there are 5 jars mentioned. I can see the code for hamcrest-core.jar and hamcrest-library.jar, but I don't see any code for hamcrest-integration.jar or hamcrest-generator.jar (I understand hamcrest-all.jar is just the combination of all the other jars). Does anyone have any idea where this missing code might be? Is it still relevant?

@sf105

This comment has been minimized.

Copy link
Member

sf105 commented Nov 9, 2018

I tried to collapse everything into one jar. We don't need the generator any more as we've stopped doing the code generation for the Matchers class. There's still a decision about whether to have separate jars for the test framework integrations as they're outside the JDK. Might make life easier for dealing with junit 4/5 and other test frameworks.

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 9, 2018

Thanks @sf105. The test frame integration classes do not appear to be checked into this github repository. Do you have them somewhere else?

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 9, 2018

I found the java files on the old Google Code repo: https://code.google.com/archive/p/hamcrest/source/default/source

I'll see if I can import them into GitHub this weekend.

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 10, 2018

@hamcrest/hamcrest-java-devs: I've just landed #228, which is preparing the Gradle build for reproducing the various jars as per the 1.3 release. Included in this change: I've set the version to 1.4-SNAPSHOT.

Are there any objections to calling the next version 1.4? If so, what should the next version be?

@ankurpathak

This comment has been minimized.

Copy link
Author

ankurpathak commented Nov 10, 2018

@tumbarumb Using 1.4 for new version will create same confusion which to use 1.3.x.x or 2.x.x.x. So lets
skip these 1 and 2. And start a new life for hamcrest with 3.x.x.x or 4.x.x.x and regular releases on maven. So their
wont be any confusion remains. Since human by their greedy nature have the tendncy to use latest and greatest one. And using 4.x.x.x skipping 3.x.x.x will mark new life for hamcrest project and will also match jdk style of rebrading from 1.x to x. In our case we can justify it as 1.4 as 4.x in our case and will
remove each confusion.

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 14, 2018

it's worth talking about exactly what artefacts we want to publish in the next release. I've just created a new pull request (#229) to bring back hamcrest-integration. As a result of this pull request, there will be 5 artifacts:

  • hamcrest-core
  • hamcrest-library (depends on hamcrest-core)
  • hamcrest-integration (depends on hamcrest-library, and hence also transitively depends upon hamcrest-core)
  • hamcrest-all (all the classes in in hamcrest-core, hamcrest-library and hamcrest-integration packaged into a single jar)
  • java-hamcrest (all the classes in hamcrest-core and hamcrest-library packaged into a single jar)

One question I have is whether we should keep the existing 4 artefacts (core, library, integration and all), or remove core and library, replaced by java-hamcrest. My personal preference: keep the existing jars, give people warning that some of them will disappear in a future version. What do other people think?

When (or if) we do remove hamcrest-core and hamcrest-library, we can change the dependency on hamcrest-integration to java-hamcrest. hamcrest-all essentially remains the same.

@sf105

This comment has been minimized.

Copy link
Member

sf105 commented Nov 14, 2018

I'd like to collapse all the artefacts. It turns out there's really no point to the division, it's just confusing and complicated. There should be exactly one jar for all the matchers with no dependencies outside the JVM (except junit for testing it). The confusion with java-hamcrest is that was a failed attempt at major new version.

I recommend that we do minor version changes into hamcrest-all and deprecate the other jars. That we deprecate java-hamcrest back to hamcrest-all. That, at the next major change, we deprecate hamcrest-all in favour of a hamcrest artefact.

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 14, 2018

I really like the idea of having an artefact called hamcrest (where we merge hamcrest-core and hamcrest-library). java-hamcrest didn't seem quite right to me.

I'm not sure about hamcrest-integration, though. I don't use it myself, so I'm not sure how popular it is. However, if we include it, it will add optional dependencies to jmock and easymock. Is it feasible to not release any new versions of it? Given that we already have a v2 artefact (a breaking change under semver), the break might be "no more integration". In that case, we can exclude the integration code from hamcrest-all, and it won't need to declare any other dependencies.

Thoughts?

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 15, 2018

I've tweaked PR #229 with a new proposal: 2 artifacts: hamcrest and hamcrest-integration. We only published hamcrest, though. More details on the PR.

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 22, 2018

I've been making a bit of progress on the release, but there are still some things to do:

  • Fix the OSSRH publication (issue #233)
  • Update the change log (issue #232)
  • I think we should support Java 9 modules (issue #185 and/or #186 ?)
  • Agree on the next version number to use. I've put in 2.1 for the moment, but I'm not sure we have consensus on that
  • Update docs in preparation for the next release, specifically:
  • Write up a promotional blog post, article or something

I have created a request on OSSRH to allow me to publish the artifact (https://issues.sonatype.org/browse/OSSRH-44320). @sf105, can you please comment on that JIRA to authorise that change?

What have I missed. Anything else important?

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 23, 2018

I've just closed issue #183, as I think the new release should resolve all the concerns raised there.

As a summary, here's what I'm thinking about at the moment for the next release:

  • Although there is no incompatible API changes (as per semantic versioning), the aborted 2.0.0.0 release is confusing, and I think we need to do a minor version increment above that, so I'm proposing the next version be 2.1.
  • We are going to repackage the old jars hamcrest-core and hamcrest-library, and merge them into a single hamcrest jar in the next version (we don't think the distinction between the two libraries was useful)
  • I'm currently thinking that hamcrest-integration isn't well used, and I'm not planning to release a new version (the 1.3 versions will of course remain in Maven Central). I'm not planning to release a version of hamcrest-all, either (which was a combination of hamcrest-core, hamcrest-library and hamcrest-integration. Hamcrest becomes just a single jar called hamcrest, which is a much less confusing story to tell.
@geekybaiyi

This comment has been minimized.

Copy link

geekybaiyi commented Nov 23, 2018

sounds good, any ETA for the release ?

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 23, 2018

Very Soon Now™

I'm actually wondering if we should publish a release candidate (RC1) build first. Give people a chance to kick the tyres.

@sf105

This comment has been minimized.

Copy link
Member

sf105 commented Nov 23, 2018

Yes. That's a good idea.

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 23, 2018

@sf105, @npryce or @scarytom : could one of you please take a leisurely stroll along to https://issues.sonatype.org/browse/OSSRH-44320 and put a comment along the lines of "tumbarumba has permission to publish to org.hamcrest" (similar to what happened at https://issues.sonatype.org/browse/MVNCENTRAL-554).

If I can get permissions in time, I'll try and put a new RC build out this weekend.

@npryce

This comment has been minimized.

Copy link
Contributor

npryce commented Nov 24, 2018

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 26, 2018

Thanks Nat!

I've just pushed out hamcrest-2.1-rc1.jar. I doesn't look like Maven Central has synced it yet. I'll send out a note to the mailing lists when it comes through.

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 26, 2018

So... first experience report of using 2.1-rc1...

I tried it on my project at work. It's an Apache Flink pipeline, with the tests using (among other things) JUnit 4.12, Mockito, Spock, and Flink Spector. What I found was that in this particular example, the hamcrest-core-1.3.jar was still pulled in as a dependency through a lot of different transitive paths. It turns out that a lot of stuff depends transitively on JUnit 4.12 (which transitively depends upon hamcrest-core), Mockito depends directly on hamcrest-core. I ended up having to do a lot of tweaks to my Gradle dependencies to get it to work. For example, my old test dependencies looked like this:

dependencies {
    testCompile 'io.flinkspector:flinkspector-datastream_2.11:0.8.4',
            'junit:junit:4.12',
            'org.hamcrest:hamcrest-library:1.3',
            'org.mockito:mockito-core:1.10.19',
            'org.spockframework:spock-core:1.1-groovy-2.4'
}

After upgrading Hamcrest, and adding in the excludes to avoid adding hamcrest-core-1.3.jar to the classpath, I had to do this:

dependencies {
    testcompile 'org.hamcrest:hamcrest:2.1-rc1'
    testCompile('io.flinkspector:flinkspector-datastream_2.11:0.8.4') {
        exclude group: 'org.hamcrest'
    }
    testCompile('junit:junit:4.12') {
        exclude group: 'org.hamcrest'
    }
    testCompile('org.mockito:mockito-core:1.10.19')  {
        exclude group: 'org.hamcrest'
    }
    testCompile('org.spockframework:spock-core:1.1-groovy-2.4')  {
        exclude group: 'org.hamcrest'
    }
}

Needless to say, I think is is probably very error prone, and don't think this looks very nice. I hate to think what it would look like in Maven.

I wonder if there's a more seamless way to allow someone to upgrade Hamcrest, and allow other third-party libraries to automatically get the new version, too. The problem at the moment is that I've given the 2.1-rc1 library a different name, so it doesn't actually get involved in version conflict resolution with the 1.3 jars.

One possible solution that comes to mind: we publish new poms for hamcrest-core and hamcrest-library for 2.1. If we make these poms just declare a dependency upon hamcrest (with no jar otherwise), I think everything should upgrade without problems (given that there should be no API incompatibility). That way, I can just declare a dependency upon 'org.hamcrest:hamcrest-library:2.1' in my project, and it will just pick up the new jar. I think we still want to get away from even publishing anything hamcrest-core and hamcrest-library, but this seems like a reasonable stepwise migration.

What do people think?

@garretwilson

This comment has been minimized.

Copy link

garretwilson commented Nov 26, 2018

So I take it the Hamcrest people aren't coordinating with the JUnit people? If JUnit depends on a Hamcrest library, and Hamcrest tries to refactor the packaging completely independently from what the JUnit people do, it will just be a mess.

Here is an idea, though: Maven will take the latest version of any transitive dependency declared in the POM. Currently I just want a newer version of hamcrest-library (which is the one I use) to get the latest functionality. JUnit depends on hamcrest-core. The Hamcrest people want to combine all their JARs into a single dependency going forward. So why not just name this new combined Hamcrest library hamcrest-core?

That way I can switch from using hamcrest-library to (the new) hamcrest core, and it will automatically replace (and "upgrade") the one referenced by JUnit. I won't have to do any other exclusions or other surgery. (This assumes you're careful and make sure that the new combined hamcrest-core is compatible with Junit.)

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 26, 2018

Thanks @garretwilson.

The JUnit 4 team chose to pick up hamcrest-core as a dependency, and talked about using assertThat(thing, matcher) as the recommended way of writing assertions. The JUnit 5 team changed directions, and has completely dropped the dependency on Hamcrest, or any other matcher library. These are now orthogonal concerns (which is a good thing, I think - you can easily mix and match the matcher library used, depending upon needs).

For the Java Hamcrest 2.1 release, I'm proposing we completely remove hamcrest-core and hamcrest-library as published artefacts. These have both been merged into a single artefact called hamcrest (e.g. hamcrest-2.1.jar). Naming the combined jar hamcrest-core might simplify the upgrade. I'm trying to think of any technical problems it might have, but I can't think of any big things up front, so this might be a good option.

However, the intent was to make a clear statement that the packaging had changed, and giving it a different name was a way to make this explicit.

Looking back at the 2.1-rc1 release again, where there is no hamcrest-core or hamcrest-library, I found a better way to exclude the old 1.3 artefacts in my Gradle script. Just do this:

configurations.all {
    exclude group: 'org.hamcrest', module: 'hamcrest-core'
    exclude group: 'org.hamcrest', module: 'hamcrest-library'
}

That way, I don't have to butcher all the other dependency declarations.

I don't have any handy Maven pom based projects to try, though I'm curious to hear about other peoples' experiences. What about other build tools? Is it very hard to make the change?

@sf105

This comment has been minimized.

Copy link
Member

sf105 commented Nov 26, 2018

interesting ideas. Could we upgrade hamcrest-core and hamcrest-library just to have a dependency on the new combined artefact? I think that's better than using hamcrest-core because that doesn't explain what happened to library.

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 26, 2018

It would be easy enough to create a pom-only artefact for hamcrest-core and hamcrest-library, each with a dependency on hamcrest. I think it will be a fairly trivial change to the Gradle build.

Unless someone is aware of any major problems with this approach, I can try this in an rc2 release in the next couple of days.

@garretwilson

This comment has been minimized.

Copy link

garretwilson commented Nov 26, 2018

However, the intent was to make a clear statement that the packaging had changed, and giving it a different name was a way to make this explicit.

Yes, I agree this is a great approach in theory. The existing JUnit dependency on hamcrest-core in a pragmatic concern, though; it throws a wrench in the theory. 😄

The ideal solution in that case is to contact the JUnit people and have them release one more version of JUnit 4 with a dependency on your new Hamcrest-with-a-different-name. Any chance of that happening?

With @sf105 's idea, it seems you would doing both: issuing a new Hamcrest naming convention, and releasing drop-in "aliases" that would override JUnit's transitive dependencies. I suppose that would work, but if a "clear statement" is what you want… wouldn't this just cause confusion for those who haven't read this thread—more confusion than just making hamcrest-core the new combined JAR? I mean, I understand what's going on. But I predict you'll get a bunch of bug requests and Stack Overflow questions saying, "Which one of these three JARS do I choose now?" The answer would be "any of them", at which point the user would say, "then why do we have three if they are the same"?

If the Hamcrest response is that "we can document all this on some page", history indicates that this isn't likely to happen (see #183). And after all, wouldn't be easier to document the new contents of hamcrest-core than trying to explain three libraries?

Lastly, aren't most people likely to use Hamcrest with JUnit, in which case they'll need to use the "alias" hamcrest-core? How would they know that without reading this thread?

My purpose here is not to argue; rather I'm just trying to lay out all the options and their consequences so you can make an informed decision.

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 26, 2018

Thanks @garretwilson, I appreciate you helping us clarify the various points of view. Let me try to summarise the various positions, as I understand them right now.

What have I missed? Are there other options?

Option 1

Merge hamcrest-core and hamcrest-library into hamcrest

We create a new jar called hamcrest-2.1.jar, which has all the code previously in core and library. We do not publish jars for the old core and library, but we do publish a pom for each of those, which just has a single dependency on the new merged jar (i.e. hamcrest-core-2.1.pom and hamcrest-library-2.1.pom).

Option 1 Implications

  • It is clear that hamcrest is a new packaged container for the hamcrest code
  • Users will have to change both version (1.3 -> 2.1) and artifactId (hamcrest-library -> hamcrest) when upgrading their projects.
  • The old jars will still be pulled in by transitive dependencies by projects that have not yet upgraded (e.g. JUnit 4.12, any Hamcrest extensions)
    • This can be addressed on a project-by-project basis by either excluding the legacy jars, or explicitly depending upon the newer poms to trigger a version upgrade.
  • Users may not even realise that the project has been repackaged (who reads the docs, anyway?)

Option 2

Merge hamcrest-library into hamcrest-core

We merge all the code from library into core. We publish a single jar called hamcrest-core-2.1.jar. We also publish a pom-only hamcrest-library-2.1.pom, which contains a dependency upon hamcrest-core.

Option 2 Implications

  • It is less clear that the libraries have been repackaged
  • Users will only have to change the version on whatever jar they are depending upon (e.g. 1.3 -> 2.1)
  • The name doesn't really reflect the updated purpose of the artefact
@monperrus

This comment has been minimized.

Copy link

monperrus commented Nov 28, 2018

For the record: we always test against the latest versions of all our dependencies using:

 mvn versions:use-latest-versions -DallowSnapshots=true

Our build is broken, because use-latest-versions recognizes a new version hamcrest-core:2.1-rc2 on Central (while there is actually no jar).

What solution would you recommend?

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 29, 2018

I've just created PR #236 to build empty jars for hamcrest-core and hamcrest-library. I believe that this should resolve this particular issue. If no one objects, I'll look to creating 2.1-rc3 later today or tomorrow.

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 29, 2018

I've just release version 2.1-rc3 to resolve the problem with maven transitive dependency resolution.

I'd be interested in hearing feedback from folks who have tried this in Gradle, Maven, or any other dependency management tool out there.

@monperrus

This comment has been minimized.

Copy link

monperrus commented Nov 30, 2018

@garretwilson

This comment has been minimized.

Copy link

garretwilson commented Nov 30, 2018

I tried the new JAR version 2.1-rc3. Remember that this is my dependency tree beforehand:

org.example:foobar:pom:1.2.3
+- junit:junit:jar:4.12:test
|  \- org.hamcrest:hamcrest-core:jar:1.3:test
+- org.hamcrest:hamcrest-library:jar:1.3:test

Here is my POM:

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>org.hamcrest</groupId>
  <artifactId>hamcrest-library</artifactId>
  <version>1.3</version>
  <scope>test</scope>
</dependency>

First I tried just the new hamcrest-library:

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>org.hamcrest</groupId>
  <artifactId>hamcrest-library</artifactId>
  <version>2.1-rc3</version>
  <scope>test</scope>
</dependency>

As expected this updated hamcrest-library but not the transitive hamcrest-core:

org.example:foobar:pom:1.2.3
+- junit:junit:jar:4.12:test
|  \- org.hamcrest:hamcrest-core:jar:1.3:test
+- org.hamcrest:hamcrest-library:jar:2.1-rc3:test

So I changed to hamcrest-core:

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>org.hamcrest</groupId>
  <artifactId>hamcrest-core</artifactId>
  <version>2.1-rc3</version>
  <scope>test</scope>
</dependency>

As expected (and desired), this replaced the hamcrest-core that JUnit transitively brings in:

org.example:foobar:pom:1.2.3
+- junit:junit:jar:4.12:test
+- org.hamcrest:hamcrest-core:jar:2.1-rc3:test
   \- org.hamcrest:hamcrest:jar:2.1-rc3:test

I personally think some users will be confused by this whole process, and it's a bit of a kludge to have this "empty" intermediate hamcrest-core. If I were doing it myself, I would either put everything in hamcrest-core, or keep things split out into hamcrest-core and hamcrest-library (understanding that I'd have to declare two things in my POM in the latter scenario for this to work with an old JUnit).

Nevertheless there are tradeoffs with all these approaches, and the current approach works reasonably well. But you'll need to make this very clear to users in a prominent documentation page or there will be confusion:

  • If you just want to use Hamcrest by itself, use org.hamcrest:hamcrest.
  • If you want to use Hamcrest with JUnit 4, use org.hamcrest:hamcrest-core.
  • If in doubt, use org.hamcrest:hamcrest-core and you can never go wrong.
  • Heck, if you declare all three (org.hamcrest:hamcrest, org.hamcrest:hamcrest-core, and org.hamcrest:hamcrest-library), nothing will actually be "wrong", even though this isn't recommended as it needlessly brings in "fake"/"alias"/"proxy" JARs.
@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Nov 30, 2018

I've done a bit of reading about Maven dependency resolution and conflict handling. This was useful: https://cwiki.apache.org/confluence/display/MAVENOLD/Dependency+Mediation+and+Conflict+Resolution

The interesting thing (to me, at least) was the different strategies that Gradle and Maven use for version conflicts by default.

  • Gradle: load the entire dependency graph, select the latest version of each artifact
  • Maven: select the first version of each artifact found on a breadth first traversal of the dependency graph. This means that artifacts which are directly declared as a dependency take precedence over first level transitive dependencies, which takes precedence over second level transitive dependencies, and so on. In cases where conflicting artfifacts are at the same depth, the first one listed will take precedence.

Here's an example of that in action. Given this 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>org.hamcrest.example</groupId>
  <artifactId>junit-hamcrest</artifactId>
  <version>1.0</version>
  <packaging>jar</packaging>
  <name>JUnit 4 with Hamcrest</name>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-library</artifactId>
      <version>2.1-rc3</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Here's the dependency tree:

% mvn dependency:tree -Dverbose
[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------< org.hamcrest.example:junit-hamcrest >-----------------
[INFO] Building JUnit 4 with Hamcrest 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ junit-hamcrest ---
[INFO] org.hamcrest.example:junit-hamcrest:jar:1.0
[INFO] +- junit:junit:jar:4.12:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] \- org.hamcrest:hamcrest-library:jar:2.1-rc3:test
[INFO]    \- (org.hamcrest:hamcrest-core:jar:2.1-rc3:test - omitted for conflict with 1.3)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.691 s
[INFO] Finished at: 2018-11-30T18:01:11Z
[INFO] ------------------------------------------------------------------------

If I change the order of dependency declaration:

<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>org.hamcrest.example</groupId>
  <artifactId>junit-hamcrest</artifactId>
  <version>1.0</version>
  <packaging>jar</packaging>
  <name>JUnit 4 with Hamcrest</name>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-library</artifactId>
      <version>2.1-rc3</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Then the hamcrest-library version "wins" the precedence:

% mvn dependency:tree -Dverbose
[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------< org.hamcrest.example:junit-hamcrest >-----------------
[INFO] Building JUnit 4 with Hamcrest 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ junit-hamcrest ---
[INFO] org.hamcrest.example:junit-hamcrest:jar:1.0
[INFO] +- org.hamcrest:hamcrest-library:jar:2.1-rc3:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:2.1-rc3:test
[INFO] |     \- org.hamcrest:hamcrest:jar:2.1-rc3:test
[INFO] \- junit:junit:jar:4.12:test
[INFO]    \- (org.hamcrest:hamcrest-core:jar:1.3:test - omitted for conflict with 2.1-rc3)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.112 s
[INFO] Finished at: 2018-11-30T18:03:43Z
[INFO] ------------------------------------------------------------------------

I'll look to update the docs with this info.

@garretwilson

This comment has been minimized.

Copy link

garretwilson commented Nov 30, 2018

If I change the order of dependency declaration … [t]hen the hamcrest-library version "wins" the precedence …

That was news to me, and I've been using Maven a very long time. Then again it makes sense, because they are both transitive dependencies at the same level, but I don't think I've seen this specific situation. I'm not sure I would want to rely on the order of declaration, though; that sounds a little brittle. I personally might stick to just declaring hamcrest-core explicitly.

But it's an interesting consideration; thanks for pointing this out.

@olibye

This comment has been minimized.

Copy link

olibye commented Dec 3, 2018

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Dec 3, 2018

Thanks @olibye. I've made a start at a pull request to describe some of the concerns (#PR #237). I don't think it's quite ready yet, but the distributables page can be previewed at https://tumbarumba.github.io/JavaHamcrest/distributables

Regarding the breaking changes, I didn't realise there were those ones until you mentioned them (though it should have been obvious in retrospect). This release was supposed to be more like a 1.4 release (increment minor version with no breaking changes). I only chose 2.1 because 2.0.0.0 had been accidentally released a few years back, and I thought we needed a clear statement that the version we're releasing now is newer.

For IsCollectionContaining and IsArrayContainingInOrder, it should be possible to re-create the original classes, but deprecate them in favour of the new classes. We can remove them if we get around to a 3.0 release.

I'm not sure what you mean by IsA. Which class is that?

@olibye

This comment has been minimized.

Copy link

olibye commented Dec 4, 2018

@sf105

This comment has been minimized.

Copy link
Member

sf105 commented Dec 4, 2018

From what I remember, the trouble is that it's not always the case that the type being checked is the same as the type of the matcher. And, AFAIR, Intellj is fussy in the opposite direction. I lost years of my life to Java's half-baked generics.

@marcphilipp

This comment has been minimized.

Copy link

marcphilipp commented Dec 5, 2018

Interesting discussion! I'm certainly glad that Hamcrest is still alive. 🙂

Have you considered publishing a pom-only artifact that uses relocation instead of publishing empty jars?

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Dec 5, 2018

Ooh, I didn't know about relocation! Thanks, @marcphilipp! I think that's exactly what we were looking for. I tried to RTFM, but didn't quite hit upon the right search terms.

I might try a few experiments, and see if it's worth releasing another rc.

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Dec 7, 2018

So, I played around with using the pom relocation feature. I couldn't get it to work. Here's what I tried...

I created this pom.xml for hamcrest-core:

<project>
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.hamcrest</groupId>
  <artifactId>hamcrest-core</artifactId>
  <version>2.1-SNAPSHOT</version>
  <packaging>pom</packaging>
  <name>Hamcrest Core</name>
  <distributionManagement>
    <relocation>
      <artifactId>hamcrest</artifactId>
    </relocation>
  </distributionManagement>
</project>

Then, in a different project, I tried adding a dependency on hamcrest-core like this:

<project>
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.hamcrest.example</groupId>
  <artifactId>junit-hamcrest</artifactId>
  <version>1.0</version>
  <packaging>jar</packaging>
  <name>JUnit 4 with Hamcrest</name>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-core</artifactId>
      <version>2.1-SNAPSHOT</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

When maven resolves the dependencies, this is what happened:

> mvn dependency:tree -Dverbose
[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------< org.hamcrest.example:junit-hamcrest >-----------------
[INFO] Building JUnit 4 with Hamcrest 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[WARNING] The artifact org.hamcrest:hamcrest-core:jar:2.1-SNAPSHOT has been relocated to org.hamcrest:hamcrest:jar:2.1-SNAPSHOT
[INFO] 
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ junit-hamcrest ---
[WARNING] While downloading org.hamcrest:hamcrest-core:2.1-SNAPSHOT
  This artifact has been relocated to org.hamcrest:hamcrest:2.1-SNAPSHOT.


[INFO] org.hamcrest.example:junit-hamcrest:jar:1.0
[INFO] +- org.hamcrest:hamcrest:jar:2.1-SNAPSHOT:test
[INFO] \- junit:junit:jar:4.12:test
[INFO]    \- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.692 s
[INFO] Finished at: 2018-12-07T07:42:53Z
[INFO] ------------------------------------------------------------------------

My interpretation of this is that Maven processes artifact relocation before it looks at artifact version conflict resolution. This means that Maven never considers that a newer version of the hamcrest-core jar is available.

My current conclusion is that we still have to publish the empty jars for hamcrest-core and hamcrest-library to allow upgrading dependencies via version conflict resolution.

Is there something I missed? Perhaps an alternative way to configure this? Maybe we should just get everyone up upgrade to JUnit 5 😃

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Dec 10, 2018

I've created a new pull request to restore the classes that were renamed or deleted as identified by @olibye (PR #238). I didn't do anything to address the problem with Eclipse and Matchers.isA. I'll give people a day or two to look over those, and if there are no objections or suggestions for improvements, I'll create a new release candidate.

@marcphilipp

This comment has been minimized.

Copy link

marcphilipp commented Dec 12, 2018

So, I played around with using the pom relocation feature. I couldn't get it to work.

Thanks for trying! It seems this feature is not very helpful given how Maven's dependency resolution works after all.

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Dec 13, 2018

I've just released Hamcrest version 2.1-rc4. This release candidate has fixes the backwards compatibility issues identified by @olibye by restoring the missing classes and marking them as deprecated.

@olibye, does this release fix the issues for you?

@nhojpatrick

This comment has been minimized.

Copy link
Contributor

nhojpatrick commented Dec 13, 2018

Just tested using 2.1-rc4, only 1 issue.

I was using org.hamcrest.Factory in a custom matcher for some reason and it appears to no longer exists.

@sf105

This comment has been minimized.

Copy link
Member

sf105 commented Dec 13, 2018

Ah, that's because we decided to stop generating the Matchers class in favour of updating it by hand. Unless you're generating your own, then you shouldn't need it.

@tumbarumba tumbarumba changed the title Release The Latest Build On Maven Release Version 2.1 On Maven Dec 13, 2018

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Dec 19, 2018

Well, it's been 6 days, with no further reported issues. I'll update the docs to clarify the point about org.hamcrest.Factory. If there are no other concerns raised, I'm planning to release the final 2.1 version in the next few days.

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Dec 21, 2018

Share and enjoy! Version 2.1 has been released!

@garretwilson

This comment has been minimized.

Copy link

garretwilson commented Dec 24, 2018

Thanks for everyone's work on this. I'll test things out. By coincidence the next lesson I'm teaching in my course is over unit testing, so I'll update the material to include Hamcrest 2.1.

I'm a little confused with the new public instructions. You make a point of saying to use hamcrest-library and to use it before junit:

<dependencies>
    <dependency>
        <groupId>org.hamcrest</groupId>
        <artifactId>hamcrest</artifactId>
        <version>2.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.hamcrest</groupId>
        <artifactId>hamcrest-library</artifactId>
        <version>2.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

That will work, but it 1) introduces an unnecessary hamcrest-library JAR, and 2) makes the build brittle because the order of declarations suddenly becomes important. (Is everyone going to document this in their POMs? More likely, some new developer will come in to many projects, "tidy up" the order of dependencies, and then suddenly things will break and no one will no why.) Plus 3) I don't even know why the example declares hamcrest, as this will be brought in automatically if the dependencies are done correctly

Remember that the problem we're trying to get around is the old hamcrest-core that junit brings in. So it seems to me better just to declare hamcrest-core:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.hamcrest</groupId>
        <artifactId>hamcrest-core</artifactId>
        <version>2.1</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Now we don't have an extra hamcrest-library which no one needs, and the declaration order no longer matters.

Of course it will work either way, but in my opinion the way it is explained in the docs now could be more confusing to users and has the potential to cause more perplexing problems.

But anyway, the important thing is that it's working. You can explain how you want on the site, and I'll explain it the way I prefer in my course. Thanks again for the new release, and happy holidays.

@tumbarumba

This comment has been minimized.

Copy link
Member

tumbarumba commented Dec 26, 2018

Hi @garretwilson, thanks for highlighting that. At the time I wrote that, I was thinking about other projects or libraries that had a dependency on hamcrest-library-1.3.jar (for example, any or the related projects could be affected). I was trying to give some advice that would cover any project (not just ones that depend on JUnit 4), but this doesn't come through in the docs.

I don't suppose you'd like to submit a pull request to improve things?

@garretwilson

This comment has been minimized.

Copy link

garretwilson commented Jan 2, 2019

I don't suppose you'd like to submit a pull request to improve things?

I'm so sorry, but I'm snowed under with work at is it. I hope my testing and feedback on this ticket has helped, however.

In fact over the holiday I read up on JUnit 5, and it turns out that our projects should really be using JUnit 5 anyway. I would recommend stressing in the documentation that anyone considering using Hamcrest with JUnit 4 should really just invest a couple of hours' research and upgrade to JUnit 5 instead.

So I spent half the day today completely rewriting my lesson on unit tests (which I'll be teaching live to students tomorrow) to cover JUnit 5 and Hamcrest 2.1. It has extensive notes on using Hamcrest 2.1 (and even Hamcrest 1.3) with JUnit 4 as well. There's a big chance I can make the lesson public at some point this year. In the meantime, I've placed a pretty complete summary on our JIRA ticket for adding JUnit 5 support to the GlobalMentor parent POM, which you may find useful as a quick overview and reference.

Happy New Year, by the way!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment