Maven plugin guide using Modello Framework


Published on

a guide for APache Maven plugin development using Modello Framework

Published in: Technology, Art & Photos
1 Like
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Maven plugin guide using Modello Framework

  1. 1. HOW TO WRITE A NEW APACHE MAVEN PLUGINThis tutorial explain how to create an Apache Maven 2.x plugin using Java 1.5 annotations.Requirements:Elipse IDE (optional)Java 1.5 (or greater)Apache Maven 2.xIs assumed that the above tools are already installed and the reader have a minimum knowledge about them (inparticular with Maven).Why this guideI have written it during the development of my plugin as “collections of notes”.It can be find at: could use it as example associated with this guide.It wont be a replacement for the official documentations, but only a quick start with tips and tricks and clarifysome possible doubts of a beginner Maven plugin developer.Base conceptsA Maven plugin is composed of one or more "unit" named Mojo, each one is mapped to a plugin goal.In Maven terminology, a goal represents a specific task that can be bound to zero or more build phases.If is not bound with a build phase, the plugin can be executed only invoking it directly.The general sintax to invoke a specific plugin goal is:mvn plugin-name:goal-nameIt execute the Mojo owned by "plugin-name" and associated with goal named “goal-name”.A Maven plugin is packaged as a JAR file.At least, a plugin is composed of two Mojo : one that perform the plugin business logic, and one with the help page(shown with mvn plugin-name:help).The "help Mojo" can be generated in automatic (see below) so that the simplest Maven plugin can have only oneMojo.The Maven specifications require the presence of a plugin descriptor named plugin.xml and placed in the META-INF/maven folder of the final JAR file.Dont worry about it: will be generated in automatic using an existing Maven plugin that exploit your javadoccomments placed in the Mojo class.Steps to create your first pluginLets see the single steps necessary to create a plugin.step 1) create a Maven project for the new plugin with:mvn archetype:generate -DgroupId=property.maven.plugin -DartifactId=maven-property-checker -DarchetypeArtifactId=maven-archetype-mojoObviously "groupId" and "artifactId" variables should set with custom values according with requirements.Follow the wizard providing the required informations (they can be modifyed after editing the pom.xml).step 2) If the previous command was executed successfully, a folder named with the provided “artifactId” iscreated. Also in the source folder a simple Mojo is created.Now open the generated pom.xml file to apply some customizations.
  2. 2. a) As you can note, the previous command has already added the necessary dependency with this plugin:<dependency><groupId>org.apache.maven</groupId><artifactId>maven-plugin-api</artifactId><version>2.0</version></dependency>dont modify it because offer the necessary API to create a Maven 2.0 plugin.Inside the <dependencies> section add some new required dependencies:<!-- A lightweight IOC container already used by Maven for dependency injection --><dependency><groupId>org.codehaus.plexus</groupId><artifactId>plexus-utils</artifactId><version>3.0.9</version></dependency><!-- Necessary to use Java 5 annotations in plugin Mojos (See below for more informations) --><dependency><groupId>org.apache.maven.plugin-tools</groupId><artifactId>maven-plugin-annotations</artifactId><version>3.2</version><scope>provided</scope></dependency>c) Inside the <plugins> section add this plugin:<!-- Necessary to import the project in Eclipse --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-eclipse-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.5</source><target>1.5</target></configuration></plugin><!--Plugin that create in auto the plugin.xml descriptor that will be placed under META-INF/maven folder--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-plugin-plugin</artifactId><!-- Use a versione equal or greater to 3.0 to use java 5 annotation (See below for more info) --><version>3.2</version><configuration><!-- see --><skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound></configuration><executions><!-- This goal is used to generate in automatic the plugin descriptor --><execution><id>mojo-descriptor</id><goals><goal>descriptor</goal></goals></execution><!-- To generate in auto the Mojo associated at the help goal using your javadoc comments --><execution><id>help-goal</id><goals><goal>helpmojo</goal></goals></execution></executions></plugin>
  3. 3. <!-- optional: add this plugin to use the "Modello" project (See dedicated section below for more info) --><plugin><groupId>org.codehaus.modello</groupId><artifactId>modello-maven-plugin</artifactId><version>1.7</version><!-- The location of the file with the Modello definitions --><configuration><models><model>src/main/mdo/checks.mdo</model></models><version>1.0.0</version></configuration><executions><!-- this phase will execute the goals "java" and "xpp3-reader" to create java bean and xpp reader object --><execution><id>myModel</id><phase>generate-sources</phase><goals><goal>java</goal><goal>xpp3-reader</goal></goals></execution><!-- During this phase will be executed the goals "xsd" which generate an XML Schema for the model --><execution><id>myModel-site-xsd</id><phase>pre-site</phase><goals><goal>xsd</goal></goals><configuration><!-- (optional) the folder where place the generated xsd --><outputDirectory>target/generated-site/resources/xsd</outputDirectory></configuration></execution><execution><id>myModel-site-doc</id><phase>pre-site</phase><goals><goal>xdoc</goal></goals></execution></executions></plugin>step 3)Create the Eclipse project so that when can develop in an IDE. Open the command line and execute:mvn eclipse:clean eclipse:eclipseNow import the project in Eclipse (File --> Import --> Existing project into workspace...)step 4) Working inside Eclipse project, we can create our first Mojo and develop our plugin.The creation command has created a simple Mojo that you can customize instead of create a new one, buti suggest to create a new one to have a clean situation.To create a Mojo, is necessary implements a class that extends "org.apache.maven.plugin.AbstractMojo" and implementthe "execute" method, which will contains the business logic of the Mojo.The naming convention recommend to append the “Mojo” suffix at the class name.The next two sections will explain how pass input parameters to a plugin and how bind a Mojo with a goal.Plugin input parametersAt this point of the tutorial we have configured the plugin project in Eclipse and created a base Mojo.The next step is understand how work the parameters injection system for plugin.To use our new plugin is necessary that another Maven project declare the dependency in his pom.xml (ie using a<plugin> block).To pass input parameters at our plugin, that <plugin> block must contains a child node named <configuration> whereplace that parameters.For example, the pom.xml of the project that want use our plugin will contain:<plugin><!-- group, artifactId, version of our plugin omitted for simplicity --><configuration><!-- Put here the input parameters for our plugin -->
  4. 4. </configuration></plugin>The input parameters are injected in the plugin Mojo fields by Plexus framework ( alreadyused by Maven for other tasks.In order that the injection work correctly is necessary respect some rules and naming conventions in the Mojo andin the above <configuration> block.In the Mojo class is necessary mark the fields with custom annotations; this can be done using a java 5 or javadocstyle.This tutorial focus on the use of java 5 annotations (for that reason is necessary use the plugin "maven-plugin-plugin"with version greater 3.0 and declare the use of the plugin "maven-plugin-annotations").Example:suppose that our Mojo have that field:@Parameterprivate File verificationFile;In the pom.xml the <configuration> block must be:<plugin><!-- other plugin data omitted for simplicity --><configuration><verificationFile>src/test/verifier/property-check-input.xml</verificationFile></configuration></plugin>As you can note, the Mojo field name is identical at the tag name inside the <configuration> block (ie“verificationFile”). In this manner the field will be set to the value “src/test/verifier/property-check-input.xml” and youcan use it in the Mojo class (remember the setter method in your Mojo class).Dont worry about the parameter type conversion, just assign the desired type at the field.See: for more examples with different parameterstype (eg. use a parameter with multiple values).Is possible inject in a Mojo field also properties values already offered by Maven, not only custom values. Forexample, we want the project base directory which is kept in the Maven property named "basedir", in that case wehave:@Parameter (property = "basedir")private File projectBasedir;To pass that types of properties is not necessary add a tag in the <configuration> block (as above) but only annotethe field and provide the setter method.See: for more info about the available annotations.Is possible provide a default value in case of a parameter is missing in the <configuration> block, for example:@Parameter( property = "myFileld", defaultValue = "this is default value" )private String myFileld;Note: the javadoc comments that you place in the Mojo class and fields will be inserted in the plugin descriptor(plugin.xml) by "maven-plugin-tools" plugin.In case we want execute our Mojo by command line rather than invoke it from another project, as explainedabove, the input prameters can be passed from the command line as "system property". In the source code isnecessary use the attribute "property" for the "parameter" annotation, for example:@Parameter(property = "query.url")private String myParameter;
  5. 5. To inject a value in the above parameter is necessary build the project using the the "-D" option followed by theproperty name (ie “query.url”):mvn clean package -Dquery.url=http://maven.apache.orgIn case of more input parameters we have many -D<property-name>=<property-value> block.Tip: the name of the system property can be different from the field name (ie “myParameter”) but is not a goodidea.Annotation for the Mojo classThis section is about the annotations to use at class level in the Mojo.For example:@Mojo( name = "check" )@Execute( goal = "verify", phase = LifecyclePhase.COMPILE)public class PropertiesCheckerMojo extends AbstractMojo{//fields and methods omitted for simplicity}The class "PropertiesCheckerMojo" is a Mojo.The attribute "name" of the @Mojo annotation define the name of the goal which the Mojo is associated (rememberthat a Mojo is binded with a goal).In the example above the Mojo named “check” is associated with the goal “verify”.So that, to execute the above Mojo directly from the command line, the sintax is:mvn <plugin-group-id>:<plugin-name>:<plugin-version>:checkTip: that command can be shorted (See: section"Shortening the Command Line").Or that command:mvn <plugin-group-id>:<plugin-name>:<plugin-version>:help -Ddetail=trueexecutes the Mojo associated with the goal named "help" (remember that “help” Mojo is generated in automatic,this means that there insnt the associate Mojo class in the plugin source tree).The not mandatory option “-Ddetail=true” display also informations about Mojo input fields.The “@Execute” annotation define also the Maven build lifecycle phase when the plugin (invoked by command line)must be executed.To invoke the plugin from another Maven project is necessary define his dependency and provide an <execution>block to attach the goal of our plugin to a particular phase of the Maven build lifecycle.So that when the user execute "mvn clean package install" will be executed the goal of our plugin, for example:<plugin><groupId>property.maven.plugin</groupId><artifactId>maven-property-checker</artifactId><version>1.0-SNAPSHOT</version><executions><execution><phase>compile</phase><goals><goal>check</goal></goals></execution></executions><configuration><verificationFile>src/test/verifier/property-check-input.xml</verificationFile></configuration></plugin>Looking at the above code, the goal "check" of the plugin "maven-property-checker" is binded with the "compile" phase:when that phase will be executed also our plugin goal will be executed too.
  6. 6. Use the Maven logging systemInside our Mojo is possible use the logging system already provided by Maven to print messages in the consoleduring the execution.To use it simply invoke the inherited "getLog" method, for example:getLog().info("Hello world !");Are also available others priority level (like log4j framework).Apache Maven and Modello projectWhat isModello is a “Data Model toolkit” ( already used inside Maven.It is used only during the build time of your project (not a runtime)As input receives a model file (written with an xml-like sintax) and generates the Java pojo that respect itstructure (like Jaxb framework) .Why use Modello instead of existing similar tools? because, as state of his web site, "Modello was created toserve the needs of Apache Maven 2“.When use itSuppose that our plugin must receives in input many parameters that must be structured toreflect a precise relation between elements.Using the traditional <configuration> block is not applicable for obvious reasons.The solution is pass in input an xml file and read it from the Mojo.But must be validated and parsed to read his values ! This gap is filled by Modello.If your plugin need only few not structerd parameters, Modello is not neccessary, is enough the<configuration> block, so that you can skip this section.How use it and how it worksa) In the plugin source code tree:- Create a "model" file (.mdo) (see below) that describe the structure of the xml input file (think the .mdo file likean XSD). Usually that file is placed in a folder named "src/main/mdo/checks.mdo".- Configure the "modello-plugin" passing it the path of the .mdo file (See sections “Steps to create your first plugin”)- Build your plugin (mvn clean package)- During the build, Modello read the ".mdo" file and generates Java pojo that will be used to access at the inputxml from the Mojo. The pojo structure reflect the relations expressed in the .mdo file.The generated pojo will be placed by default in the folder "target/generated-sources/modello" of the project sourcetree (or in the one set inside <outputDirectory> configuration block of the modello-plugin).That folder will be added in automatic as a source folder of the Eclipse project (press F5 to refresh theworkspace).- Now in your Mojo we can use the Java pojo representing xml file content.- Modello creates also a class that act as dedicated “reader” for the xml content (See below for example).b) In the pom.xml of the project that uses our plugin:- configure the dependency to our plugin and his <configuration> section providing the relative path at input xml:<plugin><!-- out plugin data omitted for simplicity →<configuration><!-- This is the input file that will be mapped to java pojo previously created by Modello using .mdo file --><verificationFile>src/test/verifier/property-check-input.xml</verificationFile></configuration>.....<plugin>Obviously the provided xml file must be conform with the .mdo model, otherwise his content cant be mapped inthe previously generated java pojo and the build fail when our Mojo will be executed.When you build the project and our plugin Mojo will be executed, the content of the provided xml file will be
  7. 7. mapped in automatic in the Java pojo contained in the plugin (previously generated by Modello) and our pluginMojo can read xml content and perform his business logic.The xml reading is done using some readers generated by Modello when generates the pojo (See section “Using theModello readers from Mojo”).An example Modello fileThe following is a mdo file that describe the concept "a CheckBlock object has associated many Check".Translating it in xml file, we have one ore more <checkBlock>, with one or more child <check> blocks.<model><!-- unique identifier of this model (can be empty). --><id>propertty-checker-model</id><!-- the prefix assigned at the generated reader for xml input (In this case will be --><name>PropertyChecker</name><description>A description for our model file</description><!--A list of many <default> block, each one is composed of <key><value> pairThe values allowed inside the <key> block must be contained in a default list--><defaults><default><!-- this pair key-value specify the PACKAGE where put the generated java pojo(NOTE: the folder that contains it is specified in the "Modello plugin " configuration)--><key>package</key><value>org.apache.maven.plugin.verifier.model</value></default></defaults><!-- This section contains a set of <class> </class> blocks each one will be mapped in a java class --><classes><!-- The class that represents the most outer xml node must have the rootElement="true".The"xml.tagName" attribute state the name of the xml tag map this object: use it in case of you want use ajava class name different from the xml tag name.--><class rootElement="true" xml.tagName="propertyChecker"><name>PropertyChecker</name><version>1.0.0</version><description>Root element of the property checks file.</description><!-- The fields are the child nodes of the element --><fields><field><name>checkBlocks</name><version>1.0.0</version><description><![CDATA[ A block the contains the file location an the checks to performs on it. ]]></description><!-- Means that this type is associated with another type --><association xml.itemsStyle="wrapped"><!-- The type name CheckBlock is defined with another class (see below )--><type>CheckBlock</type><multiplicity>*</multiplicity></association></field></fields></class><!-- represents a "<checkBlock>" xml block --><class><name>CheckBlock</name><version>1.0.0</version><fields><field><name>checks</name><version>1.0.0</version><required>true</required><description><![CDATA[ The name of the check to apply at the properties file picked up from a default list ]]></description><association xml.itemsStyle="wrapped"><!-- Means that there is an xml tag with named "check" enveloped inside a tag named "Checks" --><type>Check</type><multiplicity>*</multiplicity></association></field></fields></class>
  8. 8. <!-- Represents a "check" xml block --><class><name>Check</name><version>1.0.0</version><fields><field><name>checkName</name><version>1.0.0</version><required>true</required><description><![CDATA[ The field description ]]></description><!-- See for supported data type --><type>String</type></field></fields></class></classes><model>Note the attribute xml.itemsStyle="wrapped" in the association. It means that the tag is enveloped in a tag with hissame name, but plural:<checks><check>....</check>....</checks>In case of a tag is not “enveloped”, use the option xml.itemsStyle="flat".This is an example xml file compliant with the .mdo file describe above:<propertyChecker><checkBlocks><checkBlock><checks><check><checkName>checkName1</checkName></check><check><checkName>checkName2</checkName></check></checks></checkBlock></checkBlocks></propertyChecker>If you look at the Java pojo generated by Modello you should find a class like this:public class CheckBlock {private List<Checks> Check;…....}This is the same concepts in the mdo file, but expressed with Java sintax.See: for more examples about model file.Using the Modello readers from MojoModello, together with the Java pojo, generates also an utility class that works as "java reader" for the xml input.His name follow the naming convention <prefix>Xpp3Reader where <prefix> is the value of the tag <name> of the .mdofile.Suppose that our Mojo receives in input a File (field "verificationFile" in the example below) and we want read it:......// "verificationFile" is a file path passed to the Mojo with the <configuration> blockReader reader = new FileReader(this.verificationFile);//Using the reader class generated by Modello is possible access at the provided input file
  9. 9. PropertiesCheckerNameXpp3Reader xppReader = new PropertiesCheckerNameXpp3Reader();// "PropertyChecker" is a pojo generated by Modello that corresponds at the most external tag in the input xml (ie <propertyChecker>)PropertyChecker propertyChecker = reader );......Now, using the "propertyChecker" getters methods, is possible access at the inner xml elements with an objectoriented manner.External linksFor more detailed informations about Maven plugin development, see: