Creare un plugin con Maven

Maven è uno dei sistemi di build più noti per lo sviluppo di applicazioni Java. Aggiungere un comportamento specifico per la nostra build può essere più semplice di quello che sembra, creando quello che viene chiamato plugin.

I plugin Maven sono quella miriade di programmi che vengono utilizzati durante la fase di build per fare tutto ciò che gira intorno a questa fase, come quelli riportati per esempio nella seguente lista

  1. maven-surefire-plugin : plugin che viene utilizzato per default nella fase di esecuzione degli unit test
  2. maven-resources-plugin : gestisce la copia/filtraggio/esclusione delle risorse (cartelle resources) del nostro progetto
  3. maven-jar-plugin : crea il pacchetto jar
  4. maven-war-plugin : crea il pacchetto war
  5. maven-release-plugin : utilizzato per effettuare i rilasci nel proprio SCM e preparare il relativo tag

In realtà proprio lo stesso Maven può essere considerato come una serie di plugin messi tutti insieme che permettono di gestire tutto il ciclo di vita di un progetto. Il componente base di ogni plugin, ovvero la singola classe che verrà eseguita a fronte di uno specifico goal, è quello che viene chiamato MOJO (come POJO con Maven al posto di Plain). Esistono moltissimi plugin ma può capitare che ci serva quel comportamento particolare a cui nessuno ha mai pensato e quindi conviene capire come realizzarne uno nostro.

Generazione

Per generare lo scheletro del nostro plugin Maven possiamo ovviamente usare un plugin Maven 🙂
Esiste infatti il plugin archetype che può essere usato da riga di comando per generare per il progetto di partenza per il nostro plugin

federico@home:~$ mvn archetype:generate  -DarchetypeArtifactId=maven-archetype-plugin
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] >>> maven-archetype-plugin:3.0.1:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO] 
[INFO] <<< maven-archetype-plugin:3.0.1:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO] 
[INFO] --- maven-archetype-plugin:3.0.1:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[WARNING] No archetype found in remote catalog. Defaulting to internal catalog
Define value for property 'groupId': com.javastaff
Define value for property 'artifactId': simple-maven-plugin
Define value for property 'version' 1.0-SNAPSHOT: : 
Define value for property 'package' com.javastaff: : 
Confirm properties configuration:
groupId: com.javastaff
artifactId: simple-maven-plugin
version: 1.0-SNAPSHOT
package: com.javastaff
 Y: : 
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-plugin:1.0
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: basedir, Value: /
[INFO] Parameter: package, Value: com.javastaff
[INFO] Parameter: groupId, Value: com.javastaff
[INFO] Parameter: artifactId, Value: simple-maven-plugin
[INFO] Parameter: packageName, Value: com.javastaff
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: /simple-maven-plugin
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 14.598 s
[INFO] Finished at: 2017-10-13T09:28:11+02:00
[INFO] Final Memory: 13M/170M
[INFO] ------------------------------------------------------------------------
federico@home:~$

 

In questo modo viene generato un progetto Maven dove è presente il seguente POM

 

<project xmlns="http://maven.apache.org/POM/4.0.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.javastaff</groupId>
  <artifactId>simple-maven-plugin</artifactId>
  <packaging>maven-plugin</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>simple-maven-plugin Maven Mojo</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-plugin-api</artifactId>
      <version>2.0</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

 

e il MOJO d’esempio MyMojo che se richiamato crea un file txt nella directory di output del progetto

 

/*
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

/**
 * Goal which touches a timestamp file.
 *
 * @goal touch
 * 
 * @phase process-sources
 */
public class MyMojo
    extends AbstractMojo
{
    /**
     * Location of the file.
     * @parameter expression="${project.build.directory}"
     * @required
     */
    private File outputDirectory;

    public void execute()
        throws MojoExecutionException
    {
        File f = outputDirectory;

        if ( !f.exists() )
        {
            f.mkdirs();
        }

        File touch = new File( f, "touch.txt" );

        FileWriter w = null;
        try
        {
            w = new FileWriter( touch );

            w.write( "touch.txt" );
        }
        catch ( IOException e )
        {
            throw new MojoExecutionException( "Error creating file " + touch, e );
        }
        finally
        {
            if ( w != null )
            {
                try
                {
                    w.close();
                }
                catch ( IOException e )
                {
                    // ignore
                }
            }
        }
    }
}

 

Nei commenti della classe troviamo i metadati che servono per qualificare meglio il nostro plugin ovvero

  1. goal: specifica il goal con il quale potremo richiamare questo MOJO
  2. phase: la fase di default a cui verrà associato questo goal, che potrebbe anche essere sovrascritta

A questa classe d’esempio viene inoltre passata l’informazione relativa alla build directory, utilizzando anche in questo caso un metadato @parameter expression=”${project.build.directory}” riportato prima della variabile che conterrà quest’informazione. Proviamo quindi a compilare il progetto e successivamente a richiamare il plugin da riga di comando con la sintassi mvn GROUPID:ARTIFACTID:GOAL

 

