# Java IDL: Example with Callback Object

Client programs often react to changes or updates that occur in a server. For example, a client graph or spreadsheet program might need to be updated with each stock price update on a stock market server. The client has two options in this scenario:

- Periodically ask for the stock price via a method request on the stock server. This is known as the "polling" or "pull" model.
- Ask to be notified by the server whenever a price change occurs. This option is referred to as a "callback" or the "push" model.

The example in this document illustrates how a client program can pass a callback object to a server, and the server can then callback to notify changes to the client. At this time, we have provided the code for an extension of a simple application. Notes about simplifying the application are contained within the code.

This document provides the code for:

- The IDL for an example program with a callback.
- A server implementation that callsback to a client.
- A client that sends a callback object reference to a server.
- An implementation of the `Listener`.
- An implementation of the `MessageServer`.

Instructions for compiling and running the example are also provided.

# Callback Example: Intermediate Level

## Writing the IDL file

For the example application, the file `callback.idl` looks like this:

    interface Listener {
    	void message(in string msg);
    };

    interface MessageServer {
    	void register(in Listener lt);
    };

## Writing the Server Code

For the example application, the `Server.java` file looks like as follows.

    import org.omg.CORBA.ORB;
    import org.omg.PortableServer.POA;
    import org.omg.PortableServer.POAHelper;
    import org.omg.CosNaming.NameComponent;
    import org.omg.CosNaming.NamingContext;
    import org.omg.CosNaming.NamingContextHelper;

    public class Server {

        public static void main(String[] args) {
    	try {
                //create and initialize the ORB
                Properties props = System.getProperties();
                props.put("org.omg.CORBA.ORBInitialPort", "1050");
                //Replace MyHost with the name of the host on which you are running the server
                props.put("org.omg.CORBA.ORBInitialHost", "<MyHost>");
                ORB orb = ORB.init(args, props);
    	    System.out.println("Initialized ORB");

                //Instantiate Servant and create reference
    	    POA rootPOA = POAHelper.narrow(
    		orb.resolve_initial_references("RootPOA"));
    	    MessageServerImpl msImpl = new MessageServerImpl();
    	    rootPOA.activate_object(msImpl);
    	    MessageServer msRef = MessageServerHelper.narrow(
    		rootPOA.servant_to_reference(msImpl));

                //Bind reference with NameService
    	    NamingContext namingContext = NamingContextHelper.narrow(
    		orb.resolve_initial_references("NameService"));
                System.out.println("Resolved NameService");
                NameComponent[] nc = { new NameComponent("MessageServer", "") };
    	    namingContext.rebind(nc, msRef);

    	    //Activate rootpoa
                rootPOA.the_POAManager().activate();

                //Start readthread and wait for incoming requests
                System.out.println("Server ready and running ....");

                //REMOVE THE NEXT LINE FOR THE SIMPLER EXAMPLE
                msImpl.startReadThread();

                orb.run();

    	} catch (Exception e) {
    	    e.printStackTrace();
    	}
        }
    }

## Writing the MessageServer Implementation

The file registers new clients, accepts messages, then relays the messages to the registered clients. For the example application, the file `MessageServerImpl.java` looks like the following example.

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.util.Vector;
    import java.util.Iterator;

    public class MessageServerImpl extends MessageServerPOA {

        private Vector clients = new Vector();
        private ReadThread rt = null;

        public MessageServerImpl() {
    	rt = new ReadThread(this);
        }

        public void register(Listener lt) {
    	clients.add(lt);
        }

        public void startReadThread() {
            rt.start();
        }

        public void message(String msg) {
    	Iterator it = clients.iterator();
    	while (it.hasNext()) {
    	    Listener lt = (Listener) it.next();
    	    lt.message(msg);
    	    //FOR THE SIMPLER EXAMPLE, ADD A SIMPLE
    	    //MESSAGE TO BE CALLED BACK, FOR EXAMPLE,
    	    //SLEEP FOR 30 SECONDS, THEN SEND THE TIME
    	}
        }
    }

    //EXCLUDE THIS CLASS FOR THE SIMPLER EXAMPLE
    class ReadThread extends Thread {

        MessageServerImpl msImpl = null;

        public ReadThread(MessageServerImpl msImpl) {
    	this.msImpl = msImpl;
        }

        public void run() {
    	BufferedReader br = new BufferedReader(
    	    new InputStreamReader(System.in));

            try {
    	    for (;;) {
                    System.out.print("message > ");
    		String msg = br.readLine();
    		msImpl.message(msg);
    	    }
    	} catch (Exception e) {
    	    e.printStackTrace();
    	}
        }
    }

