Using Callbacks


In this section you modify your examples from previous lessons to use process and default callbacks.

Writing a Process Callback

To see a callback in action, define a message process callback object to operate on incoming NUMERIC_DATA messages. Process callback objects are the most common way in SmartSockets to perform the main processing of a message.

The next section describes a callback implementation in detail. This callback object, whose process() method is invoked when a message of type NUMERIC_DATA is processed with TipcSrv.process() or using TipcSrv.mainLoop(), simply accesses and prints the fields of the message. There is another example of a process callback in Processing of GMD_FAILURE Messages on page 182.

The files for this lesson are located in the directories:

Windows:
%RTHOME%\java\tutorial\lesson4 
UNIX:
$RTHOME/java/tutorial/lesson4 

Step 1

Copy the receive.java program

Copy the receive.java program into your working directory. This is an example of the receive.java program:

//--------------------------------------------------------------- 
// receive.java -- output a NUMERIC_DATA with callback 
 
1 import java.io.*; 
2 import com.smartsockets.*; 
 
3 public class receive { 
 
4   public class receiveCb implements TipcProcessCb { 
 
5     public void process(TipcMsg msg, Object arg) { 
6       System.out.println("Received NUMERIC_DATA message."); 
 
       // position the field ptr to the beginning of the message 
       try { 
7        msg.setCurrent(0); 
8   }    catch (TipcException e) { 
9          Tut.fatal(e); 
          } // catch 
 
10        System.out.println("Contents of NUMERIC_DATA message:"); 
 
          // read the data part of the message 
          try { 
11          String var_name; 
12          while (true) { 
13            var_name = msg.nextStr(); 
14            double var_value; 
15            var_value = msg.nextReal8(); 
16            System.out.println("Var name = " + var_name +  
              ", value = " + var_value); 
            } // while 
17        } catch (TipcException e) {  
            // catch end-of-message-data exception, do nothing. 
            } // catch 
          } // process 
        } // receiveCb 
 
18      public receive() { 
19        TipcMsg msg = null; 
 
          // set the project 
          try { 
20          Tut.setOption("ss.project", "smartsockets"); 
21          TipcSrv srv=TipcSvc.getSrv(); 
 
          // connect to RTserver 
22        if (!srv.create()) { 
            Tut.exitFailure("Couldn't connect to RTserver!"); 
          } // if 
          // subscribe to the appropriate subject 
23        srv.setSubjectSubscribe("/ss/tutorial/lesson4", true); 
 
          // create a new receive callback and register it 
24        receiveCb rcb = new receiveCb(); 
25        TipcCb rcbh = srv.addProcessCb(rcb, TipcMt.NUMERIC_DATA,  
                              srv); 
          // check the 'handle' returned for validity 
26        if (null == rcbh) { 
27          Tut.exitFailure 
                      ("Couldn't register process listener!"); 
          } // if 
 
          // read and process a message 
28        msg = srv.next(TipcDefs.TIMEOUT_FOREVER); 
 
          // all callbacks are triggered by TipcSrv's process()  
          // method 
29        srv.process(msg); 
 
          // clean up and disconnect from RTserver 
30        srv.removeProcessCb(rcbh); 
31        srv.destroy(); 
32      } catch (TipcException e) { 
33        Tut.fatal(e); 
        } // catch 
      } // receive (constructor) 
 
34    public static void main(String[] argv) { 
35      receive r = new receive(); 
      } // main 
   } // receive 
 

For this example, the bulk of the code has been moved to the constructor for the receive class, and main simply instantiates a receive object to begin operation. While examining the receive constructor, the first thing to notice is that the processing of the NUMERIC_DATA message has been moved out of this section of code and into the callback class, receiveCb, lines 4-17. A call to the method TipcSrv.process() is also added on line 29 to invoke the callback when it is time to process the message.

Step 2

Copy the send.java program and compile

Copy the send.java program into your working directory, and then compile the receiving and sending programs:

$ javac receive.java 
$ javac send.java 

Step 3

Ensure the RTserver is running

Make sure RTserver is running. If not, start it:

$ rtserver 

