Java language scalar function
This example uses the following file name:
TestJavaInterface.java
Code
The code in this example shows that, with minor changes, this program works in remote mode. The change is in the main where a loop is added around getAPI so that it gets called two times. If the connection is remote, it is handed off to be run by a new thread and to wait for a new connection. Finally, if the connection is not remote, break out of the loop after the first iteration. This code works like a local AE and handles two iterations as a remote AE.
import org.netezza.ae.*;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class TestJavaInterface {
private static final Executor exec =
Executors.newCachedThreadPool();
public static final void main(String [] args) {
try {
mainImpl(args);
} catch (Throwable t) {
System.err.println(t.toString());
NzaeUtil.logException(t, "main");
}
}
public static final void mainImpl(String [] args) {
NzaeApiGenerator helper = new NzaeApiGenerator();
while (true) {
final NzaeApi api = helper.getApi(NzaeApi.FUNCTION);
if (api.apiType == NzaeApi.FUNCTION) {
if (!helper.isRemote()) {
run(api.aeFunction);
break;
} else {
Runnable task = new Runnable() {
public void run() {
try {
TestJavaInterface.run(api.aeFunction);
} finally {
api.aeFunction.close();
}
}
};
exec.execute(task);
}
}
}
helper.close();
}
public static class MyHandler implements NzaeMessageHandler
{
public void evaluate(Nzae ae, NzaeRecord input, NzaeRecord output) {
final NzaeMetadata meta = ae.getMetadata();
int op = 0;
double result = 0;
if (meta.getOutputColumnCount() != 1 ||
meta.getOutputNzType(0) != NzaeDataTypes.NZUDSUDX_DOUBLE) {
throw new NzaeException("expecting one output column of type
double");
}
if (meta.getInputColumnCount() < 1) {
throw new NzaeException("expecting at least one input column");
}
if (meta.getInputNzType(0) != NzaeDataTypes.NZUDSUDX_FIXED &&
meta.getInputNzType(0) != NzaeDataTypes.NZUDSUDX_VARIABLE) {
throw new NzaeException("first input column expected to be a
string type");
}
for (int i = 0; i < input.size(); i++) {
if (input.getField(i) == null) {
continue;
}
int dataType = meta.getInputNzType(i);
if (i == 0) {
if (!(dataType == NzaeDataTypes.NZUDSUDX_FIXED
|| dataType == NzaeDataTypes.NZUDSUDX_VARIABLE)) {
ae.userError("first column must be a string");
}
String opStr = input.getFieldAsString(0);
if (opStr.equals("*")) {
result = 1;
op = OP_MULT;
}
else if (opStr.equals("+")) {
result = 0;
op = OP_ADD;
}
else {
ae.userError("invalid operator = " + opStr);
}
continue;
}
switch (dataType) {
case NzaeDataTypes.NZUDSUDX_INT8:
case NzaeDataTypes.NZUDSUDX_INT16:
case NzaeDataTypes.NZUDSUDX_INT32:
case NzaeDataTypes.NZUDSUDX_INT64:
case NzaeDataTypes.NZUDSUDX_FLOAT:
case NzaeDataTypes.NZUDSUDX_DOUBLE:
case NzaeDataTypes.NZUDSUDX_NUMERIC32:
case NzaeDataTypes.NZUDSUDX_NUMERIC64:
case NzaeDataTypes.NZUDSUDX_NUMERIC128:
switch (op) {
case OP_ADD:
result +=
input.getFieldAsNumber(i).doubleValue();
break;
case OP_MULT:
result *=
input.getFieldAsNumber(i).doubleValue();
break;
default:
break;
}
break;
default:
break;
}
} // end of column for loop
output.setField(0, result);
}
}
private static final int OP_ADD = 1;
private static final int OP_MULT = 2;
public static int run(Nzae ae)
{
ae.run(new MyHandler());
return 0;
}
}
Compilation
Use the standard compile as in local mode:
$NZ_EXPORT_DIR/ae/utilities/bin/compile_ae --language java --template \
compile TestJavaInterface.java --version 3
Registration
Registration is slightly different. First run this to register:
$NZ_EXPORT_DIR/ae/utilities/bin/register_ae --sig "rem_applyop_java(varargs)" \
--return "double" --class AeUdf --language java --template udf \
--define "java_class=TestJavaInterface" --version 3 --remote \
--rname testjavapi
This specifies the code be registered as a remote ae with a remote name of testjavapi, which matches the code. In addition, a launcher is registered:
$NZ_EXPORT_DIR/ae/utilities/bin/register_ae --sig "rem_applyop_launch(int8)" \
--return "TABLE(aeresult varchar(255))" --class AeUdtf --language java \
--template udtf --define "java_class=TestJavaInterface" --version 3 \
--remote --rname testjavapi --launch
With the exception of the --rname, --exe and the name portion of --sig, all launchers look like the above. In this case it is a UDTF, since that is the interface for all launchers. The value of --rname must match the name in the code.
Running
To run the AE in remote mode, the executable is run as a “server.” In this instance it handles only queries run on the host. Usually, AEs are started on the SPUs as well. Start an instance on the host:
SELECT * FROM TABLE WITH FINAL(rem_applyop_launch(0));
AERESULT
-------------------------------------------------------------------------
tran: 14896 session: 18286 DATA slc: 0 hardware: 0 machine: bdrosendev process:
15937 thread: 15938
(1 row)
Notice that a different syntax is used to invoke the table function style launcher. This is the syntax used to call any UDTF-based AE. Now run the AE:
SELECT rem_applyop_java('+', 4,5,1.1);
REM_APPLYOP_JAVA
------------------
10.1
(1 row)
Finally, cause an error:
SELECT rem_applyop_java(1);
ERROR: first input column expected to be a string type