Changed: 1,198c1

This tutorial will guide you through the first steps to create a Maven /Plugin?. Requirements:

Now, let's build a Maven /Plugin? together ! What do we want that plugin to do ? This is the first question that should come to your mind ! ;-) We will make a plugin named "copy" that copies the Project's /ProjectObjectModel to a directory for further processing. Now, let's see how we will do that:

Goals

First, we have to choose some goals. The workflow of plugins is organized in goals. To invoke a goal, simply type "maven ${goal name}", and Maven will run it ! To see the list of goals available to you, type "maven -g". Each goal has a name, a description, and prerequisites.

The naming convention for goals is:

Following those conventions, we will have a single goal, named "copy". It hasn't got any prerequisites yet. Thus, our plugin looks like:


<?xml version="1.0" encoding="ISO-8859-1"?> <project> <goal name="copy" description="Copies the /ProjectObjectModel to a directory"/> </goal> </project>


It does nothing yet !

Properties

Each plugin has access to its own set of properties. Those properties are set in the file plugin.properties, in the plugin's dir. The naming convention for plugins properties is "maven.${plugin name}.${property name, with additional "." if you like}". So, for instance, this plugin could have properties like :

(This is not a convention yet, but I think that the plugins should have their output set to ${maven.build.dir}/${plugin name}, so that we know easily where the plugin outputs stuff.) In this case, we set the plugin to copy the file to ${maven.copy.target.dir}/${maven.copy.target}. If the user wants the plugin to copy the file to "target/foo" for instance, he would set "maven.copy.target.dir = ${maven.build.dir}/foo". As the project's properties file is loaded after the plugin's one, the properties are overriden.

Content

Now, our plugin is ready to actually do something ! Maven and JakartaJelly? combined allow us to use Ant stuff directly within the application. So, our plugin now looks like:


<?xml version="1.0" encoding="ISO-8859-1"?> <project> <goal name="copy" description="Copies the ProjectObjectModel to a directory"/> <!-- This is Ant stuff --> <copy file="${basedir}/project.xml" tofile="${maven.copy.target.dir}/${maven.copy.target}"/> </goal> </project>


So, now when you type "maven copy", the file project.xml is copied in target/copy ! Woohoo ! This is fine, but useless.

Add features

Now, we will make a useful plugin out of that. First, let's set a property that sets the source file to copy. In our plugin.properties file, we set: maven.copy.source = ${basedir}/project.xml We default that to ${basedir}/project.xml for backward compatibility). Our copy task now looks like: <copy file="${maven.copy.source}" tofile="${maven.copy.target.dir}/${maven.copy.target}"/> Now, we can copy whatever file we want to whatever dir and file. Cool. Still we have to set that in the project's project.properties file, which is not really handy ... Let's ask the user which file the user wants to copy, and where. To do that, we add a new "ask" goal (we don't want to mess up our first goal, do we ?). This goal is useless to the end user, so we don't set the description, and to follow the naming convention, we will name it "copy:ask". The "ask" JellyTag? is in the "interaction" JellyTagLibrary?, so we need to add a namespace for it. We also need a namespace for JakartaJelly?'s core taglib, as it not the default in Maven. Let's bind it to "j".


<project xmlns:i="jelly:interaction" xmlns:j="jelly:core"> <goal name="copy:ask"> <i:ask question="Which file do you want to copy?" answer="maven.copy.source" default="${maven.copy.source}"/> <i:ask question="Where do you want to copy it?" answer="maven.copy.target.dir" default="${maven.copy.target.dir}"/> <!-- The default name of the copied filewill be the filename of the source file, so we need to know if there was a dir specified in the source file name. --> <j:set var="index" value="${maven.copy.source.indexOf('file.separator')}"/> <j:choose> <j:when test="${index != '-1'}"> <j:set var="length" value="${maven.copy.source.length()}"/> <j:set var="maven.copy.source.filename" value="${maven.copy.source.substring(index, length)}"/> </j:when> <j:otherwise> <j:set var="maven.copy.source.filename" value="${maven.copy.source}"/> </j:otherwise> </j:choose> <i:ask question="What name do you want to give it?" answer="maven.copy.target" default="${maven.copy.source.filename}"/> </goal> ... </project>