On platforms that support both 32- and 64-bit, use the rtserver64 command to run the 64-bit version of the rtserver script.

Step 4

Start the receiving program

Run the receiving program using:

$ java receive 

Step 5

Start the sending program

After a few moments, run the sending program in a second window:

$ java send 

This output is displayed by the receiving program:

Received NUMERIC_DATA message. 
 
Contents of NUMERIC_DATA message: 
--------------------------------- 
Var name = X, Value = 10.0 
Var name = Y, Value = 20.0 
Var name = Z, Value = 30.0 

Writing a Default Callback

In the previous section, the example was set up to invoke a callback when a NUMERIC_DATA message is processed. What happens if you send a message that is not of type NUMERIC_DATA? Next you try it and find out.

Step 6

Copy the send2.java program

Copy the send2.java program into your working directory.

This program is the equivalent of modifying the original sending program by adding these lines after connecting to RTserver and before creating the NUMERIC_DATA message:

TipcMsg msgi = TipcSvc.createMsg(TipcMt.INFO); 
msgi.setDest("/ss/tutorial/lesson4"); 
msgi.appendStr("Hello World!"); 
srv.send(msgi); 
srv.flush(); 

This new code sends an INFO message to your receiving program, followed by a NUMERIC_DATA message.

Note that in the next three steps, you run receive with send2 instead of the usual pairing of receive with send or receive2 with send2.

Step 7

Compile the send2.java program

Compile the send2.java program using the command:

$ javac send2.java 

Step 8

Start the receiving program

Start the original receiving program in one window of your display using the command:

$ java receive 

Step 9

Start the new sending program

After a few moments, run the new sending program (which sends an INFO message) in another window using the command:

$ java send2 

You do not see any output in the window where you ran the receiving program because the INFO message was received before the NUMERIC_DATA message. Because there was no callback created to process a message of type INFO, the message was ignored. The second message was sent, but because the receiving program is set up to read and process only one message, the NUMERIC_DATA message was never read.

Step 10

Copy the receive2.java program

Copy the receive2.java program into your working directory.

The receive2.java program is simply receive.java, modified so that it can read and process any number of messages. Copying this file is the equivalent of replacing lines 28 and 29 of the receiving program with this piece of code:

// Read and process all incoming messages 
while (null != (msg = srv.next(TipcDefs.TIMEOUT_FOREVER))) { 
srv.process(msg); 
} // while 

This code creates a while loop that continues to read and process messages until TipcSrv.next returns null.

Now you should create a default callback to process any non-NUMERIC_DATA messages by adding this code to the receiving program after the callback for NUMERIC_DATA messages has been registered:

// register receiveCallback again as a default callback 
TipcCb dcbh = srv.addDefaultCb(rcb, srv); 

To complete the program, the default callback method handle should be added to the receiveCb class. This method simply prints out the name and type of the message. These changes have been made in the receive2.java program:

//-----------------------------------------------------------------   
// receive2.java -- output a NUMERIC_DATA message with a callback 
 
1 import java.io.*; 
2 import com.smartsockets.*; 
 
