Greetings from the Edge:
Using JavaObj in DATA Step

by Richard A. DeVenezia, Back to Home

You are viewing Database Gateway (RMI)

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

ToggleIntroduction

Why the term gateway ?

Think of a manned gate in a fence, to pass from one side to the other you need to pass through the gate. Where you are coming from and where you are going is of no concern to the gate. The gatekeepers only concern is that your credentials are verified before allowing passage. Concerns of and alternatives to this access model will not be discussed.

This sample code is a general purpose JDBC powered database gateway. Any system that can instantiate a Java object will be able to connect to any database of choice, provided there is an appropriate JDBC driver, an available database and a user account in said database. SAS DATA Step with javaobj is one such system.

The gateway is an RMI scheme involving a server process and a bound manager class that lives in the server process. A Java class operating in a client mode obtains a reference to the remote manager by looking up (within the rmiregistry executor process) the instance that is bound to name JDBC-GATEWAY-MANAGER. The manager class hands out integer handles to objects it instantiates and stores in hash tables (keyed by handle). The manager class has methods for connecting to a database, creating a SQL statement, executing a SQL statement and examing returned Result Sets. Each method requires a handle originally doled out.

A low order securuity scheme is implemented to deter 'handle scanning' by rogue agents. Each handle is a random number and attempts to use a handle that was not doled out incurs a ½ second delay. The security could be improved by doling out an access code with each handle, and requiring both the handle and access code when invoking methods of the gateway manager.

Since the manager only exposes methods that require an integer handle, client systems with no mechanism for java object persistence can utilitize the manager by only remembering the integer handle between uses of the manager. In SAS, this means the handle can be maintained in a macro variable as different DATA Steps execute. In UNIX, the handle could be maintained in a property file between invocations of the Java program loader (this would be an atypical scenario in UNIX).

The implementation of the RMI scheme requires three classes

  1. GatewayInterface - enumerates the methods that will be available to client mode classes.
  2. GatewayManager - implements the methods of GatewayInterface
  3. GatewayServer - class that is run to persist a bound instance of GatewayManager

Two additional classes are created by rmic, the rmi compiler; GatewayManger_skel and GatewayManager_stub. These classes are 'necessary internal components' of any realized rmi scheme and should be ignored by most developers.

An adapter class was also written to allow a SAS javaobj to act as a client in the RMI scheme. Why ? The manager methods require integer arguments, which SAS can not pass. Additionally, the manager can throw remote exceptions which javaobj can not gracefully handle. The adapter provides the typecasting needed to invoke the methods of the manager (which itself realizes the gateway interface) and catches any exceptions thrown.

ToggleGateway Interface

This is a start. If you want to expand the gateway to have more functionality, you would list additional methods here and implement them in GatewayManager. You would also have to implement adapter versions of the additional methods in DataStepGatewayAdapter

GatewayInterface.java
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface GatewayInterface extends Remote
{
  // returned handle must be used to access all functions

  public int getConnectionHandle (String driverClass, String databaseURL,
                                           String username, String password) throws RemoteException;
  public int getStatementHandle (int cHandle) throws RemoteException;

  public void closeConnectionHandle (int handle) throws RemoteException;
  public void closeStatementHandle (int handle) throws RemoteException;
  public void closeResultSetHandle (int handle) throws RemoteException;

  public String getExceptionMessage (int handle) throws RemoteException;

  public void executeUpdate (int handle, String sql) throws RemoteException;
  public int executeQuery (int handle, String sql) throws RemoteException;
  public boolean nextRow (int handle) throws RemoteException;
  public double getValue (int handle, int colNum) throws RemoteException;
  public String getText (int handle, int colNum) throws RemoteException;
  public void setValue (int handle, int colNum, double value) throws RemoteException;
  public void setText (int handle, int colNum, String text) throws RemoteException;
  public void insertRow (int handle) throws RemoteException;
  public void moveToInsertRow (int handle) throws RemoteException;
  public String getColumnName (int handle, int colNum) throws RemoteException;
  public int getColumnType (int handle, int colNum) throws RemoteException;
  public int getColumnCount (int handle) throws RemoteException;
  public int getColumnDisplaySize (int handle, int colNum) throws RemoteException;
}

