4. Implementing Callbacks
If an application has "callbacks", that is, the server class has the capability of invoking methods on the client, then the distributed implementation is a bit more complicated than when callbacks are not present. In particular, when there are callbacks, the client must act as a server as well, and appropriate communication links must be established for this. In keeping with the philosophy that the migration from a local implementation to a distributed implementation should be as simple as possible, our goal was to have it so minimal programming changes are required when creating a distributed implementation of an application that possesses callbacks. Thus, the idea is that a programmer creates and debugs an application with callbacks and then creates a distributed implementation by making a small number of code modifications and by creating appropriate wrapper classes (classes that are automatically generated) .

As with the creation of distributed applications without callbacks, perhaps the best means of revealing the procedure and requisite programming constructs is through an example. So, consider a two component application of the form used previously :

However, in this version, Aclass contains a reference to App for callbacks, and one method of the App class (typically the constructor) calls an Aclass method to set the reference to App. To facilitate the conversion to a distributed implementation, we require Aclass set the reference to App by implementing the cam.netapp.SetClient interface.

In the purely local version of the application, the App class constructor has the form

public App()
{
   A = new Aclass();
   
   try{
   A.setClient((Object)this); // Using the method defined by
                              // cam.netapp.SetClient to set the  
   }                          // callback
   catch(Exception e){};
}

while in the Aclass class, the method setClient(...) has the form

public void setClient(Object Ob)throws Exception
{
    appClient = (App)Ob;
}

The complete codes are contained in the files Sample3/App.java and Sample4/Aclass.java.

Using these constructions gives the Aclass instance the capability of invoking App methods. For demonstration purposes App contains the methods testCallBack(...) and printString(...). Aclass contains the method useCallBack(...). The construction is tested by having App.main(..) create an App instance and invoke the testCallBack(...) method. This method immediately invokes the Aclass useCallBack(...) method which in turn invokes the client instance printString(...)method. The output from the App.main(...) is

The String "Callback Ok" Indicates Success
Callback OK

In a distributed implementation, the existence of a callback requires that the App class act as a server class. Thus, wrapper classes AppCI and AppSI are required to implement the connection between Aclass and App. Ultimately, the structure of the distributed application has the form


The additional wrapper classes consist of AppCI and AppSI, classes that enable the Aclass instance access (via AppCI methods) the methods of the App instance.

To create a distributed implementation from the local implementation consists of

  1. Creating the classes AclassCI, AclassSI, AppCI, AppSI.

  2. In App, changing the server type from Aclass to AclassCI.

  3. In Aclass changing the callback reference from type App to AppCI. Note that the setClient(...)method requires modification as well.

  4. Adding System.exit(0) to App.main(...). If one doesn't do this, then the AppSI instance remains after App has exited.

The new App constructor has the form

public App()
{
   // Using local : uncomment the next block
   // #######################################
   
   // A = new Aclass();
   
   // #######################################
   //
   // Using remote : uncomment the next block
   // #######################################
    
   A = new AclassCI();
   try
   {
      A.setVerboseFlag(true);// remove to stop messages ..
      A.createServerInstance("127.0.0.1",6789);
      
   }catch(Exception e)
   {System.out.println(e.getMessage());}
   
   // #######################################
   // 
   try{
   A.setClient((Object)this); // Using the method defined by
                              // cam.netapp.SetClient  
   }
   catch(Exception e){System.out.println(e);};  
}

while Aclass implementation of cam.netapp.setClient(...) now has the form

public void setClient(Object Ob) throws Exception
{
    appClient = (AppCI)Ob;   
}

Sample4/App.java and Sample4/Aclass.java contain the source for the distributed version of the application.

With these changes, when the setClient(...) method of Aclass is invoked, then the callback structure described above is automatically instantiated. (This assumes the AppSI is located on the client machine and AppCI is located on the server machine).

The output from the App.main(...) with the verbose mode set has the form

NetworkConnection : Setting up Connection
NetworkConnection : Connecting to [127.0.0.1] Port : 6789
NetworkConnection : Connected  to [127.0.0.1] Start Up Port : 6791
NetworkConnection : Connected  to [127.0.0.1] Server   Port : 6792
NetworkConnection : Connected  to [127.0.0.1] Server   Port : 6793
NetworkConnection : Connected  to [127.0.0.1] Server   Port : 6794
NetworkConnection : Application AclassSI requested
NetworkConnection : Remote Class Found
NetworkConnection : Remote Class Instantiated
NetworkConnection : Streams Connected to Remote Class
NetworkConnection : Connection Complete
NetworkConnection : Starting Remote Application
AclassCI : Connection Complete
The String "Callback Ok" Indicates Success
Callback OK

So, the steps to the creation of a distributed version of a two component application with callbacks consists of

  1. Creating and debugging a local version of the application, a version in which the cam.netapp.SetClient interface is used by the client class to set the callback reference.

  2. Generation of the client (CI) and server (SI) classes for both components.

  3. Modification both components to use the client (CI) classes.