Converting to Maven 2
Introduction
Converting to maven 2 is not that difficult. There are some things to remember however.
- Extend master/pom.xml from the core sakai build as this will ensure that you have all the repositories and plugins setup by default.
- Remember that Maven 2 has more dependency management that Maven 1 and so dependencies of jars that you depend on, will also appear in your build classpath and may make it into the war unexpectedly.
- At the moment we are using org.sakaiproject as the GroupID and not sakaiproject as we did in Maven 1
- The build is fully integrated into the Maven 2 lifecycle and only uses a special target for deployment, so normal Maven 2 targets are used to build.
- the target install, should not be confused with sakai:deploy. Install means, install the output of the build in my local repository.
- Builds are defined in pom.xml files
- What is built is explicity defined in the pom.xml file, and unlike maven 1 does not depend on being a subfolder in the source tree.
Typical Maven 2 pom.xml
<?xml version="1.0"?> <project xmlns="http://maven.apache.org/POM/4.0.0"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>master</artifactId> <groupId>org.sakaiproject</groupId> <version>M2</version> <relativePath>../../../master/pom.xml</relativePath> </parent> <name>sakai-rwiki-api</name> <groupId>org.sakaiproject</groupId> <artifactId>sakai-rwiki-api</artifactId> <description> This project creates the general APIs used by the Sakai RWiki Tool </description> <packaging>jar</packaging> <properties> <deploy.target>shared</deploy.target> </properties> <dependencies> <dependency> <groupId>org.sakaiproject</groupId> <artifactId>sakai-radeox</artifactId> <version>${sakai.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.sakaiproject</groupId> <artifactId>sakai-entity-api</artifactId> <version>${sakai.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.4</version> <type>jar</type> <scope>provided</scope> </dependency> </dependencies> </project>
It actually looks quite close to a project.xml file, but here are the differences.
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
The model version is different
<parent>
<artifactId>master</artifactId>
<groupId>org.sakaiproject</groupId>
<version>M2</version>
<relativePath>../../../master/pom.xml</relativePath>
</parent>
We depend on a master project in org.sakaiproject and we specify a version and a relative path. The version definition method may change in the future, but for now use M2
<groupId>org.sakaiproject</groupId>
We use a groupId of org.sakaiproject It could be anything, but using this and making all the projects the same prevents name collisions in the artifact ID's hence removing the risk of conflicts when the artifacts are deployed into a flat structure.
<packaging>jar</packaging>
<properties>
<deploy.target>shared</deploy.target>
</properties>
Here we specify the packaging as jar and indicate that this jar is to be deployed to shared. There are 4 packaging types, sakai-component jar war and pom. There are also a number of deploy targets shared, common, components. There is no deploy type required for war as it defaults to webapp.
This controls the packaging and deployment of the artifact. In Maven 1 we also specified the deployment in the dependencies section, saying that artifacts were deployed to shared as a side effect of being a dependency. In maven 2, the model has become stricter so you don't deploy dependencies as a side effect of the dependency process. Dependencies are either provided by some other mechanism or packaged as part of the packaging process. In simple operation it is not possible to package jars within jars, although maven 2 will allow you to unpack and re-pack jars. Jars can obviously be included into wars.
<dependencies>
<dependency>
<groupId>org.sakaiproject</groupId>
<artifactId>sakai-radeox</artifactId>
<version>${sakai.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.sakaiproject</groupId>
<artifactId>sakai-entity-api</artifactId>
<version>${sakai.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
</dependencies>
The dependency listing is like with Maven 1, but it uses org.sakaiproject instead of sakaiproject, and in many cases there is a scope provided which prevents the jar from being package in this artifact (not in a jar anyway), but also prevents artifacts that depend on this jar from packaging the dependency. In this example, the rwiki api depends on the entity api, which is provided. Hence rwiki-impl which depends on rwiki-api does not package entity-api since entity-api was provided in rwiki-api..... confused ?
To get jars to package in components and wars, just remove the scope provided.
Packaging to recap:
- shared jars : packaging = jar, deploy.target = shared
- server jars : packaging = jar, deploy.target = server
- common jars : packaging = jar, deploy.target = common
- normal jars : packaging = jar, deploy.target is undefined
- component wars : packaging = sakai-component, deploy.target = components
- webapp wars : packaging = war, deploy.target is undefined
Shared third party jars
Since we don't define deployment of jars in the dependencies any more, we need a mechanism of deploying 3rd party jars into shared , common and server. If you have a need to do this, define a pom.xml with a pom packaging and a deploy.type of shared,common or server eg
<?xml version="1.0"?> <project xmlns="http://maven.apache.org/POM/4.0.0"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>master</artifactId> <groupId>org.sakaiproject</groupId> <version>M2</version> <relativePath>../../../master/pom.xml</relativePath> </parent> <description>Shared Deployment Descriptor for DB</description> <name>sakai-db-shared-deploy</name> <groupId>org.sakaiproject</groupId> <artifactId>sakai-db-shared-deploy</artifactId> <organization> <name>University of Michigan</name> <url>http://sakaiproject.org/</url> </organization> <inceptionYear>2003</inceptionYear> <packaging>pom</packaging> <properties> <deploy.target>shared</deploy.target> </properties> <dependencies> <dependency> <groupId>hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>1.8.0.5</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>2.1_3</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.2.1</version> ...
Then all the dependencies of that pom, that are not provided will be deployed to shared.
Module structure
So you have converted your project.xml files to pom.xml, but they wont be included in the build until you explicitly include them. For this you need to specify a module build dependency.
<?xml version="1.0"?> <project xmlns="http://maven.apache.org/POM/4.0.0"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>master</artifactId> <groupId>org.sakaiproject</groupId> <version>M2</version> <relativePath>../master/pom.xml</relativePath> </parent> <name>Sakai RWiki Project</name> <groupId>org.sakaiproject</groupId> <artifactId>rwiki-base</artifactId> <packaging>pom</packaging> <modules> <module>rwiki-api/api</module> <module>rwiki-help</module> <module>rwiki-impl/impl</module> <module>rwiki-impl/pack</module> <module>rwiki-integration-test</module> <module>rwiki-model</module> <module>rwiki-tool/tool</module> <module>rwiki-util/jrcs</module> <module>rwiki-util/radeox</module> <module>rwiki-util/util</module> </modules> </project>
This specifies that, relative to the location of this pom.xml rwiki-api/api/pom.xml rwiki-help/pom.xml etc will be included in the build when you build this pom.xml (or execute mvn in this directory)
In the base directory of the sakai build there is a pom.xml which defines all the modules in the build.
Automated conversion.
Converting lots of projects to maven 2 can be a painful especially if there are a lot files. When I did the core M1 to M2 conversion I used XSLT and scripts. You will find them at https://source.sakaiproject.org/contrib/tfd/trunk/maven
The script to work with is https://source.sakaiproject.org/contrib/tfd/trunk/maven/scripts/convertm1m2-single.sh which will recursively convert the target directory into pom.xml from project.xml's
Warning, these scripts are not 100% up-to-date and I had to use a significant amount of editing.
maven.xml
Does not exist in maven2... but there is a Ant task plugin so you can make convert maven.xml into and and make it part of the maven 2 lifecycle.
For more information see http://maven.apache.org