So... maven you say?


Recently I have published GeDA to maven central repository and to be honest it required quite a lot of preparation. So I thought I could just outline my experience with maven starting from the basics to the deployment to central repository to that there is a clear picture of what maven is from beginning to end.
This overview is not intended to convince you to use maven it just describes my experience working with it so it is up to the reader to make judgements. If you feel the information is inaccurate of want to raise questions please use feedback form.

The first thing that I want say is that maven is not a build tool. This is the first issue that people have, they seem to view it as alternative to ant, which is completely wrong. As it is stated on the official maven site: "[it is] software project management and comprehension tool", which means that it is used to manage the project lifecycle. This includes things like dependency management, build tools, test tools, reporting tool and deployment tools.

Once you get the idea you start to see maven in a whole different perspective.

For example if we consider building the project with Ant you think of an ant task that includes other subtasks like declaring class path, choosing compiler, optionally depending on the project things like preparing templates (or auto generating code). All of the above you have to write. You need to know exactly where things are and what you want to do with them. So in essence it is a one off deal.

When you think of maven build you think in terms of build phase which is identical (bare with me on this one) to all projects. There is predefined structure and sequence of phases that happen (such as code generation, compilation, test, packaging) that is assumed by maven and shared by all project that use it. Now the main argument that "well where is the flexibility in that?". And the answer is that there is none. Well, there is in the POM, but why do you need it? Maven project structure defines the best practices approach and the sequence of tasks that happen in phases is also designed with best practices in mind. Such as - do not package a product unless test phase has been completed successfully. So, I am not going in further debate, I just wanted to explain the reason. And for the legacy systems that cannot use mvn I am truly sorry but you need to move on and re-design your systems, otherwise you stuck in the past. There are ways to configure things in the POM but I found that if you do not follow the OOTB maven settings you get in trouble. So do not waste your time on that - follow best practises and you'll be alright.

So, maven then...


It all starts with POM, which is in essence an XML file that describes your projects, dependencies that it has and configurations that you need (for example: specifying target JVM version, reporting tool, or including custom plug-in and configuring additional phases).

So a basic POM looks like this:



    4.0.0
    com.inspire-software.lib.dto.geda
    geda
    DTO utilities package
    2.0.0-SNAPSHOT
    Provides several utilities managing DTO's in n-tier applications
    pom
    http://www.inspire-software.com/confluence/display/GeDA/GeDA+-+Generic+DTO+Assembler
    
        
            GNU Lesser General Public License (LGPLv3)
            http://www.gnu.org/copyleft/lesser.html
            repo
        
    
    
        https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
        scm:svn:https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto/trunk
        scm:svn:https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto/trunk
    
    
        JIRA
        http://inspire-software.com/jira/browse/GEDA
    
    
        
            denis.pavlov
            Denis Pavlov
            denis.v.pavlov@inspire-software.com
            www.inspire-software.com
        
    

    
        core
        spring-integration
        core-ptest
    


    
        
            
                maven-compiler-plugin
                
                    1.5
                    1.5
                
            
        
        package
    





One thing to point out here that maven is modular, you can see that even compilation is done via 'maven-compiler-plugin'. This is very important since maven now has hundreds of useful plug-ins, so before you jump to conclusion that maven cannot do something check if there is a plug-in that can help you with that. In worst case scenario there is no reason why you could not write your own. But in all my 5 years with maven I have never had to do that (again my experience only, but I think I might do a trial - it could be a good exercise).

Anyway the important parts are: modelVersion, groupId, artifactId, name, version and packaging. This is the bare minimum that you need to tell maven about your project and how it will be distributed and you can read more on the official site.

