Wednesday, December 31, 2008

Maven Dependency Management using Artifactory for Repository

This blog is about setting up Maven for dependency management. Artifactory was used as a intranet maven repository. Maintaining internal maven repository speeds up the build process, and makes it easy to do clean builds, resolved conflicts with library versions.
Artifactory is free, very easy to install, and good documentation is available in the web site. It provides lot of good features. The main feature is the export feature. The organization is not locked in to this tool, it is very easy to migrate the repository contents to another tool.

My company was using Ant for the build process. We didnt felt like adopting maven as the new build process, but just decided to use it only for Dependency Management. Below is the partial content of the pom.xml.
POM.XML
<project>
…..
<dependencies>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>jsp-api</artifactId>
<version>6.0.14</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>

</dependencies>
<distributionManagement>
<repository>
<id>wwe.repo</id>
<name>Development Internal Repository</name>
<url>http://10.20.21.80:8080/artifactory/repo</url>
</repository>
</distributionManagement>
</project>

Ant build file
BUILD.XML
<property name="repository.home" value="http://10.20.21.80:8080/artifactory/repo"/>
<artifact:localRepository id="local.repository" path="${repository.home}" layout="default"/>

<!-- Export various classpath variables from the Maven -->
<target name="resolve.dependencies">
<artifact:dependencies pathId="compile.classpath" filesetId="compile.fileset" useScope="compile">
<pom refid="maven.project"/>
</artifact:dependencies>
<artifact:dependencies pathId="runtime.classpath" filesetId="runtime.fileset" useScope="runtime">
<pom refid="maven.project"/>
</artifact:dependencies>
<artifact:dependencies pathId="test.classpath" filesetId="test.fileset" useScope="test">
<pom refid="maven.project"/>
</artifact:dependencies>
</target>

<target name="builddev" depends="setup, resolve.dependencies, create-release-info, copy-config-file, compile, create-war, test" />

<target name="create-war">
<echo message="MODE: ${mode}"/>
<!-- replace the dynamic parameters for the build -->
<replace file="${project.build}/src/WEB-INF/${mode}.web.xml">
<replacefilter token="MS_SERVER_NAME" value="${ms.server.name}" />
<replacefilter token="MS_SERVER_PORT" value="${ms.server.port}" />
<replacefilter token="MS_URL" value="${ms.db.url}" />
<replacefilter token="RATING_URL" value="${rating.url}" />
</replace>
<!-- copy runtime libraries -->
<copy todir="${project.build}/${src}/${webapp.lib}">
<fileset refid="runtime.fileset" />
<mapper type="flatten" />
</copy>
<!-- create the war file, exclude all unwanted files -->
<war destfile="${project.build}/${dist}/${mode}/ROOT.war" webxml="${project.build}/src/WEB-INF/${mode}.web.xml" duplicate="preserve">
<fileset dir="${project.build}/${src}">
<exclude name="**/WEB-INF/src/**" />
<exclude name="**/WEB-INF/web.xml" />
<exclude name="**/WEB-INF/*.web.xml" />
<exclude name="**/WEB-INF/classes/*.*.properties" />
<exclude name="**/WEB-INF/classes/test/**" />
<exclude name="**/WEB-INF/templates/samples/**/*.*" />
</fileset>
<!--classes dir="${project.build}/${src}/${classes}" /-->
</war>
</target>

<target name="compile">
<!-- compile the java & jsp files -->
<javac srcdir="${project.build}/${src}/WEB-INF/src" destdir="${project.build}/src/${classes}" classpath="${classpath}" fork="true" failonerror="true" verbose="off">
<compilerarg value="-Xlint:-path -Xlint:-deprecation -Xlint:-unchecked"/>
<classpath >
<fileset dir="${classpath}" includes="*.jar"/>
</classpath>
<classpath refid="compile.classpath"/>
</javac>
</target>


I did some research on the available products for maven repository. Artifactory seem to be the best. It had built in Admin Tool, Repository Browser, Deployable in standard web server, Ability to create/edit/delete sub repositories, Bulk import/export artifacts, Easy to setup and use, Backup facility. It meet all the requirements to be used as our intranet maven repository tool. The software can be downloaded from http://www.jfrog.org/sites/artifactory/latest/. After installing the software, set the ARTIFACTORY_HOME in the web container. Since i was using Tomcat 6.0, i added below line
set JAVA_OPTS =%JAVA_OPTS% -Dartifactory.home=C:\Software\artifactory-1.3.0-rc-1
inside the cataline.bat file.

Below is the configuration for the repositories:

Artifactory Configuration file ${artifactory_home}/etc
ARTIFACTORY.CONFIG.XML
<serverName>localhost</serverName>
<localRepositories>
<localRepository>
<key>maven-repo</key>
<description>Maven repository </description>
<handleReleases>true</handleReleases>
<handleSnapshots>true</handleSnapshots>
</localRepository>
<localRepository>
<key>internal-repo</key>
<description>Private internal repository</description>
<handleReleases>true</handleReleases>
<handleSnapshots>true</handleSnapshots>
</localRepository>
<localRepository>
<key>third-party-repo</key>
<description>3rd party jars added manually, ex: mediasurface, oracle</description>
<handleReleases>true</handleReleases>
<handleSnapshots>false</handleSnapshots>
</localRepository>
</localRepositories>
<remoteRepositories>
<remoteRepository>
<key>ibiblio-repo</key>
<handleReleases>true</handleReleases>
<handleSnapshots>false</handleSnapshots>
<excludesPattern>org/artifactory/**,org/jfrog/**</excludesPattern>
<url>http://repo1.maven.org/maven2</url>
</remoteRepository>
</remoteRepositories>

After artifactory was installed, now its time to set up Maven to use the new repository. Maven uses the settings.xml file. Below is my settings.xml file:

Maven Configuration ${user.home}/.m2
SETTING.XML
<settings xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<profiles>
<profile>
<id>myprofile</id>
<activation>
<activeByDefault>true</activeByDefault>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<repositories>
<repository>
<id>central</id>
<url>http://10.20.21.80:8080/artifactory/repo</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>snapshots</id>
<url>http://10.20.21.80:8080/artifactory/repo</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>http://10.20.21.80:8080/artifactory/repo</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>snapshots</id>
<url>http://10.20.21.80:8080/artifactory/repo</url>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
<properties>
<tomcat6x.home>C:\Software\Tomcat6.0.14</tomcat6x.home>
</properties>
</profile>
</profiles>

<activeProfiles>
<activeProfile>myprofile</activeProfile>
</activeProfiles>

</settings>

After maven was set up, start Tomcat. I had to install some of my third party artifacts directly using the artifactory admin tool. It creates the pom.xml for each artifactory. We have the option to change the pom entries.

Finally all this cool stuff can be tested running the build script. In my case,
when i ran "ant builddev", the artifacts were copied from artifact repository to maven user home directory. The build was successful. I didnt see any issues setting up all above mentioned configurations.

One minor problem i had was i forgot to set property in artifactory.config.xml. It worked fine without this property in windows, but when i deployed to Linux it didnt work without this property. Other than that, everything was simple, and much quicker to implement.