XPages to Java EE, Part 6: Dependencies

Thu Jan 31 15:46:20 EST 2019

Tags: javaee
  1. XPages to Java EE, Part 1: Overview
  2. XPages to Java EE, Part 2: Terminology
  3. XPages to Java EE, Part 3: Hello, World
  4. XPages to Java EE, Part 4: Application Servers
  5. XPages to Java EE, Part 5: Web Pages
  6. XPages to Java EE, Part 6: Dependencies
  7. XPages to Java EE, Part 7: MVC
  8. XPages to Java EE, Part 8: IDE Server Integration
  9. XPages to Java EE, Part 9: IDE Features Grab Bag
  10. XPages to Java EE, Part 10: Data Storage
  11. XPages to Java EE, Part 11: Mixing MVC and an API
  12. XPages to Java EE, Part 12: Container Authentication
  13. XPages to Java EE, Part 13: Why Do This, Anyway?

This is going to be a quick post, but I think it's useful to treat the topic of third-party dependencies on its own because of how much nicer it is with a Maven-based app than an NSF or OSGi plugin.

Historically, we've handled dependencies primarily by downloading a Jar and either plunking it in jvm/lib/ext on the server, stashing it in a Java agent or script library, or importing it into the NSF as a Jar design element for XPages. With OSGi plugins, that remained the simplest way to do it too: just drop the Jar into the plugin and add it to the bundle classpath.

The two big problems with those approaches are that they rely on having just "some file" deployed around with no version management and they also don't include any source. As anyone who's tried to figure out some behavior inside the XPages stack knows, not having the source for your dependencies is a real pain in the ass.

Building a normal Maven (or Gradle) project, though, means dependency management becomes much easier and we get source support "for free".

Eclipse Prep

Before we begin, open your Eclipse preferences and go to the "Maven" category. There, turn on "Download Artifact Sources" and "Download Artifact Javadoc":

Eclipse's Maven preferences

This will cause Eclipse to automatically use Maven's ability to download associated source and Javadoc for dependencies (referred to "artifacts" in Maven parlance). You can do this manually after the fact or via the command line, but it's nice to have it on by default.

Adding the Dependency

For our example, we'll bring in a Markdown processor. Open the project's pom.xml file and set the dependencies block to this:

	<dependencies>
		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-api</artifactId>
			<version>8.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>com.atlassian.commonmark</groupId>
			<artifactId>commonmark</artifactId>
			<version>0.12.0</version>
		</dependency>
	</dependencies>

Save the file and that's it - Eclipse will automatically fetch the Jar and add it to the project's build path:

Eclipse's Maven Dependencies library

Note that the two dependencies in the pom.xml have a key difference: the Java EE API dependency is marked as <scope>provided</scope> while the new dependency has no specified scope (technically making it compile scoped). This determines the behavior of the .war packager: a "provided" dependency is available while developing, but is not packaged with the application. This is used to indicate that you expect the runtime environment to provide this dependency for you, which a Java EE container does for the EE API. With a default/compile-scoped dependency, the Jar is included in the app's WEB-INF/lib directory, which the container knows to include in the app's runtime class path.

Using the Dependency

This section shouldn't have any surprises: now that you added the dependency, it's available for your app. Create a new class in the com.example package named MarkdownExample with this contents:

package com.example;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;

@Path("markdown")
public class MarkdownExample {
	@GET
	@Produces(MediaType.TEXT_HTML)
	public String get() {
		Parser markdown = Parser.builder().build();
		Node parsed = markdown.parse("# Hello\n\nWorld"); //$NON-NLS-1$

		HtmlRenderer markdownHtml = HtmlRenderer.builder().build();
		return markdownHtml.render(parsed);
	}
}

As before, run a Maven Build with the goals clean install tomee:run and then visit http://localhost:9091/javaeetutorial/resources/markdown. If all goes well, you should see the HTML output:

Markdown HTML output

Updating the Dependency

Beyond just automatically bringing in dependencies, Maven gives us a raft of abilities to manage them. Do a Run As -> Maven Build... on the project and this time set the goals to versions:display-dependency-updates This will run for a bit to look up all of your dependencies to find if any are out of date. After running, you should see something like this near the bottom (the versions may differ based on when you do this):

[INFO] The following dependencies in Dependencies have newer versions:
[INFO]   com.atlassian.commonmark:commonmark ................. 0.12.0 -> 0.12.1
[INFO]   javax:javaee-api ..................................... 8.0 -> 8.0.1-b5

You can also have Maven automatically bump the versions in your pom.xml for you, but this demonstrates why I don't like to do that: the javaee-api update is to a beta version, and we have no need to move to that. There's no reason not to update our commonmark dependency, though, and so I like to run this (and the equivalent command to look for Maven plugin updates) periodically.

Next Steps

After these basics, the next steps are going to have to involve making some choices that won't apply as generally as the steps so far. Data storage and user authentication are going to vary greatly from environment to environment, but I'll aim to show the current ways to do those in a mostly-agnostic fashion.

Commenter Photo

Gary J. Morin - Thu Feb 07 17:46:31 EST 2019

Thanks for contributing these posts!  It is a HUGE help for those of us wanting to explore the Java world.

New Comment