Ok, we can ask the user which file he wants and where to copy it. But we don't want to have to type "maven copy:ask copy" to have the plugin ask us.

Using prerequisites

In order for the plugin to do that automatically, we have two solutions:

  1. set the prerequisites attribute of the "copy" goal (eg: <goal name="copy" prereqs="copy:foo,copy:bar"/>)

  2. add a <attainGoal name="copy:ask"/> tag before we copy the file.

I prefer the second solution, because it outputs the goals in the right order. It would output something like: copy: copy:ask: ... instead of copy:ask: copy: This is mostly a cosmetic preference, and both solutions work exactly the same way. Note that you could also set the copy:ask goal as a preGoal of "copy" (<preGoal name="copy"/>) so that it is executed before as well. So, now our "copy" goal looks like:


<goal name="copy" description="Copy a file to a directory"> <attainGoal name="copy:ask"/> <copy file="${maven.copy.source}" tofile="${maven.copy.target.dir}/${maven.copy.target}"/> </goal>


Final plugin

Let's have a look at the whole plugin:


<?xml version="1.0" encoding="ISO-8859-1"?> <!-- This plugin copies a file to a directory specified by the user. --> <project xmlns:i="jelly:interaction" xmlns:j="jelly:core"> <!--==================================================================--> <!-- Ask the user which file to copy, and where --> <!--==================================================================--> <goal name="copy:ask"> <i:ask question="Which file do you want to copy?" answer="maven.copy.source" default="${maven.copy.source}"/> <i:ask question="Where do you want to copy it?" answer="maven.copy.target.dir" default="${maven.copy.target.dir}"/> <!-- The default name of the copied filewill be the filename of the source file, so we need to know if there was a dir specified in the source file name. --> <j:set var="index" value="${maven.copy.source.indexOf('file.separator')}"/> <j:choose> <j:when test="${index != '-1'}"> <j:set var="length" value="${maven.copy.source.length()}"/> <j:set var="maven.copy.source.filename" value="${maven.copy.source.substring(index, length)}"/> </j:when> <j:otherwise> <j:set var="maven.copy.source.filename" value="${maven.copy.source}"/> </j:otherwise> </j:choose> <i:ask question="What name do you want to give it?" answer="maven.copy.target" default="${maven.copy.source.filename}"/> </goal> <!--==================================================================--> <!-- Now, let's copy the file ! --> <!--==================================================================--> <goal name="copy" description="Copy a file to a directory"> <attainGoal name="copy:ask"/> <copy file="${maven.copy.source}" tofile="${maven.copy.target.dir}/${maven.copy.target}"/> </goal> </project>


That's all Folks !

So, what do you think about our little plugin ? Cool, isn't it ? To practice the plugin development, I propose you to extend this plugin to:

Further experiments could be:

Here you are ! I hope that you enjoyed it. Feel free to show me your results here: SamplePluginAddons?. I'll be glad to answer your questions along the road. Other plugins developers will sure help you too ! <hint, hint ...> --StephaneMor


We need to talk about the plugin's ProjectObjectModel?, the plugin's dependencies, the classloader stuff ... --StephaneMor Please can you also show how you would use this building a plugin from scratch using the plugin plugin :) -- Neil


Maybe I'm dense, but there seem to be a lot of assumptions here. Into what files are these goals being set? (I'd guess project.xml like everything else, but I'm not sure.) Also, how do you install the plugin to your maven installation? Is it as simple as creating a directory and copying your files into it? Do you need to jar it up?*   

CategoryDelete (last edited 2009-09-20 23:32:18 by localhost)