How to integrate an operator

Table of Contents

1. Setting up the Maven Project Object Model File

You need to define the pom.xml. If you are a not familiar with maven as a build tool, it is recommended to read the Maven in 5 Minutes introduction. This will show you the parts of maven that you will need for building a SNAP plugin. In the following, only the parts are explained which you need to edit for your SNAP Operator Project.

For convenience, a complete pom.xml is attached. The important parts are briefly explained in the comments.

The last parameter of the configuration block 'requiresRestart' is particularly important in order to protect your users from a confusing error. It defines if SNAP asks for a restart after plugin installation. If this parameter is 'false' and no SNAP restart is done the user will run into an error when trying to launch the operator.

    <groupId>com.acme.snap.examples</groupId>                             	<!-- An ID which groups multiple artifacts (plugins) -->
    <artifactId>snap-engine-operator</artifactId>                  	        <!-- An ID for the artifact (plugin) -->
    <version>8.0.0-SNAPSHOT</version>                                     	<!-- The current version of the plugin -->
    <packaging>nbm</packaging>                                            	<!-- The value 'nbm' is mandatory to create a valid plugin for SNAP -->
    <name>SNAP Engine Operator Example</name>                      	        <!-- The name of the plugin -->
    <description>Example for a basic Operator module</description> 	        <!-- A description of the plugin -->

License information goes here:

<configuration>
    <licenseName>THE BEER-WARE LICENSE</licenseName>					<!-- The name of the license under which the plugin is distributed --> 
    <licenseFile>LICENSE.txt</licenseFile>								<!-- Path to the license file. Relative to the project directory -->
    <requiresRestart>true</requiresRestart>                             <!-- Defines if SNAP should restart after the plugin is installed. 
																			 It is recommended to leave it set to true -->
</configuration>

2. Add Help

To add a help entry for the processor operator, please see How to add help to a module.

3. Integrate into SNAP Desktop

To include the processor operator in the SNAP menu, the following needs to be set in resources/layer.xml. This will create a Default GUI for your operator.

layer.xml
<filesystem>

    <folder name="Actions">
        <folder name="Operators">
            <!-- replace the dot by '-' -->
            <file name="<Operator-package>-Action.instance">
                <attr name="instanceCreate" methodvalue="org.openide.awt.Actions.alwaysEnabled"/>
                <attr name="delegate" methodvalue="org.esa.snap.core.gpf.ui.DefaultOperatorAction.create"/>
                <attr name="displayName" stringvalue="<Menu display name>"/>
                <attr name="dialogTitle" stringvalue="<Dialog title name>"/>
                <attr name="operatorName" stringvalue="<Operator class name>"/>
                <attr name="helpId" stringvalue="<HelpId existent in the map.jhm>"/>
                <attr name="shortDescription" stringvalue="<A concise description of the operator>"/>
                <attr name="targetProductNameSuffix" stringvalue="_<short suffix identifying the processor>"/>
            </file>
        </folder>
    </folder>
    <folder name="Menu">
        <!-- The folder structure here defines where the Processor will show up in the SNAP Desktop application -->
        <folder name="Optical"> <!-- main menu -->
            <folder name="Thematic Water Processing"> <!-- sub menu -->
                <file name="<Operator-package>-Action.shadow">
                    <attr name="originalFile" stringvalue="Actions/Operators/<Operator-package>-Action.instance"/>
                    <attr name="position" intvalue="10000"/> <!-- intvalue defines the position of the menu item within the sub-menu
                </file>
            </folder>
        </folder>
    </folder>
    <folder name="Services">
        <folder name="JavaHelp">
            <file name="<groupId>-<artifactId>-helpset.xml" url="helpset.xml">
                <attr name="position" intvalue="4600"/>
            </file>
        </folder>
    </folder>
</filesystem>

Note

<Operator-package> is the fully qualified package path of the operator.
<groupId> is the maven groupId
<artifactId> is the maven artifactId

