Deploying Java Library to Maven Central

There are many blogs out there that talk about the process for setting up and distributing a Java library to the Maven Central repository. There are also many tools that claim to be a one click solution for such distribution. But, unfortunately, we did not find a single blog that had the entire process described from start to finish. And before we jump to using 3rd party tools, it is always a good idea to understand the process and see what actually happens behind the scenes.

Maven Central requires quiet a long setup and complex configuration procedures before a Java library can be available for download through Maven Central repository. The initial setup typically takes a few days before your library shows up in Maven Central search. As you read this blog, you will see why.

So let’s try to go through the entire process from start to finish and see what it takes to get it all working.

Assumptions and Prerequisites

This guide assumes that you are using a Mac (since that’s what we are using). We are also using OS X Yosemite. The steps maybe somewhat different on a Windows machine or on a different version of Mac OS (although we did test the process on El Capitan as well).

The only prerequisite for this guide is that you have the latest Java installed on your Mac. We are using 1.8.0_51-b16.

[shell title=”$”]
$ java -fullversion
java full version “1.8.0_51-b16”
[/shell]

To download Oracle’s latest Java, you must do so from Oracle’s web site. Click here to learn more about how and why.

In short, visit https://www.java.com/en/download/ to download and install the latest Java.

Make sure you set your JAVAHOME in your ~/.profile file.

[shell title=”~/.profile”]
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home
export PATH=$JAVA_HOME/bin:$PATH
[/shell]

Installing Maven 3

For this guide, we will use Maven 3 to build, test and publish our code.

Since there is no way to publish to Maven Central directly, we must use one of the approved 3rd party platforms to publish to Maven Central on our behalf. We will use the Open Source Software Repository Hosting (OSSRH) provided by Sonatype, “the stewards of the Central Repository and the creators of the Apache Maven project”.

So let’s begin by downloading and installing Maven 3 on our computer.

Maven 3 can be downloaded from the following URL:

https://maven.apache.org/download.cgi

Once you download the apache-maven-3.3.3-bin.tar.gz to your computer, unzip it to the folder where you want to keep it. We will use ~/Projects/Java/apache-maven-3.3.3 folder to store maven source.

Edit your .profile file and add the following lines at the top of the file:

[shell title=”~/.profile”]
export PATH=”$HOME/Projects/Java/apache-maven-3.3.3/bin:$PATH”
[/shell]

This will ensure that we now can call maven commands from anywhere in our terminal.

At this point you should be able to run a $ mvn -v to display the version of your Maven install.

This is what we get:

[shell title=”$”]
$ mvn -v

Apache Maven 3.3.3 (7994120775791599e205a5524ec3e0dfe41d4a06; 2015-04-22T04:57:37-07:00)
Maven home: /Users/Michael/Projects/Java/apache-maven-3.3.3
Java version: 1.8.0_51, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre
Default locale: en_US, platform encoding: UTF-8
OS name: “mac os x”, version: “10.10.5”, arch: “x86_64”, family: “mac”
[/shell]

To get a list of all available options provided by Maven, run the following command:

[shell title=”$”]
$ mvn -h
[/shell]

Congratulations, Maven is now installed and is ready to be used!

Please make sure you can get this far before you can continue any further.

Setting Up An Open Source Project With GitHub

We are going to use Github.com to host our open source project.

Our Java Core TML library that we will be deploying to Maven Central is located here:

https://github.com/translationexchange/tml-java

This is our core Java library that all of our other libraries rely on. This project and all our other projects are deployed to Maven Central using Maven.

If you feel like you can figure everything out on your own you can stop reading any further and just review our pom.xml file. It already has all the necessary configurations for being automatically built using Maven, continuously tested upon code checkins using Travis CI, and distributed to Maven Central through Sonatype.

But if you are curious to know what all those settings are for, please continue reading.

Managing Source Code and Using IDEs

It doesn’t really matter what IDE you use to manage your project. In our case, we use Eclipse, which already comes with m2e plugin that integrates Maven build process into the IDE. But for this guide we will do everything manually from the command line.

On the other hand, the directory structure you use to store your code does matter very much!

Maven makes certain assumptions of where your source code is located. Please take a look at the following guide:

https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html

The most important part is that your source code is located in src/main/java.

