Writing EPL plug-ins in Java

EPL plug-ins can be written in Java. Java plug-in classes are automatically analyzed by the correlator and any suitable methods exposed as methods that can be called from EPL.

EPL plug-ins written in Java are packaged in a JAR file with an XML manifest describing how to load them. They are deployed with the engine_inject tool into a running correlator. Both engine_inject and the correlator need to be started with the --java option.

This section describes the steps required to develop and deploy EPL plug-ins written in Java. You can develop plug-ins with Apama in Apama Plugin for Eclipse or manually, outside Apama Plugin for Eclipse. When you use Apama Plugin for Eclipse, some development steps are performed automatically for you. This section describes all development steps and notes which steps Apama Plugin for Eclipse automatically performs.

Creating an EPL plug-in using Java

To create a Java class to use as an EPL plug-in

  1. In the Java class used as a plug-in, you need to have one or more public static methods that match the permitted signatures, which are described in Permitted signatures for methods.

    All calls from an Apama application are made to these static methods from all contexts.

    As the plug-in author you are responsible for any concurrency concerns.

  2. You need to create a deployment descriptor file in the plug-in’s META-INF/jmon-jar.xml file. For the plug-in, you need to add a <plugin> element to the <application-classes> element. For more information, see Creating deployment descriptor files for EPL plug-ins written in Java.

    Instead of writing a deployment descriptor file manually, if you are using Apama Plugin for Eclipse to create the plug-in, you can annotate the plug-in class and have Apama Plugin for Eclipse automatically generate the descriptor file. For more information, see Inserting annotations for deployment descriptor files.

  3. Create a JAR file for deploying the plug-in and add the Java class file and the deployment descriptor file META-INF/jmon-jar.xml to it. In Apama Plugin for Eclipse when you create an EPL plug-in written in Java, this is done automatically.

Permitted signatures for methods

For a method to be exposed to EPL, it must be public, must be static and every argument plus the return type must be one of the following:

Java Entry

EPL Type

Notes

int

integer

Truncated when passed in, for compatibility.

long

integer

String

string

Copy in / copy out.

boolean

boolean

double

float

java.lang.Number

decimal

Is either java.lang.Double type for NaN or infinity, or java.math.BigDecimal for a value.

java.math.BigDecimal

decimal

Passing in either NaN or infinity throws an exception that kills the monitor instance if not caught. Deprecated, use java.lang.Number instead.

com.apama.epl.plugin.Context

context

New type defined for plug-ins.

com.apama.epl.plugin.Channel

com.apama.Channel

New type defined for plug-ins.

Object

chunk

Any Java object can be held in EPL via a chunk.

TYPE[]

sequence<TYPE>

Any above type except int can be passed in as an arbitrary-depth nested array->sequence. The sequence is strictly copy-in, non-modifiable, but can be returned as copy-out.

void

N/A

Permitted as a return type only.

Any method not matching this signature is ignored and logged at DEBUG.

Overloaded functions

Any function with multiple overloads is ignored (none of them are exposed) and this is logged once at WARN and once per method at DEBUG.

Using EPL plug-ins written in Java

After you create an EPL plug-in in Java, it must be injected into a Java-enabled correlator before it is available for use in Apama applications. Applications that will use the plug-in also need to import the plug-in by name.

Injecting

The JAR file containing the EPL plug-in must be injected into a correlator that has been started with the --java option. When using the Apama engine_inject utility to inject the JAR file, you also need to use the --java option.

Importing

Once a Java plug-in has been injected it is available for import using the plugin-name (including the package) defined in the deployment descriptor file. The correlator will automatically introspect the class and make available any suitable, public methods that can be called directly from EPL. For example, the following code imports a plug-in named mypackage.TestPlugin and calls its dosomething method:

monitor m {
   import "mypackage.TestPlugin" as test;
   action onload()
   {
      test.dosomething();
   }
}

Note, if the plug-in JAR file has been incorrectly injected, the correlator will try to load the plug-in as a C++ plug-in and may give an error such as Error opening plug-in library libfoo.so: libfoo.so: cannot open shared object file: No such file or directory. If this happens and you were trying to load a plug-in written in Java, then check that the JAR file was created and injected correctly before your EPL file was injected.

Classpath

