Greetings from the Edge:
Using JavaObj in DATA Step

by Richard A. DeVenezia, Back to Home

You are viewing Examples 1 to 10

javaobj requires SAS version 9. javaobj is experimental in the first release of version 9.

ToggleIntroduction

Welcome to the javaobj examples page. There is much to examine, but the really advanced stuff is the Gateway.

Before you can run any of the examples you need to start a SAS session within an environment having a CLASSPATH variable. For Windows I have found that I need the Java Runtime Environment installed.

$ sas.exe -set CLASSPATH ./examples/classes

ToggleExample 1

Hello SAS

Example1.java
public class Example1 {
  public Example1 () {}
  public String getMessage () {return "Hello SAS";}
}
Example1.sas
data _null_;
  length message $200;
  declare javaobj j ('Example1');
  j.callStringMethod ('getMessage', message);
  put message=;
run;

ToggleExample 2

Hello Java

Example2.java
public class Example2 {
  private String message;
  public Example2(String newMessage) {
    this.setMessage(newMessage);
  }
  public String getMessage() {
    return this.message;
  }
  public void setMessage(String newMessage) {
    this.message = newMessage;
  }
}
Example2.sas
data _null_;
  length message $20;
  declare javaobj j ('Example2', 'Hello Java');
  j.callStringMethod ('getMessage', message);
  put message=;
run;

ToggleExample 3

Class does not exist on CLASSPATH.

Example3.sas
data _null_;
  declare javaobj j ('ClassFooBarDoesNotExist');
run;

ToggleExample 4

java.net.URI.http() does not have signature String-String. juri is thus unitialized and will cause another error.

Example4.sas
data _null_;
  length s $200;
  declare javaobj juri ('java/net/URI', 'http', 'www.sas.com');
  juri.callStringMethod ('toString', s);
  put s=;
run;

ToggleExample 5

java.net.URI.getLastName() does not exist.

Example5.sas
data _null_;
  length s $200;
  dcl javaobj juri ('java/net/URI', 'www.sas.com');
  juri.callStringMethod ('getLastName', s);
  put s=;
run;

ToggleExample 6

java.net.URI.toString() does not have signature String-Double.

Example6.sas
data _null_;
  length s $200;
  dcl javaobj juri ('java/net/URI', 'www.sas.com');
  juri.callStringMethod ('toString', s, 100);
  put s=;
run;

ToggleExample 7

Wrappers

This example shows how a message dialog can be displayed from Java. This is a useful concept for situations where the course of action in a Java class is dependent on user input. Obtaining and handling the user input directly in Java is more convenient than dropping back to SAS DATA Step to obtain and respond to the user input.

The example is coded in an adapter pattern. showOptionDialog() and showMessageDialog() methods of the javax.swing.JoptionPane class are inaccessible to javaobj since they require int type arguments. The adapter invokes the methods for SAS passing the correct types.

You can debug your class development outside of SAS by using a main() to run the parts you want to test. main() can be run from a terminal or command prompt. The following command assumes the java class is located in the current directory.

$ java -cp . Example7

Example7.java
import javax.swing.JOptionPane;

public class Example7
{
  private String optionSelected;

  public Example7() {}

  public void showMessageDialog
                     (String message, String title)
  {
   JOptionPane.showMessageDialog(
   null, message, title,
   JOptionPane.INFORMATION_MESSAGE);
  }

  public int showOptionDialog
                     (String message, String title)
  {
    return showOptionDialog(message,title,null);
  }

  public int showOptionDialog
    (String message, String title, String _options)
  {
    String[] options;

    if (_options == null)
      options = "Yes,No".split(",");
    else
      options = _options.split(",");

    int choice = -1;
    optionSelected = null;
    try {
      choice = JOptionPane.showOptionDialog(
        null, message, title,
        JOptionPane.DEFAULT_OPTION,
        JOptionPane.QUESTION_MESSAGE, null,
        options, options[0]);
      if (choice >= 0)
        optionSelected = options[choice];
    }
    catch (Exception e) {
      JOptionPane.showMessageDialog(null,e,
      "Exception in showOptionDialog",
      JOptionPane.WARNING_MESSAGE);
    }
    return choice;
  }

  public String getOptionSelected () {
    return this.optionSelected;
  }

  public static void main (String arg[]) {
      Example7 me = new Example7 ();
      int answer = me.showOptionDialog (
        "Two plus Two equals", "Math question",
        "Zero,One,Two,Three,Four,Five");
      System.out.println ("You selected option " +
        answer + " " + me.getOptionSelected());
      me.showMessageDialog ("Thanks for playing","");
      System.exit(0);
  }
}
Example7.sas
data _null_;