ToggleGateway Manager

This class implements the methods listed in the interface. This is where the bulk of the work takes place. The manager instantiates objects as requested and places them in a hash keyed by an Integer (the handle). The integer is returned to the client who must pass it back in order to operate upon the object associated with the handle. The manager is essentially an adapter to parts of java.sql for systems unable to fully expose the java experience.

GatewayManager.java
import java.rmi.*;
import java.rmi.server.*;
import java.util.*;
import java.sql.*;
import javax.swing.JOptionPane;


public class GatewayManager
  extends UnicastRemoteObject
  implements GatewayInterface
{
  private Hashtable connections;          // handle --> Connection or Statement
  private Hashtable statements;           // handle --> Connection or Statement
  private Hashtable resultSets;           // handle to Statement --> ResultSet
  private Hashtable resultSetMetaDatas;   // handle to Statement --> ResultSetMetaData
  private Hashtable exceptionMessages ;   // handle to Statement --> String
  private int numberOfHandles;

  java.text.DecimalFormat nf = new java.text.DecimalFormat  ("########");

  //---------------------------------------------------------------------------
  public GatewayManager (int numberOfHandlesToManage) throws RemoteException
  {
    this.numberOfHandles    = numberOfHandlesToManage;
    this.connections        = new Hashtable (numberOfHandlesToManage);
    this.statements         = new Hashtable ();
    this.resultSets         = new Hashtable ();
    this.resultSetMetaDatas = new Hashtable ();
    this.exceptionMessages  = new Hashtable ();
  }

  //---------------------------------------------------------------------------
  private Integer getHandle () {
    Integer handle;  // has to be an object so that it can be used in a Hashtable
    do {
      handle = new Integer ( (int) Math.floor (Math.random() * 1e8));
    }
    while (
        connections.containsKey (handle)
     || statements.containsKey (handle)
     || resultSets.containsKey (handle)
     || exceptionMessages.containsKey (handle)
    );

    return handle;
  }

  //---------------------------------------------------------------------------
  private Object getObjectOfKey(Hashtable H, Object key, boolean clearMessage) throws Exception
  {
    Object mv = H.get (key);

    if (mv == null) {
      Thread.currentThread().sleep(500);
      throw new Exception ("Invalid handle.");
    }

    if (clearMessage)
      exceptionMessages.put (key, new String());

    return mv;
  }

  //---------------------------------------------------------------------------
  private Connection getConnectionOfHandle(int handle, boolean clearMessage) throws Exception
  {
    Integer key = new Integer(handle);
    return (Connection) getObjectOfKey (connections, key, clearMessage);
  }

  //---------------------------------------------------------------------------
  private Statement getStatementOfHandle(int handle, boolean clearMessage) throws Exception
  {
    Integer key = new Integer(handle);
    return (Statement) getObjectOfKey (statements, key, clearMessage);
  }

  //---------------------------------------------------------------------------
  private ResultSet getResultSetOfHandle(int handle, boolean clearMessage) throws Exception
  {
    Integer key = new Integer(handle);
    return (ResultSet) getObjectOfKey (resultSets, key, clearMessage);
  }

  //---------------------------------------------------------------------------
  private ResultSetMetaData getResultSetMetaDataOfHandle(int handle, boolean clearMessage) throws Exception
  {
    Integer key = new Integer(handle);
    return (ResultSetMetaData) getObjectOfKey (resultSetMetaDatas, key, clearMessage);
  }

  //---------------------------------------------------------------------------
  public int getConnectionHandle (String driverClass, String databaseURL,
                                               String username, String password) throws RemoteException
  {
    Integer cHandle = getHandle();
    Connection con ;

    if (connections.size() == numberOfHandles) {
      System.out.println ("No more handles available.");
      throw new RemoteException ("No more handles available.");
    }

    try {
      System.out.println ("loading "+driverClass);
      Class.forName(driverClass);
      System.out.println ("connecting to "+databaseURL);
      con = DriverManager.getConnection (databaseURL, username, password);
      System.out.println ("connected");
    }
    catch (Exception e) {
      System.out.println (e);
      throw new RemoteException ("Problem connecting to database", e);
    }

    connections.put (cHandle, con);

    System.out.println ("Connection handle " + nf.format(cHandle) + " is for " + username + " connected to " + databaseURL);

    return cHandle.intValue();
  }

  //---------------------------------------------------------------------------
  public int getStatementHandle (int cHandle) throws RemoteException
  {
    Integer sHandle = getHandle ();
    Connection con ;
    Statement st;

    try {
      con = getConnectionOfHandle (cHandle, true);
      st = con.createStatement ();
    }
    catch (Exception e) {
      throw new RemoteException ("Problem getting a statement", e);
    }

    statements.put (sHandle, st);

    System.out.println ("Statement handle " + nf.format(sHandle) + " ready");

    return sHandle.intValue();
  }

  //---------------------------------------------------------------------------
  public void closeConnectionHandle (int handle) throws RemoteException
  {
    Integer key = new Integer(handle);
    try {
      Connection con = getConnectionOfHandle (handle, false);
      for (Enumeration e = statements.keys() ; e.hasMoreElements() ;) {
        Integer skey = (Integer) e.nextElement();
        Statement st = (Statement) statements.get(skey);
        if (st.getConnection() == con) {
          closeStatementHandle (skey.intValue());
        }
      }
      con.close();
      connections.remove (key);
      exceptionMessages.remove (key);
    }
    catch (Exception e) { throw new RemoteException("", e); }

    System.out.println ("Connection handle " + nf.format(handle) + " was closed");
  }

  //---------------------------------------------------------------------------
  public void closeStatementHandle (int handle) throws RemoteException
  {
    Integer key = new Integer(handle);
    try {
      Statement st = getStatementOfHandle (handle, false);

      for (Enumeration e = resultSets.keys() ; e.hasMoreElements() ;) {
        Integer rkey = (Integer) e.nextElement();
        ResultSet rs = (ResultSet) resultSets.get(rkey);
        if (rs.getStatement() == st) {
          closeResultSetHandle (rkey.intValue());
        }
      }
      st.close();
      statements.remove (key);
      exceptionMessages.remove (key);
    }
    catch (Exception e) { throw new RemoteException("", e); }

    System.out.println ("Statement handle " + nf.format(handle) + " was closed");
  }

  //---------------------------------------------------------------------------
  public void closeResultSetHandle (int handle) throws RemoteException
  {
    Integer key = new Integer (handle);
    try {
      ResultSet rs = getResultSetOfHandle (handle, false);
      rs.close();
      resultSets.remove (key);
      resultSetMetaDatas.remove (key);
      exceptionMessages.remove (key);
    }
    catch (Exception e) { throw new RemoteException("", e); }

    System.out.println ("ResultSet handle " + nf.format(handle) + " was closed");
  }

  //---------------------------------------------------------------------------
  private void createExceptionMessage(int handle, Exception e)
               { createExceptionMessage (handle, e, null); }

  //---------------------------------------------------------------------------
  private void createExceptionMessage
                          (int handle, Exception e, String text)
  {
    String msg;
    Integer key = new Integer (handle);

    if (text != null)
      msg = text + "\n";
    else
      msg = "";

    msg += e.toString();
/*
    for (int i=0;i<e.getStackTrace().length; i++) {
      msg += "\n" + e.getStackTrace()[i];
    }
*/
    exceptionMessages.put(key,msg);
  }

  //---------------------------------------------------------------------------
  public String getExceptionMessage (int handle) throws RemoteException
  {
    Integer key = new Integer (handle);
    try {
      return (String) getObjectOfKey (exceptionMessages, key, false);
    }
    catch (Exception e) { throw new RemoteException("", e); }
  }

  //---------------------------------------------------------------------------
  public void executeUpdate (int sHandle, String sql) throws RemoteException
  {
    try {
      Statement st = getStatementOfHandle (sHandle, true);
      System.out.println ("handle " + nf.format(sHandle) + " is executing update: " + sql);
      try {
        int rowCount = st.executeUpdate (sql);
        System.out.println ("handle " + nf.format(sHandle) + " updated " + rowCount + " rows");
      }
      catch (Exception e) {
        createExceptionMessage(sHandle, e, sql);
      }
    }
    catch (Exception e) { throw new RemoteException("", e); }
  }

  //---------------------------------------------------------------------------
  // returns handle to a ResultSet
  public int executeQuery (int sHandle, String sql) throws RemoteException
  {
    Integer rHandle = getHandle();
    try {
      Statement st = getStatementOfHandle (sHandle, true);
      System.out.println ("handle " + nf.format(sHandle) + " is executing query: " + sql);
      try {
        ResultSet rs = st.executeQuery (sql);
        ResultSetMetaData rsmd = rs.getMetaData();
        resultSets.put (rHandle, rs);
        resultSetMetaDatas.put (rHandle, rsmd);
      }
      catch (Exception e) {
        createExceptionMessage(sHandle, e, sql);
      }
    }
    catch (Exception e) { throw new RemoteException("", e); }

    return rHandle.intValue();
  }

  //---------------------------------------------------------------------------
  public boolean nextRow (int rHandle) throws RemoteException
  {
    try {
      ResultSet rs = getResultSetOfHandle (rHandle, true);
      try
      { return rs.next(); }
      catch (Exception e)
      { createExceptionMessage(rHandle, e); return false; }
    }
    catch (Exception e) { throw new RemoteException("", e); }
  }

  //---------------------------------------------------------------------------
  public double getValue (int rHandle, int colNum) throws RemoteException
  {
    try {
      ResultSet rs = getResultSetOfHandle (rHandle, true);
      try
      { return rs.getDouble((int)colNum); }
      catch (Exception e)
      { createExceptionMessage(rHandle, e); return Double.NaN;}
    }
    catch (Exception e) { throw new RemoteException("", e); }
  }

  //---------------------------------------------------------------------------
  public String getText (int rHandle, int colNum) throws RemoteException
  {
    try {
      ResultSet rs = getResultSetOfHandle (rHandle, true);
      try
      { return rs.getString((int)colNum); }
      catch (Exception e)
      { createExceptionMessage(rHandle, e); return ""; }
    }
    catch (Exception e) { throw new RemoteException("", e); }
  }

  //---------------------------------------------------------------------------
  public void setValue (int rHandle, int colNum, double value) throws RemoteException
  {
    try {
      ResultSet rs = getResultSetOfHandle (rHandle, true);
      try
      { rs.updateDouble((int)colNum, value); }
      catch (Exception e)
      { createExceptionMessage(rHandle, e); }
    }
    catch (Exception e) { throw new RemoteException("", e); }
  }

  //---------------------------------------------------------------------------
  public void setText (int rHandle, int colNum, String text) throws RemoteException
  {
    try {
      ResultSet rs = getResultSetOfHandle (rHandle, true);
      try
      { rs.updateString((int)colNum,text); }
      catch (Exception e)
      { createExceptionMessage(rHandle, e); }
    }
    catch (Exception e) { throw new RemoteException("", e); }
  }

  //---------------------------------------------------------------------------
  public void insertRow (int rHandle) throws RemoteException
  {
    try {
      ResultSet rs = getResultSetOfHandle (rHandle, true);
      try
      { rs.insertRow(); }
      catch (Exception e)
      { createExceptionMessage(rHandle, e); }
    }
    catch (Exception e) { throw new RemoteException("", e); }
  }

  //---------------------------------------------------------------------------
  public void moveToInsertRow (int rHandle) throws RemoteException
  {
    try {
      ResultSet rs = getResultSetOfHandle (rHandle, true);
      try
      { rs.moveToInsertRow(); }
      catch (Exception e)
      { createExceptionMessage(rHandle, e); }
    }
    catch (Exception e) { throw new RemoteException("", e); }
  }

  //---------------------------------------------------------------------------
  public String getColumnName (int rHandle, int colNum) throws RemoteException
  {
    try {
      ResultSetMetaData rsmd = getResultSetMetaDataOfHandle (rHandle, true);
      try
      { return rsmd.getColumnName((int)colNum); }
      catch (Exception e)
      { createExceptionMessage(rHandle, e); return ""; }
    }
    catch (Exception e) { throw new RemoteException("", e); }
  }


  //---------------------------------------------------------------------------
  public int getColumnType (int rHandle, int colNum) throws RemoteException
  {
    try {
      ResultSetMetaData rsmd = getResultSetMetaDataOfHandle (rHandle, true);
      try
      { return rsmd.getColumnType((int)colNum); }
      catch (Exception e)
      { createExceptionMessage(rHandle, e); return -1; }
    }
    catch (Exception e) { throw new RemoteException("", e); }
  }

  //---------------------------------------------------------------------------
  public int getColumnCount (int rHandle) throws RemoteException
  {
    try {
      ResultSetMetaData rsmd = getResultSetMetaDataOfHandle (rHandle, true);
      try
      { return rsmd.getColumnCount(); }
      catch (Exception e)
      { createExceptionMessage(rHandle, e); return -1; }
    }
    catch (Exception e) { throw new RemoteException("", e); }
  }

  //---------------------------------------------------------------------------
  public int getColumnDisplaySize (int rHandle, int colNum) throws RemoteException
  {
    try {
      ResultSetMetaData rsmd = getResultSetMetaDataOfHandle (rHandle, true);
      try
      { return rsmd.getColumnDisplaySize((int)colNum); }
      catch (Exception e)
      { createExceptionMessage(rHandle, e); return -1; }
    }
    catch (Exception e) { throw new RemoteException("", e); }
  }
}

