That Java Thing, Part 15: Converting the Projects
Feb 22, 2016, 10:26 AM
- That Java Thing, Part 1: The Java Problem in the Community
- That Java Thing, Part 2: Intro to OSGi
- That Java Thing, Part 3: Eclipse Prep
- That Java Thing, Part 4: Creating the Plugin
- That Java Thing, Part 5: Expanding the Plugin
- That Java Thing, Part 6: Creating the Feature and Update Site
- That Java Thing, Part 7: Adding a Managed Bean to the Plugin
- That Java Thing, Part 8: Source Bundles
- That Java Thing, Part 9: Expanding the Plugin - Jars
- That Java Thing, Part 10: Expanding the Plugin - Serving Resources
- That Java Thing, Interlude: Effective Java
- That Java Thing, Part 11: Diagnostics
- That Java Thing, Part 12: Expanding the Plugin - JAX-RS
- That Java Thing, Part 13: Introduction to Maven
- That Java Thing, Part 14: Maven Environment Setup
- That Java Thing, Part 15: Converting the Projects
- That Java Thing, Part 16: Maven Fallout
- That Java Thing, Part 17: My Current XPages Plug-in Dev Environment
Prelude: there was a typo in the previous entry. Originally, the file URL read "file://C:/IBM/UpdateSite", but, on Windows, there should be another slash in there: "file:///C:/IBM/UpdateSite". I've corrected the original post now, but you should make sure to fix your own settings.xml
file if needed. Otherwise, Maven will complain down the line about the URI having "an authority component".
The time has come to do the dirty work of converting our existing plugin projects to Maven. There will be some filesystem-side reorganizing and not every project will make it (looking at you, source project), but overall it's mostly a job of pasting a bunch of XML into new files.
For the first leg of this, I recommend removing the projects from your Eclipse workspace by selecting them, right-clicking, and choosing Delete:
On the confirmation dialog, do not select "Delete project contents on disk" - we don't actually want to get rid of the files.
Next, find the projects on your filesystem, create a new folder alongside them named "com.example.xsp", and move the projects inside it. In Maven parlance, we're creating a "multi-module project", and this new folder is the top level in our module hierarchy. This can contain arbitrary levels and can be very helpful in project organization, but this will be a pretty simple parent-and-children case. Next, dive into the folder and delete the "com.example.xsp.source.feature" - we'll be able to generate this through Maven now, and so we can trim down our project count slightly.
Now, create a file named pom.xml
in the "com.example.xsp" folder alongside the subfolders, and fill its contents with this:
<?xml version="1.0" encoding="UTF-8"?> <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>com.example</groupId> <artifactId>parent-xsp</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>com.example.xsp.plugin</module> <module>com.example.xsp.feature</module> <module>com.example.xsp.update</module> </modules> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <tycho-version>0.24.0</tycho-version> <compiler>1.6</compiler> </properties> <repositories> <repository> <id>Luna</id> <layout>p2</layout> <url>http://download.eclipse.org/releases/luna/</url> </repository> <repository> <id>notes</id> <layout>p2</layout> <url>${notes-platform}</url> </repository> </repositories> <build> <plugins> <!-- Maven compiler options --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>${compiler}</source> <target>${compiler}</target> <compilerArgument>-err:-forbidden,discouraged,deprecation</compilerArgument> </configuration> </plugin> <!-- Tycho plugins --> <plugin> <groupId>org.eclipse.tycho</groupId> <artifactId>tycho-maven-plugin</artifactId> <version>${tycho-version}</version> <extensions>true</extensions> </plugin> <plugin> <groupId>org.eclipse.tycho</groupId> <artifactId>tycho-packaging-plugin</artifactId> <version>${tycho-version}</version> <configuration> <strictVersions>false</strictVersions> </configuration> </plugin> <plugin> <groupId>org.eclipse.tycho</groupId> <artifactId>tycho-compiler-plugin</artifactId> <version>${tycho-version}</version> <configuration> <source>${compiler}</source> <target>${compiler}</target> <compilerArgument>-err:-forbidden,discouraged,deprecation</compilerArgument> </configuration> </plugin> <plugin> <groupId>org.eclipse.tycho</groupId> <artifactId>tycho-source-plugin</artifactId> <version>${tycho-version}</version> <executions> <execution> <id>plugin-source</id> <goals> <goal>plugin-source</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.eclipse.tycho</groupId> <artifactId>target-platform-configuration</artifactId> <version>${tycho-version}</version> <configuration> <pomDependencies>consider</pomDependencies> <dependency-resolution> <extraRequirements> <requirement> <type>eclipse-plugin</type> <id>com.ibm.notes.java.api.win32.linux</id> <versionRange>[9.0.1,9.0.2)</versionRange> </requirement> </extraRequirements> <optionalDependencies>ignore</optionalDependencies> </dependency-resolution> <filters> <!-- work around Equinox bug 348045 --> <filter> <type>p2-installable-unit</type> <id>org.eclipse.equinox.servletbridge.extensionbundle</id> <removeAll /> </filter> </filters> <environments> <environment> <os>linux</os> <ws>gtk</ws> <arch>x86</arch> </environment> <environment> <os>linux</os> <ws>gtk</ws> <arch>x86_64</arch> </environment> <environment> <os>win32</os> <ws>win32</ws> <arch>x86</arch> </environment> <environment> <os>win32</os> <ws>win32</ws> <arch>x86_64</arch> </environment> <environment> <os>macosx</os> <ws>cocoa</ws> <arch>x86_64</arch> </environment> </environments> <resolver>p2</resolver> </configuration> </plugin> </plugins> </build> </project>
So... yeah, there's a lot going on here. This is the biggest of the "XML dumps" we're going to have and contains by far the greatest number of bizarre "you just have to know about it" parts. "POM" stands for "Project Object Model" - it's the language Maven uses to describe the project. Let's tackle the file from near the top (ignoring the XML header):
project
and modelVersion
These elements are effectively just boilerplate: the project
element is the root of our project descriptor and it contains some definitions to let XML editors parse the file format. In turn, the modelVersion
describes to Maven the specific version we're working with, which has been "4.0.0" for as long as I've been doing this.
groupId
, artifactId
, and version
These elements are obligatory in one form or another in every project, but are less copy-and-paste-able: they define the name and version of your project. These are Maven's equivalents to OSGi's Bundle-SymbolicName
and Bundle-Version
, though Maven makes an explicit distinction between the overall grouping of the plugin and its specific name. These are essentially arbitrary, but the convention is to use the standard reverse-DNS version of your domain name for the group ID, and then keep this group ID consistent across different projects (...mostly). The artifact ID is less consistent, but it's good to pick a pattern like "projectPrefix-submodule". Here, we actually reverse that a bit to call it "parent-xsp" in order to emphasize that this project's purpose is entirely to be a parent to the submodules and not an interesting artifact to consume itself. We'll break this convention again for the submodules due to our use of Tycho/OSGi.
packaging
A project's packaging
describes the sort of output. By default, if this is left un-specified, it's jar
- a normal, run-of-the-mill Jar file. There are a few other common ones you may run into - such as war
for J2EE web apps or bundle
for non-Tycho OSGi bundles - and the one we're using here is pom
. This is actually kind of the "none of the above" option: the "pom" is just the file we're editing now, and is included with every project type. Having a packaging type of pom
generally means that either the project has no real outputs of its own (as is the case here) or it's an "ad hoc" project that doesn't fit an existing type.
modules
This block is the hallmark of a parent project: it lists the relative folder paths that contain the submodules. In this case, the names line up with the names we'll use for the submodules, but this could potentially vary depending on the folder names and locations. Parent-child module relationships don't have to be physically hierarchical on the filesystem, but it's a good convention when you don't have a specific reason to break it.
properties
This block is the project-level equivalent to the user-level property we defined in .m2/settings.xml
. There are two types of properties that can go here, with no obvious distinction between them: known configuration properties used by Maven itself and arbitrary named variables used by the person writing the pom.
The first property - project.build.sourceEncoding
- is an example of the former. During execution, Maven will reference this property defined in the project (or one of its parents) when determining the text file encoding to use. This could be set to something else if you're working with non-Unicode files, but it's important to set it here so that file interpretation will not be platform-dependent. These properties can be read like an equivalent of EL for the project XML: it sets a property of sourceEncoding
within the build
node in project
, but more consisely (more or less).
The other two are variables for use later. The names of these have only very loose conventions, but there seem to be a couple common types: ALL_CAPS
, camelCase
, and hyphen-delimited
. You can also specify variables as dot.delimited
and they will work the same way, but that makes them more difficult to distinguish from the system-level properties.
repositories
The repositories
block is the start of our OSGi-related weirdness. The block itself isn't OSGi-specific - it has its role in other projects that want to make use of dependencies outside of the core public Maven repositories - but the contents is. We're setting two repositories here: one to point to the main Eclipse repository (the Luna version here, but that could just as well be Mars or Kepler) and one to point to the XPages Update Site. This is where the property we set before comes into play, allowing different developers to keep the update site in different locations without changing the project's config.
build
The build
section is often the largest part of a POM file - it contains definitions and configuration for various additional Maven plugins used during compilation. We have two tasks to accomplish here: ensure that we use Java 6 for compilation at the root level (to ensure the build doesn't execute as Java 7 or 8 and be incompatible with Domino) and enable a whole slew of Tycho plugins.
The maven-compiler-plugin
block specifies the version of the Java compiler plugin to use (3.1, which is actually kind of old, but the differences aren't important) and then provides it with some configuration to set the Java version level and to not choke on forbidden references. Like the Java version, the latter is a nod to Domino: depending on your JVM configuration, you may run into forbidden-reference errors relating to the lotus.domino
classes.
The next slew of blocks all relate to loading up various Tycho components. Tycho's job is esentially to construct an entire OSGi environment during the Maven build, and it consists of a number of moving parts, many of which are basically the Tycho version of normal Maven facilities. The tycho-maven-plugin
is the core, and its extensions
rule is what allows it to worm itself into various phases of the build process. The tycho-packaging-plugin
controls the process of bundling the projects as their various types: the plugin, the feature, and the update site (in our case). The tycho-compiler-plugin
is the Tycho variant of the Maven one we configured earlier. The tycho-source-plugin
is what allowed us to kick the standalone source feature to the curb - it's the equivalent of the Eclipse-specific feature we had been hooking into before.
The tycho-platform-configuration
is the scariest of the bunch. This plugin's job is to establish the OSGi Target Platform we're working with, in conjunction with the repository specified above. Not all of this configuration is necessary for our immediate needs, but may come in handy later. The pomDependencies
rule is useful when using mixed-type dependencies in more-complicated projects, while the extraRequirements
block forces the inclusion of the plugin fragment that contains Notes.jar
. The optionalDependencies
rule comes in handy from time to time with XPages projects: there are sometimes cases where there's a dependency that Eclipse has and which the server will have, but which will be awkward to get to in Maven, usually relating to dependencies-of-dependencies not related to compilation. The filters
block is... I don't know; just keep it in there. The environments
block is a way to describe the platforms on which your code can execute - I believe this is primarily used when testing. The resolver
is like filters
in that it's a "I just copy it around" thing; presumably, it refers to a specific code path for resolving plugin dependencies.
Whew!
Okay, so... that's the first one down! For the most part, you can carry around the whole bottom section pretty much as-is for your XPages Maven projects (as I do), and then gradually become comfortable with the specifics over time. Now, there's some good news and some bad news:
- The bad news is that there are three more POM files to write.
- The good news is that they are much, much simpler.
In recent versions of Tycho, they've added the ability to reduce the number of POMs involved, but there are limits to that, particularly to do with Jenkins, so we'll stick to the traditional way for now.
com.example.xsp.plugin
Go into the "com.example.xsp.plugin" folder and create a new pom.xml
containing this:
<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> <parent> <groupId>com.example</groupId> <artifactId>parent-xsp</artifactId> <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>com.example.xsp.plugin</artifactId> <packaging>eclipse-plugin</packaging> </project>
Like I promised: much simpler. Because the parent POM already brought in all the Tycho plugins and configuration, all we need to do here is the basics. One slightly-unusual aspect here is the packaging type. eclipse-plugin
isn't a packaging type known to Maven inherently; instead, it's provided by Tycho, but can be used in the same way.
The artifact ID here is a concession to Tycho: Maven artifact IDs don't usually follow the same full-reverse-DNS conversion as OSGi, but Tycho wants the artifact ID to match the OSGi bundle name.
com.example.xsp.feature
Next up is the pom.xml
file in the "com.example.xsp.feature" folder:
<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> <parent> <groupId>com.example</groupId> <artifactId>parent-xsp</artifactId> <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>com.example.xsp.feature</artifactId> <packaging>eclipse-feature</packaging> </project>
This is very similar to the last, with the only real differences being the artifact ID and the packaging type.
com.example.xsp.update
Now, the pom.xml
in "com.example.xsp.update":
<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> <parent> <groupId>com.example</groupId> <artifactId>parent-xsp</artifactId> <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>com.example.xsp.update</artifactId> <packaging>eclipse-update-site</packaging> <build> <plugins> <plugin> <groupId>org.eclipse.tycho</groupId> <artifactId>tycho-packaging-plugin</artifactId> <version>${tycho-version}</version> <configuration> <archiveSite>true</archiveSite> </configuration> </plugin> </plugins> </build> </project>
This one's slightly longer, but not by too much. Beyond the different artifact ID and packaging, we also provide some additional configuration to the tycho-packaging-plugin
. This is among the plugins that were established in the root POM, but it's re-defined here in order to enable the archiveSite
configuration option. This will give us a nice ZIP file of the Update Site at the end.
There's one other thing to note here: Tycho considers the eclipse-update-site
packaging type to be deprecated, and it may be removed in the future. In most examples you'll see outside of Domino, people use eclipse-repository
instead. This gets back to the difference between the old-style ("site.xml") Eclipse Update Sites and the new-style ("category.xml", named P2) Update Sites. For now, we use the old-style variant because it works better with Notes and Domino.
In addition to this POM file, we also have two changes to make in the site.xml
: remove the source-feature reference (we'll add this back elsewhere later) and clean up the versions:
<?xml version="1.0" encoding="UTF-8"?> <site> <feature url="features/com.example.xsp.feature_1.0.0.qualifier.jar" id="com.example.xsp.feature" version="1.0.0.qualifier"> <category name="Example"/> </feature> <category-def name="Example" label="Example"/> </site>
The version distinction is to change the Eclipse-generated timestamps at the end of the versions to "qualifier". The reason for this is that, for Tycho, the site.xml
acts as a pure configuration file, and will no longer be the site index itself. So Tycho wants the qualifier to be generic, and then will fill it in during compilation. Like the source feature, this will be covered more later.
Last Steps
With our POM files defined, the last step for now is to import the projects back into Eclipse. In Eclipse, go to File → Import, expand the "Maven" category, and choose "Existing Maven Projects":
On the next screen, browse to the "com.example.xsp" directory created earlier. If all goes well, this should find the four projects in their hierarchy:
Everything on this can be left as the defaults, though you may want to specify a more-descriptive working set name - that doesn't affect the project behavior.
When you click "Finish", Eclipse with churn for a bit and then, if you're running Mars and haven't done this before, it will present a dialog about "Maven plugin connectors":
The specifics of what is going on here are a large topic of their own, but the short of it is that Eclipse needs specialized plugins to deal with each Maven plugin, and in this case it's looked for (and found) connectors for Tycho. Click "Finish", "Next", and "OK", accept the license terms, and restart Eclipse as it tells you to.
When Eclipse restarts, it should go through some churning while it updates the Maven projects and should finally settle on no remaining errors.
Closing Out
There will be some things to discuss with the fallout from this conversion, but this will do it for today. Commit your changes, stand up and stretch, and grab a cup of relaxing tea:
Cameron Gregor - Feb 22, 2016, 5:42 PM
After the steep learning curve of java / xpages / plug-in dev / osgi / control dev I couldn't afford any more set backs and had avoided looking into maven.
Thanks for taking the time to share this. I am sure all this maven setup info was obtained from encountering a few frustrating road-blocks!