October 20th, Q&A session: Get you issues solved and questions answered!

GitHub logo
Edit

Plugins

Overview

The Ignite plugin system allows you to extend the core functionality of Ignite. Plugins have access to different internal Ignite components, such as security processor and others, and can extend the programmatic API of Ignite.

To add a custom plugin, implement the PluginProvider interface and register the implementation in the node configuration. The following is an overview of the steps involved in creating a plugin:

  1. Implement the PluginProvider interface. This is the main interface for creating plugins.

  2. Implement the IgnitePlugin interface. If your plugin adds functionality that is meant to be triggered by end users, you should add public methods to this class. An instance of this class is available to end users at runtime via Ignite.plugin(String pluginName).

  3. Register the plugin in IgniteConfiguration.setPluginProviders(…​) either programmatically or via XML configuration.

  4. If your plugin has a public API, call MyPlugin plugin = Ignite.plugin(pluginName) at runtime and execute specific actions.

The following section gives an example of a plugin and goes into details about how plugins work in Ignite.

Example Plugin

Let’s create a simple Ignite plugin that prints information about the number of entries in every cache to the console periodically, on each node. In addition, it exposes a public method that the users can call programmatically from their application to print the cache size information on demand. The plugin has one configuration parameter: the time interval for printing the cache size information.

1. Implement PluginProvider

PluginProvider is the main interface for creating Ignite plugins. Ignite calls the methods of each registered plugin provider during initialization.

The following methods must return non-null values. Other methods are optional.

  • name() - returns the name of the plugin

  • plugin() - returns the object of your plugin class

Below is an example implementation of a plugin provider. We create an object of MyPlugin class (see next step) in the initExtensions() method. Ignite passes a PluginContext object as an argument to this method. PluginContext provides access to the Ignite APIs and node configuration. See the PluginContext javadoc for more information. Here we simply pass the PluginContext and the time interval to the MyPlugin constructor.

MyPluginProvider.java:
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */
package org.apache.ignite.snippets.plugin;

import java.io.Serializable;
import java.util.UUID;

import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.plugin.CachePluginContext;
import org.apache.ignite.plugin.CachePluginProvider;
import org.apache.ignite.plugin.ExtensionRegistry;
import org.apache.ignite.plugin.PluginConfiguration;
import org.apache.ignite.plugin.PluginContext;
import org.apache.ignite.plugin.PluginProvider;
import org.apache.ignite.plugin.PluginValidationException;
import org.jetbrains.annotations.Nullable;

public class MyPluginProvider implements PluginProvider<PluginConfiguration> {

    /**
     * The time interval in seconds for printing cache size information.
     */
    private long interval = 10;

    private MyPlugin plugin;

    public MyPluginProvider() {
    }

    /**
     *
     * @param interval Time interval in seconds
     */
    public MyPluginProvider(long interval) {
        this.interval = interval;
    }

    @Override
    public String name() {
        //the name of the plugin
        return "MyPlugin";
    }

    @Override
    public String version() {
        return "1.0";
    }

    @Override
    public String copyright() {
        return "MyCompany";
    }

    @Override
    public MyPlugin plugin() {
        return plugin;
    }

    @Override
    public void initExtensions(PluginContext ctx, ExtensionRegistry registry)
            throws IgniteCheckedException {
        plugin = new MyPlugin(interval, ctx);
    }

    @Override
    public void onIgniteStart() throws IgniteCheckedException {
        //start the plugin when Ignite is started
        plugin.start();
    }

    @Override
    public void onIgniteStop(boolean cancel) {
        //stop the plugin
        plugin.stop();
    }

    /**
     * The time interval (in seconds) for printing cache size information
     * @return
     */
    public long getInterval() {
        return interval;
    }

    /**
     * Sets the time interval (in seconds) for printing cache size information
     * @param interval
     */
    public void setInterval(long interval) {
        this.interval = interval;
    }

    // other no-op methods of PluginProvider
}

The onIgniteStart() method is invoked when Ignite is started. We start the plugin by calling MyPlugin.start(), which simply schedules periodic execution of the task that prints cache size information.

2. Implement IgnitePlugin

The implementation of the IgnitePlugin returned by the plugin provider is available to the users via Ignite.plugin(String pluginName). If you want to provide public API to end users, the API should be exposed in the class that implements IgnitePlugin.

Strictly speaking, this step is not necessary if your plugin does not provide a public API. Your plugin functionality may be implemented and initialized in the PluginProvider implementation, and the PluginProvider.plugin() method may return an empty implementation of the IgnitePlugin interface.

In our case, we encapsulate the plugin functionality in the MyPlugin class and provide one public method (MyPlugin.printCacheInfo()). The MyPlugin.java implements the Runnable interface. The start() and stop() methods schedule periodic printing of cache size information.

MyPlugin.java:
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */
package org.apache.ignite.snippets.plugin;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.ignite.IgniteCache;
import org.apache.ignite.plugin.IgnitePlugin;
import org.apache.ignite.plugin.PluginContext;

/**
 *
 * The plugin prints cache size information to console
 *
 */
public class MyPlugin implements IgnitePlugin, Runnable {

    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    private PluginContext context;

    private long interval;

    /**
     *
     * @param context
     */
    public MyPlugin(long interval, PluginContext context) {
        this.interval = interval;
        this.context = context;
    }

    private void print0() {
        StringBuilder sb = new StringBuilder("\nCache Information: \n");

        //get the names of all caches
        context.grid().cacheNames().forEach(cacheName -> {
            //get the specific cache
            IgniteCache cache = context.grid().cache(cacheName);
            if (cache != null) {
                sb.append("  cacheName=").append(cacheName).append(", size=").append(cache.size())
                        .append("\n");
            }
        });

        System.out.print(sb.toString());
    }

    /**
     * Prints the information about caches to console.
     */
    public void printCacheInfo() {
        print0();
    }

    @Override
    public void run() {
        print0();
    }

    void start() {
        scheduler.scheduleAtFixedRate(this, interval, interval, TimeUnit.SECONDS);
    }

    void stop() {
        scheduler.shutdownNow();
    }
}

3. Register your Plugin

Programmatically:

IgniteConfiguration cfg = new IgniteConfiguration();

//register a plugin that prints the cache size information every 100 seconds
cfg.setPluginProviders(new MyPluginProvider(100));

//start the node
Ignite ignite = Ignition.start(cfg);

Via XML Configuration:

Compile your plugin source code and add the classes to the classpath on each node. Then, you can register the plugin as follows:

<bean class="org.apache.ignite.configuration.IgniteConfiguration">

    <property name="pluginProviders">
        <bean class="org.apache.ignite.snippets.plugin.MyPluginProvider">
           <property name="interval" value="100"/>
        </bean>
    </property>

</bean>

When you start the node, you should see the following message in the console:

[11:00:49] Initial heap size is 248MB (should be no less than 512MB, use -Xms512m -Xmx512m).
[11:00:49] Configured plugins:
[11:00:49]   ^-- MyPlugin 1.0
[11:00:49]   ^-- MyCompany
[11:00:49]

4. Access the Plugin at Runtime

You can access the instance of the plugin by calling Ignite.plugin(pluginName). The pluginName argument must be equal to the plugin name returned in MyPluginProvider.name().

//get an instance of the plugin
MyPlugin p = ignite.plugin("MyPlugin");

//print the cache size information
p.printCacheInfo();