Background
The root of the problem is, that org.junit
already has a dependency to org.hamcrest.core
. So when your test-plugins has a dependency to org.hamcrest.all
(which contains everything of hamcrest-core and all other hamcrest artifacts), all classes specified in hamcrest-core exist twice. Once in the bundle of hamcrest-core and once in hamcrest-all, which is why you get the linkage error.
If you open the Manifest of org.junit
in the Manifest-Editor of Eclipse and go to the 'Dependencies' tab it should show you org.hamcreast.core
in the "Required Plug-ins" section and org.hamcreast.core
should be re-exported. Or in the raw-manifest it should look like this:
Require-Bundle: org.hamcrest.core;bundle-version="1.3.0";visibility:=reexport
Solution 1 - add hamcrest sub-modules
Instead of adding the all hamcrest-modul containing hamcrest.all
as dependency to my Eclipse test-bundle/project (via 'Require-Bundle'), I add the hamcrest sub-modules that I require, except for hamcrest-core (because it is already re-exported in my case). For me hamcrest-library was sufficient.
The available hamcrest sub-modules are (according to the org.hamcrest:hamcrest-parent pom, which can be found here: https://repo1.maven.org/maven2/org/hamcrest/hamcrest-parent/1.3/hamcrest-parent-1.3.pom):
- hamcrest-core
- hamcrest-library
- hamcrest-generator
- hamcrest-integration
Creating the p2-Repo containing the required bundles
When using Maven and the 'org.reficio:p2-maven-plugin' to build the p2-repo that contains the mentioned test-bundles, the conversion of the maven-artifacts to OSGi-bundles does not produce fully working results by default.
Converting a maven-module to a full OSGi-bundle consists mainly of configuring the MANIFEST.MF to contain proper entries. For this the p2-maven-plugin utilizes "bnd tool".
By default the Java packages provided by all maven dependencies of a maven module are added as optional Imported-package
when that module is converted into a OSGi-bundle.
In my case this had the consequence that the org.hamcrest.library
bundle refereed to the packages from hamcrest-core only via Import-Package
in its MANIFEST.MF.
But unfortunately with only this specified, the Equinox-ClassLoader did not find the classes from hamcrest-core in the test-runtime and threw a corresponding exception. Maybe this is also caused by the fact that hamcrest-core and hamcrest-library have a package "org.hamcrest" and bnd-tools adds the exported packages of a bundle to the imported packages again.
The solution in my case was to instruct the org.reficio:p2-maven-plugin
respectively bnd-tools to add org.hamcrest.core
as "Require-Bundle" to the Manifest of hamcrest-library. For this, the instructions-element shown below needs to be add to the artifact-element of org.hamcrest:hamcrest-library
in the execution-configuration of the 'p2-maven-plugin' in the pom.xml
used to build the p2-repo:
<artifact>
<id>org.hamcrest:hamcrest-library:1.3</id>
<instructions>
<Require-Bundle>org.hamcrest.core</Require-Bundle>
</instructions>
</artifact>
If hamcrest sub-modules other than hamcrest-library are are used, the instructions need to be analogous, corresponding to the dependencies listed in their pom.
Edit
Eclipse Orbit provides org.hamcrest.library
, org.hamcrest.integrator
and org.hamcrest.generator
bundles that have have org.hamcrest.core as required bundle (if necessary):
https://download.eclipse.org/tools/orbit/downloads/
Appendix
In the end first solution caused a SecurityException:
java.lang.SecurityException: class "org.hamcrest.Matchers"'s signer information does not match signer information of other classes in the same package
Which is a known issue. The following two solutions avoid this issue and work properly during Tycho builds and from within Eclipse.
Solution 2 - bundle hamcrest sub-module jars with a plug-in
Another approach is to download the jar of the required hamcrest sub-module and bundle it with a Eclipse plugin directly, like it is described here:
https://www.vogella.com/tutorials/Hamcrest/article.html#hamcrest_eclipse
To bundle the jar with a plug-in, include it in the project and add it to the plug-ins classpath. Go to the Runtime-Tab of the Manifest-Editor and klick Add...
in the Classpath section and select the jar. This should add the jar to the .classpath, MANIFEST.MF and build.properties file properly.
Make sure the jar is included before the other plug-in dependencies (which include hamcrest-core), as stated in the mentioned tutorial.
If hamcrest should be used in multiple test-projects/fragments, add the jar to a test plug-in all other test-projects depend on.
Solution 3 - use org.hamcrest 2.x
Since hamcrest-2 there is only one org.hamcrest jar/artifact that includes everything from hamcrest. Using hamcrest 2 avoids all the issues and is my preferred solution. Except for the changed packaing of hamcrest the API did not break, so it should be sufficient to just include org.hamcrest:
https://github.com/hamcrest/JavaHamcrest/releases/tag/v2.1
In order to create a p2-repo that includes org.hamcrest
-2.2 the following sippet has to be included into the configuration
-artifacts
element of the p2-maven-plugin
execution in the pom.xml:
<artifact>
<id>org.hamcrest:hamcrest-core:2.2</id>
<instructions>
<Require-Bundle>org.hamcrest;bundle-version="2.2.0";visibility:=reexport</Require-Bundle>
</instructions>
</artifact>
<artifact>
<id>org.hamcrest:hamcrest:2.2</id>
</artifact>
The IUs org.hamcrest.core
2.2 and org.hamcrest
have to be included in the target-platform to make them available for plug-ins in Eclipse and during. All plug-ins which depend on org.junit
now have org.hamcrest
also available.
This aproach works because org.hamcrest.core
still exists in version 2 stream, even tough it is deprected and empty. Its only purpose is to redirect build-systems to the new org.hamcrest
-2.x jar/artifact. Therefore org.hamcrest.core
-2.2 specifies a compile dependency to
org.hamcrest
-2.2 in its pom.xml. Unfortunately the p2-maven-plugin
dosn't translate it directly into a bundle-requirement for org.hamcrest in the manifest, but with the sippet above enforces that.
Because org.junit
requires the bundle org.hamcrest.core with a minimal version of 1.3 (but without upper-bound) it uses the present org.hamcrest.core
-2.2 . org.hamcrest.core
-2.2 again requires org.hamcrest
-2.2 and re-exports it. This makes org.junit
use org.hamcrest
-2.2 in the end and because org.junit re-exports hamcrest-core
it also provides org.hamcrest
-2.2 immediately to all depended plug-ins.
Note
If you want to play around with different variants of a jar, don't forget to clear (means delete on the drive) the bundle pools of Maven (in <your-home>/.m2/repository/p2/osgi/bundle/
and Eclipse PDE (in <your-workspace>/.metadata/.plugins/org.eclipse.pde.core/.bundle_pool/
) in between. Otherwise you will always use the first one, because jar's with the same version are not updated.