Call Java methods from SAS/SCL, SAS/Macro (and of course from a SAS Data Step)

Introduction

The possibility of calling Java from a SAS program is available, but only with some restrictions. First of all, a Java class can only be accessed inside a SAS Data Step via JavaObj. Do not ask me why this is so. I do not know.

Dear developers at SAS, why is that JavaObj not available in SCL?

The next limitation is the lifetime.  A class or object only exists during the execution of the Data Step in which it was defined. Using the same class/object across different Data Steps is not possible. The third limitation is the type of methods, which can be called. Only methods with double and String arguments can be called.

But there is a way to lift these restrictions.

In SAS you have the possibility to call functions in external libraries via call module, modulen and modulec. So why not call the functions in jvm library directly? Nice idea, but to call these functions, a JNI (Java Native Interface) environment must exist which cannot be instantiated in SAS via call module. So no way to call it directly.

The solution is to use a proxy which handles the JNI environment and forwards all calls from SAS to Java.

Installation

Download

Operating System32 bit SAS64 bit SAS
Windowsproxy4jvm_x64.zipproxy4jvm_win32.zip
LinuxIn progressIn progress

If you are interested in further supported environments, or professional services, please call us or send us a message.

Install

Download the appropriate archive an extract it. The archive contains two files: the proxy and a dat-file which defines how the proxy functions have to be called. Place the proxy anywhere in your path or add the directory to your PATH.

In your autoexec.sas add the following lines. Replace the placeholder with the values which meets your system.

filename sascbtbl "path-to-sascbtbl/sascbtbl_arch.dat";
data _null_;
    call module("SetupJVMInstance", "path-to-jvm.dll\jvm.dll", 
         "insert-your-classpath");
run;

or as macro call:

%let func=SetupJVMInstance;
%let jvm=%quote(path-to-jvm.dll\jvm.dll);
%let cp = insert-your-classpath;
%syscall module(func, jvm, cp);

If you omit the value for classpath the proxy uses the value(s) from CLASSPATH environment variable. The jvm version has to be the same version (32 or 64 bit) as your SAS installation and your proxy. If you use a 32 bit SAS, whether on a 32 or 64 bit system, you have to use a 32 bit JRE and the 32 bit version of proxy.

That’s all.

How to use

Loading a class

To use a class, whether you what to call a static method or create a new instance, you need to load it into Java Virtual Machine. You can do this with the LoadClass function.

data _null_;
     length classref 8;
     classref = modulen("LoadClass", "testClass");
run;

The return value is a reference to the loaded class. Return value of 0 means that the class could not be loaded. With this class reference you can call static methods, set/get values or create new instances.

Access non-static and static fields

The proxy provides different functions to set and get values from (static) fields. Function calls to access fields or to modify fields have one of the following forms, depending on whether you are accessing non-static or static fields:

value = modulen("GetStatictypeField", classref, field name);
value = modulen("GettypeField", objectref, field name);

or for string and char fields

value = modulec("GetStatictypeField", classref, field name);
value = modulec("GettypeField", objectref, field name);

To modify fields use this function:

call module("SetStatictypeField", classref, field name, value);
call module("SettypeField", objectref, field name, value);

The type represents one of the following Java data types: Int, Long, Float, Double, Object, Short, Char, Boolean, String, Byte.

Example

Java source:

public class testClass {

    public static int intValue = 1;
}

SAS Statement:

data _null_;
     length classref 8;

     classref = modulen("LoadClass", "testClass");
     value = modulen("GetStaticIntField", classref, "intValue");
run;

Calling Java methods

To call a Java method it is important to know the correct method signature, because more than one method can have the same name. The signature of a method consists of the data types of its arguments plus the return value. The following table shows how the data types must be converted to the appropriate codes.

Argument data type Signature code
boolean Z
byte B
char C
double D
float F
integer I
long J
short S
java.lang.String X
any other Java object O

Function calls to call a Java method have one of the following forms, depending on whether you are calling a non-static or static method:

value = modulen("CallStatictypeMethod", classref, method name, signature, <argument-1>,<argument-2>,… );
value = modulen("CalltypeMethod", objectref, method name, signature, <argument-1>,<argument-2>,… );

or for string and char methods

value = modulec("CallStatictypeMethod", classref, method name, signature, <argument-1>,<argument-2>,… );
value = modulec("CalltypeMethod", objectref, method name, signature, <argument-1>,<argument-2>,… );

and for void methods

call module("CallStaticVoidMethod", classref, method name, signature, <argument-1>,<argument-2>,… );
call module("CallVoidMethod", objectref, method name, signature, <argument-1>,<argument-2>,… );

Example

public static int myIntMethod(long arg1, boolean arg2, String arg3) {
  ....
}

To call these function from SAS

value = modulen("CallStaticIntMethod", classref, "myIntMethod", "JZX", 20, 1, 
     "this is a string");

J=long, Z=Boolean and X=String

Creating new objects from class

Just as the invocation of methods the creation of new objects works as well.

objectref = modulen(“NewObject”, classref, signature, <argument-1>,<argument-2>,… );

If you have any questions or suggestions, do not hesitate to contact us.

comments powered by Disqus