## Writing the Client Code

For the example application, the file `Client.java` file looks like the following example.

    import java.util.Properties;
    import org.omg.CORBA.ORB;
    import org.omg.PortableServer.POA;
    import org.omg.PortableServer.POAHelper;
    import org.omg.CosNaming.NameComponent;
    import org.omg.CosNaming.NamingContext;
    import org.omg.CosNaming.NamingContextHelper;

    public class Client {

        public static void main(String[] args) {
    	try {

                //initialize orb
                Properties props = System.getProperties();
                props.put("org.omg.CORBA.ORBInitialPort", "1050");
                //Replace MyHost with the name of the host on which you are running the server
                props.put("org.omg.CORBA.ORBInitialHost", "<MyHost>");
                ORB orb = ORB.init(args, props);
    	    System.out.println("Initialized ORB");

                //Instantiate Servant and create reference
    	    POA rootPOA = POAHelper.narrow(
    		orb.resolve_initial_references("RootPOA"));
                ListenerImpl listener  = new ListenerImpl();
                rootPOA.activate_object(listener);
    	    Listener ref = ListenerHelper.narrow(
                    rootPOA.servant_to_reference(listener));

                //Resolve MessageServer
    	    MessageServer msgServer = MessageServerHelper.narrow(
    	        orb.string_to_object("corbaname:iiop:1.2@localhost:1050#MessageServer"));

                //Register listener reference (callback object) with MessageServer
                msgServer.register(ref);
                System.out.println("Listener registered with MessageServer");

    	    //Activate rootpoa
                rootPOA.the_POAManager().activate();

                //Wait for messages
                System.out.println("Wait for incoming messages");
                orb.run();

    	} catch (Exception e) {
    	    e.printStackTrace();
    	}
        }
    }

## Writing the Listener Implementation

When the Listener identifies that a message has been received from the server, it displays the message on the client. For the example application, the `ListenerImpl.java` file looks like the following example.

    public class ListenerImpl extends ListenerPOA {

        public void message(String msg) {
    	System.out.println("Message from server : " + msg);
        }
    }

## Instructions for compiling and running the example

To run the example application, you would follow these instructions on the SERVER machine:

1. If you've run the example previously, remove any existing generated files. In the Unix operating environment, this command would be as follows:

       rm -rf ./classes ./orb.db

2. Generate the stubs and skeletons. First, you need to create the directory where the stubs and skeletons will reside, then run the `idlj` compiler to generate the stubs and skeletons. This command would look like this:

       mkdir -p ./classes
       <path_to_java>/bin/idlj -fall -td ./classes callback.idl

3. Compile the `.java` files, as follows:

       <path_to_java>/bin/javac -classpath ./classes -d ./classes *.java

4. Start the `orbd` naming service, as follows:

       <path_to_java>/bin/orbd -ORBInitialPort 1050 -ORBInitialHost <host_name> &

5. Run the server, as follows:

       <path_to_java>/bin/java -classpath ./classes Server -ORBInitialPort 1050

After you have started the server, you will see output such as the following in the terminal window:

    Initialized ORB
    Resolved NameService
    Server ready and running ....
    message >

At this prompt, you will send messages to the client, but first you need to start the client application. From the CLIENT terminal, follow these steps to run the client. Make sure that you used the host name on which the server is running in the properties section of the client code.

    <path_to_java>/bin/java -classpath ./classes Client -ORBInitialPort 1050

The output from the client will display in a format similar to this:

    Initialized ORB
    Listener registered with MessageServer
    Wait for incoming messages

To display the functionality of using a callback, you would enter a data on the server terminal, at the prompt, and it will display on all client terminals. This could be anything where clients are interested in some notifications.

One example is a football game score where clients are notified whenever the score changes.

    message > Niners TD. Niners 7 - Giants 0
    message > Giants TD. Niners 7 - Giants 7
    message > Niners TD. Niners 14 - Giants 7

When messages such as those shown above are sent, the client display looks similar to the following:

    Wait for incoming messages
    Message from server : Niners TD. Niners 7 - Giants 0
    Message from server : Giants TD. Niners 7 - Giants 7
    Message from server : Niners TD. Niners 14 - Giants 7

Home

Copyright © 2002-2004 Sun Microsystems, Inc. All Rights Reserved.