Wednesday, August 11, 2010

Release and Deploy using Maven SVN Artifactory and Tomcat

Releasing and deploying a Java Web application (WAR application) involves several steps. I have been using Maven and Subversion for this task with very good results.

Preconditions
You need subversion and maven2 (version 2.2.1 or above) packages installed ( For the release server I use linux as OS )

IMO it is *not* good to release from your local machine if you are part of a team. I have released from my local machine from time to time but I have found way safer to do it always from the same server where other developers can login and follow exactly the same steps.

Preparing the environment

1. Modify the project pom.xml to specify the version number. It must end with the string “-SNAPSHOT”.
<version>0.0.1-SNAPSHOT</version>

2. Modify the project pom.xml to specify where to tag the code (svn tags folder).
...
<build>
...
<plugins>
<plugin>
<artifactId>maven-release-plugin</artifactId>
<version>2.0-beta-7</version>
<configuration>
<tagBase>
http://svn.sample.com/project/tags
</tagBase>
</configuration>
</plugin>

3. Modify the project pom.xml to specify where to upload the result of the build (final released product). Here is an example for Artifactory:
<distributionManagement>
<repository>
<id>central</id>
<name>libs-releases-local</name>
<url>
http://sample.com/artifactory/libs-releases-local
</url>
<uniqueVersion>false</uniqueVersion>
</repository>
</distributionManagement>

I have used also Archiva:
<distributionManagement>
<repository>
<id>archiva.internal</id>
<name>Internal Release Repository</name>
<url>
dav:http://archiva.sample.com/archiva/repository/internal/
</url>
<uniqueVersion>false</uniqueVersion>
</repository>
</distributionManagement>


4. Just in case you use Windows or Mac from the machine you will be releasing do yourself a favor and add the below to you pom.xml. This way the build will be using 'UTF-8' encoding to copy filtered resources.
<properties>
...
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
...
</properties>

5. Add the scm connection property to the pom.xml
<scm>
<connection>scm:svn:http://svn.sample.com/trunk/</connection>
</scm>

6. Edit your ~/.m2/settings.xml file making sure the credentials for your artifactory reposiitory are correct. Also be sure to use the mirrors node to ensure you will download dependencies only from your local repository. Here is an example:
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<mirrors>
<mirror>
<id>central</id>
<name>My Central Maven repo</name>
<url>http://sample.com/artifactory/libs-releases</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
<servers>
<server>
<id>central</id>
<username>myuser</username>
<password>mypassword</password>
</server>
</servers>
</settings>

Releasing

8. Checkout the project (svn co ). If already checked out be sure there are no differences
svn status -u

9. Prepare the release. Default options should be OK as they ensure the pom.xml gets updated to next version number. Of course when there is a business decision to change the minor or major version numbers then you simply edit the pom.xml and commit the code to SVN before attempting to run release:prepare. Pay attention to dependencies, maven will complain if you try to use snapshots but if you decide so it will allow you to point to them. So be sure you update your own jar dependencies if any forcing the project to use a release version. When you are done the project should be tagged, thing that you can confirm navigating to the tagBase url.
mvn release:prepare

10. Perform the release. This will download the project from the tag, build it and commit the final release to the artifactory repo.
mvn release:perform

Deploying

I have posted a question to the tomcat maven mojo plugin list asking for help on getting this process done the easiest possible way (using a maven plugin)

While posting also to the cargo-user mailing list (Starting from the above link you can see the history) I have come to the conclussion that environments can get really different: clustering, sticky session configurations for load balancing, symlinks to apply DRY and so on. I think still bash (perl, python or any other or your preference ;-) offers the flexibility to use maven power in literally any environment.

WAR deployment

If you are rebuilding to deploy even when static resources are changed (CSS, JS, images etc) then you will be fine with just downloading the released war file from your artifactory server, rename it and move it to the tomcat webapp folder. Below is a bash script that does exactly that:
TOMCAT_WEBAPP=/home/liferay/liferay-portal-5.2.3/tomcat-6.0.18/webapps
if [ $# -lt 1 ]
then
echo "Usage - $0 warFileUrl"
echo " Where warFileUrl is the WAR archive URL"
exit 1
fi
#getting rid of the numbers
URL=$1
LOCAL_FILE=${URL##*/}
NAME=${LOCAL_FILE%-*}
EXT=${LOCAL_FILE##*.}
DEST_FILE=$TOMCAT_WEBAPP/$NAME.$EXT

#delete local file
if [ -e "$LOCAL_FILE" ]
then
rm $LOCAL_FILE
fi

#download the file
wget $1

#tomcat deploy
mv $LOCAL_FILE $DEST_FILE

Just invoke it as
deploy.sh http://sample.com/artifactory/libs-releases/com/sample/my-app-0.0.1.war

Snapshot deployment

While this is something that can be done from a Continuum integration server you might find Business analysts trying to deploy different branches of your project from time to time. It is then a good idea to offer the possibility to actually deploy from an SVN URL. In that case the script above will need to be modified to allow a parameter with the svn project URL (using type=warSvn or explodedSvn for example). The script should then checkout from the repository, build locally and deploy to the server. That is exactly what I have done in a modified version of the script that I will be maintaining at my google repo.

You can use tomcat-deploy so far for:
1. Deploy a WAR file from an artifactory, archiva or any other maven repo:
./tomcat-deploy.sh http://artifactory.sample.com/libs-releases-local/com/sample/my-app/1.1.5/my-app-1.1.5.war warRepo

2. Deploy a WAR file build from SVN. As the repository might be organized in different ways an extra parameter “projectName” must be provided.
./tomcat-deploy.sh http://subversion.sample.com/my-app/trunk/ warSvn my-app

3. Deploy a JAR file from SVN. In case you are wondering why will you need this the answer is to be able to deploy snapshots that depend on snapshots in case you are not publishing them into a maven repo. To be honest you are better off trusting snapshots, you better build them yourself at deployment time.
./tomcat-deploy.sh http://subversion.sample.com/my-lib/trunk/ jarSvn my-lib

Exploded Deployment

Exploded deployment has a unique advantage. Look and feel changes can be pushed into the server without having to restart the whole instance. It is ideal though that your designers are forced to commit to SVN before they can actually see their changes and that is precisely the motivation for having a modification to the script above so it checks for “type” with value “explodedRepo” or "explodedSvn". Basically this is just a version of the above where the WAR is unzipped and then deployed to webapps tomcat folder. As you can imagine that would be some little extra lines of code ...

Front End Deployment

So you are using exploded deployment for something right? The next step is to allow passing type parameter as “frontend” in which case the URL will be actually an SVN URL which must end with the string “webapp”. The new stuff in the downloaded folder will be copied then to tomcat/webapps/appname (using rsync of course). Again some extra lines of code ...

2 comments:

vikram kamath said...

Thanks a lot...this was very useful!!

Pimenta Gabriel said...

Nice post!

Followers