Apama 10.7.2 | Developing Apama Applications | Developing EPL Plug-ins | Writing EPL Plug-ins in Java | Using EPL plug-ins written in Java
 
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, which enables support for JMon applications. 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 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 TestPlugin and calls its dosomething method:
monitor m {
import "TestPlugin" as test;
action onload()
{
test.dosomething();
}
}
Note, if the plug-in .jar 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 JMon or 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 application .jar 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, as with JMon applications. 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 Software AG 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 Software AG installation.