Using the Java Function API

You can call custom Java functions directly in StreamBase expressions by using the calljava function (or by using an alias defined in your configuration file). The StreamBase Java API provides two forms of the calljava function, to be used in StreamBase simple and aggregate expressions.

For detailed documentation on the calljava expressions and the StreamBase Java API, please follow the links in Related Topics. In addition, the StreamBase kit provides two samples that demonstrate the use of custom Java simple and aggregate functions. Examining and running the samples can help you understand the principles described in this topic. The samples are installed in these directories:

On Windows

C:\Program Files\StreamBase Systems\StreamBase.n.m\sample\custom-java-function\

C:\Program Files\StreamBase Systems\StreamBase.n.m\sample\custom-java-aggregate\

On UNIX

/opt/streambase/sample/custom-java-function/

/opt/streambase/sample/custom-java-aggregate/

This topic provides guidelines for creating StreamBase custom functions in Java. It describes:

Creating Custom Java Simple Functions

Simple custom Java functions can be called in several components, excluding the Aggregate operator. To create custom Java functions:

For example, RandomFun.java in the sample application declares this class and method:

public class RandomFun {
    private static Random rand = new Random();
    
    public static int random(int maxVal) {
        return rand.nextInt(maxVal);
    }
}

At compile time, the calljava implementation looks for only a single method matching the exact types of the function call in the StreamBase expression. But there can be multiple matching methods, such as these two functionally equivalent ones:

public static boolean isZero(int i) { return i == 0; }
public static Boolean isZero(Integer i) {
    return i == null ? null : Boolean.valueOf(i.intValue() == 0);
}

If this case occurs, StreamBase throws an error.

Strings

A java.lang.String may be used anywhere a byte[] is acceptable (an argument or return value). In this case the StreamBase string is transparently converted to or from a java.lang.String using the system default encoding.

Creating Custom Java Aggregate Functions

To create custom Java functions that are called by the aggregate version of calljava (that is, in an aggregate function expression):

  • Define a Java class that extends the com.streambase.sb.operator.AggregateWindow class.

  • Observe the guidelines in Method Parameter and Return Types

  • Observe the other specific guidelines in this section.

Consider the following annotated example:

package com.mycompany;

import com.streambase.sb.operator.AggregateWindow;

public class MyStdev extends AggregateWindow {                                   1
    private double sum;
    private double sumOfSquares;
    private int count;

    public void init() {                                                         2
        sum = sumOfSquares = 0.0;
        count = 0;
    }          

    public double calculate() {                                                  3
        return Math.sqrt((count * sumOfSquares - sum*sum) / count*(count-1));
    }

    public void accumulate(double value) {                                       4
        sum += value;
        sumOfSquares += value*value;
        ++count;
    }

    public void release() { /* nothing to release in this example */ }           5
}

The following annotations describe points of interest in the preceding example:

1

Declare a public class that extends the AggregateWindow class (as documented in the StreamBase Java API).

2

The init() method is called at the start of each new use of the class. Since custom aggregate objects are likely to be reused, perform all initialization in init() rather than in the constructor. (The constructor is called only once, while init() is called before each use.)

3

Your implementation must contain a calculate() method that takes no arguments and returns a value that is convertible to a StreamBase data type. The calculate() method may be called several times, or not at all. If your calculate() method returns a String or byte[], you must also implement a getResultLength() method, as described in the next section.

4

Your implementation must provide at least one accumulate() method, and can optionally provide several overloaded accumulate() methods, one per data type. calljava determines which one to call based on type. The argument types for accumulate() and the return type for calculate can be any of the types described in the table in Method Parameter and Return Types.

5

The release() method is called at the end of each use of the class.

Method Parameter and Return Types

The method can have any number of parameters (including none). Each parameter must be a Java primitive or object type corresponding to a StreamBase type as shown in the table below:

StreamBase Type Java Primitive Java Object
blob None: use the Java object com.streambase.sb.ByteArrayView
bool boolean Boolean
int int Integer
double double Double
long long Long
timestamp None: use the Java object com.streambase.sb.Timestamp
string byte[] String

Notes

  • The return type cannot be void: it must be one of the Java Primitive or Java Object types shown above.

  • If your calculate() method returns a String or byte array, you must also implement a method with the following signature: public static int getResultLength(). This method must return the length of the returned String or byte array, or a constant value that represents the length.

  • If any value of a parameter with a primitive type is null at runtime, the method that implements the custom function is not invoked. However, Java Object parameters types can be used to pass in null parameter values.

  • If a parameter's type is byte[] and its value is null, it is represented as a Java null. Likewise, if a Java method with a byte[] return type returns a null, the calling StreamBase expression will see the return value as string(null).

  • There is no Java representation for a null primitive int or boolean value. If a StreamBase custom function call would involve converting a StreamBase int(null) or bool(null) value to a primitive Java int or boolean, your method is not called, and null is assumed as the return value.

    public static boolean isZero(int i) { return i == 0; }
    
    calljava("TheClass", "isZero", 1)          /* false *
    calljava("TheClass", "isZero", 0)          /* true */
    calljava("TheClass", "isZero", int(null))   /* null */