I'm a software architect with Lockheed Martin Mission Systems and Training. Most of my recent work has been with Java, especially Java EE (JBoss) and OSGi (Karaf), but I've worked with C, C++, C#, Ada, Python, MATLAB, and a few other things over time. Alan is a DZone MVB and is not an employee of DZone and has posted 14 posts at DZone. You can read more from them at their website. View Full User Profile

Using Maven to Build with Embedded Jetty

10.18.2013
| 10153 views |
  • submit to reddit

Previous posts such as this one have shown using embedded Jetty to REST-enable a standalone Java program. Those posts were lacking an important feature for real applications: packaging into a JAR so the application will run outside of Eclipse and won’t be dependent on Maven and jetty:run. To make this happen, we will use Maven to build an executable JAR that also includes all of the Jetty and Spring dependencies we need.

The goal of this work is to get to the point where we can run the example application by:

  1. Cloning the Git repository.
  2. Running mvn package.
  3. Running java -jar target/webmvc-standalone.jar

When I started adding the necessary bits to the pom.xml file of my sample application, I expected a relatively straightforward solution. I ended up with a relatively straightforward solution that was completely different from what I expected. So I think it’s worth a detailed discussion of how this solution works and what Maven is doing for us.

Our desire to make an executable JAR is complicated by the fact that we want our Maven project to build a WAR as a default package, so that we can use this code in a Java web container if desired. Additionally, we introduce some complexity by making a single JAR with all dependencies, because that causes files in the Spring JARs to collide. I’ll show what I did to address each of these.

Build both JAR and WAR

The basic idea here is that we want Maven to make both a JAR file and a WAR file during the “package” phase. Our pom.xml file specifies war as the packaging for this project, so the WAR file will be created as expected. We need to add the JAR file without disturbing this.

I found a great post here that got me started. The basic idea is to add the following to pom.xml under build/plugins:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.4</version>
    <executions>
        <execution>
            <id>package-jar</id>
            <phase>package</phase>
            <goals>
                <goal>jar</goal>
            </goals>
        </execution>
    </executions>
</plugin>

This is the behavior we would get for “free” if we used jar packaging inpom.xml. The execution section ties it to the package phase so that it runs during the default build process. The jar goal tells the plugin what to make. This gets us a basic JAR with the classes in the normal place for a JAR (rather than in WEB-INF/classes as they must be in the WAR file).

At the same time, we need to deal with the fact that the Maven resources plugin considers only src/main/resources to be a resources directory, while in our case we have files in src/main/webapp that also need to be included. We want to copy these resources to the target directory so the JAR plugin will pick them up. (This is an important distinction; the typical Maven question, “how do I include extra resources in my JAR?” should really be “how do I get extra resources into target so the JAR plugin will pick them up?”)

We add this to the build section of pom.xml:

<resources>
    <resource>
        <directory>src/main/resources</directory>
    </resource>
    <resource>
        <directory>src/main/webapp</directory>
    </resource>
</resources>

This causes our new webmvc.jar file to include the HTML, JavaScript, etc. required for our embedded Jetty webapp.

JAR with dependencies

Next, we make an additional JAR that has the correct Main-Class entry in theMANIFEST.MF file and includes the necessary dependencies so we only have to ship one file. This is done using the Maven assembly plugin. The assembly plugin does repackaging only; that’s why we had to add a JAR artifact above. Without that JAR artifact to work from, the assembly plugin repackages the WAR, and we end up with classes in WEB-INF/classes. This causes Java to complain that it can’t find our main class when we try to run the JAR.

The assembly plugin comes with a jar-with-dependencies configuration that can be used simply by adding it as a descriptorRef to the relevant section of pom.xml, as shown in this StackOverflow question. However, this configuration doesn’t work in our particular case, as the Spring dependencies we need have files with overlapping names. As a result, we need to make our own assembly configuration. Fortunately, this is pretty simple. We first add this to the build/plugins section of pom.xml:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <descriptors>
            <descriptor>src/assemble/distribution.xml</descriptor>
        </descriptors>
        <archive>
            <manifest>
                <mainClass>org.anvard.webmvc.server.EmbeddedServer</mainClass>
            </manifest>
        </archive>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>

As before, we use the executions section to make sure this is run automaticaly during package. We also specify the main class for our application. Finally, we point the plugin to our assembly configuration file, which lives in src/assemble. I present the assembly configuration below, but first we need to talk about the issue with the Spring JARs that made this custom assembly necessary.

Spring schemas and handlers

With this sample application, we use Spring WebMVC to provide a REST API for ordinary Java classes, as discussed in this post. The Spring code we use is spread across a few different JARs.

Recent versions of Spring added a “custom XML namespace” feature that allows the contents of a Spring XML configuration file to be very extensible. Spring WebMVC, and other Spring libraries, use this feature to provide custom XML tags. In order to parse the XML file with these custom tags, Spring needs to be able to match these custom namespaces to handlers. To do this, Spring expects to find files called spring.handlers andspring.schemas in the META-INF directory of any JAR providing a Spring custom namespace.

Several of the Spring JARs used by this application include thosespring.handlers and spring.schemas files. Of course, each JAR only includes its own handlers and schemas. When the Maven assembly plugin uses the jar-with-dependencies configuration, only one copy of those files “wins” and makes it into the executable JAR.

We really just need a single spring.handlers and spring.schemas that are the concatentation of the respective files. There is probably some Maven magic to accomplish this, but I elected to do it manually as my Bash-fu is much greater than my Maven-fu. I added two files to the src/assemble directory that have the combined contents of the various files in the Spring JARs.

Maven assembly configuration

The assembly file looks like this:

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
  <id>standalone</id>
  <formats>
    <format>jar</format>
  </formats>
  <baseDirectory></baseDirectory>
  <dependencySets>
    <dependencySet>
      <unpack>true</unpack>
      <unpackOptions>
        <excludes>
          <exclude>META-INF/spring.handlers</exclude>
          <exclude>META-INF/spring.schemas</exclude>
        </excludes>
      </unpackOptions>
    </dependencySet>
  </dependencySets>
  <files>
    <file>
      <source>src/assemble/spring.handlers</source>
      <outputDirectory>/META-INF</outputDirectory>
      <filtered>false</filtered>
    </file>
    <file>
      <source>src/assemble/spring.schemas</source>
      <outputDirectory>/META-INF</outputDirectory>
      <filtered>false</filtered>
    </file>
  </files>
</assembly>

The id will be used to name this assembly. The baseDirectory tells the assembly plugin that the pieces it assembles should go at the root of the new JAR. (Otherwise they would go into a directory using the project name, in this case “webapp”.)

The next two sections are important. We want to exclude thespring.handlers and spring.schemas from the Spring JARs (a.k.a. the dependency set). Instead, we want to explicitly include them from oursrc/assemble directory, and put them into the right place. We also want the assembly plugin to unpack the dependency set JARs so we wind up with Java class files in our new JAR, rather than just JAR-files-inside-JAR-file, which would not run correctly.

Notice that there is no directive telling Spring to include all dependencies from the dependency set, including transitive dependencies. This is the default so we don’t need to specify it. It’s also the default to include the unpacked files from our own artifact (webmvc.jar) into the new JAR.

Conclusion

A real-world application would probably pick either WAR packaging or executable JAR packaging, and be simpler. Additionally, it would be possible to use multiple Maven modules to build a JAR and embed it in the WAR. But it’s interesting to see how to implement a more complex solution that builds everything we need from a single project.

Published at DZone with permission of Alan Hohn, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)