Sonatype

Previous: Chapter 13

Discovering the Magic

As you are hopefully beginning to understand, Maven leaps beyond the previous concepts of simple build tools into a realm of its own - as some fancy amalgam of an object-oriented, Ant-like toolset (but convention-based) and a Java CPAN or RubyGems repository of both tools and dependencies. Since half of the magic of Maven takes place in the Maven repository, let's take a quick look at the pieces that make it.

Wagon

Wagon is a Maven sub-project which manages Maven's transport mechanisms - tasked with uploading-to and downloading-from the remote repositories. By default Wagon can transport from HTTP (http://...) and to/from local filesystems (file://...). Other types of repositories - like WebDAV uploading - require you to add the provider extension to the POM.

Repositories

Remote repositories are where all Maven artifacts are stores - whereas local repositories are more like local caches of

Creating an In-House Repository

The most straightforward and common method of creating repositories is with a web server that points to a base directory of deployed Maven artifacts - a directory of uploaded files deployed via wagon. If it sounds complex, it's not, as we are about to see.

Repository with Apache HTTPD ("Getting")

  1. Download and install Apache HTTPD 2 server.
  2. Alter httpd.conf to point to your deployment repository. Mine looks like this (complete with fancy headers and footers, note the importance of NameWidth=* to get complete file names... else the webserver might cut them off).
    Alias /maven-snap "/mnt/maven2/snapshot_repo"
    <Directory "/mnt/maven2/snapshot_repo">
      Options +Indexes
      HeaderName /HEADER.html
      ReadmeName /FOOTER.html
      IndexOptions FancyIndexing HTMLTable NameWidth=* SuppressHTMLPreamble VersionSort
      Order allow,deny
      Allow from all
    </Directory>
    Just replace the directory path above with your own repository's base path.

    The HEAD and FOOTER used for this example are available for download here.

  3. Finally, add the repository to either your project poms (parent is best... remember inheritance!) and/or add the repository to your build system's settings.xml file.
    <settings>
      <!-- repos -->
      <profiles>
        <profile>
          <id>my-snap-repo-profile</id>
          <activation>
            <activeByDefault>true</activeByDefault>
          </activation>
          <repositories>
            <repository>
              <id>my-snap-repo</id>
              <name>My In-House SNAPSHOT Repository</name>
              <url>http://www.my-server.com/maven-snap</url>
              <releases>
                <enabled>false</enabled>
              </releases>
              <snapshots>
                <enabled>true</enabled>
                <updatePolicy>always</updatePolicy>
                <checksumPolicy>warn</checksumPolicy>
              </snapshots>
            </repository>
          </repositories>
          <pluginRepositories>
            <pluginRepository>
              <id>my-snap-plugin-repo</id>
              <name>My In-House SNAPSHOT Plugin Repository</name>
              <url>http://www.my-server.com/maven-snap</url>
              <releases>
                <enabled>false</enabled>
              </releases>
              <snapshots>
                <enabled>true</enabled>
                <updatePolicy>interval:15</updatePolicy>
                <checksumPolicy>fail</checksumPolicy>
              </snapshots>
            </pluginRepository>
          </pluginRepositories>
        </profile>
      </profiles>
      
      <!-- mirrors -->
    </settings>
    One small note while we are on the repository topic: you can alter your local repository's location in your settings.xml file. This is usually unnecessary, however, is quite useful for keeping build machine local repos in synch reguardless of who may logged in at the time.
    <settings>
      <localRepository>~/.m2/repository</localRepository>
    </settings>

Deploying to the Repository with Wagon ("Setting")

The other half of the repository equation - pushing artifacts to be available to everyone. Like the get repository settings above, distribution management must be set under a profile in settings, or within a project's POM. The three types of projects to distribute are regular (repository) deployments, snapshotRepository deployments, and site file deployments.