ToggleGateway Server, Running

This class is run to persist a GatewayManager. Java security settings, which will not be discussed, can be placed on the running of this class to restrict the resources it uses.

GatewayServer.java
import java.rmi.registry.LocateRegistry;
import java.rmi.*;

public class GatewayServer
{
  protected static final String RMI_NAME = "JDBC-GATEWAY-MANAGER";

  private String exceptionMessage;

  // That method is run when class is run as stand-alone java application,
  // from whence comes run-time persistence

  public static void main(String args[]){

    // sets security according to file named in environment variable java.security.policy
    System.setSecurityManager(new RMISecurityManager());

    try{
      // 1099 is the default port for RMI
      // That method removes the need to also run "rmiregistry" as stand-alone process prior
      // to starting this class as a stand-alone process
      LocateRegistry.createRegistry(1099);

      // Instantiate the implementation of the interface that will persist as long as this server runs
      int nCon = 5;
      GatewayManager manager = new GatewayManager (nCon);

      // Give this process the name that localhost clients use with Naming.lookup()
      Naming.rebind (RMI_NAME, manager);

      System.out.println(manager.getClass().getName() + " ready to manage " + nCon + " connections.");
    }
    catch(Exception e)
    {
      System.out.println("Error: " + e);
    }
  }

  //---------------------------------------------------------------------------
  public static GatewayInterface getReferenceToPersistentManager ()
  {
    int numberOfTimesToWait = 4;
    long durationToWaitInMillis = 250;

    Remote remote = null;

    try {
      for (int i = 0; (i<numberOfTimesToWait) && (remote == null); i++) {
        try {
          remote = Naming.lookup(RMI_NAME);
        }
        catch (java.rmi.NotBoundException e)
        {
          // if not bound, wait a little, in case server was just started and things haven't settled in yet
          System.out.println ("waiting ");
          Thread.currentThread().sleep (durationToWaitInMillis);
        }
      }
    }
    catch (Exception e) {
      System.out.println("Error " + e);
    }

    return (GatewayInterface) remote;
  }
}

