Posts Tagged ‘mikeclipse’

Working with Custom Maven Archetypes (Part 1)

January 14, 2010

Late last year we announced support for Maven on our hosted CI platform. This was something we introduced earlier than anticipated as we discovered that the majority of our private beta participants wanted to use this increasingly popular tool for their builds. We also provide support for Apache Ant and Eclipse IDE based projects that don’t include a build.xml or POM file. The rationale behind the latter is to allow developers to get up and running with CI in no time at all. We also released an (open source) Eclipse plugin – mikeclipse – that in its current incarnation allows you to generate a ‘Mike-friendly’ web application project so you can quickstart your development effort. You can create a web app with Struts2 or Spring-Mvc based project flavours. No ‘Mike-specific’ hooks are written in to the generated apps, so you can just use it as a simple wizard to generate a skeleton project. We intend to expand the features of this plugin over the course of the coming months.

Those of you who are familiar with Maven will probably be aware that this generation of skeleton projects is already available via the maven-archetype-plugin. This plugin generates a project based upon a template – called an archetype of course – that you package as discreet Maven (jar) component. Once you have installed or deployed your archetype into a Maven repository it can then be processed by the maven-archetype-plugin. At execution time, if used interactively, the plugin will prompt you with a series of questions about which archetype you wish to use and what, using Maven-ese, the ‘GAV’ (GroupId/ArtifactId/Version) coordinates are for your skeleton project.

I’ve been a user of the plugin for some years now, buts its been a little while since i’ve created a custom archetype, so I thought it might be worth sharing the process I went through to create it. Along the way I discovered some nice features that, at time of writing, did not appear to be fully documented on the plugin site, so I’ve captured them here. I’ll look into creating a issue and/or submitting a patch to the Maven project for these omissions if required later.

Step 1 – Generate Your Archetype

This was a new one for me. Previously I had always hand crafted my custom archetypes, but the plugin supports a goal that allows you to create the basis of your archetype from an existing project – very handy. So I used mikeclipse to create myself a Spring-Mvc skeleton project, did some refactorings to mavenize it – create a POM basically – and executed the following command in my shell


~/foo/example$ mvn archetype:create-from-project
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
[INFO] ---------------------------------------------------------
[INFO] Building example
[INFO]    task-segment: [archetype:create-from-project] (aggregator-style)
[INFO] ---------------------------------------------------------
[INFO] Preparing archetype:create-from-project
[INFO] ---------------------------------------------------------
[...]
[INFO] Archetype created in target/generated-sources/archetype
[INFO] ---------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ---------------------------------------------------------

This created a first-cut archetype for me in target/generated-sources/archetype that i could now tweak.

Step 2 – Removing redundant files

First thing i needed to was make some small changes to remove some superfluous Eclipse metadata files. This is probably an oversight on my part as i could have generated the archetype after removing these, but since i wanted to view the results of the archetype generation in Eclipse, the plugin had hoovered them up. So I removed them from the archetype-resources sub-directory (under target/generated-sources/archetype) and also edited the two archetype descriptor files under archetype-resources/META-INF/maven. I believe that two files are created to ensure backward-compatability with earlier versions of the archetype plugin – i was using the 2.0-alpha-4 release which supports the new archetype-metadata.xml format descriptor. For example, I removed the following entry from archetype-metadata.xml after deleting the files:


  <fileSet filtered="true" encoding="UTF-8">
     <directory></directory>
     <includes>
        <include>.project</include>
        <include>.classpath</include>
     </includes>
  </fileSet>

Step 3 – Fixing Velocity warnings

Under the hood, the archetype plugin uses Apache Velocity to do its file filtering – for example, the substitution of the GAV values I discussed above. Those who have used Velocity will be aware that it can be quite verbose in its output, especially when it encounters syntax that resembles a filterable value. This manifests itself in warnings like the following when you generate a project using your archetype (I’ll cover that process shortly):


   [WARNING] ... : ${mvnVersion} is not a valid reference.

