Publish Java Packages to Maven Central Repository

Introduction

You can push your own Java packages to Maven Repository - Central and make it available to everyone. It's a tricky process to set up the first time, but once you get it ready, it is incredibly easy to push updates. This tutorial will walk you through the basics of registering and pushing projects to Maven central using Sonatype's OSSRH (OSS Repository Hosting)

Helpful Links

Usually these go at the end but these are such valuable links, all your answers are probably found within these links:

Prerequisites

Before pushing to the central repository you need to have proper access and a GPG key on a public server.

Sign up with Sonatype JIRA

Before publishing to the central repository, you must have a reserved namespace. The namespace is typically a domain name, but could also be a GitHub or BitBucket namespace like com.github.nanodano. To request your namespace, you need an account on the Sonatype JIRA first.

Sign up for Sonatype JIRA and follow the instructions to request your namespace.

Here is a link to my DevDungeon namespace request in Sonatype JIRA if it helps as a reference.

Create a GPG key

You will need a private GPG key to sign the code to verify you are the author and it has not been tampered with. Create a private key and save it somewhere secure and publish your public key to a trusted server.

If you don't know how to use GPG, check out my GPG Basics tutorial for a walkthrough of how to install gpg, generate keys, import and export, and push your private key to MIT's (and other) public key servers.

Here's a couple of the most important commands:

gpg --gen-key    # Create a new private key
gpg --list-keys  # To get the key id
gpg --export-secret-keys --armor <key_id> > private_gpg_key_backup.asc
gpg --keyserver hkp://pgp.mit.edu --send-keys <key_id>

Set up settings.xml

You can configure your GPG key and Sonatype password in the settings.xml file. The file should go inside your project directory next to your pom.xml or in $HOME/.m2/settings.xml. The server and profile provided here will be utilized in the pom.xml later.

Here is a sample settings.xml:

<settings>
  <servers>
    <server>
      <!-- Match the server id used in <distributionManagement> and <serverId>
           for sonatype staging plugin in `pom.xml` -->
      <id>ossrh</id> 
      <username>nanodano</username>
      <password>secretpass</password>
    </server>
  </servers>
  <profiles>
    <profile>
      <id>sonatype-oss-release</id>
      <properties>
        <gpg.keyname>NanoDano</gpg.keyname>
        <!-- GPG should prompt for passphrase if not provided here. -->
        <!-- <gpg.passphrase>secretpassphrase</gpg.passphrase> -->
        <!-- <gpg.executable>/path/to/gpg.exe</gpg.executable> -->
      </properties>
    </profile>
  </profiles>
</settings>

Create a package that meets the requirements

Once you have been granted access in the Sonatype JIRA from the previous step, you need to create a package that meets the Sonatype package requirements before you can push it up to the central repository.

Some of the requirements include:

  • Required fields in the pom.xml
  • Javadoc and Sources (<package>-javadoc.jar, <package>-sources.jar)
  • GPG signature (*.asc for each file)
  • License info (e.g. MIT, Apache)
  • Developer information
  • Source code URLs

Building and deploying a package

You can run each of these steps individually or just run the last one which will call all previous ones in the build step.

mvn clean   # Delete build contents
mvn compile # Compile classes
mvn package # Build JAR
mvn verify  # Will sign/check GPG
mvn install # Install to local maven repo
mvn deploy  # Push to Sonatype->maven central

Sample project pom.xml

This is an example pom.xml for a Maven project that is intended to be published on Maven central. It has all of the plugins and required fields in the template. It also has the Sonatype repository addresses configured.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.devdungeon.tools</groupId>
  <artifactId>http</artifactId>
  <version>1.0.1</version>

  <name>${project.groupId}:${project.artifactId}</name>
  <description>An HTTP utility library</description>
  <url>https://www.devdungeon.com/projects</url>

  <licenses>
    <license>
      <name>The Apache License, Version 2.0</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
    </license>
  </licenses>

  <developers>
    <developer>
      <name>NanoDano</name>
      <email>[email protected]</email>
      <organization>DevDungeon</organization>
      <organizationUrl>https://www.devdungeon.com</organizationUrl>
    </developer>
  </developers>

  <dependencies>
    <!--
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.5.4</version>
    </dependency>
    -->
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-source-plugin</artifactId>
        <version>3.0.1</version>
        <executions>
          <execution>
            <id>attach-sources</id>
            <goals>
              <goal>jar-no-fork</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-javadoc-plugin</artifactId>
        <version>3.0.0</version>
        <executions>
          <execution>
            <id>attach-javadocs</id>
            <goals>
              <goal>jar</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-gpg-plugin</artifactId>
        <version>1.6</version>
        <executions>
          <execution>
            <id>sign-artifacts</id>
            <phase>verify</phase>
            <goals>
              <goal>sign</goal>
            </goals>
            <configuration>
              <!-- GPG keyname var set in the settings.xml maven file -->
              <keyname>${gpg.keyname}</keyname>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.sonatype.plugins</groupId>
        <artifactId>nexus-staging-maven-plugin</artifactId>
        <version>1.6.8</version>
        <extensions>true</extensions>
        <configuration>
          <serverId>ossrh</serverId><!-- Server config in settings.xml -->
          <nexusUrl>https://oss.sonatype.org/</nexusUrl>
          <autoReleaseAfterClose>true</autoReleaseAfterClose>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <distributionManagement>
    <snapshotRepository>
      <id>ossrh</id><!-- Server config in settings.xml -->
      <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    </snapshotRepository>
    <repository>
      <id>ossrh</id><!-- Server config in settings.xml -->
      <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
    </repository>
  </distributionManagement>

  <scm>
    <connection>scm:git:git://github.com/DevDungeon/tools-http.git</connection>
    <developerConnection>scm:git:ssh://github.com:DevDungeon/tools-http.git</developerConnection>
    <url>https://github.com/DevDungeon/tools-http/tree/master</url>
  </scm>

</project>

Conclusion

You should know have an understanding of how to publish a package to the Maven central repository using Sonatype's OSSRH.