ToggleRunning the server

The GatewayServer needs to be running separate from the clients that will access it. You can use this script to start the server when running the DataStepGatewayAdapterTester. When using the Gateway from SAS, there is a macro you invoke to start the server.

start-server.bat.txt
rem report current path
cd

rem run GatewayServer
java -cp ./class;{vendor-jdbc.jar};%CLASSPATH% -Djava.security.policy=gateway.policy -D:java.rmi.server.hostname=localhost GatewayServer
gateway.policy
grant {
permission java.net.SocketPermission "*", "accept, connect, listen, resolve";
};

ToggleDATA Step Gateway Adapter

The adapter was written to let javaobjs to interact with the GatewayManager. All the methods have numeric arguments as double, the only numeric type javaobj can pass to java object methods. The adapter adds one method, getSasColumnType, which is not declared in GatewayInterface. The method maps the variety of remote database types to the fundamental SAS data types of character or numeric.

DataStepGatewayAdapter.java
import java.rmi.Remote;
import java.rmi.RemoteException;

// This wrapper class exists because DATA Step can only deal with java objects created via JavaObj
// component object.
// The reference returned by GatewayServer.getReferenceToPersistentManager is not directly
// accessible to DATA Step.
//
// If DATA Step is ever updated to handle javaobj methods that return other java objects, this wrapper
// class will not be needed