One of the important things is how you specify version number. Usually it is three numbers separated by full stops like so: 1.1.1. However during development you need to use '-SNAPSHOT'. What this does it tells maven that this is still in development, so if you have intermodule dependencies (such as in GeDA spring-integration depends on core) this will trigger a chain on builds so that your project is up to date. So if I were to build the spring-integration module jar, then maven would first build core.jar and then would use that as dependency to build the spring-integration.jar. If I were to change the dependency version in spring-integration POM for core to 1.1.1 then maven would not rebuild core but rather would just use the core 1.1.1 from its repository. It is very important to understand this. There is also another technique 'snapshot locking', whereby you add time stamp after '-SNAPSHOT' which has the effect of locking that snapshot and not rebuilding it every time. The best way to manage this is using the versions maven plugin

You will also notice however that there is modules section. This part is awesome since it promotes modular software development. So in here I effectively define project family 'geda' (that is why packaging is pom) that consists of three modules.

Now obviously you do not start from scratch so if we look into one of the modules you can see the following:



    4.0.0
    
        com.inspire-software.lib.dto.geda
        geda
        2.0.0-SNAPSHOT
    
    geda.core
    DTO utilities package core
    2.0.0-SNAPSHOT
    Provides several utilities managing DTO's in n-tier applications
    jar
    http://www.inspire-software.com/confluence/display/GeDA/GeDA+-+Generic+DTO+Assembler
    
        
            GNU Lesser General Public License (LGPLv3)
            http://www.gnu.org/copyleft/lesser.html
            repo
        
    
    
        https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
        scm:svn:https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto/trunk
        scm:svn:https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto/trunk
    
    
        JIRA
        http://inspire-software.com/jira/browse/GEDA
    
    
        
            denis.pavlov
            Denis Pavlov
            denis.v.pavlov@inspire-software.com
            www.inspire-software.com
        
    

    
        
            
                maven-compiler-plugin
                
                    1.5
                    1.5
                
            
        
        package
    

    
        
            javassist
            javassist
            3.8.0.GA
            compile
        
        
            org.apache.bcel
            bcel
            5.2
            compile
        
        
            junit
            junit
            4.6
            test
        
        
            org.slf4j
            slf4j-api
            1.5.8
            compile
        
        
            org.slf4j
            slf4j-log4j12
            1.5.6
            provided
        
    




The dependencies section describes all the other libraries that I need to make my project work. The scope of the dependency defines which phase it will participate in. So it is quite important to do those right since you do not want some of those in your deployment. For example: slf4j-log4j12 is provided so it will not be dragged into deployment (the chances are that it will already be available), junit has test scope (there is not need to use it for build phase, just for testing). So this is another powerful thing about maven - you do not need a huge library of jars that you do not even always use. It is very simple a clear what is used and where, and the best part is that maven will fetch the right version for you.

Another thing to mention here is how this module is linked to 'parent'.

Right, a little about the project structure, roughly it looks like this:

Maven project structure

So, once you have done all that what can you do with maven?


Well, a number of things actually. There are two approaches - you can use command line tool (my preferred method), or use it via IDE such as Eclipse or Intellij IDEA.

For the command line you need to be in the root directory, where the POM is.
So in GeDA example (if you can see it from screenshot) it is: /development/projects/java/geda where the root of the main project is.
So any mvn command you run for GeDA it will do it for geda, geda/core, geda/spring-integration and geda/core-ptest, but if you move down to core directory it will do it only for core module

1. You can setup eclipse or idea environment like so:

# This will create project files for the project you currently located in and all modules specified in its POM
$ mvn eclipse:eclipse


# This is for IDEA workspace setup
$ mvn idea:idea


2. You can clean project

$ mvn clean

3. You can create dependency tree to see what module uses what

$ mvn dependency:tree

4. You can test (and see reports if you use the reporting plugins such as surefire)

$ mvn test

5. You can build

# This will install the built project into your .m2/repository
$ mvn install


6. You can deploy to a maven repository (e.g. when you want to install to maven central)

# This will require either some configuration in you maven settings.xml or adding extra parameters to actually specify where to deploy to
$ mvn deploy


7. And finally release

$ mvn release:clean
$ mvn release:prepare
$ mvn release:perform