Each Java plug-in application is loaded into its own separate classloader. This means that they have no access to any classes loaded in other JAR files. If your plug-in requires any other Java libraries, they must be listed in the classpath element of the deployment descriptor, included in the correlator’s global classpath, or injected in the same plug-in JAR file as the plug-in. See Specifying the classpath in deployment descriptor files for more details.

Deleting

An EPL plug-in can be explicitly deleted by calling engine_delete with the application name defined in the deployment descriptor. Monitors using the plug-in depend on the plug-in type in the normal fashion. The plug-in will not be deleted until the application and all dependent monitors are deleted.

As each plug-in is loaded in its own classloader, once the application has been deleted, the plug-in can be re-injected and it will be loaded into a new classloader.

Interacting with contexts

EPL plug-ins can be passed context objects using the com.apama.epl.plugin.Context type. The Context object is defined as:

package com.apama.epl.plugin;
public class Context
{
  public String toString();
  public Context();
  public String getName();
  public int hashCode();
  public boolean isPublic();
  public boolean equals(Context other);
  public static native Context getCurrent();
}

The getCurrent method returns the context that this method was called from.

Interacting with the correlator

EPL plug-ins can use the com.apama.epl.plugin.Correlator class to send an event, subscribe to a channel, or to specify blocking behavior. The Correlator class is defined as:

package com.apama.epl.plugin;
public class Correlator
{
  public static native void sendTo(String evt, String chan);
  public static native void sendTo(String evt, Context ctx);
  public static native void sendTo(String evt, Context[] ctxs);
  public static native void sendTo(String evt, Channel c);

  public static native void subscribe(EventHandler handler, String[] channels);
  public static native void unsubscribe(EventHandler handler, String[] channels);
  public static native void unsubscribe(EventHandler handler);

  public static native void pluginMethodBlocking();
}

The Correlator methods are:

  • sendTo(String, String) – Sends the event represented in the first String to the channel specified in the second String. Any contexts and external receivers that are subscribed to the specified channel receive the event. If there are no subscribers the event is discarded.
  • sendTo(String, Context) – Sends the event represented in String to the context referred to by the com.apama.epl.plugin.Context argument. An exception is thrown if the context reference is invalid.
  • sendTo(String, Context[]) – Sends the event represented in String to the array of contexts referred to by the com.apama.epl.plugin.Context[] argument. If one context reference is invalid an exception is thrown and the event is not sent to any context.
  • sendTo(String, Channel) — Sends the event represented in String. If the specified com.apama.epl.plugin.Channel object contains a string then the event is sent to the channel that has that name. If Channel contains a context then the event is sent to that context.
  • subscribe(EventHandler, String[]) – Subscribes the handler object to the channels listed in the string array. If the handler is already subscribed to some channels then the channels listed in the array are added to the list of existing subscriptions. Subscribing to the same channel multiple times results in a single subscription. However, to completely remove a channel subscription that has been added multiple times you must unsubscribe from that channel the same number of times that it was subscribed to.
  • unsubscribe(EventHandler, String[]) – For the channels specified in the string array, this method removes the subscriptions from the specified handler. It is possible for the result of this method to be that the handler is not subscribed to any channels. Unsubscription from a channel that the handler is not subscribed is ignored.
  • unsubscribe(EventHandler) – Removes all subscriptions from the specified handler. If this handler is not subscribed to any channels the method is ignored.
  • pluginMethodBlocking() – Informs the correlator that the plug-in is potentially blocking for the rest of this call and the correlator is free to spin up additional threads on which to run other contexts.

For more information on com.apama.epl.plugin.Context and com.apama.epl.plugin.Correlator, see the API reference for Java (Javadoc).

Receiving events from named channels

A Java plug-in can register callbacks to receive events that are sent to named channels. This is similar to the monitor.subscribe() method in EPL. Events are delivered in string form by means of a method on a known interface.

To register a callback, the plug-in must define a class that implements the com.apama.epl.plugin.EventHandler interface:

public interface EventHandler
{
   void handleEvent(String event, String channel);
}

The handleEvent() method is called once for each event sent to a channel that this handler is subscribed to, with the channel on which it was received. To manage EventHandler object channel subscriptions, use the subscribe() and unsubscribe() methods on com.apama.epl.plugin.Correlator. When a handler is unsubscribed from all channels any in-progress callbacks will complete, but no further callbacks will be made to that handler.