While this warning does not materially affect what gets generated it doesn’t build confidence in your archetype as it will be one of the first things your users will see. So lets see how we can fix this. In my case, its due to the fact that my template POM contains a number of properties that I don’t want to be filtered and should be left intact as part of the generation. This is not an uncommon problem with POM’s – they often contain properties that allow a runtime interpolated value to be changed in one place. We can use some simple Velocity syntactical sugar as follows. Declare the following directive at the top of your template pom.xml file under archetype-resources


  #set( $symbol_dollar = '$' )

Then, wherever you declared a property that you want to be ‘ignored’ by Velocity do something akin to the following


  <requireMavenVersion>
    <version>${symbol_dollar}{mvnVersion}</version>
  </requireMavenVersion>

Step 4 – Filtering a filename

One of the nice features in the latest version of the archetype plug-in is the ability to re-name files during the generation process. In my scenario, I want to use this feature to ensure that my Spring servlet descriptor matches up with the associated declaration in my web.xml file. However, I did have to do a little digging to find out how to do this via the plugin JIRA as when I looked it did’nt jump out from the plugin web site examples or usage pages. Anyways, its pretty simple to do. Just create the template file using the filterable property as part of its name, using double underscores as token separators. In my case:


  $ mv src/main/webapp/WEB-INF/example-servlet.xml \
  src/main/webapp/WEB-INF/__artifactId__-servlet.xml

Step 5 – Install and invoke the archetype

A quick local installation is simply accomplished by running an mvn install inside the target/generated-sources/archetype directory.

The install creates a metadata file under ~/.m2/ called archetype-catalog.xml.


~/.m2$ cat archetype-catalog.xml
  <?xml version="1.0" encoding="UTF-8"?>
  <archetype-catalog>
   <archetypes>
    <archetype>
     <groupId>com.mikeci</groupId>
     <artifactId>example-archetype</artifactId>
     <version>1.0-SNAPSHOT</version>
     <description>example-archetype</description>
    </archetype>
   </archetypes>
  </archetype-catalog>


Once installed I can invoke it using the archetype:generate goal. By default this will search the public archetype catalog from the Maven central repositories and display all the available ones plus my local one. To tell the plugin to only search my local Maven repository I can use a simple mojo parameter as follows:


~/foo$ mvn archetype:generate -DarchetypeCatalog=local
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
[INFO] --------------------------------------------------------------
[INFO] Building Maven Default Project
[INFO]    task-segment: [archetype:generate] (aggregator-style)
[INFO] --------------------------------------------------------------
[INFO] Preparing archetype:generate
[...]
Choose archetype:
1: local -> example-archetype (example-archetype)
Choose a number:  (1): 1
Define value for groupId: : com.acme
Define value for artifactId: : mywebapp
Define value for version:  1.0-SNAPSHOT: :
Define value for package:  com.acme: :
Confirm properties configuration:
groupId: com.acme
artifactId: mywebapp
version: 1.0-SNAPSHOT
package: com.acme
Y: :
[INFO] --------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] --------------------------------------------------------------

Then finally:


~/foo/mywebapp$ mvn package jetty:run -P mikeci,itest
[INFO] Scanning for projects...
[INFO] --------------------------------------------------------------
[INFO] Building example
[INFO]    task-segment: [package, jetty:run]
[INFO] --------------------------------------------------------------
[INFO] [enforcer:enforce {execution: enforce-versions}]
[...]
[INFO] Started Jetty Server

Et voila, my browser displays the simple landing page for the generated web app:

Obviously, I’ll need to subsequently take the generated archetype code and turn it into a proper project in its own right, add it to version control and ultimately deploy/release it to a Maven repository so it can be shared. I’m planning on using Github for the VCS (or SCM in maven-ese) as we are currently working on adding Git support to Mike. I also want to see how the maven-release-plugin handles Git. I’ll cover these topics in the next part of my blog.

Adam