Protect Your Resources
Class files are usually loaded from the classpath. Non-class files (resources) can be loaded from the classpath as well, using the various
getResource methods. If you want to keep these resources from getting wiped out every time you invoke the
clean goal, you need to store resources outside of the classes directory and copy them into the classes directory as part of the build process. Maven has a goal for this purpose called
jar-resources. It doesn't jar anything, it just copies resources to the
target/classes directory. Maven automatically calls the
jar-resources goal when you run the
jar goal.
First, you need to tell Maven where your resource files are:
- Add the
resources element, as shown below, to project.xml:
...
</unitTest>
<resources>
<resource>
<directory>
${basedir}/src/conf
</directory>
</resource>
</resources>
</build>
...
- Place a resource file, say
box.properties, in <project_home>/src/conf.
- Run the
jar goal, which indirectly runs the jar-resources goal:
maven jar
Look in the target/classes folder. You should see that Maven copied box.properties from src/conf to target/classes.
Creating Goals and Plug-ins
Like Ant, Maven allows you to create your own goals. You can utilize Maven goals at varying levels:
- Use an existing goal as is.
- Extend an existing goal.
- Create your own project-level goal.
- Create your own cross-project goal by creating a Maven plug-in.
I discuss each of these in the following sections.
Project-specific Goals
Project-specific goals are defined in a file called maven.xml, which is roughly analogous to Ant's build.xml. Here are the steps to create a project-specific goal in Maven:
- Create a file called
maven.xml in your project root:
c:\daven\maven.xml
- Add the following XML snippet:
<project>
<goal name="myGoal-1">
<mkdir dir="c:\myFolder"/>
<echo>Hello Maven</echo>
</goal>
</project>
- Invoke the goal:
maven myGoal-1
As you can see, with maven.xml, Maven can act just like Ant. (By the way, mkdir is an Ant task.) Maven can execute any Ant task, and it also supports most of the JSTL tags, which provide basic logic and looping functionality. For example, if you wanted to create a goal that makes five directories, you could use the following:
<goal name="myGoal-2">
<c:forEach begin="1" end="5" indexVar="i">
<mkdir dir="c:\myFolder${i}"/>
<echo>${i}</echo>
</c:forEach>
</goal>
The maven.xml file is actually a Jelly Script. Jelly is a general-purpose, XML-based scripting language. Maven uses it, but it's not specific to Maven. (See the sidebar "Jelly: Executable XML" for more information.)
Extending a Goal
You can extend an existing goal in a project-specific way by defining a preGoal and/or a postGoal element in maven.xml. For example, if you're happy with Maven's built-in clean goal but you would also like Maven to clear your application server's temporary cache, you could extend clean with a postGoal. My application server, Caucho Resin, uses a directory called WEB-INF/work for it's temporary files. Try adding the following block of code to maven.xml:
<postGoal name="clean:clean">
<delete dir="${basedir}/WEB-INF/work"/>
<echo>Deleted Resin work directory</echo>
</postGoal>
Then rerun the clean goal:
maven clean
Cross-project Goals (Plug-ins)
Maven allows you to create generic, reusable goals, which in my opinion are what make Maven so useful. By comparison, Ant supports reusable tasks, but not reusable targets. To create a reusable goal, you need to create a Maven plug-in.
As an example, I use Solarmetric's Kodo, a JDO compliant object/relational mapping tool. JDO is a great technology but it adds an extra step to the build process. Specifically, you have to run a separate task that adds persistence functionality to your class files (JDO refers to this as byte-code enhancement). Luckily, you can call the byte-code enhancer from a Maven goal. And since I use Kodo in multiple projects, it's a great candidate for a cross-project goal, or in other words, a plug-in.
For the sake of simplicity, start with a slightly less ambitious "hello world" plug-in. Take the following steps to create a minimal Maven plug-in:
- Create a directory for the plug-in:
<USER_HOME>\.maven\plugins\maven-hello-plugin-1.0
- Create the
plugin.jelly file:
File name:
<USER_HOME>\.maven\plugins\maven-hello-plugin-1.0\plugin.jelly
Content:
<project>
<goal name="hello">
<echo>Hello Maven Plug-in!</echo>
</goal>
</project>
- Create a minimal
project.xml file inside the plug-in's folder:
File name:
<USER_HOME>\.maven\plugins\maven-hello-plugin-1.0\project.xml
Content:
<project></project>
This project.xml file obviously adds no value to your plug-in, and logically you don't need it. But Maven will return an error message if you don't provide it. More sophisticated plug-ins will make use of this file to extend the POM with plug-in-specific settings.
- Invoke the new plug-in:
maven hello
You've completed your first plug-in!
More Than a Build Tool
Although I've described and primarily used Maven as a build tool, it is actually more than just that. The Maven Web site describes Maven as "a Java project-management and project-comprehension tool." For example, the POM (project.xml) has tags to specify the developers' names, the developers' e-mail addresses, the project inception date, etc. Goals such as site:generate will then use that information to auto-generate a project Web site to document your project.
Remember, the power of Maven is in its plug-ins. So spend some time exploring the many plug-ins and goals that it provides. For if you have to maintain multiple, large build scripts and you're tired of copy-and-paste reuse, Maven and its long list of plug-ins will make your life easier.
Acknowledgements
Dave Ford thanks Jason van Zyl (Maven's architect) and his associates, Ron Hitchens, Dave Solum, John Sheehan, and Jordan Fisher, for contributing valuable feedback for this article.