Working with Channel objects

Similar to context objects, you can pass EPL com.apama.Channel objects into a Java plug-in. The equivalent Java class is com.apama.epl.plugin.Channel and you can use objects of this class to send events to channels. Like the EPL Channel type, the Java Channel class has three constructors:

Channel (String name)
Channel (com.apama.epl.plugin.Context c)
Channel ()

A Channel object can contain a string that is the name of a channel or it can contain a context. The no-argument constructor creates a Channel object that contains an empty context. If you try to send an event to an empty context the sendTo() method throws an exception.

You can call the empty() method on a Java Channel object. It returns true only if the object contains an empty context.

Exceptions

If a method throws an exception, that exception is passed up to the calling EPL and can be caught by the calling monitor. If an exception is not caught it will terminate the monitor instance. Details on catching exceptions in EPL can be found in Exception handling.

If a Java plug-in throws a java.lang.RuntimeException, or subclass, which is in the java. namespace (for example, java.lang.NullPointerException) then it will be logged at ERROR with a stacktrace before being rethrown. Unchecked exceptions from other sources (for example client exception types) will not be logged.

Load, unload, and shutdown hooks

If a plug-in needs to run anything when it is loaded, you can do this in a static initializer:

public class Plugin
{
   static {
    ... // initialization code here
   }
}

It is not natively possible for a plug-in to run anything when it is unloaded. If you need this functionality you can declare a method to be called when the plug-in is unloaded using annotations:

public class Plugin
{
   @com.apama.epl.plugin.annotation.Callback(
      type=com.apama.epl.plugin.annotation.Callback.CBType.SHUTDOWN)
   public static void shutdown()
   {
    ... // shutdown code here
   }
}

The method must be a public static function which takes no arguments and returns void. Currently, Apama does not support callbacks other than SHUTDOWN.

Non-blocking plug-ins and methods

In a correlator some threads have the potential to block and others do not. If a thread might block, the correlator starts new threads if it has additional runnable contexts. By default the correlator assumes that a plug-in call may block and will start additional threads on which to run other contexts. In situations where the plug-in call can never block, the additional overhead of starting new threads when all CPUs are busy is unnecessary. If you know that a plug-in or an individual method is non-blocking, you can improve efficiency by annotating either entire plug-ins or individual methods as non-blocking.

Note, however, if a method declared as non-blocking does block, the correlator can block all threads waiting for them to finish, resulting in a deadlocked correlator. For methods that are normally non-blocking, but may block in predictable situations, see “Sometimes-blocking functions” below.

  • Annotations. You can apply the annotation com.apama.epl.plugin.annotation.NoBlock with no arguments to either a plug-in class, or to a method on a class:

    @com.apama.epl.plugin.annotation.NoBlock()
    public class Plugin
    {
        ...
    }
    

    When applied to a class, the annotation indicates that no method on the plug-in can ever block.

    public class Plugin
    {
      @com.apama.epl.plugin.annotation.NoBlock()
      public static String getValue() {... }
    }
    

    When applied to a method, the annotation indicates that this method will never block, but other methods may block.

  • Sometimes-blocking functions. If you have a function that usually will not block, but under some known conditions may block, then the method can be declared as NoBlock as long as it then uses a callback to indicate when it is starting the potentially-blocking behavior. The callback is a static method on com.apama.epl.plugin.Correlator called pluginMethodBlocking. This function takes no arguments, returns no value and is idempotent. When it is called, the correlator will then assume that the plug-in is potentially blocking for the rest of this call and is free to spin up additional threads on which to run other contexts.

    public class Plugin
    {
       @com.apama.epl.plugin.annotation.NoBlock()
       public static String getValue()
       {
          if (null != localValue) return localValue;
          else {
             com.apama.epl.plugin.Correlator.pluginMethodBlocking();
             localValue = getRemoteValue();
             return localValue;
          }
       }
    }
    

Logging

EPL plug-ins written in Java can log to the correlator’s log file. This is done via the com.apama.util.Logger class, or alternatively using the open-source SLF4J API. The SLF4J API is provided in the /common/lib/ext directory of your installation.

Each plug-in must create a static instance of com.apama.util.Logger using the static getLogger method. This instance provides debug(...), info(...), warn(...) and error(...) methods, which log a string at that log level in the correlator log file. See also Setting correlator and plug-in log files and log levels in a YAML configuration file.

