Saturday, May 12, 2012

Maven Archetypes Part 3: How do I set up a multi-module project?

welcome back

As per the disclaimer in Part 2, if you haven't read the previous articles or aren't familiar with Maven archetypes already, this likely won't make much sense to you...

So far, we've created an archetype that can generate a jar that contains a "Hello world!" class and a very simple unit test that verifies what the message is. Another common case is that you want to create a project with multiple modules. For example, you may want a project that contains a model and webapp module, or perhaps a client and server with a module of common classes that define the data being sent between them.

It doesn't take much additional effort to get to that point; much of it is just replaying what's already been done in the previous example.

reviewing the previous article and creating some new stuff

Let's start by reviewing what our archetype's layout looked like from the previous article:

The first thing we need to do is move the jar down a level. If we're going to make a multimodule project, we need to have a parent project that produces a pom. That said, let's move our jar to the model module of our multi-module project.

You may notice that the new directory is __artifactId__-model. If you remember from Part 2, if you use double underscores before and after a property name, Maven will replace that with the property. With the directory structure above, if our artifact id from the project generated with the archetype was sample-project, the directory we just created would be called sample-project-model.

creating and refactoring the pom files

Now that we've moved our jar, we have nothing in the base of our archetype; we need to put a pom there that will act as the parent of our multi-module project. Let's go create a new pom at src/main/resources/archetype-resources/pom.xml:

This pom file is very similar to the pom file created in part 1, except that it lists out the order to build the modules nested within the project. Notice that we're using the string ${artifactId}-model, since this will match the directory generated from __artifactId__-model.

We also need to update our model project to use this one as a parent, and change its artifact id. Let's update src/main/resources/archetype-resources/__artifactId__-model/pom.xml with this information:

You may have noticed that the version and groupId elements of the pom have been removed. Having the parent defined will force the children to inherit these values, so there's no need to repeat them in the children.

We can also take this a step further to consolidate more of the behavior of our project. First, we can move the build section to the parent and make it inherited so that all of our projects have the same compile setting, and we can define version of junit-dep we're using in the dependency management section to keep all the modules consistent.

After making these changes, our parent pom at src/main/resources/archetype-resources/pom.xml should look like this:

Our model module's pom at src/main/resources/archetype-resources/__artifactId__-model/pom.xml should look like this now:

updating the archetype's metadata

Now, remember that all of our efforts are completely fruitless unless we update the archetype metadata descriptor to include the files we've added. Let's add the directories to src/main/resources/META-INF/maven/archetype-metadata.xml:

creating the webapp module

We've now completed adding our first module to the build. To follow the case above of having a model and webapp module, let's create the initial webapp directories:

If we're making a webapp, then we'll need the servlet api. Let's define this in the parent pom's dependency management section, with the provided scope. We'll also define the version of the model module so that we can import it into the webapp project without specifying the version:

We also need to add the webapp module to our list of modules in the parent:

Let's now create the pom for our webapp at src/main/resources/archetype-resources/__artifactId__-webapp/pom.xml. We'll add a dependency on the servlet-api, our model module, and add the Maven Jetty plugin so we can start it up from Maven after we create the project:

If we're dealing with a webapp, we need a web.xml file located in src/main/webapp/WEB-INF in our project for the Maven war plugin. Let's create the directory we'll need for this and add the web.xml file:

We can filter this file as well to name the servlet after our project, hence the ${artifactId} tokens in the web.xml file.

We also need a very basic servlet class that matches what we defined in the web.xml file. Go ahead and create this class at src/main/resources/archetype-resources/__artifactId__-webapp/src/main/java/__packageInPathFormat__/Servlet.java with the contents below:

updating the archetype's metadata (yes, again), and reviewing the file structure

We're almost in the home stretch here, I promise!

We need to update src/main/resources/META-INF/maven/archetype-metadata.xml one more time to add the resources for our webapp module. Here's what the final archetype-metadata.xml file will look like:

Let's review the topology of our archetype one last time, just to make sure everything is in place:

installing the archetype, creating a project, and running the webapp

Now let's install our archetype, go back a directory, and create a project from it.

Awesome! We have our project, so let's compile it!

Now that the artifacts are installed, let's run the webapp!

Now, if everything was set up correctly, you should be able to see "Hello world!" in your browser by going to the following URL after the Jetty server has launched: http://localhost:8080/sample-project-webapp/sample-project

Congratulations on getting through these articles and writing your first archetype! Hopefully this will put you on the path to making archetypes that will reduce the overhead of creating projects based on certain standards and settings.

3 comments:

  1. Do you have this archetype available somewhere as a binary/jar download (ex: Github)?

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. When I saw this article on your blog, at first I did not understand anything. So I fired up interest. And I had to find all the information that you laid out earlier.
    It's awesome, powerful and informative!
    Thank you retrace your path, and in the end I saw a cherished phrase "Hello World")
    Richard Brown data room m&a

    ReplyDelete