📜 ⬆️ ⬇️

Writing Maven Plugin

I have a profile on some maven projects that is used to copy the shared libraries and then restart the Tomcat server.
Maven profile
<profile> <id>deploy-deps</id> <build> <plugins> <plugin> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <useSubDirectoryPerScope>true</useSubDirectoryPerScope> <excludeGroupIds>  ,   war-</excludeGroupIds> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <executions> <execution> <id>05-stop-tomcat</id> <phase>package</phase> <goals> <goal>exec</goal> </goals> <configuration> <arguments> <argument>-ssh</argument> <argument>-4</argument> <argument>-agent</argument> <argument>-i</argument> <argument>${putty.key}</argument> <argument>${ssh.user}@${ssh.host}</argument> <argument>${tomcat.dir.root}/bin/shutdown.sh</argument> </arguments> <executable>plink</executable> </configuration> </execution> <execution> <id>10-clean-shared-jars</id> <phase>package</phase> <goals> <goal>exec</goal> </goals> <configuration> <arguments> <argument>-ssh</argument> <argument>-4</argument> <argument>-agent</argument> <argument>-i</argument> <argument>${putty.key}</argument> <argument>${ssh.user}@${ssh.host}</argument> <argument>rm</argument> <argument>-Rf</argument> <argument>${tomcat.dir.shared}/*.jar</argument> </arguments> <executable>plink</executable> </configuration> </execution> <execution> <id>15-upload-shared-jars</id> <phase>package</phase> <goals> <goal>exec</goal> </goals> <configuration> <arguments> <argument>-scp</argument> <argument>-4</argument> <argument>-agent</argument> <argument>-i</argument> <argument>${putty.key}</argument> <argument>${project.build.directory}/dependency/compile/*.jar</argument> <argument>${ssh.user}@${ssh.host}:${tomcat.lib.shared}/</argument> </arguments> <executable>pscp</executable> </configuration> </execution> <execution> <id>20-start-tomcat</id> <phase>package</phase> <goals> <goal>exec</goal> </goals> <configuration> <arguments> <argument>-ssh</argument> <argument>-4</argument> <argument>-agent</argument> <argument>-i</argument> <argument>"${putty.key}"</argument> <argument>${ssh.user}@${ssh.host}</argument> <argument>bin/startup.sh</argument> </arguments> <executable>plink</executable> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile> 

moving aside, tell for what this profile
In part of the projects used a bunch of Nginx + Tomcat. The following is implemented for this bundle:
  1. For all static content, a certain directory outside the webapps is used. Nginx "looks" into this directory and gives it via the "/ static / *" web path
  2. All shared java libraries (rarely modified) are loaded into the $ {catalina.home} / shared directory, and the variable “shared.loader” is configured for this in Tom / jatcat in the conf / catalina.properties file
  3. A separate system user has been created for each Tomcat instance.
  4. For access via SSH keys are used and each developer has his own

Accordingly, loading static content and shared libraries are separate profiles. Everything else is collected in the war-archive and installed via the standard Tomcat web-manager.
And in order not to produce configurations, PAgent is used, in which the private keys we need are already added. They are used to connect via Putty.

It lays down the profile in pom.xml, does not seem to bite, even plows slowly for the benefit of the programmer, but only there are a couple of "minuses" in it - it takes a lot of space when pom.xml is deployed and you also have to insert it into new projects.
And if you can get rid of the second minus by writing a template in your favorite_IDE or navigating your archetype , it is not so easy to save from the first minus.

Is it not so easy? can we “wrap” this profile as a plugin for Maven? No sooner said than done.

Step 1. Create a draft project for the maven plugin


in which we indicate the type of assembly “maven-plugin”. We will also need dependencies:
1) org.apache.maven.plugin-tools:maven-plugin-annotations for the possibility of specifying Mojo classes not through JavaDoc, but using annotations
2) org.twdata.maven:mojo-executor to be able to run other plugins from ours.
While dependencies are enough - it's time to start the actual implementation of the Mojo class itself.
commit

Step 2. We write Mojo-class


Class blank
 @Mojo(name = "deploy-deps", defaultPhase = LifecyclePhase.PROCESS_SOURCES, threadSafe = true) public class DeployDepsMojo extends AbstractMojo { @Component protected MavenProject mavenProject; @Component protected MavenSession mavenSession; @Component protected BuildPluginManager pluginManager; protected MojoExecutor.ExecutionEnvironment _pluginEnv; @Override public void execute() throws MojoExecutionException, MojoFailureException { _pluginEnv = executionEnvironment(mavenProject, mavenSession, pluginManager); } } 


commit
')
We will need to generate mojo tags from annotations ( commit ):
Class blank
 <build> <!-- ... --> <plugins> <plugin> <artifactId>maven-plugin-plugin</artifactId> <executions> <execution> <id>help-goal</id> <goals> <goal>helpmojo</goal> </goals> </execution> <execution> <id>mojo-descriptor</id> <goals> <goal>descriptor</goal> </goals> </execution> </executions> <configuration> <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound> </configuration> </plugin> </plugins> </build> 


Add copying dependencies
It was
 <plugin> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <useSubDirectoryPerScope>true</useSubDirectoryPerScope> <excludeGroupIds>  ,   war-</excludeGroupIds> </configuration> </execution> </executions> </plugin> 

has become
 @Mojo(name = "deploy-deps", requiresDependencyResolution = ResolutionScope.TEST, defaultPhase = LifecyclePhase.PROCESS_SOURCES, threadSafe = true) public class DeployDepsMojo extends AbstractMojo { // ... @Override public void execute() throws MojoExecutionException, MojoFailureException { _pluginEnv = executionEnvironment(mavenProject, mavenSession, pluginManager); copyDependencies(); } private void copyDependencies() throws MojoExecutionException { // TODO expects corrections https://github.com/TimMoore/mojo-executor/issues/18 Plugin pluginDependency = plugin("org.apache.maven.plugins", "maven-dependency-plugin", "2.8"); final Xpp3Dom cfg = configuration(element(name("useSubDirectoryPerScope"), "true")); executeMojo(pluginDependency, goal("copy-dependencies"), cfg, _pluginEnv); } } 

commit
Briefly:
  1. "RequiresDependencyResolution = ResolutionScope.TEST" is required to get the list of dependencies - without this, the maven-dependency-plugin plugin will not copy them
  2. "ThreadSafe = true" indicates that this Mojo can be run in a separate thread - it is self-sufficient
  3. The static executeMojo method allows you to execute any goal for any available plug-in describing the environment configuration. In this case, the environment remains the same (_pluginEnv variable)

Add a method to stop the tomcat server
It was
 <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <executions> <execution> <id>05-stop-tomcat</id> <phase>package</phase> <goals> <goal>exec</goal> </goals> <configuration> <arguments> <argument>-ssh</argument> <argument>-4</argument> <argument>-agent</argument> <argument>-i</argument> <argument>${putty.key}</argument> <argument>${ssh.user}@${ssh.host}</argument> <argument>${tomcat.dir.root}/bin/shutdown.sh</argument> </arguments> <executable>plink</executable> </configuration> </execution> <!-- ... --> </executions> </plugin> 

has become
 public class DeployDepsMojo extends AbstractMojo { public static final String PLG_EXEC_CFG_ARGUMENTS = "arguments"; public static final Xpp3Dom PLG_EXEC_CFG_EXEC_PLINK = element(name("executable"), "plink").toDom(); public static final String PLG_EXEC_GOAL_EXEC = goal("exec"); public static final String PLG_EXEC_PROTOCOL_SSH = "-ssh"; // ... @Override public void execute() throws MojoExecutionException, MojoFailureException { _pluginEnv = executionEnvironment(mavenProject, mavenSession, pluginManager); _pluginExec = plugin("org.codehaus.mojo", "exec-maven-plugin", "1.2.1"); copyDependencies(); tomcatShutdown(); } private void tomcatShutdown() throws MojoExecutionException { Xpp3Dom cfg = getBaseConfigExec(PLG_EXEC_PROTOCOL_SSH); final Xpp3Dom arguments = cfg.getChild(PLG_EXEC_CFG_ARGUMENTS); arguments.addChild(element(name("argument"), "${ssh.user}@${ssh.host}").toDom()); arguments.addChild(element(name("argument"), "bin/shutdown.sh").toDom()); cfg.addChild(PLG_EXEC_CFG_EXEC_PLINK); executeMojo(_pluginExec, PLG_EXEC_GOAL_EXEC, cfg, _pluginEnv); } private Xpp3Dom getBaseConfigExec(String protocol) { final Element el0 = element(name("argument"), protocol); final Element el1 = element(name("argument"), "-4"); final Element el2 = element(name("argument"), "-agent"); final Element el3 = element(name("argument"), "-i"); final Element el4 = element(name("argument"), "${putty.key}"); return configuration(element(name(PLG_EXEC_CFG_ARGUMENTS), el0, el1, el2, el3, el4)); } } 


Add the remaining methods

By analogy with the previous paragraph, we add methods for remotely cleaning the tomcat.lib.shared directory, copying new libraries into it and then starting the Tomcat server.
commit

Step 3. Install the plugin in the repository and edit the configuration of the Maven project


Installing the plug-in to the local repository is done with a simple “mvn clean install” command.

And rule the configuration of the project:
It was
 <profile> <id>deploy-deps</id> <build> <plugins> <plugin> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <useSubDirectoryPerScope>true</useSubDirectoryPerScope> <excludeGroupIds>  ,   war-</excludeGroupIds> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <executions> <execution> <id>05-stop-tomcat</id> <phase>package</phase> <goals> <goal>exec</goal> </goals> <configuration> <arguments> <argument>-ssh</argument> <argument>-4</argument> <argument>-agent</argument> <argument>-i</argument> <argument>${putty.key}</argument> <argument>${ssh.user}@${ssh.host}</argument> <argument>${tomcat.dir.root}/bin/shutdown.sh</argument> </arguments> <executable>plink</executable> </configuration> </execution> <execution> <id>10-clean-shared-jars</id> <phase>package</phase> <goals> <goal>exec</goal> </goals> <configuration> <arguments> <argument>-ssh</argument> <argument>-4</argument> <argument>-agent</argument> <argument>-i</argument> <argument>${putty.key}</argument> <argument>${ssh.user}@${ssh.host}</argument> <argument>rm</argument> <argument>-Rf</argument> <argument>${tomcat.dir.shared}/*.jar</argument> </arguments> <executable>plink</executable> </configuration> </execution> <execution> <id>15-upload-shared-jars</id> <phase>package</phase> <goals> <goal>exec</goal> </goals> <configuration> <arguments> <argument>-scp</argument> <argument>-4</argument> <argument>-agent</argument> <argument>-i</argument> <argument>${putty.key}</argument> <argument>${project.build.directory}/dependency/compile/*.jar</argument> <argument>${ssh.user}@${ssh.host}:${tomcat.lib.shared}/</argument> </arguments> <executable>pscp</executable> </configuration> </execution> <execution> <id>20-start-tomcat</id> <phase>package</phase> <goals> <goal>exec</goal> </goals> <configuration> <arguments> <argument>-ssh</argument> <argument>-4</argument> <argument>-agent</argument> <argument>-i</argument> <argument>"${putty.key}"</argument> <argument>${ssh.user}@${ssh.host}</argument> <argument>bin/startup.sh</argument> </arguments> <executable>plink</executable> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile> 

has become
 <profile> <id>deploy-deps</id> <build> <plugins> <plugin> <groupId>info.alenkov.tools.maven</groupId> <artifactId>tomcat7-ewar-plugin</artifactId> <executions> <execution> <phase>process-sources</phase> <goals> <goal>deploy-deps</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </profile> 

The description of the maven project can be simplified if you delete the “deploy-deps” profile and call the goal directly:
mvn clean process-sources tomcat7-ewar:deploy-deps

At this, the process of improving the readability of the maven-project and the removal of the often used "outward" is completed ( final commit for this post) - the maven-project was reduced by 106 lines. There is more code optimization, adding parameters, and much more, but this is another story that I will ever tell the audience.

UPD: Published a plugin in your repository . I plan to develop it further, therefore, if there are suggestions / comments, I accept them on github .
UPD: Updated plugin to version 1.1
UPD: Updated plugin to version 2.0 - got rid of using Putty

Source: https://habr.com/ru/post/205118/


All Articles