For more information on using the Logger class, including how to override the default log level, see the API reference for Java (Javadoc).

The following is an example of logging in an EPL plug-in:

package test;
import com.apama.util.Logger;
public class MyPlugin
{
   private static final Logger logger = Logger.getLogger("plugins.test.MyPlugin");
   public static void foo()
   {
      logger.info("A string that's logged at INFO");
   }
}

This will produce entries in the correlator log file like this:

2019-06-24 14:22:21.974 INFO [1167792448:processing] - <test.MyPlugin> A string that's logged at INFO

It is recommended to put “plugins.” at the beginning of the category name (as shown in the above example).

If your plug-in uses a third-party library that logs with SLF4J or Log4j 2, then the log output goes to the main correlator log file automatically. The root classloader also contains the Log4j 1.x bridge (log4j-1.2-api.jar) which redirects calls to most of the Log4j 1 API through to Log4j 2. So provided you do not use internal features such as programmatic configuration, Log4j 1 libraries should work fine. However, we recommend that you transition away from Log4j 1 since it has reached end-of-life status.

When using a library which uses some other logging implementation, such as the JDK logger, or Apache Java commons logging (JCL), then add a bridging JAR to convert it to SLF4J where possible. Several bridges are available in the common/lib/ext and common/lib/ext/log4j directories of your installation.

Steps for developing EPL plug-ins written in Java in Apama Plugin for Eclipse

To develop EPL plug-ins written in Java in Apama Plugin for Eclipse

  1. Add Java support to a project. See also Adding the Java nature to an Apama project.

  2. Create the source files.

    In the Project Explorer view, right-click your project and select New > Java EPL Plugin.

    A wizard appears that lets you specify the plug-in name, a description, the Apama package name, the Java source folder and the Java package. See also Adding an EPL Plug-in written in Java.

    Apama Plugin for Eclipse automatically adds an entry for the EPL plug-in to the jmon-jar.xml deployment descriptor file and regenerates the JAR file to include the new plug-in.

    If you want to build your JAR files manually, right-click your project and select Apama > Build JAR Files. This is useful if you unselected the Build jar files automatically option in the apama_java.xml file, which is in the config directory of your project. One reason you might not want to build the JAR files automatically is that the build takes too long. When Build jar files automatically is selected, Apama Plugin for Eclipse builds the JAR files every time you modify the EPL plug-in.

  3. Create your plug-in’s launch configuration.

    Apama Plugin for Eclipse adds all JAR files for your plug-in to the correlator initialization list and all non-plug-in JAR files to the correlator class path.

    If you want to build your project’s files outside Apama Plugin for Eclipse and Eclipse, right-click your project and select Apama > Generate Ant Buildfiles. Apama Plugin for Eclipse generates an Ant build file (with the name build-project_name.xml), which you can use only to build your project’s plug-in JAR files outside of the Eclipse environment. Note that this is unrelated to the Apama Plugin for Eclipse feature for exporting an Ant build file that you can use for deployment. See also Defining custom launch configurations.

  4. Run and test your plug-in. See Launching Projects.

  5. Debug your plug-in. See Debugging EPL Applications.

  6. Deploy your plug-in. See Deploying EPL plug-ins written in Java.

    Apama Plugin for Eclipse generates your plug-in’s JAR file in the config_name java application files folder of your project’s directory. By default, config_name is the project name.

    You can manage the content of the plug-in JAR file and jmon-jar.xml file by using the editor in Apama Plugin for Eclipse to update the apama_java.xml file, which is located in the project’s config folder. You can use this editor to do the following:

    • Set the metadata.
    • Set the injection order.
    • Add non-plug-in Java classes to the plug-in JAR files.
    • Add Java classes that were not created by the Apama wizards in Apama Plugin for Eclipse to the plug-in JAR file.

Java prerequisites for using Apama's API for EPL plug-ins written in Java

When you install Apama, the installation script installs the Java plug-in API as ap-correlator-extension-api.jar in the Apama lib directory.

Apama Plugin for Eclipse includes the required Java compiler for running your application.

Steps for developing EPL plug-ins written in Java manually

