Asynchronous Command Execution Sample

This sample demonstrates a convenient feature of ADO/WFC: the ability to asynchronously execute commands and populate large Recordsets on background threads. This sample extends the framework code by spawning a thread and using an event listener to catch the “population complete” event.

// This import statement is required for the
// delegate event callback class definition. 
import com.ms.lang.*;

... // framework code 

boolean done = false;
boolean execOK = false;

// Implement an event handling routine to
// catch the Recordset “population completion” 
// event from the population thread. 
public synchronized void onExecuteComplete(ConnectionEvent e)
{
   done = true;
   notify();
}

Recordset rs;
Command cmd;

public synchronized boolean run()
{
   execOK = false;
   
   Connection c = new Connection();
   c.setCursorLocation (AdoEnums.adUseClientBatch);
   c.open ("dsn=aDSN", "aName", "aPWD");
   
   // Establish a callback handler for the execute complete 
   // event and add it to the Connection object. 
   ConnectionEventHandler handler = 
            new ConnectionEventHandler(this, "onExecuteComplete");
   c.addExecuteCompleteHandler(handler);

   cmd = new Command();
   cmd.setActiveConnection (c);
   cmd.setCommandText ("select * from authors");

   Runnable asyncExecute = new Runnable() {   
      public void run() {
         try {
          rs = cmd.execute(null,AdoEnums.adRunAsync);
         } catch (Exception e)
            {
               System.out.println(e);
               synchronized (DemoAsyncCommand.this) {
               done = true;
               execOK = false;
               DemoAsyncCommand.this.notify();
            }
         }
      }
   };

   new Thread(asyncExecute,"RunAsync").start();
   execOK = true;
      
   while (!done)
      try {
         wait();
      } catch (Exception e)
      {
         execOK = false;
      }

   if (execOK)
   {
      rs.moveFirst();
      rs.moveLast();
   
      rs.close();
      c.close();
      return true;
   } else 
      return false;
}

The first set of initialization code establishes the event handling “listener” for the Connection “execute complete” event — an event fired when the Command associated with the Connection finishes executing. A method, onExecuteComplete(ConnectionEvent e), is defined within the sample class, which implicitly triggers the notify method. The following code is a standard delegate-based event registration call sequence:

ConnectionEventHandler handler = new ConnectionEventHandler(this,"onExecuteComplete");
c.addExecuteCompleteHandler(handler);

This event model diverges sharply from the JavaBean EventSource-EventAdpater-EventListener model that ships with the JDK 1.1. The Delegate class is a member of the com.ms.lang package, and should be imported into applications that rely on event handling between components on Win32 platforms. ADO/WFC components do not support the JDK 1.02 or JavaBean event models. The Command code is basically identical to the code in the Command sample, without the parametric aspect.

Note that the run method is prefixed with the Java access modifier, synchronized. This denotes that only one active thread may be running in the method body at any given time, ensuring that race conditions do not occur. The lines that define a new Runnable object are worth examining, since this is where the execution thread is being defined.

Note   The Runnable interface is implemented by classes that want to operate as threaded objects, but do not want to subclass java.lang.Thread. The Runnable interface has only one method: the entry point run, which is invoked by the thread scheduler to start the threaded object.

Runnable asyncExecute = new Runnable() {   
      public void run() {
         try {
          rs = cmd.execute(null,AdoEnums.adRunAsync);
         } catch (Exception e)
            {
               System.out.println(e);
               synchronized (DemoAsyncCommand.this) {
               done = true;
               execOK = false;
               DemoAsyncCommand.this.notify();
            }
         }
      }
   };

This code instantiates an anonymous Runnable object with one defined method, run. The body of the run method performs the cmd.execute(null, AdoEnums.adRunAsync). This segment of code only prepares the thread object to run; the object must be “started” through the following call sequence:

new Thread(asyncExecute,"RunAsync").start();

This starts the Command execution, which will perform in asynchronous mode. The Thread constructor used in the sample takes, as parameters, a Runnable instance and a “thread name.” (In this sample, “RunAsync” is the name associated with the thread.) The new thread will begin executing the method body of the asyncExecute object. The first code line is a Command object executing the current command string (“select * from author”) in asynchronous mode, specified by the constant parameter passed to the method. Had no constant been passed to the execute method, the command would have executed in synchronous mode. The ADO and OLE DB components that run underneath the covers of ADO/WFC are free-threaded objects and need to be configured just like their Java counterparts.

While the command-execute thread runs in the background, the foreground thread enters a wait loop until notified, through the callback on the OnExecuteComplete event handler, that the command has finished executing. Once the event is fired from the background thread, the boolean variable done is set to true and the main thread can proceed to the task of navigating through the Recordset.

This sample is for slightly more advanced users. It is not necessary to run ADO/WFC with explicit multi-threading, but there are certain scenarios, especially those involving long wait periods, where efficient use of threads will bolster the end user experience and lead to better control flow. As always, the final lines of the sample release the Recordset and Connection resources.

Try experimenting with multi-threaded versions of the other samples, such as the Connection and Recordset samples. Or, create a version of this sample with multiple Commands being executed simultaneously; for example, “select * from authors” and “select * from publishers, titles where publishers.pubid = titles.pubid”. Also, remember to use Runnable instead of subclassing Thread directly. You can always convert to a full Thread object through the overloaded constructor.