<settings>
  <profiles>
    <profile>
      <distributionManagement>
        <repository>
          <id>wicket-repo</id>
          <url>scpexe://shell.sourceforge.net/home/groups/w/wi/wicket/htdocs/maven2</url>
        </repository>
        <snapshotRepository>
          <uniqueVersion>false</uniqueVersion>
          <id>maven.sateh.com</id>
          <url>scpexe://maven.sateh.com/sateh/services/lighttpd/maven.sateh.com/www/wicket</url>
        </snapshotRepository>
        <site>
          <id>wicket-site</id>
          <url>scpexe://shell.sourceforge.net/home/groups/w/wi/wicket/htdocs</url>
        </site>
      </distributionManagement>
    </profile>
  </profiles>
</settings>

Note: The deploy function is not currently transaction-based. This shall be fixed in a later version of Wagon - so take care when deploying, and keep an eye out for deployment failures (don't be scared - they are not common)

File

This is built-in and the simplest method of deployment, especially if you have a closed-network. In pure MS Windows shops this can easily be enacted by creating a common shared directory and prefix it with "file://".

file://\\someserver\mavenrepo1\

Or if you can - map it to a drive letter.

file://M:\

In non-Windows shops, it's as easy as mounting a network drive (if not deploying locally, which is usually a good idea to seperate your build machines from the repository machines).

file:///mtn/mvn-repo/

FTP

The file-transfer protocol is the first in our list of providers that usually require password. More on that later. For now note the distributionManagement url of FTP, which should look familiar.

ftp://someserver/mavenrepo1/

FTP is not available by default, and must be added as a Maven extension. Adding an extension to Maven is as simple as adding a dependency, yet in the extensions element under project build.

<project>
  ...
  <build>
    <extensions>
      <extension>
        <groupId>org.apache.maven.wagon</groupId>
         <artifactId>wagon-ftp</artifactId>
         <version>1.0-beta-2</version>
      </extension>
    </extensions>
  </build>
</project>

The ability to leave out oft unused extensions keeps Maven small and fast, and the ability to add extensions helps shield it from obsolescence. This is the most common type of extension you will use from the Maven core project set.

Quick note on servers

It is important to note that you would never want to add a password to a POM - so Maven does not support that. Instead, each user/system must set his/her/its own credentials in the settings.xml file. The server id must be the same as the id used in the distributionManagement used on repository, snapshotRepository or site deployment.

<settings>
  <servers>
    <server>
      <id>my-ftp-repo</id>
      <username>joe</username>
      <password>pa$$w0rd</password>
    </server>
  </servers>
</settings>

Using the ftp URL and matching ID, the deployment repository may be defined in the POM as the following:

<project>
  ...
  <distributionManagement>
    <repository>
      <id>my-ftp-repo</id>
      <url>ftp://someserver/mavenrepo1/</url>
    </repository>
  </distributionManagement>
</project>

HTTP(s)

The http and https deployment servers are built into Maven by default. You do not need to add an http extension, you just need to set up some sort of REST server that will accept uploads.

https://someserver/mavenrepo1/

WebDAV

A WebDAV server can be easily set up using Plexus.

dav:http://someserver/mavenrepo1/

SSH

Secure Shell is the de facto method of deploying for the security-minded.

SCP

scp://someserver/mavenrepo1/
scpexe://someserver/mavenrepo1/

To use scpexe, you must add the wagon-ssh-external extension.

<project>
  ...
  <build>
    <extensions>
      <extension>
        <groupId>org.apache.maven.wagon</groupId>
         <artifactId>wagon-ssh-external</artifactId>
         <version>1.0-beta-2</version>
      </extension>
    </extensions>
  </build>
</project>

SCM

There are cases where, for some reason or another, you need to put artifacts into a repository. The wagon-scm extension is created for just such occasions. Let me take this opportunity to beg, to plead, not to ever use this. Unless you are required by some laws or processes to have a version control repository to hold your artifacts, ignore this - it is slow, harder to scan, and redundant (versions are part of the artifact name anyway, remember?).

With that said, use it like this.

Crazy SCM url.

scm://

Deploy

Once the repository is available for download and upload (or "getting" and "putting", in Wagon parlance) all you must do is type:

mvn deploy

Here is where the power of Maven truely becomes apparent. Maven will - due to the build lifecycle - execute every phase up to deployment. If you follow good testing conventions, your code must successfully pass all unit tests as well as integration tests before Maven allows your project to live in the public realm. The benefits are hopefully obvious.

Repository Managers

Although creating a local repository with Apache HTTPd server is fine for serving local repositories, it is not optimal for managing several or very large remote repositories.

Proximity

Proximity lies outside of the Maven Apache projects family - however is the oldest repository manager in active development.

Tips and Tricks

A Custom Artifact Handler

When creating a new packaging type you sometimes wish to name the packaging name differently from the extension it generates.

The default artifact handler maps the packaging, type, and file extension to the same value. For example, if you create a packaging of plexus-application, then the file in the repository will be myartifactId-1.0.plexus-application. The same applies if you request a particular type from a dependency. If you want to change that extension, or change how a dependency's type field is mapped to the other fields, you must include a custom artifact handler in your plugin.

Like giving a packaging, this is currently achieved by adding a Plexus descriptor to your plugin (or modifying it if it already exists). This file is META-INF/plexus/components.xml in the plugin JAR.

A complete artifact handler for the test-jar type looks like this:

<component-set>
  <components>
    <component>
      <role>org.apache.maven.artifact.handler.ArtifactHandler</role>
      <role-hint>test-jar</role-hint>
      <implementation>org.apache.maven.artifact.handler.DefaultArtifactHandler</implementation>
      <configuration>
        <classifier>tests</classifier>
        <extension>jar</extension>
        <type>test-jar</type>
        <packaging>jar</packaging>
        <language>java</language>
        <addedToClasspath>true</addedToClasspath>
      </configuration>
    </component>
  </components>
</component-set>

The fields are configured as follows:

Required

  • role-hint - The type being defined.
  • type - Must match the role-hint.

Not Required

  • extension - The extension to give the artifact in the repository.
  • packaging - The packaging of the artifact to look for.
  • classifier - The classifier to append to the artifact name (after version and before extension) when using this type.
  • language - The language the artifact is written in, it defaults to java.
  • addedToClasspath - Confirms whether the artifact should be included in a classpath or library path when used, defaults to true.
  • includesDependencies - If the artifact already includes all of its dependencies, this setting ensures they are not propogated transitively, which by default they are not (false).

As above, the extensions flag will need to be provided whenever the plugin is declared and the type is used.

Going Offline

You're not always connected to the internet, or even your network. The maven-dependency-plugin makes going offline simple with the go-offline goal. Just run in the base of the project you will work on offline:

mvn dependency:go-offline

A great thing about this plugin is for organizations who wish to block the outside world and use only "authorized" projects. Just run the above goal, disconnect from the network.

Some of the following settings are useful for limiting the scope of this plugin (check the online documentation for an exhaustive list). Note: all excludes are ignored if includes are set, and default to include all of each respective field

  • silent - If the plugin should be silent. Default value is false.
  • includeGroupIds, excludeGroupIds - Comma Seperated list of groupIds to include or exclude.
  • includeArtifactIds, excludeArtifactIds - Comma Seperated list of Artifact names to include or exclude.
  • includeScope, excludeScope - Maximum scope to include or exclude. For example, test will also include compile scopes.
  • overWriteIfNewer - Overwrite artifacts that don't exist or are older than the source, it defaults to true.
  • overWriteReleases - Overwrite release artifacts, it defaults to false.
  • overWriteSnapshots - Overwrite snapshot artifacts, it defaults to false.

Summary

Maven repositories are the hub of the Maven's artifact management philosophy - without them a large part of Maven power is gone. Because of this, it is important that repositories be populated correctly and utilized as well, since one missing piece makes the whole puzzle pointless. Due to Maven's Wagon project which abstracts communication between a user and remote repository, there are many different ways of both deploying artifacts and accessing them from a repository - though the latter tends to stick to http downloads, since it is trivial to set up a good webserver that can handle lots of download traffic.


Previous: Chapter 13