To develop EPL plug-ins written in Java outside Apama Plugin for Eclipse

  1. Ensure that ap-correlator-extension-api.jar is in your Java CLASSPATH environment variable.

  2. Create a folder in which to develop your plug-in.

  3. In this development folder, define one .java file for each event type and one .java file for each monitor class.

  4. Ensure that there is a deployment descriptor file named jmon-jar.xml. See also Creating deployment descriptor files for EPL plug-ins written in Java.

  5. In your development folder, compile all your Java source code.

    javac *.java
    

    If ap-correlator-extension-api.jar is not already in your CLASSPATH environment variable, you can specify the –classpath command-line option to point to ap-correlator-extension-api.jar.

  6. In your development folder, create a JAR file that contains the deployment descriptor and all class files. The command line format is as follows:

    jar –cf plugin_name.jar META-INF/jmon-jar.xml *.class
    

    Replace plugin_name with the name you choose for your plug-in. On Windows, use backslashes (\) instead of forward slashes (/).

  7. If any of your plug-in’s .class files are in your CLASSPATH environment variable, remove them. If the JRE can resolve a class path by using either your plug-in’s JAR file or your CLASSPATH environment variable, Apama fails to load your plug-in.

Deploying EPL plug-ins written in Java

To deploy and run your EPL plug-in written in Java outside Apama Plugin for Eclipse

  1. Start a correlator with Java enabled:

    correlator –j other_options
    
  2. Inject the plug-in JAR file:

    engine_inject –j plugin_name.jar
    

    Apama loads each plug-in class defined in the deployment descriptor file and makes them available to be imported into EPL code.

    The classes in the plug-in’s JAR file cannot also exist (have the same packaging and name) anywhere else on the classpath. If they do, it causes the plug-in to fail to load.

    When you start the correlator, you can pass properties and options to the embedded JVM with the –J option. Specify the -J option with each property or option you want to specify.

    For example, you can use this mechanism to specify a global classpath for the JVM with: -J-Djava.class.path=PATH. Apama prepends its own internal classpath .jar files to the path you specify. If you specify both the CLASSPATH environment variable and a classpath on the correlator start-up command line, the classpath specified on the command line takes precedence. See also Specifying the classpath in deployment descriptor files for information about specifying the classpath for each individual plug-in.

Removing EPL plug-ins written in Java from the correlator

To stop and delete a running EPL plug-in written in Java, execute the engine_delete operation:

engine_delete [options_to_identify_correlator] plugin_name

If the plug-in you want to delete is not running on the local host on the default correlator port, be sure to specify options that indicate the correlator that is running the plug-in you want to delete.

Replace plugin_name with the name of the plug-in as specified in the deployment descriptor. This is not necessarily the same as the name of the plug-in’s JAR file.

Deleting a plug-in written in Java can only be done when it is not used by any EPL monitors. If the --force option of the engine_delete tool is used, then all monitors importing the plug-in are also deleted.

Creating deployment descriptor files for EPL plug-ins written in Java

The plug-in’s JAR file must contain a deployment descriptor file. Inside the correlator, the JVM processes the plug-in’s deployment descriptor file and uses it as a guide to the event types and monitor classes to load. The name of the deployment descriptor file must be jmon-jar.xml.

When you use the Java support in Apama Plugin for Eclipse to develop your plug-in, the deployment descriptor file is generated for you. If you develop your plug-in outside Apama Plugin for Eclipse, there are two ways to create a deployment descriptor file:

  • Manually write the deployment descriptor XML file. Use your favorite editor to create this XML file according to the format described in Format for deployment descriptor files.
  • Insert Java annotations in your source files and run a utility to generate the deployment descriptor file. The annotations you can insert are defined in the java.apama.jmon.annotation package.

Of course, you can use the utility to generate the deployment descriptor file and then manually edit the result. If you then run the utility again, you would lose any manual changes you had made.

The technique you use is largely a matter of personal preference, hand-coded or machine-generated. If you have a very large plug-in with many event types and monitors, you might prefer to insert the annotations and generate the deployment descriptor file. If you have a small plug-in, you might find it easier to write the deployment descriptor file.

Format for deployment descriptor files

The format of the deployment descriptor file must be compliant with the XML defined by the following XML Document Type Definition (DTD):

http://www.apama.com/dtd/jmon-jar_1_2.dtd