8. You can chain any of the phases like so:

# This will first clean all previously compiled classes and then will run the test phase
$ mvn clean test


An obviously because mvn does not require specifics of the project continuos integration is extremely easy. There is not need to specific scripts or special settings, all you need is maven and to a simple 'mvn install'.

To be perfectly honest, there is not much else you need to know apart from settings.xml file and location of you local repository (which is next to settings.xml in the same directory).
Of course there are configurations for proxy servers, passwords, profiles and other things but this is beyond the scope of this article.

Lastly, how do you deploy to maven central?



There is a comprehensive guide from maven but I just want to highlight my experience.

The first important thing is that you really need to think about the project namespace.
There are rules for project naming that maven require. In fact GeDA had to change namespace in order to be compliant with maven regulations, so v.2.0.0 is in fact v.1.1.3 but with packages renamed (well, a few minor tweaks as well). But the point is: think of the name carefully!

The sign up for a JIRA account is pretty straightforward.
When you create the project ticket, however, I was puzzled about the groupId.
The guide says that you should use groups and subgroups and this was very confusing because maven does not have subgroups.
The groupId actually refers to your groupId in the POM. However when you register all you need to put in is your domain name.
So for example GeDA has 'com.inspire-software.lib.dto.geda' but in the ticket it was changed to be only the domain name 'com.inspire-software'. You do not need to worry about the rest of it, since a space will be setup for all your projects under the domain name you control.

Then the guide says that the setup takes up to 2 business days - I had mine setup in probably less than 2 hours, so a special thanks to the maven team so doing this so quickly!

Next step is that you need to make sure you are running mvn 2.2.1+

To check just run:

$ mvn -version
Apache Maven 2.2.0 (r788681; 2009-06-26 14:04:01+0100)
Java version: 1.6.0_31
Java home: /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
Default locale: en_US, platform encoding: MacRoman
OS name: "mac os x" version: "10.6.8" arch: "x86_64" Family: "mac"
# This is a sample output that you will get


I use macports to install all my command line tools (and I strongly recommend you do too if you are using mac).

You will also need GPG Agent and generate a key

# This will install it on mac through macports
$ sudo port install gnupg
# Once you have it run
$ gpg --gen-key
# You will need to choose the kind of key, bit length, expiry time, enter your name, email and comment (optional)
# and see something like:
gpg: /Users/denispavlov/.gnupg/trustdb.gpg: trustdb created
gpg: key X591DEX8 marked as ultimately trusted
public and secret key created and signed.
# Then you distribute the key like so:
gpg --keyserver hkp://pool.sks-keyservers.net --send-keys X591DEX8


At this point you need to double check that you have everything in your POM file that is needed.
Do not forget to amend your POM to include the org.sonatype.oss:oss-parent.
I was puzzled myself but yes you just have to add that into you POM (GeDA adds it just to the parent POM).
Another thing to add is maven-gpg-plugin, so that signing the files with GnuPG key is done automatically.

First we are going to publish a snapshot version.
So, we need update the POM versions. As we already know we use versions plugin for that:

# This will update project and its modules to version '2.0.0-SNAPSHOT'.
# This is just an example but make sure it has '-SNAPSHOT' part or release plugin
# will not work.

