Using Java plug-ins
After you create a correlator 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, as is done with correlator plug-ins written in C or C++.
Injecting
The .jar file containing the correlator 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/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 plugin 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 classpath in deployment descriptor files for more details.
Deleting
A correlator 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 plugin 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
Correlator 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
Correlator 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 enqueue(String evt);
public static native void enqueueTo(String evt, Context c);
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.
enqueue(String) – Adds the event represented in
String to the back of the input queue of all public contexts. This method is expected to be deprecated and then removed in future releases. Use a
sendTo() method instead.
enqueueTo(String, Context) – Adds the event represented in
String to the back of the input queue of the specified context. This method is expected to be deprecated and then removed in future releases. Use a
sendTo() method instead.
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
Javadoc for the JMon APIs .
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
Catching exceptions.
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.
Persistence
No Java plug-ins are persistent and they are not permitted in a persistent monitor, but they are permitted in non-persistent monitors in a persistent correlator.
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
Correlator plug-ins written in Java can log to the correlator's log file. This is done via the com.apama.util.Logger class. Each plug-in must create a static instance of the 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. The level is configured either by means of the correlator command line and management commands or using a log4j configuration file.
For more information on using the
Logger class, including how to override the default log level, see the
Apama Javadoc reference material.
The following is an example of logging in a correlator plug-in:
package test;
import com.apama.util.Logger;
public class Plugin
{
private static final Logger logger = Logger.getLogger(Plugin.class);
public static void foo()
{
logger.info("A string that's logged at INFO");
}
}
This will produce entries in the correlator log file like this:
2013-06-11 15:14:21.974 INFO [1167792448:processing] - <test.Plugin> A string that's logged at INFO