/ java

How to publish artifacts on maven central

If you want to publish your libraries on the maven central repository in order for them to be available to the world, be ready to go through several steps. It's not hard, it's just a little complex and bureaucratic.

Follow this guide to understand the complete process

Overview

1° Get a user on sonatype.org
2° Request ownership of the groupId
3° Make sure your project is acceptable
4° Sign your artifacts with GPG
5° Upload your artifact
Release it to the public

This guide is based on my experience following these 2 pages: https://knowm.org/a-practical-guide-to-uploading-artifacts-to-maven-central/ and https://blog.idrsolutions.com/2015/06/how-to-upload-your-java-artifact-to-maven-central/. But you have the official documentation on: https://central.sonatype.org/pages/ossrh-guide.html#deployment

1. Get a user on Sonatype.org

Only users who own a groupId are allowed to publish artifact with that groupId, so you'll need to create one.
This is a one time process, and you can share your user with your team/company.

Follow this link and fill out the form if you don't already own a sonatype user.

Post_1_form

That should provide you with a user account, and a link to access a JIRA instance.
Post_2_email

That JIRA instance is where you ask the team behind sonatype for ownership of a groupId.

2. Request ownership of a groupId

To publish an artifact with a specific groupId you have to own it first. This ensures no one can publish artifacts on behalf of others.
There's a little catch here, to own a groupId you also have to own the domain that is implicit with it. For instance, if your groupId is com.greatcompany.coder that means you own the internet domain coder.greatcompany.com. And there's no exception.

GroupId and domain

Your pom needs to use a groupId that matches a domain you own. If you don't do that, your request will be rejected. In my case, I had to rename all the groupIds because I was using something I don't own.

There's a workaround though for Open Source projects. If your code is hosted on github, sourceforge, or any other public domain site, then you can use that as your groupId without needing to buy a domain.
For example, if a have a github.com user greatcoder then you can use com.github.greatcoder as your groupId. Anyone can check that you own that.

Requesting the ownership

Asking for the ownsership is as easy as creating a JIRA issue. It will take some time to get processed because it requires for real people to check your domain. For me it took around 2 working days.

Use your sonatype user and create a Jira issue with this link

Post_3_domain
(see this video if you have doubts)

If you have more than one project, link all of them in the description and they will check them out. After some time you will get feedback through the issue, so remember to enable notifications to your email.

3. Make sure your project meets the requirements

The full details on requirements is available here. I will focus on the non obvious (at least for me).

Complete all the project data

Make sure you include:

  • maven coordinates need to be consistent with the groupId you own
  • project name
  • project description
  • project url (this is not the scm url)
  • scm information (where your code is at)
  • license information (see below)
  • developer information (this is the team that works on the project)
  • distributionManagement (this needs to point to maven central)
 <distributionManagement>
    <snapshotRepository>
      <id>sonatype-nexus-snapshots</id>
      <name>Sonatype Nexus Snapshots</name>
      <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    </snapshotRepository>
    <repository>
      <id>sonatype-nexus-staging</id>
      <name>Nexus Release Repository</name>
      <url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url>
    </repository>
  </distributionManagement>

See this example for easy copy&pasting ;-).
Or use this one from one of my projects which includes necessary plugins.

Choosing a license

I had some doubts choosing the right license, as I wanted to allow as much as possible, without losing recognition for my work.
This page helped me. And also this simple license comparator.

Add necessary plugins

You will need to attach sources and javadoc with your jars. In order to do so, include the following plugin configurations in the pom. This will reduce the rejection causes when trying to release.

 <!-- Attach source jars-->
  <plugin>
    <artifactId>maven-source-plugin</artifactId>
    <version>${version.plugin.source}</version>
    <executions>
      <execution>
        <id>attach-source</id>
        <phase>compile</phase>
        <goals>
          <goal>jar-no-fork</goal>
        </goals>
      </execution>
    </executions>
  </plugin>
<!-- Attach javadocs jar -->
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-javadoc-plugin</artifactId>
    <version>${version.plugin.javadoc}</version>
    <executions>
      <execution>
        <id>attach-javadocs</id>
        <goals>
          <goal>jar</goal>
        </goals>
      </execution>
    </executions>
  </plugin>

4. Sign your artifacts

