Here's how I usually convert a simple web (war) project from Ant to Maven.
Start out by installing maven and creating a new structure for your maven project in a different directory than the one containing your Ant project:
mvn archetype:create -DgroupId=com.maaloe.myproject -DartifactId=myproject -DarchetypeArtifactId=maven-archetype-webapp
Where com.maaloe.myproject matches whatever base package you use in your project structure and artifactId matches the name of your project
Now... The objective is to make the war file that Maven creates when running "mvn clean install" as similar to the one that Ant creates as possible, so we should compare the two war files by doing a "jar -tf projectname.war" on both projects.
The existing project in my case contains a build.xml file with three targets:
- "ant -Dmode=dev"
- "ant -Dmode=test"
- "ant -Dmode=prod"
In my case, the Ant war file looks like this:
>> jar -tf ninjaspwebinterface.war
META-INF/ META-INF/MANIFEST.MF WEB-INF/ WEB-INF/classes/ WEB-INF/classes/com/ WEB-INF/classes/com/maaloe/ WEB-INF/classes/com/maaloe/myproject/ WEB-INF/classes/com/maaloe/myproject/modules/ WEB-INF/classes/com/maaloe/myproject/modules/user/ WEB-INF/classes/com/maaloe/myproject/tags/ WEB-INF/classes/com/maaloe/myproject/tags/utils/ WEB-INF/classes/com/maaloe/myproject/utils/ WEB-INF/classes/com/maaloe/myproject/webapp/ WEB-INF/classes/com/maaloe/myproject/webapp/actions/ WEB-INF/lib/ WEB-INF/admin-config.xml WEB-INF/classes/ApplicationResources.properties WEB-INF/classes/log4j.properties WEB-INF/classes/com/maaloe/myproject/modules/user/User.class WEB-INF/classes/com/maaloe/myproject/utils/Constants.class WEB-INF/classes/com/maaloe/myproject/webapp/ActionServlet.class WEB-INF/classes/com/maaloe/myproject/webapp/actions/LogonAction.class WEB-INF/classes/com/maaloe/myproject/webapp/actions/LogonForm.class WEB-INF/lib/activation.jar WEB-INF/lib/commons-collections.jar WEB-INF/lib/commons-httpclient-2.0.jar WEB-INF/lib/commons-logging-api.jar WEB-INF/lib/commons-logging.jar WEB-INF/lib/log4j-1.2.8.jar WEB-INF/lib/nls_charset12.jar WEB-INF/lib/servlet.jar WEB-INF/lib/struts.jar WEB-INF/lib/project_utils.jar WEB-INF/lib/xalan.jar WEB-INF/lib/xercesImpl.jar WEB-INF/lib/xml-apis.jar WEB-INF/struts-bean.tld WEB-INF/struts-config.xml WEB-INF/struts-config_1_0.dtd WEB-INF/struts-form.tld WEB-INF/struts-html.tld WEB-INF/struts-logic.tld WEB-INF/struts-template.tld WEB-INF/struts.tld WEB-INF/web.xml index.html logon.jsp
There are several problems in creating a war file that matches the one Ant creates.
- We need to figure out what versions of the jar files in WEB-INF/lib/ to use as in most cases, this is not specified.
- WEB-INF/lib/project_utils.jar is not a jar file we can find in any maven repository, so we need to find a solution for this.
- As the existing Ant build.xml contains targets for deploying to both development, test and production environment, we need to make sure that out maven conversion adds the correct properties to the war file based on a maven target.
Let's adress the problems one at a time.
First of all, we need to figure out what dependencies exist in the Ant war file. Some is straight forward like "commons-httpclient-2.0.jar" as this exists on an existing repository (check http://www.mvnrepository.com/) so we can just add the following dependency to the pom.xml file:
commons-httpclient commons-httpclient 2.0
Others, like struts.jar, we have no idea what version is used. In those cases you can usually extract the struts.jar file (jar -xvf
If you have the guts, you can upgrade to the latest release of the jar file, but that could go wrong.
In my case, the MANIFEST.MF contains:
Manifest-Version: 1.0
Implementation-Version: 1.0.2
Specification-Title: Struts Framework
Specification-Version: 1.0
...
Eg. Current ant war file depends on Struts 1.0.2, so we'll add the following to the pom.xml:
struts struts 1.0.2
Secondly, we need to figure out what to do with the "WEB-INF/lib/project_utils.jar" file as this is a local project jar file that we depend on. This such cases, we need to deploy this to a repository from where others can download the dependency when they need to compile the new version of the project build on Maven.
What I have done is to install "Artifactory". It's a simple application file that you install on a central server and then point your maven pom.xml file to your new repository. For more information on installing Artifactory, check here.
You can ofcourse just put the jar file in your local repository but then no one will be able to compile the project out of the box which is one of the main arguments I have for switching to Maven.
This is what I have added to the pom.xml file for Maven to download the jar files:
central http:// :8080/artifactory/repo false snapshots http:// :8080/artifactory/repo false
Third point. We want to be able to handle the three targets for the three different environments. This can be done by adding a profile to Maven and then using the "maven-antrun-plugin" plugin. In my case I can write:
environment maven-antrun-plugin test run
The above copies two property files from the conf/
Now, I can simply call Maven with the following command to package the development version of the project war:
mvn clean install -Denvironment=dev
Here's the full pom.xml file I used for the above project.
4.0.0 com.maaloe.myproject myproject war 1.0 MyProject Maven Webapp Maaloe.com http://dev.maaloe.com jm Jakob Maaloe jam@maaloe.com Developer Consultant +1 javax.servlet servlet-api 2.5 provided javax.servlet.jsp jsp-api 2.1 provided javax.activation activation 1.1 provided log4j log4j 1.2.15 struts struts-ninja 1.0.2 commons-collections commons-collections 3.2.1 commons-httpclient commons-httpclient 3.1 xerces xercesImpl 2.8.1 project_utils project_utils 0.1 junit junit 3.8.1 test myproject central http://repo.maaloe.com:8080/artifactory/repo false snapshots http://repo.maaloe.com:8080/artifactory/repo false central http://repo.maaloe.com:8080/artifactory/repo false snapshots http://repo.maaloe.com:8080/artifactory/repo false environment maven-antrun-plugin test run
So, that's it for changing from Ant to Maven. I have marked some of the dependencies as provided as they actually don't need to be part of the war file, since the web container (Tomcat in my case) provides this.
Of course you may run in to problems that I haven't taking in to account, but the above hopefully gives you a few hints to the way I usually solves the conversion process.
Please, if you run in to interesting problems that you think others could benefit from, comment on the blog!
1 comment:
Informative post. Thank you. I am trying to convert a huge (3000+classes)ant web project to maven. The problem I am running into is when there are path references to /WEB-INF/classes/<file-name> and when I want to use embedded jetty. So that 'mvn jetty:run' just works on my webapp source code. The problem is WEB-INF is is in webapp and classes is in target. I used to think that the both of them are 'seen' together runtime. But, I get something like this: java.lang.IllegalArgumentException: Invalid log4jConfigLocation parameter: Log4j config file [{base-dir}/src/main/webapp/WEB-INF/classes/log4j.xml] not found.
Any ideas, how to make this work? May be I am not very clear so please ask if more info is needed. Thanks again.
Post a Comment