$ mvn versions:set -DnewVersion=2.0.0-SNAPSHOT
[INFO] Scanning for projects...
[INFO] Reactor build order:
[INFO] DTO utilities package
[INFO] DTO utilities package core
[INFO] DTO utilities package spring integration
[INFO] DTO utilities package core integration and performance tests
[INFO] Searching repository for plugin with prefix: 'versions'.
[INFO] ------------------------------------------------------------------------
[INFO] Building DTO utilities package
[INFO] task-segment: [versions:set] (aggregator-style)
[INFO] ------------------------------------------------------------------------
[INFO] [versions:set {execution: default-cli}]
[INFO] Searching for local aggregator root...
[INFO] Local aggregation root: /development/projects/java/geda
[INFO] Processing com.inspire-software.lib.dto.geda:geda
[INFO] Updating project com.inspire-software.lib.dto.geda:geda
[INFO] from version 2.0.0 to 2.0.0-SNAPSHOT
[INFO]
[INFO] Processing com.inspire-software.lib.dto.geda:geda.core
[INFO] Updating parent com.inspire-software.lib.dto.geda:geda
[INFO] from version 2.0.0 to 2.0.0-SNAPSHOT
[INFO] Updating project com.inspire-software.lib.dto.geda:geda.core
[INFO] from version 2.0.0 to 2.0.0-SNAPSHOT
[INFO]
[INFO] Processing com.inspire-software.lib.dto.geda:geda.core-ptest
[INFO] Updating parent com.inspire-software.lib.dto.geda:geda
[INFO] from version 2.0.0 to 2.0.0-SNAPSHOT
[INFO] Updating dependency com.inspire-software.lib.dto.geda:geda.core
[INFO] from version 2.0.0 to 2.0.0-SNAPSHOT
[INFO] Updating project com.inspire-software.lib.dto.geda:geda.core-ptest
[INFO] from version 2.0.0 to 2.0.0-SNAPSHOT
[INFO]
[INFO] Processing com.inspire-software.lib.dto.geda:geda.spring-integration
[INFO] Updating parent com.inspire-software.lib.dto.geda:geda
[INFO] from version 2.0.0 to 2.0.0-SNAPSHOT
[INFO] Updating dependency com.inspire-software.lib.dto.geda:geda.core
[INFO] from version 2.0.0 to 2.0.0-SNAPSHOT
[INFO] Updating project com.inspire-software.lib.dto.geda:geda.spring-integration
[INFO] from version 2.0.0 to 2.0.0-SNAPSHOT
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL


Then we do a local install to make sure all is fine

# You will notice it will ask you for a pass phrase that we generated previously for GnuPG
$ mvn install


At this point make sure you have committed all changes to the svn or the release plugin will complain:


[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Cannot prepare the release because you have local modifications :


Another point on the scm tag in POM - make sure you are specifying full path to the trunk (not just to the root of your repo)
This is very important since release plugin will just tag the whole repo and then you will have problems later with releasing.

Remember to add the sonatype-nexus servers to your settings.xml. These configs should contain the same login details as the account you previously created for jira.

Now we can deploy the SNAPSHOT.

$ mvn clean deploy
...
[INFO] [jar:jar {execution: default-jar}]
[INFO] Building jar: /development/projects/java/geda/core-ptest/target/geda.core-ptest-2.0.0-SNAPSHOT.jar
[INFO] [gpg:sign {execution: sign-artifacts}]
[INFO] [install:install {execution: default-install}]
[INFO] Installing /development/projects/java/geda/core-ptest/target/geda.core-ptest-2.0.0-SNAPSHOT.jar to /Users/denispavlov/.m2/repository/com/inspire-software/lib/dto/geda/geda.core-ptest/2.0.0-SNAPSHOT/geda.core-ptest-2.0.0-SNAPSHOT.jar
[INFO] Installing /development/projects/java/geda/core-ptest/target/geda.core-ptest-2.0.0-SNAPSHOT.jar.asc to /Users/denispavlov/.m2/repository/com/inspire-software/lib/dto/geda/geda.core-ptest/2.0.0-SNAPSHOT/geda.core-ptest-2.0.0-SNAPSHOT.jar.asc
[INFO] Installing /development/projects/java/geda/core-ptest/target/geda.core-ptest-2.0.0-SNAPSHOT.pom.asc to /Users/denispavlov/.m2/repository/com/inspire-software/lib/dto/geda/geda.core-ptest/2.0.0-SNAPSHOT/geda.core-ptest-2.0.0-SNAPSHOT.pom.asc
[INFO] [deploy:deploy {execution: default-deploy}]
[INFO] Retrieving previous build number from sonatype-nexus-snapshots
Uploading: https://oss.sonatype.org/content/repositories/snapshots//com/inspire-software/lib/dto/geda/geda.core-ptest/2.0.0-SNAPSHOT/geda.core-ptest-2.0.0-20120420.115929-2.jar
2K uploaded (geda.core-ptest-2.0.0-20120420.115929-2.jar)
[INFO] Retrieving previous metadata from sonatype-nexus-snapshots
[INFO] Uploading repository metadata for: 'snapshot com.inspire-software.lib.dto.geda:geda.core-ptest:2.0.0-SNAPSHOT'
[INFO] Retrieving previous metadata from sonatype-nexus-snapshots
[INFO] Uploading repository metadata for: 'artifact com.inspire-software.lib.dto.geda:geda.core-ptest'
[INFO] Uploading project information for geda.core-ptest 2.0.0-20120420.115929-2
[INFO] Retrieving previous build number from sonatype-nexus-snapshots
Uploading: https://oss.sonatype.org/content/repositories/snapshots//com/inspire-software/lib/dto/geda/geda.core-ptest/2.0.0-SNAPSHOT/geda.core-ptest-2.0.0-20120420.115929-2.jar.asc
487b uploaded (geda.core-ptest-2.0.0-20120420.115929-2.jar.asc)
[INFO] Retrieving previous build number from sonatype-nexus-snapshots
Uploading: https://oss.sonatype.org/content/repositories/snapshots//com/inspire-software/lib/dto/geda/geda.core-ptest/2.0.0-SNAPSHOT/geda.core-ptest-2.0.0-20120420.115929-2.pom.asc
487b uploaded (geda.core-ptest-2.0.0-20120420.115929-2.pom.asc)
[INFO]
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] ------------------------------------------------------------------------
[INFO] DTO utilities package ................................. SUCCESS [25.011s]
[INFO] DTO utilities package core ............................ SUCCESS [40.492s]
[INFO] DTO utilities package spring integration .............. SUCCESS [20.788s]
[INFO] DTO utilities package core integration and performance tests SUCCESS [2:43.497s]
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL




Once that is done you can actually use your browser to go to https://oss.sonatype.org/content/repositories/snapshots/ to see that the files have been uploaded.

Once that is done you can do the release (you must do the snapshot first).

# Clean. This is just a tidy up from previous release
$ mvn release:clean


# This is actually preparing the release so you need to be in synch with version control
# system you use (the plug-in will check this)
# The build will re-run and prepare all files
# It will also tag the release in the version control system
# Note: you will be asked what version to use for the release, development version and what tag name to use
# (you can just hit ENTER for all of them since those already have reasonable preset values).
$ mvn release:prepare

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] ------------------------------------------------------------------------
[INFO] DTO utilities package ................................. SUCCESS [24.548s]
[INFO] DTO utilities package core ............................ SUCCESS [1:00.872s]
[INFO] DTO utilities package spring integration .............. SUCCESS [13.097s]
[INFO] DTO utilities package core integration and performance tests SUCCESS [2:40.274s]
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4 minutes 19 seconds
[INFO] Finished at: Fri Apr 20 13:49:02 BST 2012
[INFO] Final Memory: 39M/86M
[INFO] ------------------------------------------------------------------------
[INFO] Checking in modified POMs...
[INFO] Executing: /bin/sh -c cd /development/projects/java/geda && svn --non-interactive commit --file /var/folders/m0/m0q0DP1mHIu8KR9-G-J1dU+++TI/-Tmp-/maven-scm-523617331.commit --targets /var/folders/m0/m0q0DP1mHIu8KR9-G-J1dU+++TI/-Tmp-/maven-scm-5286914217546174077-targets
[INFO] Working directory: /development/projects/java/geda
[INFO] Tagging release with the label geda-2.0.0...
[INFO] Executing: /bin/sh -c cd /development/projects/java/geda && svn --non-interactive copy --file /var/folders/m0/m0q0DP1mHIu8KR9-G-J1dU+++TI/-Tmp-/maven-scm-460638230.commit --revision 159 https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto/trunk https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto/tags/geda-2.0.0
[INFO] Working directory: /development/projects/java/geda
[INFO] Transforming 'DTO utilities package'...
[INFO] Transforming 'DTO utilities package core'...
[INFO] Transforming 'DTO utilities package spring integration'...
[INFO] Updating geda.core to 2.0.1-SNAPSHOT
[INFO] Transforming 'DTO utilities package core integration and performance tests'...
[INFO] Updating geda.core to 2.0.1-SNAPSHOT
[INFO] Not removing release POMs
[INFO] Checking in modified POMs...
[INFO] Executing: /bin/sh -c cd /development/projects/java/geda && svn --non-interactive commit --file /var/folders/m0/m0q0DP1mHIu8KR9-G-J1dU+++TI/-Tmp-/maven-scm-854032390.commit --targets /var/folders/m0/m0q0DP1mHIu8KR9-G-J1dU+++TI/-Tmp-/maven-scm-4466125605741101582-targets
[INFO] Working directory: /development/projects/java/geda
[INFO] Release preparation complete.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL


