Work process automation with NetBeans custom modules and Maven Inoker.

June 6, 20160

article_automation

As a company we are oriented to help our customers to improve their business processes with our software solution. As a development team, we are often busy with projects which are not visible to our beneficiaries but are highly valuable for the QA and development process itself. This article will describe our internal experience in work process automation, ideas and tools which we are using to decrease delivery time of our solutions.


Prehistory…

The background for this article is a true story related to our needs to speed up the development process by automation of several manual steps. For example, we came up with a “Template” solution to populate our databases with data for static configurations and dynamic business entities. The solution would be to write JSF .xhmtl files which through a custom maven goal are converted to .xml files. These in turn, are processed by the liquidbase framework to create DML statements which are run over the target database. JSF gives us the power of reusable composite components, interfaces, inheritance, control statements and loops, and autocomplete in our IDE. As a result, we can create an order with thousands of articles with a few lines of JSF DSL, and we don’t have to worry about schema structure, relations between tables and constraints.

The “Templates” work excellently, they were perfectly integrated with our Automated Testing framework. But there is still a quite inconvenient step in this whole happy story … In order to load a template into database manually we have to run a maven goal manually, and we have to pass the desired files as parameters

Example:

custome:loadtemplates -Dloadtemplates.templates="ILC-001@inther-module-abc:TRUNK-SNAPSHOT,ILC-002@inther-module-abc:TRUNK-SNAPSHOT"

This line will process 2 templates ILC-001.xhtml and ILC-002.xhtml, but there is an internal technical reason to specify the module and the version tag. We are used to save maven goals as custom action in NetBeans and to execute them with 2 mouse clicks. But it won’t work for this specific purpose since we are forced to specify the .xhtml files as parameters. And it’s quite annoying to run them manually, even if you do it from a NetBeans window. And it looks like an unfinished solution and the final users (testers and developers) won’t fully appreciate the amount of work that was done.

The first (and I think the best) idea was to develop a custom NetBeans plugin. I’ve read about the NetBeans platform and I knew that there were options to create new actions for the context menu for every NetBeans node. The plugin was a success in our company because it added the cherry on the pie with the complete automation of templates loading. This article will show the complete process of the NetBeans plugin development since I think this is the worst documented topic from all the technologies specified above.

Start

The requirements for this task might sound like this:
“As a user, I want to select one or more .xhtml files, and run a maven goal which will use them as parameters”

Development process

1) Create a maven based “NetBeans Module” project. I recommend you to use maven since it will help you in solving the dependencies required.

img1

2) Fill in the project name, location and maven artifact description.

img2

3) Select the target NetBeans version.

img3

Our project is ready. Now clean and build the application to download all dependencies related to the NetBeans platform.

4) We have to create a new Action, we can place it in main package. An “Action”, is a kind of a process triggered by the user with a mouse click or pressing some keys. If you don’t find “Actions…” in your list, look in the “Other…” window.

img4

5) We want to trigger our Action from the context menu when we perform the left click on an .xhtml file in our project. Check the radio boxes as in the following image to filter the nodes and to allow multiple selection for our Action.

img5

6) The next step is to specify the file type for which it should be enabled, and the position (order) in the context menu.

img6

7) Now we can specify the class name and the display name, the last one will appear in the context menu.

img7

8) The result should look like this.

img8

So we’ve just created our first Action, but it doesn’t do anything and we can’t even see what it looks like in the context menu.

9) Let’s create another simple maven project to test our Action on.

img9

10) Add several .xhtml files to the XhtmlContainer project.

img10

11) Now we are ready to run the NetBeans module. This will start a new NetBeans instance.

img11

12) In the started IDE, open the XhtmlContainer project, navigate to .xhtml files and right click one of them. You should see your new context menu item.

img12

13) Now we only need to add the code to the XhtmlLoader class. Let’s create new perform Action() method which will do the proposed job. The method will be called in a separated thread when an ActionEvent will be raised. The call in a separated thread is necessary because the maven goal might be executed in more than 1 second, and we don’t want to keep the context menu until the execution is complete. Here is the full class with comments as description:

package com.isd.mavengoalrunner;
 import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.util.Arrays;