federico@home:~/simple-maven-plugin$ mvn com.javastaff:simple-maven-plugin:touch
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building simple-maven-plugin Maven Mojo 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- simple-maven-plugin:1.0:touch (default-cli) @ simple-maven-plugin ---
[INFO] outputDirectory /simple-maven-plugin/target
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.289 s
[INFO] Finished at: 2017-10-13T09:31:56+02:00
[INFO] Final Memory: 6M/119M
[INFO] ------------------------------------------------------------------------

 

Andando nella directory di output troveremo il file touch.txt creato dal nostro plugin.

 

Mojo con annotation

Le stesse informazioni che vengono gestite tramite i metadati che abbiamo visto nell’esempio base, possono essere specificate usando alcune annotation. Per fare ciò prima di tutto dobbiamo importare la nuova dipendenza nel progetto

<dependency>
	<groupId>org.apache.maven.plugin-tools</groupId>
	<artifactId>maven-plugin-annotations</artifactId>
	<version>3.2</version>
	<scope>provided</scope>
</dependency>

 

poi vediamo un esempio, banale più o meno come il precedente, dove andiamo a stampare un Hello World

 

package com.javastaff;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

@Mojo(name = "hello")
public class HelloMojo extends AbstractMojo {

    @Parameter(property = "msg",defaultValue = "dal plugin")
    private String msg;

    public void execute() throws MojoExecutionException {
        getLog().info("Ciao " + msg);
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

}

 

Abbiamo definito solo il goal utizzando l’annotation Mojo e invece per quanto riguarda la variabile msg prendiamo il valore definito dalla variabile d’ambiente msg, ma abbiamo definito anche un valore di default. Provando quindi a lanciare il progetto prima senza nessun parametro e poi con un semplice parametro passato dalla console (avremmo lo stesso risultato anche definendo il plugin in un altro progetto e inserendo nella configurazione la variabile msg)

 

federico@home:~/simple-maven-plugin$ mvn com.javastaff:simple-maven-plugin:hello
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building simple-maven-plugin Maven Mojo 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- simple-maven-plugin:1.0-SNAPSHOT:hello (default-cli) @ simple-maven-plugin ---
[INFO] Ciao dal plugin
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.239 s
[INFO] Finished at: 2017-10-13T15:12:13+02:00
[INFO] Final Memory: 6M/119M
[INFO] ------------------------------------------------------------------------
federico@home:~/simple-maven-plugin$ mvn com.javastaff:simple-maven-plugin:hello -Dmsg="dalla console"
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building simple-maven-plugin Maven Mojo 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- simple-maven-plugin:1.0-SNAPSHOT:hello (default-cli) @ simple-maven-plugin ---
[INFO] Ciao dalla console
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.212 s
[INFO] Finished at: 2017-10-13T15:12:20+02:00
[INFO] Final Memory: 6M/119M
[INFO] ------------------------------------------------------------------------

 

Generare ulteriori risorse

Per provare a creare qualcosa di leggermente più complesso di un Hello World possiamo vedere il seguente MOJO. Maven già all’interno del MANIFEST riporta alcune informazioni relative alla build, però potremmo aver bisogno di riportare alcune informazioni custom che potrebbero esserci utili in seguito.

package com.javastaff;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

@Mojo(name = "version",
	  defaultPhase=LifecyclePhase.GENERATE_RESOURCES)
public class VersionMojo extends AbstractMojo {

	@Parameter(property = "project.build.directory")
	private String outputDirectory;

    public void execute() throws MojoExecutionException {
    	File f=new File(outputDirectory+"/classes/version.txt");
    	try {
    		String username = System.getProperty("user.name");
    		
    		String osArch = System.getProperty("os.arch");
    		String osName = System.getProperty("os.name");
    		String osVersion = System.getProperty("os.version");
    		
    		SimpleDateFormat sdf=new SimpleDateFormat("dd-MM-yyyy HH:mm:ssSS");
		PrintWriter pw=new PrintWriter(f);
		pw.print("Build time: ");
		pw.print(sdf.format(new Date()));
		pw.print(" - Build user: ");
		pw.println(username);
		pw.println("Build Os: "+osArch+" "+osName+" "+osVersion);
		pw.flush();
		pw.close();
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	}
    }

}

 

In questo esempio definiamo la fase di default in cui verrà lanciato il MOJO (generate resources) e andiamo semplicemente a create un file testuale con qualche informazione che finirà nel pacchetto finale. Ora per utilizzare questo plugin in un nostro progetto possiamo configurarlo nel seguente modo

 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.javastaff</groupId>
	<artifactId>test-maven</artifactId>
	<packaging>jar</packaging>
	<version>1.0-SNAPSHOT</version>

	<build>
	   <plugins>
		<plugin>
		   <groupId>com.javastaff</groupId>
		   <artifactId>simple-maven-plugin</artifactId>
		   <version>1.0-SNAPSHOT</version>
			<executions>
			   <execution>
				<id>version</id>
				<goals>
				   <goal>version</goal>
				</goals>
			   </execution>
			</executions>
		</plugin>
	</plugins>
   </build>
</project>

 

Nel progetto Github di seguito è possibile vedere questi semplici esempi che sono stati riportati, ma ovviamente per creare degli esempi più interessanti dovreste pensare principalmente alle necessità che avete nei vostri progetti e cercare di riportarle all’interno si un semplice plugin Maven

 

Federico Paparoni

Looking for a right "about me"...

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.