XPages to Java EE, Part 6: Dependencies
Thu Jan 31 15:46:20 EST 2019
- XPages to Java EE, Part 1: Overview
- XPages to Java EE, Part 2: Terminology
- XPages to Java EE, Part 3: Hello, World
- XPages to Java EE, Part 4: Application Servers
- XPages to Java EE, Part 5: Web Pages
- XPages to Java EE, Part 6: Dependencies
- XPages to Java EE, Part 7: MVC
- XPages to Java EE, Part 8: IDE Server Integration
- XPages to Java EE, Part 9: IDE Features Grab Bag
- XPages to Java EE, Part 10: Data Storage
- XPages to Java EE, Part 11: Mixing MVC and an API
- XPages to Java EE, Part 12: Container Authentication
- 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":
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:
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:
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.