import java.util.List;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.shared.invoker.DefaultInvocationRequest;
import org.apache.maven.shared.invoker.DefaultInvoker;
import org.apache.maven.shared.invoker.InvocationRequest;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.openide.loaders.DataObject;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionRegistration;
import org.openide.filesystems.FileObject;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.IOProvider;
import org.openide.windows.InputOutput;
@ActionID(category = "Bugtracking", id = "com.isd.mavengoalrunner.XhtmlLoader")
@ActionRegistration(displayName = "#CTL_XhtmlLoader")
@ActionReference(path = "Loaders/text/xhtml/Actions", position = 2100)
@Messages("CTL_XhtmlLoader=Load to database")
public final class XhtmlLoader implements ActionListener
{
  private final List<DataObject> context;
  public XhtmlLoader(List<DataObject> context)
 {
   this.context = context;
  }
  @Override
  public void actionPerformed(ActionEvent ev)
  {
    Runnable task = ()
            ->
            {
              try
              {
                performAction();
              }
              catch (Exception ex)
              {
                ex.printStackTrace();
              }
    };
    new Thread(task).start();
  }
  private void performAction() throws Exception
  {
    //Get info about the project project.
    //I know for sure that context has at least 1 element, otherwise the action wouldn't be rised.
    FileObject file = context.get(0).getPrimaryFile();
    Project project = FileOwnerQuery.getOwner(file);
    String projectPath = project.getProjectDirectory().getPath();

    //Read pom.xml of the target project.
    File pomFile = new File(projectPath + "/pom.xml");
    MavenXpp3Reader reader = new MavenXpp3Reader();
    Model model = reader.read(new FileInputStream(pomFile));

    //Get artifactId and version from pom file.
    final String artifactId = model.getArtifactId();
    final String version = model.getVersion();

    //Prepare InvocationRequest, set maven goal and properties
    InvocationRequest request = new DefaultInvocationRequest();
    request.setPomFile(pomFile);
    request.setGoals(Arrays.asList("install"));
    /*
    // uncomment in case you need to set some properties
    Properties properties = new Properties();
    properties.setProperty("name", "value");
    request.setProperties(properties);
     */

    //Prepare DefaultInvoker which will execute the request.
    DefaultInvoker invoker = new DefaultInvoker();

    //Forward maven output to Netbeans's output window.
    final InputOutput io = IOProvider.getDefault().getIO("Maven Runner", true);
    io.select();
    invoker.setOutputHandler(line -> io.getOut().println(line));
    invoker.setErrorHandler(line -> io.getErr().println(line));

    //output some usefoul information.
    io.getOut().println("Project name: " + artifactId);
    io.getOut().println("Project version: " + version);
    context.forEach(e -> io.getOut().println("Selected file: " + e.getName()));
    io.getOut().println();

    //trigger the execution
    try
    {
      invoker.execute(request);
    }
    finally
    {
      io.getOut().close();
      io.getErr().close();
    }
  }
}

14) These are the required dependencies.

<dependencies>
    <dependency>
      <groupId>org.netbeans.api</groupId>
      <artifactId>org-netbeans-api-annotations-common</artifactId>
      <version>RELEASE81</version>
    </dependency>
    <dependency>
      <groupId>org.netbeans.api</groupId>
      <artifactId>org-openide-util</artifactId>
      <version>RELEASE81</version>
    </dependency>
    <dependency>
      <groupId>org.netbeans.api</groupId>
      <artifactId>org-openide-awt</artifactId>
      <version>RELEASE81</version>
    </dependency>
    <dependency>
      <groupId>org.netbeans.api</groupId>
      <artifactId>org-openide-loaders</artifactId>
      <version>RELEASE81</version>
    </dependency>
    <dependency>
      <groupId>org.netbeans.api</groupId>
      <artifactId>org-netbeans-modules-projectapi</artifactId>
      <version>RELEASE81</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.netbeans.api</groupId>
      <artifactId>org-openide-filesystems</artifactId>
      <version>RELEASE81</version>
    </dependency>
    <dependency>
      <groupId>org.apache.maven.shared</groupId>
      <artifactId>maven-invoker</artifactId>
      <version>2.1.1</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.netbeans.api</groupId>
      <artifactId>org-openide-io</artifactId>
      <version>RELEASE81</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-project</artifactId>
      <version>3.0-alpha-2</version>
      <type>jar</type>
    </dependency>
  </dependencies>

When the code is done, and the project compiles, you can run it and test it. When you select the “Load to database” option from the context menu, you should get the following output.

img13

Leave a Reply

Your email address will not be published.

https://isd-soft.com/wp-content/uploads/2022/08/whitelogo150.png
Connect with us
Bulgara Street 33/1, Chisinau MD-2001, Moldova
+ 373 22 996 170
info@isd-soft.com
De Amfoor 15, 5807 GW Venray-Oostrum, The Netherlands
+ 31 478 502944

Subscribe to our newsletter today to receive updates on the latest news, releases and special offers.

Copyright ©2022, ISD. All rights reserved | Cookies Policy | Privacy Policy