3 public class receive2 { 
 
4   public class receiveCb  
5   implements TipcProcessCb, TipcDefaultCb { 
 
6     public void process(TipcMsg msg, Object arg) { 
7       System.out.println("Received NUMERIC_DATA message"); 
 
        // position the field ptr to the beginning of the message 
        try { 
8         msg.setCurrent(0); 
9       } catch (TipcException e) { 
10          Tut.fatal(e); 
        } // catch 
 
        // read the data part of the message 
        try { 
11        String var_name; 
12        while (true) { 
13          var_name = msg.nextStr(); 
14          double var_value; 
15          var_value = msg.nextReal8(); 
16          System.out.println("Var name = " + var_name +  
                             ", value = " + var_value); 
          } // while 
17        } catch (TipcException e) { } 
          // catch end-of-message-data exception, do nothing. 
        } // process 
 
        // handle() is for responding to default messages 
18      public void handle(TipcMsg msg, Object arg) { 
19        System.out.println("Receive: unexpected message type name" + 
          " is <" + msg.getType().getName() + ">"); 
        } // handle 
      } // receiveCb 
 
20    public receive2() { 
21      TipcMsg msg = null; 
        // set the ss.project 
        try { 
22        Tut.setOption("ss.project", "smartsockets"); 
23        TipcSrv srv=TipcSvc.getSrv(); 
 
          // create a new receive listener and register it 
24        receiveCb rcb = new receiveCb(); 
25        TipcCb rcbh = srv.addProcessCb( 
          rcb, TipcSvc.lookupMt(TipcMt.NUMERIC_DATA), srv); 
          // check the 'handle' returned for validity 
26        if (null == rcbh) { 
            Tut.exitFailure("Couldn't register process listener!"); 
          } // if 
 
          // register receiveCb again as a default listener 
27        TipcCb dcbh = srv.addDefaultCb(rcb, srv); 
          // check the 'handle' returned for validity 
28        if (null == dcbh) { 
29          Tut.exitFailure("Couldn't register default listener!"); 
          } // if 
 
          // connect to RTserver 
30        if (!srv.create()) { 
31          Tut.exitFailure("Couldn't connect to RTserver!"); 
          } // if 
 
          // subscribe to the appropriate subject 
32        srv.setSubjectSubscribe("/ss/tutorial/lesson4", true); 
 
          // read and process all incoming messages 
33        while (null != (msg = srv.next(TipcDefs.TIMEOUT_FOREVER))) { 
34            srv.process(msg); 
          } // while 
 
          // disconnect from RTserver 
35        srv.destroy(); 
 
          // unregister the listeners for completeness 
36        srv.removeProcessCb(rcbh); 
37        srv.removeDefaultCb(dcbh); 
38      } catch (TipcException e) { 
39        Tut.fatal(e); 
      } // catch 
    } // receive2 (constructor) 
 
40  public static void main(String[] argv) { 
41     receive2 r = new receive2(); 
    } // main 
  } // receive2 class 

Before running your updated receiving program, copy the send3.java program to your working directory. The send3.java program is the send2.java program, modified to send multiple messages.

This is the send3.java program:

//-----------------------------------------------------------------   
// send3.java -- write an INFO and then NUMERIC_DATA messages 
 
1 import java.io.*; 
2 import com.smartsockets.*; 
 
3   public class send3 { 
 
4     public static void main(String[] argv) { 
        try { 
5         Tut.setOption("ss.project", "smartsockets"); 
 
6         TipcSrv srv=TipcSvc.getSrv(); 
7         if (!srv.create()) { 
8           Tut.exitFailure("Couldn't connect to RTserver!"); 
          } // if 
 
          // send a message of type INFO 
9         TipcMsg msgi = TipcSvc.createMsg(TipcMt.INFO); 
10        msgi.setDest("/ss/tutorial/lesson4"); 
11        msgi.appendStr("Hello World!"); 
12        srv.send(msgi); 
13        srv.flush(); 
 
          // create a message of type NUMERIC_DATA 
14         TipcMsg msg = TipcSvc.createMsg(TipcMt.NUMERIC_DATA); 
 
           // set the destination subject of the message 
15         msg.setDest("/ss/tutorial/lesson4"); 
 
           // each time through the loop send a NUMERIC_DATA 
           // message with three values 
16         for (int i = 0; i < 30; i = i + 3) { 
17           msg.setNumFields(0); 
18           msg.appendStr("X"); 
19           msg.appendReal8(i); 
20           msg.appendStr("Y"); 
21           msg.appendReal8(i+1.0); 
22           msg.appendStr("Z"); 
23           msg.appendReal8(i+2.0); 
   
             // send the message 
24           srv.send(msg); 
25           srv.flush(); 
           }  
 
             // disconnect from RTserver 
26           srv.destroy(); 
27         } catch (TipcException e) { 
28           Tut.warning(e); 
           } // catch 
        } // main 
      } // send3 

Let’s examine the key lines in this program:

Lines 16-25
This is a for loop that sends out a series of NUMERIC_DATA messages.
Line 17
The same message is re-used each time; only the data part of the message is changed. At the beginning of the loop, TipcMsg.setNumFields resets the data part of the message to have zero fields.