You should become familiar with this DTD to understand the exact definition of the deployment descriptor file. However, the normal structure of the file is as follows. In the following format, all text inside XML element tags, which is in italic typeface, indicates placeholders for which you would supply an actual value.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jmon-jar PUBLIC "-//Apama, Inc.//DTD Java Monitors 1.2//EN"
  "http://www.apama.com/dtd/jmon-jar_1_2.dtd">
<jmon-jar>
  <name>Plug-in name in the correlator</name>
  <version>Version number</version>
  <author>Author</author>
  <company>Company name</company>
  <description>Description of this plug-in</description>
  <classpath>${sys:MY_THIRD_PARTY_DIR}/lib/foo.jar;
             ${sys:MY_THIRD_PARTY_DIR}/lib/bar.jar</classpath>
  <application-classes>
    <plugin>
      <plugin-name>The name visible to EPL</plugin-name>
      <plugin-class>The class to load from the JAR</plugin-class>
      <description>The description that appears in the log messages</description>
    </plugin>
  </application-classes>
</jmon-jar>

The most important part of the deployment descriptor file is the application-classes element. This element must contain a plugin element for each plug-in. See also Defining plug-ins in deployment descriptor files.

The plug-in name that you specify in the name element is important because it defines the plug-in’s name in the correlator. The engine_inspect management tool displays this name when it lists data for your plug-in. If you want to delete your plug-in, you specify this name. The plug-in name must be unique across all currently loaded plug-ins. If the plug-in name is not unique, injection fails.

Specifying the classpath in deployment descriptor files

Each Java plug-in JAR is loaded in its own dedicated Java classloader, which by default has access only to its own classes, and those available globally in the correlator’s system classloader.

Info
The correlator’s system classloader includes some standard Apama libraries such as the ap-correlator-extension-api.jar and ap-util.jar plus any additional JAR files the user chooses to specify on the correlator command line using -J-Djava.class.path=PATH.

It is also possible to specify additional JAR files for use by a specific Java plug-in, to provide access to any third-party libraries that the JAR requires. This approach is more self-contained than adding to the correlator’s global classloader.

The classpath string for a Java plug-in is specified in its deployment descriptor XML file as follows:

  • If you are manually writing the deployment descriptor XML, add the optional classpath element just after the description element, for example:

    ...
    <description>*Description of this plug-in*</description>
    <classpath>${sys:MY_THIRD_PARTY_DIR}/lib/foo.jar;
               ${sys:MY_THIRD_PARTY_DIR\}/lib/bar.jar*</classpath>
    ...
    

    Note that the classpath element is only available in the 1.2 (and greater) versions of the DTD (jmon-jar_1_2.dtd), so it may be necessary to update the DOCTYPE of the deployment descriptor to specify this DTD version if it does not already.

  • If you are generating the deployment descriptor automatically using Java annotations, then use the optional classpath attribute in the @Application annotation:

    @Application(
       name = "Simple",
       author = "My Name",
       version = "1.0",
       company = "Apama",
       description = "My simple Java plug-in",
       classpath = "${sys:MY_THIRD_PARTY_DIR}/lib/foo.jar;
                    ${sys:MY_THIRD_PARTY_DIR}/lib/bar.jar"
       )
    
  • If you are using Apama Plugin for Eclipse to generate the JAR file and deployment descriptor, use the @Application annotation approach to specify the classpath.

In both cases, the classpath string consists of any number of classpath entries, delimited by semicolon characters (;). Note that the semicolon must be used even on platforms that typically use a colon or other character to separate path entries, and also that forward slashes (/) should be used instead of backslashes (\), in order to ensure that the application works in the same way regardless of the platform it is deployed on.

Avoid using absolute paths in the classpath, as this makes it difficult to use the plug-in JAR on different machines. Instead, use ${...} placeholders to identify the first part of each path, for example, the installation directory of a third party whose libraries you wish to use. Currently, two types of placeholder are supported:

  • ${sys:MY_SYS_PROP_NAME} is replaced by a Java system property called MY_SYS_PROP_NAME
  • ${env:MY_ENV_VAR_NAME} is replaced by an environment variable called MY_ENV_VAR_NAME

The values for system property placeholders can be specified on the correlator command line as follows:

-J-DMY_SYS_PROP_NAME=path

