Creating a simple plugin

In the previous recipe we saw the site plugin, but ElasticSearch allows creating a most powerful type of plugin, the native JAR ones.

Native plugins allow extending several aspects of the ElasticSearch server, but they require good Java knowledge. Because they are compiled in native JVM, they are generally very fast.

In this recipe we will see how to set up a system to develop native plugins.

Getting ready

You need a working ElasticSearch node, a Maven built tool, and an optional Java IDE. The code of this recipe is available in the chapter12/simple_plugin directory.

How to do it...

Generally ElasticSearch plugins are developed in Java using the Maven built tool and deployed as a ZIP file.

For creating a simple JAR plugin, we need to perform the following steps:

  1. To correctly build and serve a plugin, the following files must be defined:
    • pom.xml: This is used to define the build configuration for Maven.
    • es-plugin.properties: This defines the namespace of the plugin class that must be loaded.
    • <name>Plugin.java: This is the main plugin class, which is loaded at start up and initializes the plugin actions.
    • plugin.xml: These are the assemblies that define how to execute the assembly steps of Maven. It is used to build the ZIP file to deliver the plugin.
  2. A standard pom.xml file for creating a plugin contains the following code:
    • The maven pom.xml header is as follows:
      <?xml version="1.0" encoding="UTF-8"?>
      <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/xsd/maven-4.0.0.xsd">
          <name>elasticsearch-simple-plugin</name>
          <modelVersion>4.0.0</modelVersion>
          <groupId>com.packtpub</groupId>
          <artifactId>simple-plugin</artifactId>
          <version>0.0.1-SNAPSHOT</version>
          <packaging>jar</packaging>
          <description>A simple plugin for ElasticSearch</description>
          <inceptionYear>2013</inceptionYear>
          <licenses>…   </licenses>
    • The parent pom.xml file used to derive common properties or settings is as follows:
          <parent>
              <groupId>org.sonatype.oss</groupId>
              <artifactId>oss-parent</artifactId>
              <version>7</version>
          </parent>
    • Some properties mainly used to simplify the dependencies are defined as follows:
          <properties>
              <elasticsearch.version>0.90.5</elasticsearch.version>
          </properties>
    • A list of jar dependencies is as follows:
          <dependencies>
              <dependency>
                  <groupId>org.elasticsearch</groupId>
                  <artifactId>elasticsearch</artifactId>
                  <version>${elasticsearch.version}</version>
                  <scope>compile</scope>
              </dependency>
      
              <dependency>
                  <groupId>log4j</groupId>
                  <artifactId>log4j</artifactId>
                  <version>1.2.17</version>
                  <scope>runtime</scope>
              </dependency>
             <!—test dependencies -->
         </dependencies>
    • A list of Maven plugins required to build and deploy the artifact is as follows:
          <build>
              <plugins>
                  <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-compiler-plugin</artifactId>
                      <version>2.3.2</version>
                      <configuration>
                          <source>1.6</source>
                          <target>1.6</target>
                      </configuration>
                  </plugin>
                  <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-surefire-plugin</artifactId>
                      <version>2.12.3</version>
                      <configuration>
                          <includes>
                              <include>**/*Tests.java</include>
                          </includes>
                      </configuration>
                  </plugin>
                  <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-source-plugin</artifactId>
                      <version>2.1.2</version>
                      <executions>
                          <execution>
                              <id>attach-sources</id>
                              <goals>
                                  <goal>jar</goal>
                              </goals>
                          </execution>
                      </executions>
                  </plugin>
                  <plugin>
                      <artifactId>maven-assembly-plugin</artifactId>
                      <version>2.3</version>
                      <configuration>
                          <appendAssemblyId>false</appendAssemblyId>                    <outputDirectory>${project.build.directory}/releases/</outputDirectory>
                          <descriptors>                        <descriptor>${basedir}/src/main/assemblies/plugin.xml</descriptor>
                          </descriptors>
                      </configuration>
                      <executions>
                          <execution>
                              <phase>package</phase>
                              <goals><goal>single</goal></goals>
                          </execution>
                      </executions>
                  </plugin>
              </plugins>
          </build>
      </project>
  3. In the JAR file, there must be an es-plugin.properties file, which defines the entry point class that must be loaded during plugin initialization. It generally contains a single line of code. For example:
    plugin=org.elasticsearch.plugin.simple.SimplePlugin
  4. The SimplePlugin.java class is an example of the base minimum required code to be compiled for executing a plugin and its definition is as follows:
    package org.elasticsearch.plugin.simple;
    import org.elasticsearch.plugins.AbstractPlugin;
    public class SimplePlugin extends AbstractPlugin {
    
        @Override
        public String name() {
            return "simple-plugin";
        }
    
        @Override
        public String description() {
            return "A simple plugin implementation";
        }
    }
  5. To complete the compile and deploy workflow, we need to define a plugin.xml file used in Maven assembly step as follows:
    <?xml version="1.0"?>
    <assembly>
        <id>plugin</id>
        <formats>
            <format>zip</format>
        </formats>
        <includeBaseDirectory>false</includeBaseDirectory>
        <dependencySets>
            <dependencySet>
                <outputDirectory>/</outputDirectory>
                <useProjectArtifact>true</useProjectArtifact>
    <useTransitiveFiltering>true</useTransitiveFiltering>
                <excludes>
    <exclude>org.elasticsearch:elasticsearch</exclude>
                </excludes>
            </dependencySet>
            <dependencySet>
                <outputDirectory>/</outputDirectory>
                <useProjectArtifact>true</useProjectArtifact>
              <useTransitiveFiltering>true</useTransitiveFiltering>
                <includes></includes>
            </dependencySet>
        </dependencySets>
    </assembly>

    This file defines the resources that must be packaged into the final ZIP archive.

How it works...

Several parts compose the development lifecycle of a plugin, such as designing, coding, building, and deploying. To speed up the building and deploying parts, which are always common to every plugin, we need to create a Maven pom.xml file.

The preceding pom.xml file is a standard one to develop ElasticSearch plugins. This file is composed by:

  • Several section entries used to set up the current Maven project. In detail, we have:
    • The name of the plugin (that is, elasticsearch-simple-plugin)
      <name>elasticsearch-simple-plugin</name>
    • The groupId and artifactId tags are used to define the plugin artifact name as follows:
      <groupId>com.packtpub</groupId>
      <artifactId>simple-plugin</artifactId>
    • The plugin version using the version tag:
      <version>0.0.1-SNAPSHOT</version>
    • The type of packaging using the packaging tag:
      <packaging>jar</packaging>
    • A project description with the starting year as follows:
      <description>A simple plugin for ElasticSearch</description>
      <inceptionYear>2013</inceptionYear>
  • An optional license section, in which we can define the license for the plugin. For the standard Apache one, the code should look as follows:
    <licenses>
       <license>
          <name>The Apache Software License, Version 2.0</name>
          <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
          <distribution>repo</distribution>
       </license>
    </licenses>
  • A parent pom is used to inherit common properties. Generally for plugins, it is useful to inherit from Sonatype base pom.
    <parent>
        <groupId>org.sonatype.oss</groupId>
        <artifactId>oss-parent</artifactId>
        <version>7</version>
    </parent>
  • Global variables set for all the builds. Typically in this section the ElasticSearch version and other library versions are set as follows:
    <properties>
        <elasticsearch.version>0.90.5</elasticsearch.version>
    </properties>

    Tip

    It is very important that the Elasticsearch JAR version matches the ElasticSearch cluster one to prevent issues due to changes between releases.

  • For compiling a plugin, the ElasticSearch JAR and the log4j library, and the list of dependencies are required in the compiling phase:
    <dependency>
        <groupId>org.elasticsearch</groupId>
        <artifactId>elasticsearch</artifactId>
        <version>${elasticsearch.version}</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
        <scope>runtime</scope>
    </dependency>
  • The Maven plugin section contains a list of Maven plugins that executes several build steps. We have:
    • The compiler section, which requires a source compilation. The Java version is fixed to 1.6.
      <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>2.3.2</version>
          <configuration>
              <source>1.6</source>
              <target>1.6</target>
          </configuration>
      </plugin>
    • The source section, which enables the creation of source packages to be released with the binary (useful for debugging) is as follows:
      <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-source-plugin</artifactId>
          <version>2.1.2</version>
          <executions>
              <execution>
                  <id>attach-sources</id>
                  <goals>
                      <goal>jar</goal>
                  </goals>
              </execution>
          </executions>
      </plugin>
  • The assembly section, which builds a ZIP file taking a configuration file (plugin.xml) and inserting the output in the releases directory is as follows:
    <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <version>2.3</version>
        <configuration>
            <appendAssemblyId>false</appendAssemblyId>
    <outputDirectory>${project.build.directory}/releases/</outputDirectory>
            <descriptors>
    <descriptor>${basedir}/src/main/assemblies/plugin.xml</descriptor>
            </descriptors>
        </configuration>
        <executions>
            <execution>
                <phase>package</phase>
                <goals><goal>single</goal></goals>
            </execution>
        </executions>
    </plugin>

    Related to pom.xml, we have the plugin.xml file, which describes how to assemble the final ZIP file. This file is usually contained in the /src/main/assemblies/ directory of the project.

The most important sections of this file are as follows:

  • formats: In this section the destination format is defined as follows
    <formats><format>zip</format></formats>
  • excludes sets in dependencySet: This contains the artifacts to be excluded from the package. Generally, we exclude ElasticSearch JAR as it is already provided in the server install.
    <dependencySet>
        <outputDirectory>/</outputDirectory>
        <useProjectArtifact>true</useProjectArtifact>
        <useTransitiveFiltering>true</useTransitiveFiltering>
        <excludes>
            <exclude>org.elasticsearch:elasticsearch</exclude>
        </excludes>
    </dependencySet>
  • includes sets in dependencySet: This contains the artifacts to be included into the package. They are mainly the required JARs to run the plugin.
    <dependencySet>
        <outputDirectory>/</outputDirectory>
        <useProjectArtifact>true</useProjectArtifact>
        <useTransitiveFiltering>true</useTransitiveFiltering>
        <includes>…</includes>
    </dependencySet>

While packaging the plugin, the include and exclude rules are verified and only the files that are allowed to be distributed are put in the ZIP file.

After having configured Maven, we can start to write the main plugin class.

Every plugin class must be derived by the AbstractPlugin one and it must be public otherwise it cannot be loaded dynamically from the JAR.

import org.elasticsearch.plugins.AbstractPlugin;
public class SimplePlugin extends AbstractPlugin {

The AbstractPlugin class needs the two methods to be defined: the name and description.

The name method must return a string and it's usually a short name. This value is shown in the plugin loading log as follows:

    @Override
    public String name() {
        return "simple-plugin";
    }

The description method must return a string too. It is mainly a long description of the plugin.

@Override
    public String description() {
        return "A simple plugin implementation";
    }

After defining all the required files, to generate a ZIP release of our plugin it is enough to invoke the maven package command. This command will compile the code and create a ZIP package in the target/releases directory of your project.

In this recipe we have configured a working environment to build, deploy, and test plugins. In the next recipes we will re-use this environment to develop several plugin types.

There's more...

Compiling and packaging the plugin is not enough to define a good lifecycle for your plugin, you need to add a test phase.

Testing the plugin functionalities with test cases reduces the number of bugs that can affect the plugin when released.

It's possible to add a test phase in Maven build pom.xml.

The ElasticSearch community mainly uses the testNG (http://testng.org/) and the hamcrest (https://code.google.com/p/hamcrest/) libraries. To use them you need to add their dependencies in the dependency section of the pom.xml file:

<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>6.8</version>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
        </exclusion>
        <exclusion>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-all</artifactId>
    <version>1.3</version>
    <scope>test</scope>
</dependency>

These dependencies are not standard, because they are tuned to exclude some unwanted packages. Note that the compiling scope is test, which means that these dependencies are only considered during the test phase.

To complete the test part, a Maven plugin which executes the tests, must be defined as follows:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.12.3</version>
    <configuration>
        <includes><include>**/*Tests.java</include></includes>
    </configuration>
</plugin>

The include section lists all the possible classes that contains test via glob expression.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.190.159.164