In the next few steps, you run receive2 with send3 instead of the usual pairing of receive2 with send2 or receive3 with send3.

Step 11

Copy the send3.java program

Copy the send3.java program into your working directory, and compile it with the command:

$ javac send3.java 

Step 12

Compile the new receive2.java program

Compile your new receive2.java program using the command:

$ javac receive2.java 

Step 13

Start the receiving program

Start the receiving program in one window of your display using the command:

$ java receive2 

Step 14

Start the new sending program

In another window, to send a message to the receiving program, run the new sending program using the command:

$ java send3 

After running the sending program, this output is displayed in the receiving program window:

Receive: unexpected message type name is <info> 
Received NUMERIC_DATA message 
Var name = X, value = 0.0 
Var name = Y, value = 1.0 
Var name = Z, value = 2.0 
Received NUMERIC_DATA message 
Var name = X, value = 3.0 
Var name = Y, value = 4.0 
Var name = Z, value = 5.0 
 
// ... Output omitted here ... 
Received NUMERIC_DATA message 
Var name = X, value = 27.0 
Var name = Y, value = 28.0 
Var name = Z, value = 29.0 

When the send3 program has completed, notice that the receive2 program is still hanging; it is waiting for more messages.

Step 15

Interrupt the receiving program

Type Ctrl-c to interrupt the receive2 program.

For each NUMERIC_DATA message received, the callback method receiveCb.process() was invoked to print out the contents of the data part of the message. The very first message received was an INFO message. Because there were no process callbacks available for INFO messages, the default callback, receiveCb’s handle method, was called and printed the type of unexpected message received.

Writing a Subject Callback

Rather than processing a message based on its type, you can process a message based on its destination using subject callbacks. With a subject callback, you can specify a separate function for each subject or group of subjects you wish to operate on. When a message arrives at the receiver for the specified subject and is ready to be processed, the callback is executed.

To create a subject callback, you invoke one of TipcSrv’s addProcessCb method’s overloaded forms that allow a String subject to be specified, as shown:

addProcessCb(callback, mt, subject, arg) 
addProcessCb(callback, subject, arg) 

where

subject
is the destination you wish to specify the callback on and
mt
is the message type the callback should be applied to.

You can specify a value of null for subject or mt to specify "any." (It may be necessary to explicitly cast null as a String so the compiler can determine which method implementation to use.) Subject callbacks are actually a superset of process callbacks as they allow message type and subject callbacks to be mixed (for example, execute this callback when a message of type T arrives on subject S).

Some examples of creating subject callbacks are shown:

TipcSrv srv = TipcSvc.getSrv(); 
TipcMt mt = TipcSvc.lookupMt(TipcMt.INFO); 
 
// Call subj_cb’s process() method upon receipt of any 
// message that has a destination of "/tutorial" 
srv.addProcessCb(subj_cb, "/tutorial", null); 
 
// Execute subj_cb’s process() method upon receipt of any 
// messages of type INFO, regardless of the destination 
srv.addProcessCb(subj_cb, mt, (String)null, null); 
 
// Execute the function subj_cb for any messages of type  
// INFO, which have a destination of "/tutorial" 
srv.addProcessCb(subj_cb, mt, "/tutorial", null); 

In this section you modify the examples used for process callbacks to show how easy it is to use subject callbacks. The next code example describes a specific subject callback in detail. The callback object’s process method is invoked when a message is received that has a destination of /ss/tutorial/lesson4. The process method simply gets the type of the message and then prints the fields of the message.

Step 16

Copy the subjcbs.java program

Copy the subject callback program, subjcbs.java, into your working directory. The contents of the file subjcbs.java are:

//----------------------------------------------------------------- 
// subjcbs.java -- output messages through subject callbacks 
 
1 import java.io.*; 
2 import com.smartsockets.*; 
 