The correlator logs a warning for any path that cannot be found, but fails to inject the plug-in entirely if the classpath includes any ${...} placeholders that are not defined.

You can make the location of your project directory available by defining an environment variable in a YAML configuration file as described in Setting environment variables for Apama components. You can use ${PARENT_DIR} as the value of the property if the YAML file is located in your project directory root. If you do this, you can specify JAR files from your project directory. For example:

<classpath>
  ${env:APAMA_PROJECT_DIR}/lib/foo.jar;${env:APAMA_PROJECT_DIR}/lib/bar.jar
</classpath>

Defining plug-ins in deployment descriptor files

The deployment descriptor file must define a plugin element for each plug-in in your plug-in’s JAR file. Each plugin element must contain the following elements:

  • plugin-name - Defines the name visible to EPL.
  • plugin-class - Indicates the class to load from the JAR for this plug-in.
  • description - A simple textual description that appears in the log messages.

For example:

<plugin>
  <plugin-name>TestPlugin</plugin-name>
  <plugin-class>test.TestPlugin</plugin-class>
  <description>A test plugin</description>
</plugin>

Inserting annotations for deployment descriptor files

In your source files, you can specify the following annotations:

  • @Application - This annotation indicates the name of the plug-in, as well as the author, version, company, and description of the plug-in. Insert this annotation in any one, and only one, of your source files. Each value is required. This annotation must be after any import statements and before the class definition statement. For example:

    @Application(
        name = "Simple",
        author = "Moray Grieve",
        version = "1.0",
        company = "Apama",
        description = "Deployment descriptor for a simple plug-in",
        classpath = "${sys:MY_THIRD_PARTY_DIR}/lib/foo.jar;
                     ${sys:MY_THIRD_PARTY_DIR}/lib/bar.jar"
    )
    
  • @com.apama.epl.plugin.annotation.EPLPlugin - This annotation is used to mark a Java class. The name specifies the import name for the plug-in, and the description serves as meta information for human consumption. For example:

    @com.apama.epl.plugin.annotation.EPLPlugin(
        name="TestPlugin",
        description="A test plugin"
    )
    class testplugin
    {
        ...
    }
    

Generating deployment descriptor files from annotations

There are two utilities that you can use to generate the deployment descriptor file from annotations in your source files:

  • com.apama.jmon.annotation.DirectoryProcessor - This utility processes a directory and generates the deployment descriptor file, which you must add to your plug-in’s JAR file.
  • com.apama.jmon.annotation.JarProcessor — This utility processes a plug-in’s JAR file and adds the deployment descriptor file to that JAR file.

You can execute these utilities from the command line or from a Java build file.

The DirectoryProcessor utility takes three optional arguments:

  • -r indicates that you want to recursively process the .class files in each directory and subdirectory in the specified directory. The default is that the utility processes only the .class files that are in the specified directory.
  • -d specifies the directory that contains the .class files you want to process. The default is that the utility processes any .class files in the current working directory.
  • -o specifies the file in which to store the output. The default is that output goes to stdout. In the plug-in JAR file, the name of the deployment descriptor file must always be jmon-jar.xml.

After you generate the deployment descriptor file, you must place it in the META-INF directory of your development directory. For example, you can execute the DirectoryProcessor utility from the command line as follows:

cd src
javac -classpath
$APAMA_CORRELATOR_HOME/lib/ap-correlator-extension-api.jar
*.java
java -DAPAMA_LOG_LEVEL=WARN -classpath
$APAMA_CORRELATOR_HOME/lib/ap-correlator-extension-api.jar
com.apama.jmon.annotation.DirectoryProcessor -r -d./src -o
./src/META-INF/jmon-jar.xml
jar -cf../simple-jmon.jar META-INF/jmon-jar.xml *.class

The JarProcessor utility takes one required argument, which is the name of the JAR file to operate on. To execute the JarProcessor utility from a Java build file, you can define something like the following:

<!--Target to process the annotations in the Java classes
    to produce jmon-jar.xml -- the deployment descriptor file.
-->
<target name="process-jar" depends="jar">
  <echo message=
    "Process annotations in JAR file: ${process-jar-file}" />
  <java jvm="java"
    classname="com.apama.jmon.annotation.JarProcessor" dir="."
    fork="yes">
    <classpath>
      <fileset dir="${lib-dir}">
        <patternset refid="libs" />
      </fileset>
    </classpath>
    <jvmarg value="-DAPAMA_LOG_LEVEL=WARN" />
    <arg value="${process-jar-file}" />
  </java>
