This guide explains how you can publish your own Java software components to the Maven Central Repository.
The document is structured into four main parts:
- Understanding Maven Central
- Claim Your Namespace on Maven Central
- Deploy to OSSRH
- Release from OSSRH to Maven Central
Important URL Shortcuts
- https://search.maven.org/: Maven Central search engine
- https://repo1.maven.org/maven2/: Maven Central file system
- http://central.sonatype.org/: Sonatype OSSRH documentation portal
- https://oss.sonatype.org/: Sonatype OSSRH Nexus Repository
- https://issues.sonatype.org: Sonatype JIRA issue tracking system
Understanding Maven Central
The Maven Central Repository is the official public software component repository of the Maven build system. It hosts open-source software components that can be automatically consumed by different build systems, including Maven, Ant, and Gradle.
Currently, Maven Central is the largest collection of Java and open-source software components of the world (see here).
For sure you’re used to include dependencies from Maven Central in your build.gradle
file, like this:
dependencies {
compile 'com.google.code.gson:gson:2.8.2'
}
The dependency com.google.code.gson:gson:2.8.2
references to a JAR file that is hosted on the Maven Central Repository. When you build your application, Gradle downloads this JAR file so that the classes defined in this dependency are accessible to your application.
Dependencies on Maven Central are specified in three parts. For the above example dependency these are:
- Group ID:
com.google.code.gson
- Artifact ID:
gson
- Version:
2.8.2
These three parts together define a unique path to a directory in the Maven Central Repository in which the Google Gson JAR file is stored:
com/google/code/gson/gson/2.8.2
You can verify this if you go to https://search.maven.org and click BROWSE in the title bar:
Alternatively, you can directly go to https://repo1.maven.org/maven2/.
This is the root directory of the Maven Central Repository. If now you navigate to the com/google/code/gson/gson/2.8.2
directory, you will see that there’s a bunch of files:
Don’t worry, you will not have to generate all these files manually when you publish to Maven Central, they will be generated automatically.
There are actually just four files with a real content:
gson-2.8.2-javadoc.jar
: contains the Javadocs of the projectgson-2.8.2-sources.jar
: contains the source code files of the projectgson-2.8.2.jar
: contains the compiled class files of the projectgson-2.8.2.pom
: the Maven POM file of the project
All the other files are signature and hash files of the four main files.
The Maven POM file is there because Maven Central has been originally designed for the Maven build system, even though today it can be used with a variety of build system, including Gradle.
The main file that you are interested in is gson-2.8.2.jar
. This file contains all the compiled classes of the Google Gson v2.8.2 project. If you download this JAR file and add it to the classpath of your application for compilation and execution, you have successfully “incorporated” Google Gson v2.8.2 in your application, and you can use all the public Google Gson classes.
And, as you guessed, the download of the gson-2.8.2.jar
file and the inclusion in the classpath is automatically done by Gradle if you specify the following in your build.gradle
file:
repositories {
mavenCentral()
}
dependencies {
compile 'com.google.code.gson:gson:2.8.2'
}
What Is It All About?
So, we have seen that Google publishes its projects in subdirectories of com/google/
on the Maven Central Repository. For example, they have com/google/firebase/
, com/google/guava/
, or com/google/maps/
.
The directory com/google/
is reserved for Google, and only Google can write files to this directory and any subdirectories. In other words, com.google
is Google’s namespace on Maven Central.
This guide is about how you can claim your personal namespace on Maven Central to which only you can publish, and how you can publish your projects to this namespace.
For example, if you claim the namespace com.mydomain
, then you can publish your projects under the com/mydomain/
directory, and only you can publish to this directory.
If you have a project named myproject
with version 0.0.1
, you are going to publish it to the directory com/mydomain/myproject/0.0.1/
, and now everybody on the world can include your project in their application by simply adding the Maven Central dependency
com.mydomain:myproject:0.0.1
to their build.gradle
file.
Claim Your Namespace
Rules of the Game
First of all, you never interact with the Maven Central Repository directly, but any interaction goes through a service called OSSRH provided by Sonatype.
Sonatype is a company producing software component management solutions, and it is the “steward, maintainer and financial sponsor” of the Maven Central Repository (see here).
OSSRH stands for Open-Source Software Repository Hosting, and it is a deployment of Sonatype’s Nexus Repository Manager.
To publish a software component to Maven Central, you will first deploy it to OSSRH, and then release it from OSSRH to Maven Central, as explained in the next section.
Documentation
Sonatype’s official documentation about publishing to Maven Central is extremely good, and can be found here: http://central.sonatype.org
This documentation also includes a set of very informative videos that you definitely shouldn’t miss. All the videos are listed here for convenience:
- 01 - Claim Your Namespace
- 02 - Applying for Access to OSSRH
- 03 - Requirements and Signing Tips for OSSRH
- 04 - Accessing OSSRH
- 05 - First Deployments
- 06 - Project Object Model (POM)
- 07 - Javadoc, Sources and Signing
- 08 - Getting Help
In general, the Sonatype documentation is the ✅ resource for questions about publishing to Maven Central.
Let’s Go
The first thing you have to do when you’re new to Maven Central is to claim your namespace on the Maven Central Repository. This has to be done by opening an issue on the Sonatype OSSRH JIRA issue tracking tool.
For this, you have to do two things:
Claiming a namespace works best if you own a domain, for example, mydomain.com
, and then you claim the namespace com.mydomain
. It’s also beneficial if you use an email address with this domain name for signing up to the OSSRH JIRA, e.g. me@mydomain.com
.
It works in any other way too, even if you claim a namespace that does not reflect a domain that you own, but the process will take longer, because it requires manual intervention. In these cases, it may take up to 2 business days until you finally get your namespace.
In my case, I claimed the namespace net.weibeld
, because I own the domain weibeld.net
, and signed up with a @weibeld.net
email address, and the JIRA issue was automatically handled within less than 10 minutes.
Note that if you claim the namespace com.mydomain
, you can not only use the group ID com.mydomain
, but also any nested group ID like com.mydomain.this
, or com.mydomain.this.that
.
Remember that the tedious process of applying for a namespace has to be done only once, and then you can publish as many projects as you want to this namespace.
Watch the Video
The relevant section in the OSSRH documentation is here.
The most important information is actually contained in the video 02 - Applying for Access to OSSRH, and you should definitely watch it.
Deploy to OSSRH
Once you got granted your Maven Central namespace, you are ready to publish your projects to Maven Central.
Publishing a project to Maven Central actually requires two steps:
- Deploy the project to OSSRH
- Release the project from OSSRH to Maven Central
I explain the first step in this section, and the second step in the next section.
Different Ways to Deploy to OSSRH
First of all, different build tools can be used to deploy a project to OSSRH, for example Ant, Maven, or Gradle. They all result in the same final content on OSSRH, but each build tool works in its own way and must be configured accordingly.
In this guide I only cover how to use Gradle to deploy to OSSRH.
With Gradle, again, there are different ways to deploy a software component to OSSRH. The two most important options are to use either the maven
plugin with the upload
task, or the maven-publish
plugin with the publish
task.
A great article comparing these two approaches can be found here.
In this guide I’m going to to cover how to use the maven
plugin with the upload
task.
Using the maven
Plugin
The relevant documentation section about deploying to OSSRH with Gradle and the maven
plugin is here.
In essence, what we have to do now is to compose a build.gradle
file with all the required information. This build.gradle
file will have an uploadArchives
task, and when we run this task, our project will be built, signed, and uploaded to OSSRH.
That’s right, all the files need to be cryptographically signed before uploading them to OSSRH. This is done automatically by the Gradle build script that we are going to create, but it requires some preliminary setup, which is explained in this section.
The Build Script
Here is an example build.gradle
file for uploading a single artifact to OSSRH:
Coordinates
As you can see, this Gradle build script defines the following coordinates for the artifact:
- Group ID:
net.weibeld.rabbitmq
- Artifact ID:
rabbitmq-manager
- Version:
0.0.1
That means, this artifact can be included by other Gradle scripts with the identifier net.weibeld.rabbitmq:rabbitmq-manager:0.0.1
.
Plugins
Furthermore, the build script uses three plugins:
- The standard
java
plugin providing the standard build tasks - The
signing
plugin handling the signing of the artifact files - The
maven
plugin providing theuploadArchives
task
The signing
and maven
plugins require some sensitive information that we cannot include in the build.gradle
file, because this file will be checked in to Git and most probably end up in a public GitHub repository. Therefore, we define this information in the ~/.gradle/gradle.properties
file:
Note that <YOUR-OSSRH-USERNAME>
and <YOUR-OSSRH-PASSWORD>
is the username and password, respectively, that you used to sign up for the OSSRH JIRA account.
Meeting OSSRH Requirements
Projects uploaded to OSSRH must meet certain requirements that are described here.
These requirements include:
- Include a Javadoc JAR and sources JAR
- All files must be signed with PGP
- A minimum set of metadata must be provided
The largest part in the above build.gradle
file is occupied by the definition of metadata. The minimum set of metainformation must include:
- Project name, description, and URL
- License information
- Developer information
- Source control management (SCM) information
Note that behind the scenes the uploadArchives
task creates a Maven POM file, that is required for software components on Maven Central, and all this metainformation ends up in this POM file.
Upload to OSSRH
Once your build.gradle
file looks like the example above, execute the uploadArchives
task:
./gradlew uploadArchives
This builds the project, generates the POM file, signs all the files with PGP, and uploads them to your account on OSSRH.
When the task is finished, go to the OSSRH Nexus Repository Manager on https://oss.sonatype.org/, and log in with the username and password that you initially used for signing up for the OSSRH JIRA account (the same credentials that you saved in your ~/.gradle/gradle.properties
file).
Once you’re logged in to OSSRH, click on Staging Repositories in the left sidebar:
In the appearing list of repositories, search your repository that you just uploaded. It has a name consisting of your group ID with the dots removed:
If your repository meets all the OSSRH requirements, then there should be a green tick ✅ in the Summary tab, and your repository should be labelled as Open.
If this is the case, then you are ready for the last step, release your repository to Maven Central.
If your repository displays errors, then you need to amend your build.gradle
file and upload again. The video 06 - Project Object Model (POM) explains this process.
Setting Up PGP Signing
Relevant documentation is here, and especially the video 03 - Requirements and Signing Tips for OSSRH is very helpful.
All the files uploaded to OSSRH must be digitally signed with the Pretty Good Privacy (PGP) encryption standard. You don’t need to sign the files yourself, this is done automatically by the Gradle build script, but you just need to set up the signing tool and keys.
Get the Tool
A command-line tool that can produce PGP signatures is GnuPG. If you haven’t yet, install GnuPG with Homebrew:
brew install gpg
This provides the command-line command gpg
.
Set Up the Keys
To set up the keys for signing, we have to do the following:
- Generate a private/public key pair
- Upload the public key to a key server
- Configure the Gradle
signing
plugin to use the keys
The private key will be used to sign the documents that we publish to Maven Central. The public key will be used by other users to verify the signature of our files on Maven Central. That is, our public key must be publicly accessible, and that’s why we have to upload it to a key server.
Generate Private/Public Key Pair
To create a new key pair, run the following:
gpg --gen-key
And provide all the information asked by the prompts.
The generated keys will be saved in ~/.gnupg
.
Upload Public Key to Key Server
For this, first check the identifier of the just generated public key:
gpg --list-keys
The identifier is an alphanumeric string listed in the pub
row of the output of above command.
Then, run:
gpg --keyserver hkp://pool.sks-keyservers.net --send-keys <PUBLID-KEY-ID>
Where <PUBLIC-KEY-ID>
is the identifier of the public key to upload.
Configure the signing
Plugin
We use the signing
plugin in the Gradle build script, and we need to tell it somehow which keys to use for signing.
We can do this by adding the following lines to the ~/.gradle/gradle.properties
file:
signing.keyId=<PUBLIC-KEY-ID>
signing.password=<YOUR-PASSWORD>
signing.secretKeyRingFile=~/.gnupg/secring.gpg
Where <PUBLIC-KEY-ID>
are the last 8 digits of the public key identifier displayed with gpg --list-keys
, and <YOUR-PASSWORD>
is the password you chose when you created the key pair.
The ~/.gnupg/secring.gpg
file is the private key to be used for signing.
There’s a gotcha here: if you use GnuPG v2.1 or later, there is no ~/.gnupg/secring.gpg
file. This issue is discussed here, and you can work around it by creating the secring.gpg
file like this:
gpg --export-secret-keys >~/.gnupg/secring.gpg
And that’s it! Now everything is set up for your Gradle build script to sign your artifact files.
Release from OSSRH to Maven Central
Releasing a repository from OSSRH to Maven Central means to make it available on https://repo1.maven.org/ and http://search.maven.org/.
The relevant section in the Sonatype documentation is here.
The release is typically done manually in the OSSRH Nexus Repository Manager GUI. It consists of simply closing and then releasing your OSSRH staging repository.
Select your repository in the list and click on Close:
Wait for the closing process to finish. When this is done, the Refresh button should become activated for your repository. Click it:
Again, wait for the release process to finish. Your repository should now be removed from the OSSRH staging repositories.
Congratulations! At this point your project is successfully published to Maven Central. You can now go to https://repo1.maven.org/ and navigate to the directory structure corresponding to your group ID, artifact ID, and version. All the files should now be there!
Note that while on https://repo1.maven.org/ your project appears immediately, it may take up to some hours for your project to appear in the search on http://search.maven.org/.
Automated Release
You can use the Gradle Nexus Staging plugin to automate the release process. That means, you can release your project to Maven Central without logging in to the OSSRH Nexus Repository Manager GUI. However, I didn’t yet test this feature.