3 public class subjcbs { 
 
4   public class processLesson4 implements TipcProcessCb { 
 
5     public void process(TipcMsg msg, Object arg) { 
6       System.out.println("*** Received message of type <" + 
                         msg.getType().getName()+">"); 
 
        // position the field ptr to the beginning of the message 
        try { 
7         msg.setCurrent(0); 
        } 
8       catch (TipcException e) { 
9         Tut.fatal(e); 
10       } 
 
         // display message contents based on type 
11       int mt = msg.getType().getNum(); 
12       switch (mt) { 
13         case TipcMt.INFO: 
             try { 
14             System.out.println("Text from message = "+  
                                     msg.nextStr()); 
15           } catch (TipcException e) { } 
16           break; 
 
17           case TipcMt.NUMERIC_DATA: 
18             String var_name; 
               try { 
                 // display the repeating part of NUMERIC_DATA message 
19               while (true) { 
20                 var_name = msg.nextStr(); 
21                 double var_value; 
22                 var_value = msg.nextReal8(); 
23                 System.out.println("Var name = " + var_name +  
                               ", value = " + var_value); 
               } // while 
               // catch end-of-message-data exception, do nothing. 
24           } catch (TipcException e) { } 
25           break; 
 
26         default: 
            // handle messages of unknown type 
27          System.out.println("Unable to process messages of this type!"); 
28          break;  
        } // switch 
      } // process 
    } // processLesson4 
 
29  public subjcbs() { 
30    TipcMsg msg = null; 
 
      // set the ss.project 
      try { 
31      Tut.setOption("ss.project", "smartsockets"); 
32      TipcSrv srv=TipcSvc.getSrv(); 
 
        // create a new receive SUBJECT callback and register it 
33      processLesson4 pl = new processLesson4(); 
34      TipcCb rcbh = srv.addProcessCb(pl, "/ss/tutorial/lesson4", srv); 
        // check the 'handle' returned for validity 
35      if (null == rcbh) { 
36        Tut.exitFailure("Couldn't register subject callback!"); 
        } // if 
 
        // connect to RTserver 
37      if (!srv.create()) { 
38        Tut.exitFailure("Couldn't connect to RTserver!"); 
        } // if 
        // subscribe to the appropriate subject 
39      srv.setSubjectSubscribe("/ss/tutorial/lesson4", true); 
 
        // read and process all incoming messages 
40      while (srv.mainLoop(TipcDefs.TIMEOUT_FOREVER)) { 
        } // while 
 
        // unregister the callbacks 
41      srv.removeProcessCb(rcbh); 
 
        // disconnect from RTserver 
42      srv.destroy(); 
43    } catch (TipcException e) { 
44      Tut.fatal(e); 
      } // catch 
    } // subjcbs (constructor) 
 
45  public static void main(String[] argv) { 
46    new subjcbs(); 
    } // main 
  } // subjcbs class 
 

Some interesting things to learn from your new subjcbs program are:

Lines 5-28
The processing of messages of all types is now in the callback object ProcessLesson4 process method. The method first gets the type of the message and then prints outs the contents based on the type. In effect, you have a simple process (message type) callback within a subject callback.
Lines 11-12
The received message’s type is extracted and acted upon with a switch statement.
Lines 33-34
A subject callback object, pl, is created and registered for messages arriving with a destination of /ss/tutorial/lesson4.
Line 39
Even though we have defined a subject callback on /ss/tutorial/lesson4, we still need to make sure that the program subscribes to the subject.
Line 40
TipcSrvMainLoop invokes the subject callback whenever a message arrives with the given destination.

You now execute the new program using subject callbacks to verify that it works correctly.

Step 17

Copy the subjcbs.java program and compile

Copy the subjcbs.java program into your working directory, and compile it with the command:

$ javac subjcbs.java 

Step 18

Start the subject callback program

Start the subject callback program in one window of your display using the command:

$ java subjcbs 

Step 19

Start the sending program

In another window, run the sending program used earlier in this lesson with the command to send a message to the subject callback program:

$ java send3 

After running the sending program, this output is displayed in the window where you ran the subject callback program:

Attempting connection to <tcp:_node:5101> RTserver. 
Connected to <tcp:_node:5101> RTserver. 
*** Received message of type <info> 
Text from message = Hello World! 
*** Received message of type <numeric_data> 
Var name = X, value = 0.0 
Var name = Y, value = 1.0 
Var name = Z, value = 2.0 
*** Received message of type <numeric_data> 
Var name = X, value = 3.0 
Var name = Y, value = 4.0 
Var name = Z, value = 5.0 
 
// more output omitted here... 
 
*** Received message of type <numeric_data> 
Var name = X, value = 27.0 
Var name = Y, value = 28.0 
Var name = Z, value = 29.0 

When the sending program has completed, notice that the subject callback program is still hanging. It is waiting for more messages.

Step 20

Interrupt the subject callback program

Type Ctrl-c to interrupt the subject callback program.

For each message received, the callback object ProcessLesson4’s process method was invoked to print out the contents of the data part of the message, regardless of the type of the message.

Specifying a Callback Based on Subject and Message Type

The example in the previous section can be further modified to specify a different subject callback for each of the different message types: INFO and NUMERIC_DATA. This is done by creating two new callback objects: ProcessInfo and ProcessNumData. In the main program, two calls are required to TipcSrv.addProcessCb, one for each of the message types. The complete example is shown:

//----------------------------------------------------------------- 
// subjcbs2.java -- output messages through subject/mt callbacks 
 
import java.io.*; 
import com.smartsockets.*; 
 
public class subjcbs2 { 
 
  public class processInfo implements TipcProcessCb { 
 
      public void process(TipcMsg msg, Object arg) { 
        System.out.println("*** Received INFO message"); 
        try { 
          msg.setCurrent(0); 
          System.out.println("Text from message = " + msg.nextStr()); 
        } catch (TipcException e) { } 
      } // process 
    } // processInfo 
 
    public class processNumData implements TipcProcessCb { 
 
      public void process(TipcMsg msg, Object arg) { 
        System.out.println("*** Received NUMERIC_DATA message"); 
        String var_name; 
        try { 
          msg.setCurrent(0); 
          // display the repeating part of NUMERIC_DATA message 
          while (true) { 
            var_name = msg.nextStr(); 
            double var_value; 
            var_value = msg.nextReal8(); 
            System.out.println("Var name = " + var_name +  
                             ", value = " + var_value); 
          } // while 
        // catch end-of-message-data exception, do nothing. 
        } catch (TipcException e) { } 
      } // process 
    } // processNumData 
 
    public subjcbs2() { 
      TipcMsg msg = null; 
 
      // set the ss.project 
      try { 
        Tut.setOption("ss.project", "smartsockets"); 
        TipcSrv srv=TipcSvc.getSrv(); 
 
        // create a new info mt/subject callback and register it 
        processInfo pi = new processInfo(); 
        TipcCb rcbh1 = srv.addProcessCb(pi, 
                TipcSvc.lookupMt(TipcMt.INFO),
            "/ss/tutorial/lesson4", null); 
        // check the 'handle' returned for validity 
        if (null == rcbh1) { 
          Tut.exitFailure("Couldn't register subject callback!"); 
        } // if 
 
        // create a new info mt/subject callback and register it 
        processNumData pnd = new processNumData(); 
        TipcCb rcbh2 = srv.addProcessCb(pnd, 
                         TipcSvc.lookupMt(TipcMt.NUMERIC_DATA),  
                         "/ss/tutorial/lesson4", null); 
        // check the 'handle' returned for validity 
        if (null == rcbh2) { 
          Tut.exitFailure("Couldn't register subject callback!"); 
        } // if 
 
        // connect to RTserver 
        if (!srv.create()) { 
          Tut.exitFailure("Couldn't connect to RTserver!"); 
        } // if 
        // subscribe to the appropriate subject 
        srv.setSubjectSubscribe("/ss/tutorial/lesson4", true); 
 
        // read and process all incoming messages 
        while (srv.mainLoop(TipcDefs.TIMEOUT_FOREVER)) { 
        } // while 
 
        // unregister the callbacks 
        srv.removeProcessCb(rcbh1); 
        srv.removeProcessCb(rcbh2); 
 
        // disconnect from RTserver 
        srv.destroy(); 
      } catch (TipcException e) { 
        Tut.fatal(e); 
      } // catch 
    } // subjcbs2 (constructor) 
 
