Tuesday, 10 February 2009

Running and distributing Maven projects

Maven makes building java projects very easy, dealing with dependencies and transitive dependencies (dependencies of dependencies, dependencies of dependencies of dependencies etc...), but what happens if you want to run a program? Or to create a distribution for people who don't use Maven?

Well, it turns out that there are several easy things we can do...

1) Run a class' main method:

The first thing we can do is run the main method of a class inside a Maven built project, having Maven take care of all the dependencies, using the exec plugin:
mvn exec:java -Dexec.mainClass=(package.className) [ -Dexec.args=(commandLineArgs) ]
We can specify which class to run, and pass arguments to its main method. To pass more than one argument the arguments need enclosing in double quotes, and separating by spaces. Spaces that are part of arguments are escaped with a slash, as are slashes...

Commandargs passed to main method
-Dexec.args=fooOne argument: 'foo'
-Dexec.args="foo"One argument: 'foo'
-Dexec.args="foo bar"Two arguments: 'foo' and 'bar'
-Dexec.args="foo\ bar"One argument: 'foo bar'
-Dexec.args="foo\\ bar"Two arguments: 'foo\' and 'bar'

2) Build a classpath:

What if we want to use our code without Maven? If we're going to be working on the machine we've been building our project on, we can get Maven to tell us how to set our classpath to point at all the dependency JAR files in our local repository:
mvn dependency:build-classpath

[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'dependency'.
[INFO] ------------------------------------------------------------------------
[INFO] Building CMLXOM
[INFO] task-segment: [dependency:build-classpath]
[INFO] ------------------------------------------------------------------------
[INFO] [dependency:build-classpath]
[INFO] Dependencies classpath:
/home/sam/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/sam/.m2/reposi
tory/jaxen/jaxen/1.1-beta-8/jaxen-1.1-beta-8.jar:/home/sam/.m2/repository/jdom/
jdom/1.0/jdom-1.0.jar:/home/sam/.m2/repository/junit/junit/4.0/junit-4.0.jar:/h
ome/sam/.m2/repository/log4j/log4j/1.2.13/log4j-1.2.13.jar:/home/sea36/.m2/repo
sitory/xalan/xalan/2.7.0/xalan-2.7.0.jar:/home/sam/.m2/repository/xerces/xerces
Impl/2.8.0/xercesImpl-2.8.0.jar:/home/sam/.m2/repository/xerces/xmlParserAPIs/2
.6.2/xmlParserAPIs-2.6.2.jar:/home/sam/.m2/repository/xml-apis/xml-apis/1.3.03/
xml-apis-1.3.03.jar:/home/sam/.m2/repository/xom/xom/1.1/xom-1.1.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
3) Export a project's dependencies out of the local repository:

Alternatively, we can export all the depencency JAR files for the project, collating them in the target/dependency directory:
mvn dependency:copy-dependencies

[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'dependency'.
[INFO] ------------------------------------------------------------------------
[INFO] Building CMLXOM
[INFO] task-segment: [dependency:copy-dependencies]
[INFO] ------------------------------------------------------------------------
[INFO] [dependency:copy-dependencies]
[INFO] Copying dom4j-1.6.1.jar to .\target\dependency\dom4j-1.6.1.jar
[INFO] Copying jaxen-1.1-beta-8.jar to .\target\dependency\jaxen-1.1-beta-8.jar
[INFO] Copying jdom-1.0.jar to .\target\dependency\jdom-1.0.jar
[INFO] Copying junit-4.0.jar to .\dependency\junit-4.0.jar
[INFO] Copying log4j-1.2.13.jar to .\target\dependency\log4j-1.2.13.jar
[INFO] Copying xalan-2.7.0.jar to .\target\dependency\xalan-2.7.0.jar
[INFO] Copying xercesImpl-2.8.0.jar to .\target\dependency\xercesImpl-2.8.0.jar
[INFO] Copying xmlParserAPIs-2.6.2.jar to .\target\dependency\xmlParserAPIs-2.6.2.jar
[INFO] Copying xml-apis-1.3.03.jar to .\target\dependency\xml-apis-1.3.03.jar
[INFO] Copying xom-1.1.jar to .\target\dependency\xom-1.1.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
4) Build a single jar containing project and all dependencies:

And finally, we can get Maven to build us a single JAR file containing both our project, and all its dependencies:
mvn assembly:assembly -DdescriptorId=jar-with-dependencies
Beware with this approach:
If there are multiple dependencies containing files with the same name and path, only one version will be included in this JAR file.

Monday, 9 February 2009

Attaching javadocs and sources to Maven install/deploy

When installing maven artifacts to your local repository, or deploying them to a remote repository, it is really helpful to attach a copy of the source code and the javadoc.

If you code Java in eclipse, you can use the m2eclipse plugin to automatically download the source and javadoc files for dependencies, and then display the associated javadoc by hovering over a method or when using dot-complete, and inspect the source code by pressing F3.

So, how do you attach the source and javadoc to an installation or deployment?
mvn clean javadoc:jar source:jar install
This will install three jar files to your local repository:
  • (artifactId)-(version).jar - containing the compiled artifact
  • (artifactId)-(version)-javadoc.jar - containing the artifact's javadoc
  • (artifactId)-(version)-sources.jar - containing the artifact's source files