Michael Elhadad

Software Engineering - Fall 1999


Lecture 9: Corba (Part 2) -- Stock Example

Motivation

Read Schmidt's columns on Corba. This page uses Schmidt's Stock example translated into Java.

Stock Quoter in Java/Corba

Everything starts with an IDL definition. This definition supports an interaction in two modes: pull (the client asks the server whenever it needs info on a particular stock) and push (the client registers interest in an event; the server sends notification to the client whenever something happens).
module Stock
{
  exception Invalid_Stock {};

  interface Quoter
  {
    long get_quote(in string stock_name)
      raises (Invalid_Stock);
  };

  module Callback {
    struct Info {
      string stock_name;
      long value;
    };

    // Distributed callback interface 
    // (invoked by the Supplier)
    interface Handler {
      void push(in Info data);
    };
  };

  interface Notifying_Quoter {
    // Register a distributed callback handler
    void register(in string stock_name,
                  in long threshold,
                  in Callback::Handler handler)
      raises (Invalid_Stock);
  };
};

IDL to Java

Run idltojava to convert this IDL to a set of files:
% idltojava stock.idl
% ls -lR Stock/*.java

-rw-r--r--   347 Stock/Invalid_Stock.java
-rw-r--r--  1564 Stock/Invalid_StockHelper.java
-rw-r--r--   832 Stock/Invalid_StockHolder.java
-rw-r--r--   393 Stock/Notifying_Quoter.java
-rw-r--r--  1897 Stock/Notifying_QuoterHelper.java
-rw-r--r--   859 Stock/Notifying_QuoterHolder.java
-rw-r--r--   326 Stock/Quoter.java
-rw-r--r--  1747 Stock/QuoterHelper.java
-rw-r--r--   769 Stock/QuoterHolder.java
-rw-r--r--  2872 Stock/_Notifying_QuoterImplBase.java
-rw-r--r--  1715 Stock/_Notifying_QuoterStub.java
-rw-r--r--  2135 Stock/_QuoterImplBase.java
-rw-r--r--  1594 Stock/_QuoterStub.java

-rw-r--r--   315 Stock/Callback/Handler.java
-rw-r--r--  1879 Stock/Callback/HandlerHelper.java
-rw-r--r--   841 Stock/Callback/HandlerHolder.java
-rw-r--r--   470 Stock/Callback/Info.java
-rw-r--r--  1991 Stock/Callback/InfoHelper.java
-rw-r--r--   814 Stock/Callback/InfoHolder.java
-rw-r--r--  1775 Stock/Callback/_HandlerImplBase.java
-rw-r--r--   906 Stock/Callback/_HandlerStub.java

The Java CORBA Client in Pull Mode

The Pull mode is very similar to the Hello client. It does not need anything special:
  1. Import the IDL generated package
  2. Connect to the orb (ORB.init)
  3. Connect to the Naming service (orb.resolve_initial_references)
  4. Obtain an object reference from the naming service (ncRef.resolve)
  5. Narrow the object reference to a stub interface (use the Helper class)
  6. Use the object reference (use the Stub class)
This is illustrated in this program:
import Stock.*;              // The package containing our stubs.
import org.omg.CosNaming.*;  // StockClient uses the naming service.
import org.omg.CORBA.*;      // All CORBA applications need these classes.


public class StockClient
{
  public static void main(String args[])
  {
    try{
      // Create and initialize the ORB
      ORB orb = ORB.init(args, null);
      
      // Get the root naming context
      org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
      NamingContext ncRef = NamingContextHelper.narrow(objRef);
      
      // Resolve the object reference in naming
      NameComponent nc = new NameComponent("Stock", "");
      NameComponent path[] = {nc};
      Quoter quoterRef = QuoterHelper.narrow(ncRef.resolve(path));
      
      // Call the Hello server object and print results
      int val = quoterRef.get_quote("IBM");
      System.out.println("IBM = "+val);      
    } catch(Exception e) {
        System.out.println("ERROR : " + e);
        e.printStackTrace(System.out);
    }  
  }
}

The Java CORBA Server in Pull Mode

Similarly, the Server in pull mode is very similar to the Hello server:
  1. Import the IDL generated package
  2. Connect to the orb (ORB.init)
  3. Create a servant object for each implemented IDL interface (new QuoterServant)
  4. Connect the servant with the orb (orb.connect)
  5. Connect to the Naming service (orb.resolve_initial_references)
  6. Register (bind) the servant object with the naming service (ncRef.Rebind)
  7. Enter in a loop to wait for client connections
This is illustrated in the this program:
import Stock.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;


public class StockServer 
{
  public static void main(String args[])
  {
    try{
      // Create and initialize the ORB
      ORB orb = ORB.init(args, null);
      
      // Create the servant and register it with the ORB
      QuoterServant quoterRef = new QuoterServant();
      orb.connect(quoterRef);
      
      // Get the root naming context
      org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
      NamingContext ncRef = NamingContextHelper.narrow(objRef);
      
      // Bind the object reference in naming
      NameComponent nc = new NameComponent("Stock", "");
      NameComponent path[] = {nc};
      ncRef.rebind(path, quoterRef);
      
      // Wait for invocations from clients
      java.lang.Object sync = new java.lang.Object();
      synchronized(sync){
        sync.wait();
      }
      
    } catch(Exception e) {
        System.err.println("ERROR: " + e);
        e.printStackTrace(System.out);
    }  
  }
}

class QuoterServant extends _QuoterImplBase
{
  public int get_quote(String stock_name)
       throws Stock.Invalid_Stock {
    return 103;
  }
}

The Java CORBA Client in Push Mode

When moving to the push mode, things get more interesting.
import Stock.*;              // The package containing our stubs.
import Stock.Callback.*;
import org.omg.CosNaming.*;  // StockClient uses the naming service.
import org.omg.CORBA.*;      // All CORBA applications need these classes.


public class StockClient2
{
  public static void main(String args[])
  {
    try{
      
      // Create and initialize the ORB
      ORB orb = ORB.init(args, null);
      
      // Create the Callback servant and register it with the ORB
      HandlerServant handlerRef = new HandlerServant();
      orb.connect(handlerRef);

      // Get the root naming context
      org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
      NamingContext ncRef = NamingContextHelper.narrow(objRef);
      
      // Resolve the object reference in naming
      NameComponent nc = new NameComponent("NotifyingStock", "");
      NameComponent path[] = {nc};
      Notifying_Quoter quoterRef =
	Notifying_QuoterHelper.narrow(ncRef.resolve(path)); 
      
      // Register Handler with server object
      quoterRef.register("IBM", 100, handlerRef);

      // Wait for invocations from notifying server
      java.lang.Object sync = new java.lang.Object();
      synchronized(sync){
        sync.wait();
      }
          
    } catch(Exception e) {
        System.out.println("ERROR : " + e);
        e.printStackTrace(System.out);
      }  
  }
}

class HandlerServant extends _HandlerImplBase
{
  public void push(Stock.Callback.Info data) {
    System.out.println(data.stock_name + " = " + data.value);
  }
}

The Java CORBA Server in Push Mode

The server in push mode is organized in 3 threads:
  1. Main thread: handles initiation protocol with ORB and naming service.
  2. Registration servant: receives register requests from clients.
  3. Monitoring thread: polls the data source and sends events when relevant.
A central element is the table of registered clients. For each subscription, the table records:
  1. The name of the stock
  2. The threshold value to monitor
  3. An object reference to notify whenever the stock goes over the threshold
This table is implemented using a standard Hashmap container from Java 2's container library. Note the use of a fully synchronized map using the Collections.Synchronizedmap() utility.

import Stock.*;
import Stock.Callback.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;

// For maps
import java.util.*;


public class StockServer2
{
  public static void main(String args[])
  {
    try{
      // Create and initialize the ORB
      ORB orb = ORB.init(args, null);

      // Create map: table of registered clients
      Map cb_map = Collections.synchronizedMap(new HashMap());

      // Create the servant and register it with the ORB
      Notifying_QuoterServant quoterRef = new Notifying_QuoterServant(cb_map);
      orb.connect(quoterRef);
      
      // Get the root naming context
      org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
      NamingContext ncRef = NamingContextHelper.narrow(objRef);
      
      // Start monitoring thread: will send events to clients
      (new Stock_Monitoring(cb_map)).start();

      // Bind the object reference in naming
      NameComponent nc = new NameComponent("NotifyingStock", "");
      NameComponent path[] = {nc};
      ncRef.rebind(path, quoterRef);
      
      // Wait for invocations from clients
      java.lang.Object sync = new java.lang.Object();
      synchronized(sync){
        sync.wait();
      }
      
    } catch(Exception e) {
        System.err.println("ERROR: " + e);
        e.printStackTrace(System.out);
      }  
  }
}


// This is only needed for the map of clients
class Pair 
{
  public Pair(int first, Stock.Callback.Handler second) {
    _first = first;
    _second = second;
  }
  public int threshold() {return _first;}
  public Stock.Callback.Handler handler() {return _second;}

  private int _first;
  private Stock.Callback.Handler _second;
}

// Handles the registration of clients
class Notifying_QuoterServant extends _Notifying_QuoterImplBase
{
  public Notifying_QuoterServant(Map cb) {
    super();
    map = cb;
  }
  public void register(String stock_name, int threshold, 
		Stock.Callback.Handler handler) 
       throws Stock.Invalid_Stock {
	 System.out.println("Registering " + stock_name);
	 map.put(stock_name, new Pair(threshold, handler));
  }	 
  private Map map;
}


// Thread that monitors (in polling mode) the source of stock prices
// and sends notification to subscribed clients when relevant.
// The table of subscribed clients is shared with the registration servant.
class Stock_Monitoring extends Thread 
{
  public Stock_Monitoring(Map cb) {
    super();
    map = cb;
    System.out.println("Constructing monitor -- " + map.size() + " elements");
  }

  private Map map;

  public void run() {
    // Loop -- get new value
    // Verify if someone needs it
    // For now, just iterate over all clients
    for (;;) {
      try {
	sleep(1000);
	Iterator iter = map.entrySet().iterator();
	while (iter.hasNext()) {
	  Map.Entry e = (Map.Entry)iter.next();
	  String stock = (String)e.getKey();
	  Pair p = (Pair)e.getValue();
	  System.out.println("Pushing " + stock + " " + p.threshold());
	  try {
	    p.handler().push(new Stock.Callback.Info(stock,
						     p.threshold()));
	  } catch (Exception e1) {
	    System.err.println("Client for " + stock + " has unregistered");
	    iter.remove();
	  }
	}
      } catch(Exception e2) {
	System.err.println("ERROR: " + e2);
	e2.printStackTrace(System.out);
      }  
    }
  }
}

Running the Example

The files needed are:
Last modified June 6th, 1999 Michael Elhadad