Skip to main content
Previous sectionNext section

Calling ObjectScript Methods and Functions

This chapter describes methods of class IRIS that allow you to call ObjectScript class methods and user-defined functions directly from your Java application. See the following sections for details and examples:

Class Method Calls

The following methods of class IRIS call ObjectScript class methods, returning values of the type indicated by the method name: classMethodBoolean(), classMethodBytes(), classMethodDouble(), classMethodIRISList(), classMethodLong(), classMethodObject(), classMethodString(), and classMethodVoid(). You can also use classMethodStatusCode() to retrieve error messages from class methods that return ObjectScript %Status (see “Catching %Status Error Codes”).

All of these methods take String arguments for className and methodName, plus 0 or more method arguments, which may be any of the following types: Integer, Short, String, Long, Double, Float, byte[], Boolean, Time, Date, Timestamp, IRISList, or IRISObject. If the connection is bidirectional (see “Using Java Reverse Proxy Objects”), then any Java object can be used as an argument.

Trailing arguments may be omitted in argument lists, either by passing fewer than the full number of arguments, or by passing null for trailing arguments. An exception will be thrown if a non-null argument is passed to the right of a null argument.

Calling ObjectScript class methods

The code in this example calls class methods of each supported datatype from ObjectScript test class User.NativeTest (listed immediately after the example). Assume that variable iris is a previously defined instance of class IRIS and is currently connected to the server.

  String className = "User.NativeTest";
  String comment = "";

  comment = "cmBoolean() tests whether two numbers are equal (true=1,false=0): ";
  boolean boolVal = iris.classMethodBoolean(className,"cmBoolean",7,7);
  System.out.println(comment+boolVal);

  comment = "cmBytes creates byte array [72,105,33]. String value of array:: ";
  byte[] byteVal = iris.classMethodBytes(className,"cmBytes",72,105,33);
  System.out.println(comment+(new String(byteVal)));

  comment = "cmString() concatenates \"Hello\" + arg: ";
  String stringVal = iris.classMethodString(className,"cmString","World");
  System.out.println(comment+stringVal);

  comment = "cmLong() returns the sum of two numbers: ";
  Long longVal = iris.classMethodLong(className,"cmLong",7,8);
  System.out.println(comment+longVal);

  comment = "cmDouble() multiplies a number by 1.5: ";
  Double doubleVal = iris.classMethodDouble(className,"cmDouble",10);
  System.out.println(comment+doubleVal);

  comment = "cmProcedure assigns a value to global array ^cmGlobal: ";
  iris.classMethodVoid(className,"cmVoid",67);
  // Read global array ^cmGlobal and then delete it
  System.out.println(comment+iris.getInteger("^cmGlobal"));
  iris.kill("cmGlobal");

  comment = "cmList() returns a $LIST containing two values: "; 
  IRISList listVal = iris.classMethodList(className,"cmList","The answer is ",42);
  System.out.println(comment+listVal.get(1)+listVal.get(2));
Copy code to clipboard
ObjectScript Class User.NativeTest

To run the previous example, this ObjectScript class must be compiled and available on the server:

Class User.NativeTest Extends %Persistent
{
ClassMethod cmBoolean(cm1 As %Integer, cm2 As %Integer) As %Boolean
{
    Quit (cm1=cm2)
}
ClassMethod cmBytes(cm1 As %Integer, cm2 As %Integer, cm3 As %Integer) As %Binary
{
    Quit $CHAR(cm1,cm2,cm3)
}
ClassMethod cmString(cm1 As %String) As %String
{
    Quit "Hello "_cm1
}
ClassMethod cmLong(cm1 As %Integer, cm2 As %Integer) As %Integer
{
    Quit cm1+cm2
}
ClassMethod cmDouble(cm1 As %Double) As %Double
{
    Quit cm1 * 1.5
}
ClassMethod cmVoid(cm1 As %Integer)
{
    Set ^cmGlobal=cm1
    Quit ^cmGlobal
}
ClassMethod cmList(cm1 As %String, cm2 As %Integer)
{
    Set list = $LISTBUILD(cm1,cm2)
    Quit list
}
}
Copy code to clipboard