</target>

<target name="process" depends="jar">
  <antcall target="process-jar">
    <param name="process-jar-file" value="${jar-file}" />
  </antcall>
</target>

Sample EPL plug-ins written in Java

Apama provides sample EPL plug-ins written in Java, located in the samples\correlator_plugin\java directory of your Apama installation. The samples are:

  • SimplePlugin – a basic plug-in with one method that takes a string, and returns another string.
  • ComplexPlugin – a plug-in that has several methods and handles more complex types.
  • SendPlugin – a plug-in that demonstrates passing contexts around and sending events.
  • SubscribePlugin – a plug-in that shows how to subscribe to receive events sent on a particular channel.

The samples\correlator_plugin\java directory contains the Java code for the samples, the EPL code for the Apama applications that call each of the plug-ins, the deployment descriptor files, and an Ant build.xml file for building all of the samples. The directory also contains a README.txt that describes how to build and run the samples as well as text files that depict what the output of the samples should be like.

A simple plug-in in Java

The simple plug-in sample can be found in the samples\correlator_plugin\java directory of your Apama installation.

The Java code for the SimplePlugin class contains the public static test method. (Methods that will be called from EPL code need to be public and static.)

public class SimplePlugin
{
  public static final String TEST_STRING = "Hello, World";
  public static String test(String arg)
  {
    System.out.println("SimplePlugin function test called");
    System.out.println("arg = "+arg);
    System.out.println("return value = "+TEST_STRING);
    return TEST_STRING;
  }
}

The SimplePlugin.xml file is the deployment descriptor and contains the following <plugin> stanza that illustrates how to specify the plug-in.

  <application-classes>
    <plugin>
      <plugin-name>SimplePlugin</plugin-name>
      <plugin-class>SimplePlugin</plugin-class>
      <description>A test plugin</description>
    </plugin>
  </application-classes>

The SimplePlugin.mon file contains the EPL code. It imports the plug-in and calls the test method.

monitor SimplePluginTest {
  // Load the plugin
  import "SimplePlugin" as simple;

// To hold the return value
  string ret;
  string arg;

  action onload() {
    // Call plugin function
    arg := "Hello, Simple Plugin";
       ret := simple.test(arg);

    // Print out return value
    log "simple.test = " + ret at INFO;
    log "arg = " + arg at INFO;
  }
}

A more complex plug-in in Java

The complex plug-in sample can be found in the samples\correlator_plugin\java directory of your Apama installation.

The Java code for the ComplexPlugin class contains the public static methods: test1, test2, test3, and test4. It also contains an object, ComplexChunk that represents a complex type.

The complex_plugin.xml file is the plug-in’s deployment descriptor and contains the <plugin> stanza that specifies the name, class, and description for the plug-in.

The sample’s ComplexPlugin.mon file contains the EPL code for the Apama application. It imports the plug-in and calls the various test*x* methods.

A plug-in in Java that sends events

The SendPlugin.java file in the samples\correlator_plugin\java directory of your Apama installation is a sample plug-in that shows how to pass contexts around and how to send events to specific contexts.

The Java class for the plug-in imports com.apama.epl.plugin.Context and com.apama.epl.plugin.Correlator and it declares a public method that sends an event to a channel and another public method that sends an event to a particular context.

The SendPlugin.xml deployment descriptor file contains the name, class, and description of the plug-in in the <plugin> stanza.

The Apama application SendPlugin.mon first imports the plug-in and then calls the plug-in’s sendEventToChannel() method as well as its sendEventTo() method with a variety of contexts.

A plug-in in Java that subscribes to receive events

The SubscribePlugin.java file in the samples\correlator_plugin\java directory of your Apama installation is a sample that shows how a plug-in subscribes to receive events sent on a particular channel.

The Java code for the SubscribePlugin class contains the public static createHandler method. (Methods that will be called from EPL code need to be public and static.)

The deployment descriptor file SubscribePlugin.xml contains the <plugin> stanza that illustrates how to specify the plug-in.

The EPL code in the file SubscribePlugin.mon imports the plug-in and calls the createHandler() method.