    public static void main(String[] argv) { 
      new subjcbs2(); 
    } // main 
 
  } // subjcbs2 class 

For more details on subject and message type callbacks, see the TIBCO SmartSockets User’s Guide.

Using the TipSrv.mainLoop Convenience Method

In the receive2 program, this while loop is added to read and process all incoming messages:

// read and process all incoming messages 
while (null != (msg = srv.next(TipcDefs.TIMEOUT_FOREVER))) { 
      srv.process(msg); 
    } // while 

This entire loop can be replaced by this single call:

srv.mainLoop(TipcDefs.TIMEOUT_FOREVER); 

The TipcSrv.mainLoop() convenience method receives and processes messages from RTserver by calling TipcSrv.next and TipcSrv.process over and over. TipcSrv.mainLoop is a convenience method that keeps calling TipcSrv.next with the time remaining from timeout until TipcSrv.next returns false. For each message that TipcSrv.mainLoop gets, it processes the message with TipcSrv.process. Use 0.0 for timeout to poll the RTserver connection and catch up on all pending messages that have accumulated or to return immediately if no messages are pending. Use TipcDefs.TIMEOUT_FOREVER for timeout to read and process messages indefinitely. See the online documentation on TipcSrv.mainLoop for more details.

A modified receive2 program, which uses TipcSrv.mainLoop, is located in the file receive3.java. You can compile and run it with send3 if you want to verify that it produces the same output as before.

Using Server Create and Destroy Callbacks

Earlier in this lesson, you saw example programs that used process and default callbacks to work with messages. In this section two new callback types are shown: server create and server destroy. A server create callback’s create method is executed when an RTclient connects to RTserver, and a server destroy callback’s destroy method is executed when an RTclient disconnects from RTserver.

In this lesson, you trigger these callbacks with a simple example. The program, srvcbs, prompts you for a password each time it tries to connect to RTserver. If the password is incorrect, the program is disconnected from RTserver and terminated.

Step 21

Copy the svrcbs.java file

Copy the create callback program, svrcbs.java, into your working directory.

This is the svrcbs.java program:

//------------------------------------------------------------- 
// svrcbs.java -- server create/destroy callbacks 
 
1 import java.io.*; 
2 import com.smartsockets.*; 
 
