Event Handling Sample

This sample introduces the powerful new event handling architecture based on delegates for the Virtual Machine (VM) for Java. The delegate event model is a replacement for the JavaBean model and is not compatible with it. The two styles should not be intermixed in application code. This is a large sample that delineates all of the ADO/WFC Recordset events that can be registered for callback by Java application developers.

public class DemoFramework 
{
   Recordset recordSet;

   RecordsetEventHandler handler = 
      new RecordsetEventHandler(this,"onWillMove");
   RecordsetEventHandler handler2 = 
      new RecordsetEventHandler(this,"onWillMove2");
   RecordsetEventHandler handler3 = 
      new RecordsetEventHandler(this,"onMoveComplete");
   RecordsetEventHandler handler4 = 
      new RecordsetEventHandler(this,"onEndOfRecordset");
   RecordsetEventHandler handler5 = 
      new RecordsetEventHandler(this,"onWillChangeField");
   RecordsetEventHandler handler6 = 
      new RecordsetEventHandler(this,"onFieldChangeComplete");

   public void addEventListeners()
   {
      recordSet.addWillMoveHandler(handler);
      recordSet.addWillMoveHandler(handler2);
      recordSet.addMoveCompleteHandler(handler3);
      recordSet.addEndOfRecordsetHandler(handler4);
      recordSet.addWillChangeFieldHandler(handler5);
      recordSet.addFieldChangeCompleteHandler(handler6);
   }

   public void removeEventListeners()
   {
      recordSet.removeWillMoveHandler(handler);
      recordSet.removeWillMoveHandler(handler2);
   }

   public void onEndOfRecordset(RecordsetEvent e)
   {
      System.out.println("onEndOfRecordset:" + e);
   }

   public void onMoveComplete(RecordsetEvent e)
   {
      System.out.println("onMoveComplete:" + e);
   }

   public void onWillMove(RecordsetEvent e)
   {
      System.out.println("onWillMove:" + e);
   }

   public void onWillMove2(RecordsetEvent e)
   {
      System.out.println("onWillMove2:" + e);
   }

   public void onWillChangeField(RecordsetEvent e)
   {
      System.out.println("onWillChangeField:" + e);
   }

   public void onFieldChangeComplete(RecordsetEvent e)
   {
      System.out.println("onFieldChangeComplete:" + e);
   }

   
   void FieldChange()
   {
      try {
         recordSet.getFields().getItem("au_lname").setString("someWeirdValue");
      } catch (Throwable t)
      {
         t.printStackTrace();
      }
   }

   public Recordset getRecordset(String strConnection, String strSQL)
   {
      Recordset rs;
      
      rs = new Recordset();
      rs.setCursorLocation(AdoEnums.adUseClient);
      rs.setLockType (AdoEnums.adLockBatchOptimistic);
      rs.setCacheSize (10);
      rs.setMaxRecords (1000);
   
      rs.open(strSQL, strConnection);
      rs.moveLast();
      rs.moveFirst();
      recordSet = rs;

      return rs;
   }

   public void ScrollForward()
   {
      
      recordSet.moveFirst();

      int lRows = 0;
      while (!recordSet.getEOF())
      {
         lRows++;
         recordSet.moveNext();
      }
   }
   
   public boolean run()
   {
      String query = "select * from authors";
      String connection = "dsn=aDSN;database=pubs";

      boolean case1 = false, case2 = false, case0 = false;
      recordSet = getRecordset(connection,query);

      addEventListeners();
      FieldChange();
      ScrollForward();
      removeEventListeners();

      recordSet.close();
      recordSet.release();

      return true;
   }

   public static void main(String args[])
   {
      new DemoFramework().run();
   }
}

A delegate can be thought of as a Java callback pointer (or function pointer). An event listener can arrange to be called back on any method it has. Delegates in ADO/WFC are wrapped within the <type>EventHandler classes. There is one EventHandler for each of the three main types of event-generating ADO components: RecordsetEventHandler, ConnectionEventHandler, and CommandEventHandler. The following code creates six RecordsetEventHandler delegates:

   RecordsetEventHandler handler = 
      new RecordsetEventHandler(this,"onWillMove");
   RecordsetEventHandler handler2 = 
      new RecordsetEventHandler(this,"onWillMove2");
   RecordsetEventHandler handler3 = 
      new RecordsetEventHandler(this,"onMoveComplete");
   RecordsetEventHandler handler4 = 
      new RecordsetEventHandler(this,"onEndOfRecordset");
   RecordsetEventHandler handler5 = 
      new RecordsetEventHandler(this,"onWillChangeField");
   RecordsetEventHandler handler6 = 
      new RecordsetEventHandler(this,"onFieldChangeComplete");

Each of the second parameters to the RecordsetEventHandler constructor statements above designate one of the possible Recordset object events, or conditions, that the Recordset can enter while manipulated within an application context. This sample is set up to listen to all of the events. Typically, a Java component may only be interested in a subset of that total; for example, a business object may be designed for validating any field changes in a form, but is disinterested in currency change notifications.

Note that events are comprised of before (“will”) and after (“complete”) pairs of events. Listeners that want to be able to react to an event before it happens, with the opportunity to reverse the event, typically register for the “will” events. The following code shows how to add event listeners to the source object:

recordSet.addWillChangeFieldHandler(handler);
recordSet.addFieldChangeCompleteHandler(handler2);

Once an event listener is registered, the method specified in the RecordsetEventHandler will be invoked every time an event of the specified type occurs within the object. To show how an event is triggered, examine the FieldChange method. The DemoFramework class registers itself (using the this reference) to receive both the before (“will”) and after (“complete”) events. The FieldChange method, called during the run method, then triggers the event propagation:

void FieldChange()
   {
      try {
         recordSet.getFields().getItem("au_lname").setString("someWeirdValue");
      } catch (Throwable t)
      {
         t.printStackTrace();
      }
   }

Accessing the Field value triggers the event propagation mechanism. The WillChangeFieldHandler routine receives the event first, and outputs a simple console message acknowledging receipt. Then, the FieldChangeCompleteHandler routine receives the event, and also outputs a simple console message. Of course, the behavior of the two event handling methods could have been much more elaborate; for example, throwing a CancelChangeException to roll back the proposed change. (This is equivalent to the VetoableEventListener concept of JavaBeans, but without the immense overhead of the JavaBean model.)

Familiarizing yourself with this new approach to event notifications and callback methods is a requisite for successful ADO/WFC programming in the component assembly paradigm. Classical, non-event-driven applications are still feasible without the delegate model, but most modern form-based UI applications stand to be improved through assimilation of this new event model.