You can test these methods by calling them from the Terminal. For example:

USER>write ##class(User.NativeTest).cmString("World")
Hello World
Copy code to clipboard

Function Calls

Function calls are similar to method calls, but the arguments are in a different order. The function label is specified first, followed by the name of the routine that contains it. This corresponds to the order used in ObjectScript, where function calls have the following form :

  set result = $$myFunctionLabel^myRoutineName([arguments])
Copy code to clipboard

Functions are supported because they are necessary for older code bases (see “Callable User-defined Code Modules” in Using ObjectScript), but new code should always use method calls if possible.

The Native API methods in this section call user-defined ObjectScript functions or procedures and return a value of the type indicated by the method name: functionBoolean(), functionBytes(), functionDouble(), functionIRISList(), functionObject(), functionLong(), functionString(), or procedure() (no return value).

They take String arguments for functionLabel and routineName, plus 0 or more function arguments, which may be any of the following types: Integer, Short, String, Long, Double, Float, byte[], Boolean, Time, Date, Timestamp, IRISList, or IRISObject. If the connection is bidirection (see Using Java Reverse Proxy Objects), then any Java object can be used as an argument.

Trailing arguments may be omitted in argument lists, either by passing fewer than the full number of arguments, or by passing null for trailing arguments. An exception will be thrown if a non-null argument is passed to the right of a null argument.

Note:
Built-in system functions are not supported

These methods are designed to call functions in user-defined routines. ObjectScript system functions (which start with a $ character. See “ObjectScript Functions” in the ObjectScript Reference) cannot be called directly from your Java code. However, you can call a system function indirectly by writing an ObjectScript wrapper function that calls the system function and returns the result. For example, the fnList() function (at the end of this section in ObjectScript Routine NativeRoutine.mac) calls $LISTBUILD.

Calling functions of ObjectScript routines with the Native API

The code in this example calls functions of each supported datatype from ObjectScript routine NativeRoutine (File NativeRoutine.mac, listed immediately after this example). Assume that iris is an existing instance of class IRIS, and is currently connected to the server.

  String routineName = "NativeRoutine";
  String comment = "";

  comment = "fnBoolean() tests whether two numbers are equal (true=1,false=0): ";
  boolean boolVal = iris.functionBool("fnBoolean",routineName,7,7);
  System.out.println(comment+boolVal);

  comment = "fnBytes creates byte array [72,105,33]. String value of the array: ";
  byte[] byteVal = new String(iris.functionBytes("fnBytes",routineName,72,105,33));
  System.out.println(comment+(new String(byteVal)));

  comment = "fnString() concatenates \"Hello\" + arg: ";
  String stringVal = iris.functionString("fnString",routineName,"World");
  System.out.println(comment+stringVal);

  comment = "fnLong() returns the sum of two numbers: ";
  Long longVal = iris.functionInt("fnLong",routineName,7,8);
  System.out.println(comment+longVal);

  comment = "fnDouble() multiplies a number by 1.5: ";
  Double doubleVal = iris.functionDouble("fnDouble",routineName,5);
  System.out.println(comment+doubleVal);

  comment = "fnProcedure assigns a value to global array ^fnGlobal: ";
  iris.procedure("fnProcedure",routineName,88);
  // Read global array ^fnGlobal and then delete it
  System.out.println(comment+iris.getInteger("^fnGlobal")+"\n\n");
  iris.kill("fnGlobal");

  comment = "fnList() returns a $LIST containing two values: "; 
  IRISList listVal = iris.functionList("fnList",routineName,"The answer is ",42);
  System.out.println(comment+listVal.get(1)+listVal.get(2));
Copy code to clipboard
ObjectScript Routine NativeRoutine.mac

To run the previous example, this ObjectScript routine must be compiled and available on the server:

fnBoolean(fn1,fn2) public {
   quit (fn1=fn2)
}
fnBytes(fn1,fn2,fn3) public {
    quit $CHAR(fn1,fn2,fn3)
}
fnString(fn1) public {
    quit "Hello "_fn1
}
fnLong(fn1,fn2) public {
    quit fn1+fn2
}
fnDouble(fn1) public {
    quit fn1 * 1.5
}
fnProcedure(fn1) public {
    set ^fnGlobal=fn1
    quit
}
fnList(fn1,fn2) public {
    set list = $LISTBUILD(fn1,fn2)
    quit list
}
Copy code to clipboard

You can test these functions by calling them from the Terminal. For example:

USER>write $$fnString^NativeRoutine("World")
Hello World
Copy code to clipboard

Calling Class Library Methods

Most of the classes in the InterSystems Class Library use a calling convention where methods only return a %Status value. The actual results are returned in arguments passed by reference. This section describes how to pass by reference and read %Status values.

Using Pass-by-reference Arguments

The Native API supports pass by reference for both methods and functions. To pass an argument by reference, assign the argument value to an instance of class IRISReference and pass that instance as the argument:

  IRISReference valueRef = new IRISReference(""); // set inital value to null string
  iris.classMethodString("%SomeClass","SomeMethod",valueRef); 
  String myString = valueRef.value;  // get the method result
Copy code to clipboard

Here is a working example:

Using pass-by-reference arguments

This example calls %SYS.DatabaseQuery.GetDatabaseFreeSpace() to get the amount of free space (in MB) available in the iristemp database.

  IRISReference freeMB = new IRISReference(0); // set inital value to 0
  String dir = "C:/InterSystems/IRIS/mgr/iristemp"; // directory to be tested
  Object status = null;

  try {
    System.out.print("\n\nCalling %SYS.DatabaseQuery.GetDatabaseFreeSpace()... ");
    status = iris.classMethodObject("%SYS.DatabaseQuery","GetDatabaseFreeSpace",dir,freeMB);
    System.out.println("\nFree space in " + dir + " = " + freeMB.value + "MB");
  } 
  catch (RuntimeException e) {
    System.out.print("Call to class method GetDatabaseFreeSpace() returned error:");
    System.out.println(e.getMessage());
  }
Copy code to clipboard

prints:

Calling %SYS.DatabaseQuery.GetDatabaseFreeSpace()...
Free space in C:/InterSystems/IRIS/mgr/iristemp = 8.9MB
Copy code to clipboard

Catching %Status Error Codes

When a class method has ObjectScript %Status as the return type, you can use classMethodStatusCode() to retrieve error messages. When a class method call fails, the resulting RuntimeException error will contain the %Status error code and message.

In the following example, the ValidatePassword() method returns a %Status object. If the password is invalid (for example, password is too short) an exception will be thrown and the %Status message will explain why it failed. Assume that variable iris is a previously defined instance of class IRIS and is currently connected to the server.

Using classMethodStatusCode() to catch ObjectScript %Status values

This example passes an invalid password to %SYSTEM.Security.ValidatePassword() and catches the error message.

  String className = "%SYSTEM.Security";
  String methodName = "ValidatePassword";
  String pwd = ""; // an invalid password
  try {
// This call will throw a RuntimeException containing the %Status error message:
    iris.classMethodStatusCode(className,methodName,pwd);
// This call would fail silently or throw a generic error message:
    Object status = iris.classMethodObject(className,methodName,pwd);
    System.out.println("\nPassword validated!");

  } catch (RuntimeException e) {
    System.out.println("Call to "+methodName+"(\""+pwd+"\") returned error:");
    System.out.println(e.getMessage());
  }
Copy code to clipboard

Notice that this example deliberately calls a method that does not use any pass by reference arguments.

To experiment with a more complex example, you can try catching the status code in the previous example (Using pass-by-reference arguments). Force an exception by passing an invalid directory.

Note:
Using IRISObject.invokeStatusCode() when calling Instance methods

The classMethodStatusCode() method is used for class method calls. When you are invoking proxy object instance methods (see “Using Java Reverse Proxy Objects”) the IRISObject.invokeStatusCode() method can be used in exactly the same way.