&Example7_Stop;

  dcl javaobj jd ('Example7');
  jd.callIntMethod ('showOptionDialog',
    'Two plus Two equals','Math question',
    'Zero,One,Two,Three,Four,Five', x);
  put x=;
  length s $100;
  jd.callStringMethod ('getOptionSelected', s);
  put s=;
  if x = 4
    then answer = trim(s)||' is correct'||'0a'x;
    else answer = trim(s)||' is incorrect'||'0a'x;

  jd.callVoidMethod ('showMessageDialog',
   trim(answer)||'Thank you for playing', 'Aloha');
run;

ToggleExample 8

Java Environment

This example demonstrates how non-trivial tasks require more Java programming than SAS programming. It also shows how looping can be defered by having method that performs a loop iteration [getProperty()] each time it is invoked by an external agent (SAS in this case). The agent is responsible for properly recognizing the end of loop condition.

In programming Java applications, it is often important to know what version of the VM is running. The javaobj interface is still experimental and there is no documentation on how it selects the VM it will use. This example will reveal the properties of the VM that is hosting the SAS javaobj.

Example8 is a wrapper class that surfaces the Enumeration returned by the static method System.getProperties().

By assuming all properties are not blank, the class can return a null to indicate the last property has been delivered. SAS uses the blank string (null) as the condition for not requesting more properties.

Example8.java
import java.util.Enumeration;

public class Example8 {
  private Enumeration e;
  public Example8 () {
    e = System.getProperties().propertyNames();
  }
  public String getProperty () {
    if (e.hasMoreElements()) {
      String p = (String) e.nextElement();
      return p + "=" + System.getProperty(p);
    }
    else {
      return null;
    }
  }
}
Example8.sas
data _null_;
  dcl javaobj j ('Example8');
  length s $200;
  j.callStringMethod ('getProperty', s);
  do while (s ne '');
    put s;
    j.callStringMethod ('getProperty', s);
  end;
run;

ToggleExample 9

Class throws an exception because file named 'foobar.file' does not exist.

Often a SAS program will need to respond to an exception thrown by a Java method. The default behavior is to generate a SAS error. Catching exceptions and making information about the exception available should be part of the design of a wrapper class.

According to a little birdie:
SAS version 9.1 will have new Javaobj methods for dealing with exceptions:
- exceptionCheck ()
- exceptionClear ()
- exceptionDescribe ()

This example is a wrapper for FileInputStream, when the constructor throws an exception it generates a SAS error.

Example9.java
import java.io.FileInputStream;

public class Example9 {
  private FileInputStream fis;
  public Example9 (String path) throws Exception
  {
    this.fis = new FileInputStream (path);
  }
}
Example9.sas
data _null_;
  dcl javaobj j ('Example9', 'foobar.file');
run;

ToggleExample 10

Example 10 is Example 9 modified to catch the Exception and show the exception message in the SAS log. Since newlines in a string are not newlines in the SAS log when 'put', each line must be parsed out and put separately.

Example10.java
import java.io.FileInputStream;

public class Example10 {
  private FileInputStream fis;
  private String exceptionMessage;
  public Example10(String path) {
    try {
      this.fis = new FileInputStream (path);
    }
    catch (Exception e) {
      this.exceptionMessage = e.toString();
      for (int i=0;i<e.getStackTrace().length; i++)
      {
        this.exceptionMessage
        += "\n" + e.getStackTrace()[i];
      }
    }
  }
  public String getExceptionMessage () {
    return this.exceptionMessage;
  }
}
Example10.sas
data _null_;
  dcl javaobj j ('Example10', 'foobar.file');
  length s $1000;
  j.callStringMethod ('getExceptionMessage', s);
  if s ne '' then do;
    put "Exception message from java";
    i = 1;
    do while (scan (s,i,'0a'x) ne '');
      l = scan (s,i,'0a'x);
      i+1;
      put l;
    end;
  end;
run;

ToggleDo all examples

Rather than run each example individually, you may want to compile and run everything in one go. A zip file has been constructed for this. Extract the files, maintaining the folders.

All-Examples.zip

You should adjust the batch file to meet the conditions of your SAS installation.

do-examples.bat
rem Windows batch file
rem Easily tweaked for other OS


rem path to SAS

set SAS9="c:\opt\sas\v9\sas.exe"


rem compile example java into classes and combine into a jar

mkdir classes
javac -d classes/ java/*.java
cd classes
jar cf ..\examples.jar *.class
cd ..


rem run examples

%SAS9% -sysin do-examples.sas -pagesize 10000 -set CLASSPATH examples.jar


rem examine log

notepad do-examples.log