Publishing Maven Artifacts

The previous labs covered how to add project visibility on quality and progress. Now let's add visibility on deliverables by publishing a project's artifacts. Let's consider the QOTD project. It has several artifacts: a JAR in qotd/core, a WAR in qotd/web, and a zip in qotd/packager. Imagine you want to deploy them to a Maven remote repository.

How do I do that?

Several Maven plug-ins—including the JAR, WAR, EAR, and RAR plug-ins—deploy the artifact they generate. Thus, to deploy a JAR you use jar:deploy, to deploy a WAR you use war:deploy, to deploy an EAR you use ear:deploy, etc.

Under the hood all these deploy goals use the Artifact plug-in's artifact:deploy Jelly tag to perform the actual deployment. Thus, to properly deploy an artifact you need to find out how to configure the Artifact plug-in.

Let's practice by deploying the qotd/core JAR. The first thing to decide is what deployment protocol you're going to use. The Artifact plug-in supports several deployment protocols: SCP, file copy, FTP, and SFTP (see http://maven.apache.org/reference/plugins/artifact/protocols.html for more details).

You are going to use the SCP method to deploy the JAR artifact, as it is one of the most commonly used protocols, and it's secure. You also need to tell the Artifact plug-in where to deploy to. Let's imagine you'd like to publish to http://www.mavenbook.org, for example.

As these properties are true for any subproject in QOTD, add the following Artifact plug-in properties to common/project.properties:

maven.repo.list = mavenbook
  
maven.repo.mavenbook = scp://www.mavenbook.org
maven.repo.mavenbook.directory = /var/www/html/mavenbook/maven

Unfortunately, you won't be able to publish to http://mavenbook.org. If you want to try this, you'll need your own SSH server. Sorry!

Deployment properties are defined for the mavenbook deployment repository using the following syntax: maven.repo.[repository name].*, where [repository name] is mavenbook. maven.repo.list is a comma-separated list containing all the repositories to deploy to, and in this case you are publishing to only one remote repository—mavenbook. The maven.repo.mavenbook property defines both the protocol to use (SCP here) and the deployment host. maven.repo.mavenbook.directory specifies the deployment directory on the host server.

You also need to specify deployment credentials. It's best to define those in a build.properties file, as this file is not meant to be checked in your SCM and you want your password to remain secret. If your deployment server uses username/password authentication, you'll define:

maven.repo.mavenbook.username = vmassol
maven.repo.mavenbook.password = somepassword

Note

Simply define the following properties:

maven.repo.mavenbook = ftp://www.mavenbook.org
maven.repo.mavenbook.directory = /var/www/html/mavenbook/maven
maven.repo.mavenbook.username = vmassol
maven.repo.mavenbook.password = somepassword

to publish using FTP.

If your SSH server supports private key authentication, you can use the maven.repo.mavenbook.privatekey and maven.repo.mavenbook.passphrase properties instead of a password. A more secure approach is to configure an authentication key for SSH in a user account on the machine you want to deploy from.

Note

The Artifact plug-in uses the JSch framework (http://www.jcraft.com/jsch/) under the hood for supporting the SSH protocol. You'll get the following kind of stack trace error:

com.jcraft.jsch.JSchException: Auth fail
        at com.jcraft.jsch.Session.connect(Unknown Source)
        at org.apache.maven.deploy.deployers.GenericSshDeployer...

if you have not configured authentication correctly.

Publish the core JAR artifact:

C:\dev\mavenbook\code\reporting\core>maven jar:deploy
[...]
jar:deploy:
    [echo] maven.repo.list is set - using artifact deploy mode
Will deploy to 1 repository(ies): mavenbook
Deploying to repository: mavenbook
Deploying: C:\dev\mavenbook\code\reporting\core\project.xml-->mdn/poms/qotd-core-1.0.pom
Executing command: mkdir -p /var/www/html/mavenbook/maven/mdn/poms
  
Executing command: chmod g+w /var/www/html/mavenbook/maven/mdn/poms/qotd-core-1.0.pom
  
Deploying: C:\dev\mavenbook\code\reporting\core\project.xml.md5-->mdn/poms/qotd-core-1.0.pom.md5
Executing command: mkdir -p /var/www/html/mavenbook/maven/mdn/poms
  
Executing command: chmod g+w /var/www/html/mavenbook/maven/mdn/poms/qotd-core-1.0.pom.md5
  
Will deploy to 1 repository(ies): mavenbook
Deploying to repository: mavenbook
Deploying: C:\dev\mavenbook\code\reporting\core\target\qotd-core-1.0.jar-->mdn/jars/qotd-core-1.0.jar
Executing command: mkdir -p /var/www/html/mavenbook/maven/mdn/jars
  
Executing command: chmod g+w /var/www/html/mavenbook/maven/mdn/jars/qotd-core-1.0.jar
  
Deploying: C:\dev\mavenbook\code\reporting\core\target\qotd-core-1.0.jar.md5-->mdn/jars/qotd-core-1.0.jar.md5
Executing command: mkdir -p /var/www/html/mavenbook/maven/mdn/jars
  
Executing command: chmod g+w /var/www/html/mavenbook/maven/mdn/jars/qotd-core-1.0.jar.md5
  
BUILD SUCCESSFUL

What just happened?

The artifact:deploy tag is executing commands on the remote machine using SSH. The structure of the repository is being created, and all directories and files are made group-writable with chmod. As you can see from the console, the artifact:deploy tag has deployed not only the core JAR, but also the core project's POM. This is because the POM is the identity of a Maven project and it may be useful for a user browsing the repository to know more about the project producing the artifacts he's looking for. In practice the POMs will also enable Maven 2 to support transitive dependencies. This means that in the future you'll be able to specify only the direct dependencies your project is depending upon, and Maven will auto-discover the dependencies of your dependencies.

Note

Transitive dependencies are going to be a huge timesaver.

Because you can never be too security conscious, the artifact:deploy tag creates MD5 signatures for every deployed artifact. Maven currently does not use them when downloading artifacts, but they'll certainly be implemented in the future.

What about...

...publishing the packager's zip file?

Publishing the JAR was easy because there's an existing jar:deploy goal. However, there's no zip plug-in, and thus no zip:deploy goal! The solution is to write a custom goal in your maven.xml file. Edit the qotd/packager/maven.xml file and add the code in bold:

<?xml version="1.0"?>
  
<project default="qotd:build" 
    xmlns:ant="jelly:ant"
    xmlns:artifact="artifact">
  
  <goal name="qotd:build">
    <ant:mkdir dir="${maven.build.dir}"/>
    <ant:zip destfile=
        "${maven.build.dir}/${pom.artifactId}-${pom.currentVersion}.zip">
      <ant:fileset file="${pom.getDependencyPath('mdn:qotd-web')}"/>
      <ant:fileset dir="${maven.src.dir}/conf"/>
    </ant:zip>
  </goal>
  
  <goal name="qotd:deploy" prereqs="qotd:build">
               <artifact:deploy
               artifact="${maven.build.dir}/${pom.artifactId}-${pom.currentVersion}.zip"
        type="zip"
        project="${pom}"/>
               </goal>
  
</project>

The qotd:build goal creates the QOTD zip (see Chapter 3 for a refresher). You've now added a qotd:deploy goal that uses the artifact:deploy Jelly tag to deploy the zip file, and then passed in the artifact attribute. The type attribute corresponds to the artifact extension that is used to decide in which directory to put the artifact in the remote repository. You need to pass a reference to the current project's POM in the project attribute. Running qotd:deploy deploys the zip to the remote repository, in [REMOTE_REPO_ROOT]/mdn/zips/qotd-packager-1.0.zip, following the standard repository mapping rule, [REPO_ROOT]/<groupId>/<type>s/<artifactId>-<currentVersion>.<type>, discussed in Section 1.1.

Get Maven: A Developer's Notebook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.