Signing the artifacts is understandable for security reasons, but for me it was annoying since it required me to change the build process in CI. I opted for doing it as part of the build process using maven (there's a manual alternative I didn't explore).

Generate a GPG key

If you don't already have it, you'll need to install gpg and generate a key to make sure your sonatype user is the only one allowed to upload artifacts with your groupId.
Follow this page to generate your key.

Be aware that, once generated, you are expected to distribute your public key to one of the public keys server. I uploaded mine on keyserver.ubuntu.com, but there are other alternatives that sonatype can access in order to validate your jars.

DO NOT upload or share your private key. That's for you to keep and use everytime you release a new jar. And rememeber your passphrase as it will be needed everytime you build the artifacts.

** Key distribution troubleshooting**
I had problems with gpg2 when trying to upload the public key following the steps on that page, so I had to change the command to upload from this:
gpg2 --keyserver hkp://pool.sks-keyservers.net --send-keys C6EED57A
to this one
gpg --send-keys --keyserver keyserver.ubuntu.com
(but I'm no gpg expert)

Include the gpg plugin

Once you have public-private keys, you can sign your artifacts. Adding this plugin will ensure the artifacts are signed with your key while generating them. It's the easiest option.

<!-- Sign artifacts for uploading to maven central -->
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-gpg-plugin</artifactId>
    <version>${version.plugin.gpg}</version>
    <executions>
      <execution>
        <id>sign-artifacts</id>
        <phase>verify</phase>
        <goals>
          <goal>sign</goal>
        </goals>
      </execution>
    </executions>
  </plugin>

Now, when you run mvn package the plugin will try to find the gpg user for the current user and sign the artifacts.
If everything went right, a passphrase will be needed to access the keys from your user. Pass that parameter like so
mvn package -Dgpg.passphrase=<the-passphrase-used-on-key-creation>

At this point you should have at leat 4 artifacts on the target directory: the binary jar, the artifact pom, a javadoc jar, and a sources jar. All of them are necessary for releasing on maven central.

Manage your keys

As I have a CI server, in order to generate the artifacts correctly I needed to move the keys from my machine to that server.
This required exporting and importing them from one machine to another.
You can follow this link to do the same, if needed.

Remember to exclude any information about the keys from your sources. Do not commit them on the repo, or include any passphrase on the pom.
You will need to provide that passphrase as external resources to the CI server. That means importing the keys on the user that runs the CI program and using environmental variables for the task that generates the artifacts like -Dgpg.passphrase=${GPG_PASSPHRASE}

5. Upload your artifact

The official recommendation for releasing your artifacts is using the release plugin. I prefer to have less features and a faster deploy process by only using the deploy plugin. Both will work but deploy doesn't tag or double check your sources, or re-clones the repo.

To deploy, I do mvn deploy from the CI server with the passphrase to sign the artifacts.

Sonatype credentials

Remember that maven will need your sonatype credentials to upload your artifacts on your behalf. This is configured on the ~/.m2/settings.xml file for your user.
If you don't have this file, then copy it from maven installation folder. Somewhere, in there, there's an example file you can copy to your user folder.

<servers>
     <server>
        <id>sonatype-nexus-snapshots</id>
        <username>sonatype username</username>
        <password>sonatype password</password>
    </server>
    <server>
        <id>sonatype-nexus-staging</id>
        <username>sonatype username</username>
        <password>sonatype password</password>
    </server>
</servers>

Make sure the ids of the servers match the ones you declared on the pom file

6. Release the artifacts

Once you upload the artifacts, sonatype will put them on a pre-release stage. Their nexus instance is configured so your sonatype user can manage your uploaded artifacts.
Access their nexus artifact repository instance to manage your artifacts.

Post_4_repo

From the "staging repositories" view you can see all the artifacts being uploaded, but only yours will have a name different than the generic "central_bundles-nnnnn".

To release your artifacts you will need to select your bundle, click on "close", wait for the validation steps to complete (this may require pressing "refresh" to update the state). And once the close process finishes correctly you will be able to click on "release" and make it public. See full details here.

You will need to be patient after releasing the artifacts for other sites to pick up the changes. Use https://search.maven.org/ to look for your public artifacts.
It may take some time for them to appear on https://mvnrepository.com/ or other derivative sites

Post_5_public