Create plugin functions
In addition to creating new input, output, and transform tools that appear on the palette, you can also use the RedPoint Data Management (RPDM) SDK to create new functions. These functions automatically inject themselves into the expression language used by the Calculate and Filter tools, and by the Expression step. They are "first class" functions: they appear in the function insert menu and the expression editor's auto-completion lists.
Writing a new function to plug into RPDM is like writing a method in Java, with a few restrictions and annotations. To create a new plugin function, follow these steps:
The fundamentals
Create a new public class, extending either
FunctionInstance
orFunctionSingleton
from thenet.redpoint.function
package.Implement the
getCategory()
function to return the category (or submenu) under which the functions will be listed in the expression-builder menu.Create static methods in a
FunctionSingleton
subclass, or non-static methods in aFunctionInstance
subclass.Plugin functions must be public.
Plugin functions must return a value, they cannot be void.
Plugin functions must have the
@Function
annotation, from thenet.redpoint.function.annotations
package.Make a resource under
META-INF.services
listing your classes.
FunctionSingleton vs FunctionInstance
RPDM provides two classes that you can extend to create new plugin functions, and you must choose which one to extend. These classes serve distinct and different purposes.
FunctionSingleton
is appropriate for functions that don't need an object instance per use—and most functions don't. So you will usually extend FunctionSingleton
. The following code demonstrates a function that divides two numbers:
CODE
|
Because the divide()
function doesn't need any state or objects, it can be implemented in a subclass of FunctionSingleton
. Some things to note about creating functions in a subclass of FunctionSingleton
:
Methods must be static.
No instance of your class is created when your function is used; however, an instance will be created as part of inserting it into the expression language.
You may use static fields in your class.
If you use static fields in your class, your methods should have the
synchronized
keyword to serialize access to the fields, because the same object will be used for all instances of the expression.
It is perfectly fine to use FunctionSingleton
even if your function maintains state, provided that state can be shared and access to it synchronized:
CODE
|
You should only extend FunctionInstance
(rather than FunctionSingleton
) if your function is both of these things:
Expensive to execute, and
Uses fields of your class.
For example, suppose that you are writing a plugin function that uses a library to carry out its work. You could implement this by extending FunctionSingleton
:
CODE
|
Note that we have to add the synchronized
keyword to the method to serialized access to "thing". But this means that multiple instances of this function used in separate methods will have to wait for access to the singleton thing.
To improve performance, extend FunctionInstance
instead. This will cause a separate instance of your class to be created for every function used in an expression, and there will be no penalty for parallelism:
CODE
|
Supported Types
RPDM can process only certain data types. Use only the following types for arguments and return values in your methods:
java.lang.Boolean, boolean
java.lang.Byte, byte
java.lang.Short, short
java.lang.Integer, int
java.lang.Long, long
java.math.BigDecimal
java.lang.Float, float
java.lang.Double, double
org.joda.time.LocalDateTime
org.joda.time.LocalDate
org.joda.time.LocalTime
java.sql.Timestamp
java.sql.Date
java.sql.Time
java.lang.String
byte[]
@Function
annotation
You must precede your method definition with an @Function
annotation. RPDM will ignore methods that lack this annotation.
Argument names
RPDM uses Java reflection to determine the signatures of the plugin functions. However, while argument types are returned by reflection, argument names are not. Use the argName
property of the @Function
annotation to make RPDM aware of the argument names. The property shown below contains a comma-separated list of names. If this is missing, RPDM will make up some names for you, but they will not be very helpful.
CODE
|
META-INF.services resource
RPDM uses the Java ServiceLoader to list classes that contain plugin functions. In order to "publish" your function class, you'll need to create a text-file resource in the META-INF.services package whose name matches the base class(es) being extended. For example, in the NetBeans IDE, functions implemented by extending FunctionInstance
will have a resource that resembles this:
That resource file contains a simple list of all the classes you want to publish. For example, RPDM uses this mechanism internally to create and publish its own set of "Hmac" cryptography functions. Its resource file looks like this:
CODE
|
Conversions, error values, and null values
RPDM will automatically convert types where possible. For example, if your function takes a String
, but you pass it an Integer
, RPDM will format the integer into a string and pass it to your function. However, not all conversions are possible. For example RPDM will not convert numbers into Date
, Time
, or DateTime
values. If a non-convertible value is passed to a function, RPDM will return the special "Error" value on behalf of the function. If a null
value is passed to your function, it will be turned into a "default" value instead, such as 0 for integers or Jan 1 1600 for Dates
.