public class DataStepGatewayAdapter
{
  private GatewayInterface dbi;

  public DataStepGatewayAdapter() throws Exception {
    dbi = GatewayServer.getReferenceToPersistentManager ();
  }

  public int getConnectionHandle (String driverClass, String databaseURL, String username, String password) throws Exception
{ return dbi.getConnectionHandle (driverClass, databaseURL, username, password); }

  public int getStatementHandle (double cHandle) throws Exception
{ return dbi.getStatementHandle ( (int) cHandle); }

  public void closeConnectionHandle (double cHandle) throws Exception
        { dbi.closeConnectionHandle ( (int) cHandle); }

  public void closeStatementHandle (double sHandle) throws Exception
        { dbi.closeStatementHandle ( (int) sHandle); }

  public void closeResultSetHandle (double rHandle) throws Exception
        { dbi.closeResultSetHandle ( (int) rHandle); }

  public String getExceptionMessage (double handle) throws Exception
   { return dbi.getExceptionMessage ( (int) handle); }

  public void executeUpdate (double sHandle, String sql) throws Exception
        { dbi.executeUpdate ( (int) sHandle,        sql); }

  public int executeQuery (double sHandle, String sql) throws Exception
{ return dbi.executeQuery ( (int) sHandle,        sql); }

  public boolean nextRow (double rHandle) throws Exception
    { return dbi.nextRow ( (int) rHandle); }

