Maven, Eclipse Tycho and 3rd-party dependencies

I was recently trying to solve problem with an Eclipse RCP application that has its build managed entirely by Maven (with Tycho plugin) and I have spent hours trying to find a clear and reliable solution on how-to integrate it properly with Maven pom-defined dependent components.

The problem definition

  • Creating a single standalone Eclipse RCP that builds with Maven using Tycho plugin, as for headless automated compilation,
  • and at the same time uses the Simple Framework (for convenient Java object serialization to XML) taken from Maven central repository that way.

The problem turned out to be a bit non-trivial - mainly becuase it requires bridging two separate realms:

  • the native Maven view of things
  • with the way Eclipse sees the world

They are not compatible to each other.

Note: The term plug-in is heavily overloaded here in this post. It is very well applicable to the context of Eclipse development (PDE and plug-ins as fundamental block the entire Eclipse’s is made of) as well to Maven context (the Maven’s plug-ins for performing various build tasks in the build system). So be careful.

An RCP application

I have defined the following structure of an example RCP application

eclipse
.
├── bundles
│   ├── rcp
│   │   ├── build.properties
│   │   ├── META-INF
│   │   ├── plugin.xml
│   │   └── src
│   └── pom.xml
├── releng
│   ├── configuration.neon
│   │   └── pom.xml
│   ├── pom.xml
│   ├── product
│   │   ├── example.product
│   │   ├── pom.xml
│   ├── targetplatform
│   │   ├── example.target.target
│   │   └── pom.xml
│   └── updatesite
│       ├── category.xml
│       └── pom.xml
└── pom.xml

This is pretty popular layout of this kind of projects, where Tycho plugin is being used to build an Eclipse product.

You may have a look to this tutorial for that guides through process of getting a similar structure for an arbitraty Eclipse RCP project with build governed by Maven.

Once you have your pom.xml files setup, you can just execute:

mvn clean verify

from a command line and you’ll get your product built in the eclipse/releng/product/target/products directory.

Jolly good.

This is the world of Eclipse Tycho, and what it means is that ALL DEPENDENCIES between various components within this complex structure are described using OSGi bundle manifests (sitting in META-INF directories of Eclipse’s plugins).

The entire application is made of two software components groups:

  1. Binary components that come from Eclipse’s p2 repository and contribute to the target platform definition (as described in releng/configuration.neon/pom.xml)
  2. A source code that in this case reside in the bundles directory (the rcp sub-directory with RCP application skeleton as well as two simple plugins: feature-a and feature-b).

Both Tycho Maven plugin as well as Eclipse can take such a structure and perform a valid build of the final RCP product.

The Ecilpse target platform definition can be easily extended to any other thirdparty components as long as they are packaged to form of OSGi bundles. There are already popular public repositories of such ones (like Orbit one) so chances are you might find what you already need there. Adding those components is easy, just edit target platform XML file, add repository and features you need.

But how about non-OSGi components? Now comes the challenge.

Maven dependencies

At the same time there is a vast selection of thirdparty and universal Java components out there, managed by Maven dependency mechanism in pom.xml files, e.g. like this for Simple Framework:

...
    <dependency>
        <groupId>org.simpleframework</groupId>
        <artifactId>simple-xml</artifactId>
        <version>2.7.1</version>
    </dependency>
...

At this point I have a rather said message: there seem to be no good way to have a single-pass and purely Maven-driven build process that starts with clean sources annd correctly bridges the two worlds to deliver final artifacts (like RCP app correctly intergrated with thirdparty Maven components).

You have to use two-step approach instead, with a local Maven repository as an intermediate storage.

The process can look like this.

The first Maven build will:

  • create an OSGi wrapper for a Maven component that you want to use (like the Simple Framework shown above)
  • install this component in the local m2 repository.

By this time your local repository will have two variants of the library co-exising side-by side: the original one and the one wrapped in OSGi container.

The second, separate Maven build, will build RCP application with Maven and Tycho that includes the bundle created in the previous step into Eclipse’s target platform definition - thus making it visible to the entire Eclipse project setup and Tycho build.

The wrapper project

To achieve all that, the project with Maven pom-defined dependencies can be placed in sibling maven-deps directory and can look simply that:

maven-deps
.
└── pom.xml

With the pom.xml file using maven-bundle-plugin:

<project>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>com.example.mavendeps</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <packaging>bundle</packaging>

    <dependencies>
        <dependency>
            <groupId>org.simpleframework</groupId>
            <artifactId>simple-xml</artifactId>
            <version>2.7.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <version>3.0.1</version>
                <extensions>true</extensions>

                <configuration>
                    <instructions>
                        <Export-Package>*</Export-Package>
                        <Embed-Dependency>*;scope=compile|runtime;inline=false</Embed-Dependency>
                        <Embed-StripGroup>true</Embed-StripGroup>
                        <Embed-Transitive>true</Embed-Transitive>
                        <Import-Package></Import-Package>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

This special plugin will create an OSGi bundle jar with
Simple Framework jar taken from Maven repository, as well as all dependent required components (controlled by <Embed-Transitive>true</Embed-Transitive>), and finally, also an embedded MANIFEST.MF file that exports all packages available there. With mvn install command, this bundle will be installed in local Maven m2 repository and become available for any other Maven-govern build.

Altering RCP project

To ensure our RCP project gets that Maven dependency bundle, we need to edit MANIFEST.MF file of the Eclipse rcp project and add in the Require-Bundle: section a com.example.mavendeps entry.

In addition, we need to enable Tycho to get that OSGi bundle from where it is available, that is our local m2 Maven repository. This is done by adding an extra configuration parameter pomDependencies like this:

...
<plugin>
    <groupId>org.eclipse.tycho</groupId>
    <artifactId>target-platform-configuration</artifactId>
    <version>1.0.0</version>
    <configuration>
        <pomDependencies>consider</pomDependencies>
    <configuration>
    ...
</plugin>
...

This will command Tycho to look for required and non-resolved bundles in a local maven repository, and when found there, add them to the target platform with all the rest.

Once those two modifications are done, classes from org.simpleframework package can be directly imported in RCP java files, and will be correctly resolved at both: compile- and run-time.

Building from a command line

The combined command line build sequence for the entire solution will look like this then:

# the first building phase...
cd maven-deps
mvn clean install
cd ..
# ... and the second
cd eclipse
mvn clean verify

Remark: The very first mvn clean install step is required to be performed only once, when you start working with the checked-out sources. When done, it will not have to be repeated over and over again, until some extra-ordinary event happens (like invalidation of your m2 local Maven repository or bumping the required thirdparty library version number etc).

References

https://wiki.eclipse.org/Tycho/How_Tos/Dependency_on_pom-first_artifacts

comments powered by Disqus