This file defines the action which will invoke the GUI of the operator and it also defines the location of the action within the menu. The Actions folder must contain another folder named Operators. Inside this, the Operator Action and its attributes are defined. As a collector for the attributes, a file is created and this file receives the name that is specified here. You are free to choose any name as long as it is unique and ends with the suffix .instance. The attributes instanceCreate and delegate should always have the same value as in the example. The attribute operatorName is used to specify which GUI of which operator shall be called. This value must be the same as the alias specified in the info.xml file. The displayName defines the text of the menu entry and dialogTitle defines, as the name indicates, the title of the dialog. The attribute ShortDescription is a short description of the operator and used as tool-tip. If not specified otherwise by the user, the target product files will get the suffix specified by targetProductNameSuffix. This suffix is prepended before the file extension. The attribute helpId specifies the id of the help page set up in the previous step.

In the folder Menu, the menu structure of SNAP is reproduced. For each folder in the menu, you create also a folder here with the same name. The final menu entry is then represented by a file which refers to the file defined above in the Actions section. Here it should end with the suffix .shadow. The originalFile attribute links to the above-defined action by using the full path to it. The position attribute defines the position of the entry within its parent folder.

Based on the definitions in the info.xml file, a generic GUI is created for the operator when the defined menu entry is clicked. This comes right out of the box and without the need for additional GUI design work.

In case you would like to have a more sophisticated GUI (because, for example, you have complex parameters), you can create your own GUI. For this, you will need to sub-class AbstractSnapAction.

4. Service Registration

Operator implementations are published via the Java service provider interface (SPI). A JAR publishes its operators in the resource file src/resources/META-INF/services/org.esa.snap.core.gpf.OperatorSpi.

Edit the file. For each operator SPI in the module you need to add its fully qualified class name on a single line (no spaces, no word wrapping). If the SPI is an inner class, please note that the last separator is a '$', for example: org.esa.s3tbx.fu.FuOp$Spi . It is a common practice in SNAP to include the SPI as an inner class of the operator class.

In your Operator package, add a class to extend the OperatorSpi interface. This class may also serve as a factory for new operator instances.


Operator SPI
public static class Spi extends OperatorSpi {
	public Spi() {
		super(FuOp.class);
	}
}

5. Add extra module metadata

Most of your module's metadata is derived from your project's Maven pom.xml file. However, some so called module "manifest-headers" cannot be derived from it (e.g. the location of the layer.xml file), so we have to provide them in a special configuration file. 

In the source directory src/main/nbm, edit file manifest.mf with following content:

manifest.mf
Manifest-Version: 1.0
AutoUpdate-Show-In-Client: true
AutoUpdate-Essential-Module: false
OpenIDE-Module-Java-Dependencies: Java > 1.8
OpenIDE-Module-Layer: layer.xml
OpenIDE-Module-Requires: org.netbeans.api.javahelp.Help

6. Build the Plugin

In order to build the plugin, you need to switch to the command line of your operating system or use the terminal provided within your IDE. Navigate to the project directory and execute the maven command: mvn clean package .The command will clean the output of previous builds and then package the plugin. It will create a new directory target beneath the project directory. Inside this folder, you will find an NBM file. It follows the naming convention <artifactId>-<version>.nbm. Both variables are defined in the pom.xml

This is the plugin you can hand over to other people if they want to use your plugin. They can install it via the Plugin Manager in SNAP.

7. Use your operator in SNAP

After you have set up and implemented the first runnable version of the operator and you can build the plugin with maven, you can configure SNAP to use the build output automatically. 

Inside the target folder, there is the so-called NetBeans cluster created. It is located at <PROJECT_DIR>\my-project\target\nbm\netbeans\<cluster>. The name of the cluster is specified in the pom.xml (see above). You can tell SNAP to use this cluster directory as an additional directory where to look for plugins.

To do so you need to edit the snap.conf file in the etc folder of the installation directory of SNAP. 

Open the file in a text editor (with admin rights) and provide the path to the cluster as value to the extra_cluster property. It should then look like:


# Extra cluster paths separated by semicolon ';'
extra_clusters="<PROJECT_DIR>\snap-zarr\target\nbm\netbeans\<cluster>"

Don't forget to remove the '#' at the beginning of the line. Otherwise, the change will not be active. If you have made any changes to the code, you will need to repeat Step 6 in order to see your changes in effect.