  public double getValue (double rHandle, double colNum) throws Exception
   { return dbi.getValue ( (int) rHandle,  (int) colNum); }

  public String getText (double handle, double colNum) throws Exception
   { return dbi.getText ( (int) handle,  (int) colNum); }

  public void setValue (double handle, double colNum, double value) throws Exception
        { dbi.setValue ( (int) handle,  (int) colNum,        value); }

  public void setText (double handle, double colNum, String text) throws Exception
        { dbi.setText ( (int) handle,  (int) colNum,        text); }

  public void insertRow (double handle) throws Exception
        { dbi.insertRow ( (int) handle); }

  public void moveToInsertRow (double handle) throws Exception
        { dbi.moveToInsertRow ( (int) handle); }

  public String getColumnName (double handle, double colNum) throws Exception
   { return dbi.getColumnName ( (int) handle,  (int) colNum); }

  public int   getColumnCount (double handle) throws Exception
  { return dbi.getColumnCount ( (int) handle); }

  public int   getColumnDisplaySize (double handle, double colNum) throws Exception
  { return dbi.getColumnDisplaySize ( (int) handle,  (int) colNum); }

  public String getSasColumnType (double handle, double colNum) throws Exception
  { int type = dbi.getColumnType ( (int) handle,  (int) colNum);
    switch (type) {
      case java.sql.Types.CHAR:    return "C";
      case java.sql.Types.VARCHAR: return "C";
      case java.sql.Types.BIGINT:  return "N";
      case java.sql.Types.BINARY:  return "N";
      case java.sql.Types.BOOLEAN: return "N";
      case java.sql.Types.DECIMAL: return "N";
      case java.sql.Types.DOUBLE:  return "N";
      case java.sql.Types.FLOAT:   return "N";
      case java.sql.Types.INTEGER: return "N";
      case java.sql.Types.NUMERIC: return "N";
      case java.sql.Types.REAL:    return "N";
      case java.sql.Types.SMALLINT:return "N";
      default: return "?";
    }
  }
}