3 public class svrcbs { 
4   String password_correct = "ssjava"; 
 
5   public class serverConnect implements TipcCreateCb {  
 
6      public void create(Object srv_obj) { 
7        TipcSrv srv = (TipcSrv)srv_obj; 
8        System.out.println("Connecting to RTserver..."); 
9        System.out.print("Please enter password: "); 
10       BufferedReader in = new BufferedReader( 
             new InputStreamReader(System.in) ); 
11       String password_entered = null; 
         try { 
12            password_entered = in.readLine(); 
13       } catch (IOException e) { 
14         System.out.println("Error! "+e.getMessage()); 
         } // catch 
15       if (password_entered.equals(password_correct)) { 
16         System.out.println("Password accepted!"); 
         }  
         else { 
17         System.out.println("Password is not correct! " + 
             "You are being disconnected from RTserver"); 
           try { 
18           srv.destroy(); 
19           Tut.exitSuccess(); 
20         } catch (TipcException e) { 
21           Tut.warning("Can't destroy server connection: " + 
                  e.getMessage()); 
          } // catch 
        } // else 
      } // create 
    } // serverConnect 
 
22  public class serverDisconnect implements TipcDestroyCb { 
23    public void destroy(Object obj) { 
24      System.out.println("...Disconnecting from RTserver"); 
      } // destroy 
    } // serverDisconnect 
 
25  public svrcbs() { 
26    TipcMsg msg = null; 
 
      try { 
27      TipcSrv srv = TipcSvc.getSrv(); 
 
        // create a new connect callback and register it 
28      serverConnect sc = new serverConnect(); 
29      TipcCb sch = srv.addCreateCb(sc, srv); 
        // check the 'handle' returned for validity 
30      if (null == sch) { 
31         Tut.exitFailure("Couldn't register create callback!"); 
        } // if 
        // and a destroy callback 
32      serverDisconnect sd = new serverDisconnect(); 
33      TipcCb sdh = srv.addDestroyCb(sd, srv); 
        // check the 'handle' returned for validity 
34      if (null == sdh) { 
35        Tut.exitFailure("Couldn't register destroy callback!"); 
        } // if 
 
        // connect to RTserver 
36      srv.create(); 
 
        // read and process all incoming messages 
37      while (true) { 
38        srv.mainLoop(2.0); 
        } // while 
 
39    } catch (TipcException e) { 
40      Tut.fatal(e); 
      } // catch 
      // svrcbs (constructor) 
 
41   public static void main(String[] argv) { 
42     new svrcbs(); 
    } // main 
  } // svrcbs 

Let’s examine the key lines in this program:

Line 29
The server create callback (serverConnect) is registered with the call to TipcSrv.addCreateCb.
Line 33
The server destroy callback (serverDisconnect) is registered with the call to TipcSrv.addDestroyCb.
Line 36
Notice that both the server create and server destroy callbacks are registered before the initial connection to RTserver is made through a call to TipcSrv.create.
Lines 17-21
The server create callback disconnects the program from RTserver with the TipcSrv.destroy method if an incorrect password is given.

Let’s see how these programs use callbacks, and how RTserver affects their operation.

Step 22

Compile the svrcbs.java program

Compile the svrcbs.java program using the command:

$ javac svrcbs.java 

Step 23

Start the create callback program

Start the create callback program in one window of your display using the command:

$ java svrcbs 

This output is displayed:

Attempting connection to <_node>. 
Connecting to RTserver... 
Please enter password:  

The last two lines of output are from the server create callback. This was executed when the process tried to connect to RTserver for the first time. You are prompted for a password.

Step 24

Enter a password

Enter this password and press the return key:

Please enter password: ssjava 

When the correct password is entered, this text is displayed:

Password accepted! 

The program is now successfully connected to RTserver. Let’s manually break the connection to RTserver and see what happens.

Step 25

Stop the RTserver

In another window, stop RTserver using a command line argument to the rtserver command:

$ rtserver -stop 

This new output is displayed in the window where you ran the create callback program:

WARNING: lost connection: reader: in: connection dropped Connection 
reset 
...Disconnecting from RTserver 
Waiting before reconnecting. 
Attempting connection to <_node> RTserver. 
WARNING: lost connection: Connection refused: connect 
Attempting connection to <_node> RTserver. 
WARNING: lost connection: Connection refused: connect 
Attempting connection to <_node> RTserver. 
WARNING: lost connection: Connection refused: connect 
Attempting connection to <_node> RTserver. 

This output continues until another RTserver is found. Stopping RTserver resulted in a sequence of events happening:

  1. The server destroy callback was executed and output:
  2. ...Disconnecting from RTserver 
    
  1. The create callback program then attempted to re-connect with RTserver. This is a fault-tolerant feature of SmartSockets.

Step 26

Start a new RTserver

In the other window (where you stopped RTserver), start a new RTserver:

$ rtserver 

On platforms that support both 32- and 64-bit, use the rtserver64 command to run the 64-bit version of the rtserver script.

When the connection is re-established, the server create callback is executed and you are again prompted for the password:

Connecting to RTserver... 
Please enter password: 

Step 27

Enter an incorrect password

This time, enter an incorrect password:

Please enter password: foo 

This output is displayed:

Password is not correct! 
You are being disconnected from RTserver 
...Disconnecting from RTserver 

In this case, the server create callback disconnected from RTserver and terminated the program. When disconnecting from RTserver, the server destroy callback was executed. This demonstrates how callbacks can trigger events to which other callbacks then respond.


TIBCO SmartSockets™ Java Library User’s Guide and Tutorial
Software Release 6.8, July 2006
Copyright © TIBCO Software Inc. All rights reserved
www.tibco.com