05 January 2016
Jonathan Graham

I recently wrote a java http server, which included specific routing so that it would pass the 8th Light cobspec suite of tests. So that I could use my server to serve other apps, I wanted to pull it out as a separate jar. I could then include it as a dependency within my cobspec app, and any other web apps.

I had already built a clean interface, so separation of the code into two new projects, java server and cobspec app, was fairly trivial. I used Maven for the builds, and with mvn package the compiled server code successfully packaged into a jar, with all the associated server unit tests passing. For the cobspec app to function as before it just needed to depend on the new server jar. Easy, right?

Maybe, but although there is plenty of information out there, in the form of docs, blogs, stackoverflows, etc, nothing I found actually gave me everything I needed. However, not having everything working straight away gives you a chance to understand what is really happening, and below is what I learned about adding a local jar as a dependency in a Maven project.

With a Maven project, you can use mvn install to install the package into a local repository, for use as a dependency in other projects locally. Running this put my server jar and pom files in Users/user/.m2/repository/com/jgraham/server/server/1.0-SNAPSHOT (the server Maven project was created with a groupID of com.jgraham.server, an artifactID of server, and version 1.0-SNAPSHOT). I assumed that I could then simply add a dependency to the server in my cobspec app pom.xml file:

  <dependency>
      <groupId>com.jgraham.server</groupId>
      <artifactId>server</artifactId>
      <version>1.0</version>
  </dependency>   

However, on running mvn package for the cobspec app, I got the error The POM for com.jgraham.server:server:jar:1.0 is missing, no dependency information available and the build failed.

Instead of installing the server to a local respoitory, I decided to deploy it for direct use in the cobspec app:

mvn deploy:deploy-file -Durl=file:/<path_to_app>/app/lib/ -Dfile=server-1.0-SNAPSHOT.jar -DgroupId=com.jgraham.server -DartifactId=server -Dpackaging=jar -Dversion=1.0

This put the jar and pom in a lib folder in my cobspec app.

To enable Maven to know where to find the server during the cobspec app build, I added the new lib repository to the POM file:

<repositories>
    <repository>
        <id>lib</id>
        <url>file:${project.basedir}/lib</url>
    </repository>
</repositories> 

With this in place, the cobspec app successfully built using mvn package, with all the unit tests passing.

However, on running the jar (java -cp target/cobspec-1.0-SNAPSHOT.jar com.jgraham.cobspec.Main) I got the error Exception in thread "main" java.lang.NoClassDefFoundError: com/jgraham/server/Routers/iAppRouter.

Although the server was available for the build, its classes were not put on the cobspec classpath for runtime.

To copy the dependencies into the classpath during the build, I used the maven-dependency-plugin by adding to the POM:

<build>
    <plugins>
        <plugin>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <phase>compile</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>${project.build.directory}/lib</outputDirectory>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

This creates a lib folder within target, holding all the jars that cobspec depends on. These are still not available on the classpath, however, and I used the maven-jar-plugin to add the contents of the lib folder to the classpath:

  <plugin>
      <artifactId>maven-jar-plugin</artifactId>
      <configuration>
        <archive>
          <manifest>
            <addClasspath>true</addClasspath>
            <classpathPrefix>lib/</classpathPrefix>
          </manifest>
        </archive>
      </configuration>
  </plugin>

After rebuilding with mvn package, the cobspec jar can be run using java -cp target/cobspec-1.0-SNAPSHOT.jar com.jgraham.cobspec.Main, which serves on localhost:5000 by default.

By adding <mainClass>com.jgraham.cobspec.Main</mainClass> to the <manifest> the jar can be run directly with java -jar target/cobspec-1.0-SNAPSHOT.jar, and everything works just as it should.

So, in retrospect it was easy to add a local jar as a dependency in a Maven project, even if the hours slipped away in the process!

You can find the full source code for the java server and cobspec app on github.



blog comments powered by Disqus