And finally,

$ mvn release:perform
...
2K uploaded (geda.core-ptest-2.0.0.jar)
[INFO] Retrieving previous metadata from sonatype-nexus-staging
[INFO] Uploading repository metadata for: 'artifact com.inspire-software.lib.dto.geda:geda.core-ptest'
[INFO] Uploading project information for geda.core-ptest 2.0.0
Uploading: https://oss.sonatype.org/service/local/staging/deploy/maven2//com/inspire-software/lib/dto/geda/geda.core-ptest/2.0.0/geda.core-ptest-2.0.0-javadoc.jar
340b uploaded (geda.core-ptest-2.0.0-javadoc.jar)
Uploading: https://oss.sonatype.org/service/local/staging/deploy/maven2//com/inspire-software/lib/dto/geda/geda.core-ptest/2.0.0/geda.core-ptest-2.0.0.jar.asc
487b uploaded (geda.core-ptest-2.0.0.jar.asc)
Uploading: https://oss.sonatype.org/service/local/staging/deploy/maven2//com/inspire-software/lib/dto/geda/geda.core-ptest/2.0.0/geda.core-ptest-2.0.0.pom.asc
487b uploaded (geda.core-ptest-2.0.0.pom.asc)
Uploading: https://oss.sonatype.org/service/local/staging/deploy/maven2//com/inspire-software/lib/dto/geda/geda.core-ptest/2.0.0/geda.core-ptest-2.0.0-javadoc.jar.asc
487b uploaded (geda.core-ptest-2.0.0-javadoc.jar.asc)
[INFO]
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] ------------------------------------------------------------------------
[INFO] DTO utilities package ................................. SUCCESS [25.729s]
[INFO] DTO utilities package core ............................ SUCCESS [1:16.042s]
[INFO] DTO utilities package spring integration .............. SUCCESS [23.946s]
[INFO] DTO utilities package core integration and performance tests SUCCESS [3:03.265s]
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5 minutes 9 seconds
[INFO] Finished at: Fri Apr 20 14:45:34 BST 2012
[INFO] Final Memory: 42M/89M
[INFO] ------------------------------------------------------------------------
[INFO] Cleaning up after release...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL

Note: If you get "No SCM URL was provided to perform the release from" you probably have not run release:prepare before you did release:perform.

After this you need to follow the guide section 8 to make final checks and actually push the release.
If this is your first time you will need to comment on the jira ticket that you have released, so that maven team can double check all you have done.
After your ticker is closed and repos are synched (for me it took a day) then you can check it on maven central

Hope you enjoyed this tutorial and it will help you save some time.

This page was last updated on: 20/09/2012 05:16