If you are starting from scratch, then you can even use the project generator provided by Maven. There are many various project architectures that you can choose from. The most basic, can be generated using:

[shell title=”$”]
$ mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
[/shell]

Setting Up pom.xml

Maven keeps everything it needs to know about your project in a single file, called pom.xml, which stands for “Project Object Model”. The file must be located in the root of your project – on the same level where you have your src folder.

You can review all the elements you can use in this XML file by visiting Maven’s documentation:

https://maven.apache.org/pom.html

In this guide, we will focus on the key elements that allow us to build our project using Travis and push the code to Maven Central.

Continuous Integration with Travis and Code Coverage with Coveralls

We want to make sure that our code is always tested once it is checked into Github. We also want to display the build status and code coverage of our project right on the Github page.

Travis-ci.org is a continuous integration service that can help us achieve exactly what we want. It is also free for all open source projects! First we need to create an account with travis-ci. We use our Github login to signup with Travis and it immediately displays all our Github repositories. We can choose the repository we want to build and enabled the build process.

Once this is done, we add .travis.yml file to the root folder of our project.

[shell title=”.travis.yml”]
language: java

jdk:
– oraclejdk7

after_success:
– mvn clean test jacoco:report coveralls:jacoco
[/shell]

Here is our file:

https://github.com/translationexchange/tml-java/blob/master/.travis.yml

You can see all our Travis builds for our project here:

https://travis-ci.org/translationexchange/tml-java

Every time we make changes to the code and check it in, travis-ci will run a build process.

By default, Travis uses Maven to perform our builds. Notice, that in our case, we want to clean the project, run all tests, use jacoco to generate test code coverage report and submit our code coverage to the coveralls service.

Coveralls.io is a separate service that keeps track of your code coverage as you run builds on travic-ci. You can see our code coverage changes at the following URL:

https://coveralls.io/github/translationexchange/tml-java

To use the service, create a free account with coveralls.io. Coveralls pulls in your repos and allows you to enable coverage tracking for any of your public projects.

We added badges on Github by adding the following lines to our README.md at the root of our project:

[text title=”README.md”]
[![Build Status](https://travis-ci.org/translationexchange/tml-java.png?branch=master)](https://travis-ci.org/translationexchange/tml-java)
[![Coverage Status](https://coveralls.io/repos/translationexchange/tml-java/badge.png?branch=master)](https://coveralls.io/r/translationexchange/tml-java?branch=master)
[/text]

Now every time we check in our code, travis will build and test the code, jakoko will produce code coverage and the coverage will be submitted to coveralls service.

To make sure our Travis builds don’t do any more work than necessary (like generating javadocs, or signing code), we must structure our pom.xml in such a way that we keep our main build script to the minimum.

The best way to setup our builds and automate most of the process, is by using maven-release-plugin.

Setting Up Maven Release Plugin

Let’s first look at our pom.xml and examine our build steps:

[xml title=”pom.xml”]
org.jacoco
jacoco-maven-plugin
0.7.5.201505241946


default-prepare-agent

prepare-agent



default-report prepare-package
report



default-check

check




BUNDLE COMPLEXITY
COVEREDRATIO
0.60




org.eluder.coveralls
coveralls-maven-plugin
2.2.0
UTF8
org.apache.maven.plugins
maven-release-plugin
2.5.3

true
release
deploy

[/xml]

As you can see, the first plugin in our build section is Jacoco. This plugin automatically generate test coverage report when our tests are run.

Once you run tests using the following command:

[shell title=”$”]
$ mvn test
[/shell]

You can see the report generated by Jacoco under target/site/jacoco.

[shell title=”$”]
$ open target/site/jacoco/index.html
[/shell]

You can read more about the plugin and its goals here:
http://eclemma.org/jacoco/trunk/doc/maven.html

The next plugin we use is coveralls-maven-plugin. This plugin takes the test coverage report data generated by Jacoco and submits it to the coveralls.io service.

You can read more about the plugin and its goals here:
https://github.com/trautonen/coveralls-maven-plugin

And, finally, we add the maven-release-plugin under the pluginManagement section of our build section. This plugin automates the entire release process and provides many useful goals that we will use to build our project.

You can read more about the plugin and its goals here:
http://maven.apache.org/maven-release/maven-release-plugin

We need to configure a few additional steps in our release process. But rather than configuring them under the build section, we will create a separate profile for our release and let maven release management plugin perform those additional steps.

Let’s look at our profiles section:

[xml title=”pom.xml”] release
performRelease
true

org.sonatype.plugins
nexus-staging-maven-plugin
1.6.3
true

ossrh
https://oss.sonatype.org/
true
org.apache.maven.plugins
maven-source-plugin
2.4


attach-sources

jar-no-fork


org.apache.maven.plugins
maven-javadoc-plugin
2.10.3


attach-javadocs

jar


org.apache.maven.plugins
maven-gpg-plugin
1.6


sign-artifacts verify
sign


[/xml]

The first plugin in the build section of our release profile is nexus-staging-maven-plugin. This plugin manages the workflow for staging our OSS project on Sonatype’s staging servers.

You can read more about it here:
https://github.com/sonatype/nexus-maven-plugins/tree/master/staging/maven-plugin

For this plugin to work correctly, we need to add another section to our pom.xml to indicate where our snapshot and staging servers are located.

[xml title=”pom.xml”]


ossrh
https://oss.sonatype.org/content/repositories/snapshots


ossrh
https://oss.sonatype.org/service/local/staging/deploy/maven2/


[/xml]

One of the requirements for pushing our OSS project to Maven Central is that we provide our source code and javadocs together with our compiled jar. Based on Maven’s cookbook, we attach the sources and javadocs under the build plugins of the release profile.

To learn more about the source plugin, visit the following url:
https://maven.apache.org/plugins/maven-source-plugin

To learn more about the javadoc plugin, visit the following url:
https://maven.apache.org/plugins/maven-javadoc-plugin

Another requirement for pushing our library to Maven Central is that we sign all our packages with GnuPG. This will be handled by the maven-gpg-plugin. But for this to work, we first must install GnuPG on our Mac. The library can be downloaded from the following URL:

https://www.gnupg.org/download

Once you install the library, run the following command to make sure it is setup correctly:

[shell title=”$”]
$ gpg –version
gpg (GnuPG/MacGPG2) 2.0.28
libgcrypt 1.6.3
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: ~/.gnupg
Supported algorithms:
Pubkey: RSA, RSA, RSA, ELG, DSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: MD5, SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2
[/shell]

Next, we need to generate a key pair that will allow us to sign artifacts with GPG.

[shell title=”$”]
gpg –gen-key
[/shell]

Provide all the requested information. Now, let’s list the keys we just generated:

[shell title=”$”]
$ gpg –list-keys

pub 4096R/YOUR_PUBLIC_KEY DATE_GENERATED [expires: DATE_EXPIRES]
uid [ultimate] YOUR_NAME

[/shell]

You should see your public key in the output. Finally, let’s register your public key with the keys server:

[shell title=”$”]
$ gpg –keyserver hkp://pool.sks-keyservers.net –send-keys YOUR_PUBLIC_KEY
[/shell]

That is it, you have successfully generated public/private key pair and registered your public key with sks-keyservers.net server.
If you would like to learn more about what we just did, please visit the following documentation:

http://central.sonatype.org/pages/working-with-pgp-signatures.html

From now on maven-gpg-plugin will automatically generate signatures for all your artifacts using your private key, and the users of your files will be able to download your public key and verify the authenticity of the downloaded files.

To read more about PGP goals, visit the following URL:
https://maven.apache.org/plugins/maven-gpg-plugin

At this point we are done with setting up our pom.xml file. But we still need to deal with some administrative tasks.

Setting Up A Sonatype Account

Before you can deploy your library to Maven Central using Sonatype service, you must request a permission to be added to Sonatype repository. This can be done by opening a Jira account with Sonatype and creating a ticket requesting access.

The following is a ticket we created with Sonatype:
https://issues.sonatype.org/browse/OSSRH-17663

Once your configuration is prepared, you can proceed with the releases to the Sonatype staging servers that will then be synced with Maven Central.

Releasing to Maven Central

Congratulations, you’ve read that far! The final thing you need to do is to check in all your code to Github and run the following command:

[xml title=”pom.xml”]
$ mvn release:clean release:prepare release:perform
[/xml]

Follow the steps you are prompted about your release, and there it goes to Maven Central repository.

The End. 😉

Get Started Today!

Create an account to get started now! No credit card required.

Get Started