ToggleData Step Gateway Adapter Tester

This test unit instantiates a DataStepGatewayAdapter and works with it in the same way one would with a javaobj in a DATA Step. In order to test the adapter, the server process needs to be running. Start the test using a command such as this:

$ java -classpath ./class DataStepGatewayAdapterTester \
org.postgresql.Driver \
jdbc:postgresql://www.devenezia.com:5434/sesug03demo \
sesug03demo \
D3m0oeoe

DataStepGatewayAdapterTester.java
//import java.rmi.*;
//import java.rmi.server.*;
//import java.sql.*;

public class DataStepGatewayAdapterTester
{
  // Test the interface class that SAS will use to access remote databases through JDBC

  // This client is not designed for use as a java application
  // main() should only be used for testing purposes

  private static void usage ()
  {
    System.out.println (
      "Usage:\n" + DataStepGatewayAdapterTester.class.getName() + " <handle>, or\n"
    + DataStepGatewayAdapterTester.class.getName() + " <JDBC driver class> <Database URL> <username> <password>"
    );
  }

  public static void main(String[] args){

    double cHandle = 0;
    double sHandle = 0;
    double rHandle = 0;

    String query;
    String message;

    java.text.DecimalFormat nf = new java.text.DecimalFormat  ("########");

    if (args.length != 1 && args.length != 4) {
      usage ();
      System.exit(1);
    }

//  System.setSecurityManager ( new RMISecurityManager() );

    try {
      DataStepGatewayAdapter dbi = new DataStepGatewayAdapter();

      if (args.length == 4) {
        cHandle = dbi.getConnectionHandle ( args[0], args[1], args[2], args[3] );
      }
      else {
        cHandle = Double.parseDouble ( args[0] );
      }

      System.out.println ("Handle:"+nf.format(cHandle));

      sHandle = dbi.getStatementHandle (cHandle);

      query = "update NotATable set x=100 where x=1";
      System.out.println ("\n"+query);
      dbi.executeUpdate (sHandle, query);
      message = dbi.getExceptionMessage (sHandle);
      if (message != null)
        System.out.println ("exception message:"+message);

      query = "select relname from pg_class";
      System.out.println ("\n"+query);
      rHandle = dbi.executeQuery (sHandle, "select relname from pg_class where relname not like 'pg_%' order by relname");
      message = dbi.getExceptionMessage (sHandle);
      if (message != null)
        System.out.println ("exception message:"+message);

      System.out.println (dbi.getColumnCount(rHandle));

      while (dbi.nextRow (rHandle)) {
        String s = dbi.getText (rHandle, 1);
        System.out.println (s);
      }
      System.out.println ("\nTester is done.");

      dbi.closeConnectionHandle(cHandle);
    }
    catch (Exception e) {
      System.out.println ("Exception in tester:"+e);
      e.printStackTrace();
      System.exit(1);
    }

    System.exit(0);
  }
}

The Bottom

Congratulations! You made it to the bottom of the page.