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.

Maven Archetypes Part 2: How do I create my first jar?

introduction and review

Before you start with this article, you need to have a basic understanding of Maven archetypes or have read the first article in my guide.  If you haven't, I have a feeling most of this won't make sense to you.

That said, let's start off with a quick review.  In the last article, we left things with an archetype layout of:

This is the most basic archetype you can really have; all it does is create a pom artifact. Typically, you won't be interested in generating a pom from an archetype; you'll want to at least create a jar, and potentially a multi-module project. For the next step, let's create our first jar.

Updating the project pom file to create a jar

Before we start to add files for our archetype, let's update src/main/resources/archetype-resources/pom.xml to change the packaging to jar. Let's also add a dependency on junit so that we can package a unit test in the project that's created, as well as the compiler setting:

Creating the necessary directories and updating the archetype metadata

Next, let's add the default directories that Maven projects use. I've added __packageInPathFormat__ as a subdirectory of both the main and test paths. When you generate a project from an archetype, Maven will create the property packageInPathFormat for you, which changes all of the dots in a package name to slashes. For example, if you have a package of com.theotherian, the packageInPathFormat property will be com/theotherian.

We also need to update the src/main/resources/META-INF/maven/archetype-metadata.xml file so that it knows to include and filter our new directories:

creating a basic Java class and a unit test

Now that we have our directories set up, let's create some source code.
First, let's add your standard "Hello world!" Java class in the file src/main/resources/archetype-resources/src/main/java/__packageInPathFormat__/App.java:

Next, let's add a basic unit test just to flesh things out a bit more at src/main/resources/archetype-resources/src/test/java/__packageInPathFormat__/AppTest.java:

Wrapping things up

Now that we've added all of this additional stuff, let's do a quick recap of the project layout:

If your project looks like that, go ahead and install the archetype and go back a directory.

creating a project from the archetype

Let's generate another project from our local archetype (if you haven't included this archetype in your local catalog, follow the steps in part 1):

Our project from the archetype has been created, so let's try building it:

Awesome, you've got an archetype building a jar!

But wait, there's more! Check out the next post in this series, which shows you how to build multi-module archetypes, to learn more!

Maven Archetypes Part 1: Where Do I Start?

creating the initial files and directories

Let's start with creating a directory for your archetype:

$ mkdir sample-archetype

Inside of that directory, let's create the files and directories you're going to need:

You should now have the current file structure in your sample-archetype directory:

Here's a breakdown of what the things you added do:

  • pom.xml: This tells Maven to construct the archetype artifact. It's a pretty standard pom and won't change much if at all for the life of your archetype.
  • src/main/resources/archetype-resources: This is the directory that Maven looks at to generate your project. The files you add in here will get copied and filtered into your project.
  • src/main/resources/archetype-resources/pom.xml: This will be the main pom file for the project your archetype creates.
  • src/main/resources/META-INF/maven: This is where Maven looks to find information about what to include in your project, what properties to ask the user for, and what files it should filter.
  • src/main/resources/META-INF/maven/archetype-metadata.xml: This is the file that Maven uses to configure your project. It includes instructions for how the files of your project should be filtered and where they should exist

setting up the pom files and updating the archetype metadata

Alright, now that we have all the files set up, let's construct the pom for the archetype (not the generated project) which is the pom.xml file in the current directory you're currently in (your sample-archetype directory):

Now that we have the archetype's pom file complete, let's add what we need for the project we're looking to generate. We'll start small and just generate a pom as the artifact for the project. When you're adding files for the project you'll be generating via the archetype, you'll use a lot of variables in your poms like ${artifactId} and ${version}. You'll want to specify that those poms should be filtered via the archetype-metadata.xml file so that those values turn into what the user generating the project is prompted for. Add the contents below to the src/main/resources/archetype-resources/pom.xml file:

Next, we need to update the src/main/resources/META-INF/maven/archetype-metadata.xml file to include the pom in our project:

installing your archetype and creating a project from it

Now that we have all of the basic files we need in place, go ahead and install the archetype running:

$ mvn clean install

Installing the archetype should place it in your local repository (wherever you have that configured to be, typically ~/.m2/repository). Update your local catalog (on my system, ~/.m2/archetype-catalog.xml) and add your new archetype:

After adding that to your archetype catalog, run the following command (but go back one directory first so you don't try to create a project within your archetype's directory):

You should see the following output (or something similar) after maven checks your repos:

Select the sample-archetype and add the criteria for the project as you're prompted:

You should now have a sample-project directory created that contains a pom.xml that should look like this:

Go to the directory for your newly created project, and install the project with the following command:

You should see the following output:

We've got ourselves an archetype now!

Don't stop here though. There's a lot of value in learning how to build a jar from an archetype, so check that post out next!

How to toggle Mac WiFi (Airport) via a keyboard combination with BetterTouchTool

I was playing around with BetterTouchTool today (which, by the way, is one of the best Mac addons I've ever seen) and decided that I wanted to have a keyboard combination to toggle my Wifi settings.  BetterTouchTool gives you the ability to execute a script, however, that causes a Terminal window to open if you write a bash script.  This can be disabled through other means, but I found out it's easiest to just write an AppleScript file.

The code below is what I'm using to do this.  I've bound this script to a keystroke combination via BetterTouchTool, and it's working perfectly:

Wednesday, May 9, 2012

thread-local-dns RC3 released

Exciting times are among... me.

I've cut an RC of my open source project thread-local-dns and pushed it to Maven Central for everyone to use.

If you'd like to learn more about thread-local-dns, please check out the project page on github:
https://github.com/theotherian/thread-local-dns

If you'd like to use it in your project, add the following to your pom:

  
  <dependency>
    <groupId>com.theotherian.dns</groupId>
    <artifactId>thread-local-dns</artifactId>
    <version>1.0-RC3</version>
  </dependency>
I'll be updating the project page and this blog soon with a practical example of using this library.