4 Applet-Server Programming


This section describes how to write HORB-enabled applets for the World Wide Web. Double Clock is an example of Applet-Server programming and World Clock is an example of Applet-Applet programming. As you probably know, an applet is a small program that runs in a Java-capable World Wide Web browser. Java is now a standard language for writing such WWW applets.

With a few moderations, we can employ the techniques of HORB programming we've learned so far for writing HORB-enabled, communicating applets. There are, however, a few restrictions due to the security of WWW browsers:

In spite of these restrictions, HORB is very suitable for creating Web-based Applet-Server applications. For example, let's consider a database system that is accessible from WWW browsers. In the conventional WWW programming style, we would use CGI scripts:

However, in HORB-style Applet-Server programming, HORB does the boring stuff behind the scenes. There is no need to write CGI scripts.

Let's imagine ServerA is a WWW server machine. ServerA both serves HTML pages containing an applet written in HORB, and houses a server application written in HORB. When a user accesses the HORB-enabled page, the applet is able to connect to the HORB server we have written. The applet can then directly communicate with the server class by remote object operations.

While we must write an entire response page in case of CGI programming, HORB allows the more natural approach of directly influencing the applet's relevant input/output widgets. All classes required to communicate with the server can be supplied by the server automatically. Although users must wait during runtime (ORB) downloading, this time is minimal. (30KB, equivalent to a small picture.)

One exception may arise if firewalls exist. In this case we may need holes: one outgoing hole at the firewall of the client site, and another incoming hole at the firewall of the server site. IP routers do such screening. In the future, Java runtime will have proxying socket facility so that users can go over a firewall.


4.1 Double Clock

(See horb/examples/wclock for example code.)

In this subsection we will create a small example of HORB-enabled Applet-Server programming. The title of the applet is Double Clock, and it will display two digital clocks in the applet. One reading displays local time and the other displays the time of the server machine.

Below is class Server. This simple class contains only one method which returns the current time in string representation. If you require more complex functions, for example, to access to a database server, you can use arbitrary socket communications, or other utility packages such as an ODBC package for Java (if it exists). (Since a thread of a server object is created for each connection from clients, you may need to do synchronization within the server objects.)

It is necessary to use the package feature in order to show the relationship between the package feature of java and Applet tag. In our example, Server.class will belong to package horb.examples.wclock, and is stored in the lowest subdirectory wclock. The subdirectory horb is directly below a path mentioned in the CLASSPATH environment variable. Hence, if CLASSPATH includes C:\HTTPD\DATA, and this is where you keep your WWW materials, horb\examples\wclock might be set up directly below DATA.

        // server.java
        package horb.examples.wclock;
        import java.util.Date;

        public class Server {
          //
          // this class is a time server
          //
          public String currentTime() {
            Date date = new Date();     // get current system date and time
            String s;
            int hour = date.getHours();
            int min = date.getMinutes();
            int sec = date.getSeconds();
                    s = ""+ hour/10 + hour%10 + ":" + min/10 + min%10 + ":"
                      + sec/10 + sec%10;
            return s;
          }
        }

Here is a fragment of a WWW page, wclock.htm that contains the WClock applet. Let's assume we put all class files and this HTML file in a directory, in our example C:\HTTPD\DATA\horb\examples\wclock. Again, note that the path C:\..\horb\examples\wclock\ contains the package name.

        // wclock.htm (fragment)
        <Applet code="horb.examples.wclock.WClock.class" width=300 height=150 codebase="../../..">
        </applet>

Codebase="../../.." forces WWW browsers to find class files from .. \..\..\horb\examples\wclock, where there are three additional subdirectories higher to complete the path. (Hopefully Java will someday solve this confusing package-classpath situation!)

Here is the complete source of class WClock (horb.examples.wclock.WClock).

        // WClock.java
        package horb.examples.wclock;

        import java.awt.*;
        import java.net.*;
        import horb.orb.*;
        import horb.examples.wclock.*;

        public class WClock extends java.applet.Applet implements Runnable {
          boolean die;
          String host;
          Server_Proxy remoteServer;
          Server localServer;
          Thread kicker = null;
          int sleep;
          String errMsg = null;
          int port = 8887;

          public void init() {
            die = false;
            resize(300,150);
            setFont(new Font("TimesRoman",Font.BOLD,48));
          }
        
          public void start() {
            die = false;
            if (kicker != null)
              return;
            try {
              host = getDocumentBase().getHost();
            } catch (Exception e0) {}
            if (host == null || host.length() == 0)
              host = "localhost";
        
            localServer = new Server(); // create local time server
            sleep = 1;
            kicker = new Thread(this);
            kicker.start();
          }
        
          public void paint(Graphics g) {
            // get current local time and display it
            String localTime = localServer.currentTime();
            g.setColor(Color.blue);
            g.drawString(localTime, 40, 100);
        
            // get remote time and display it
            if (remoteServer == null) {
              try {
                showStatus("creating object on "+host+"...");
                remoteServer = new Server_Proxy(new HorbURL(host, port, null));
                showStatus("calling server object on "+host);
                sleep = 1;
              } catch (Exception e) {
                showStatus("Connect failed. Firewall? DNS? or server down?");
                System.out.println(e);
                sleep = 10;
              }
            }
            if (remoteServer != null) {
              try {
                String remoteTime = remoteServer.currentTime();
                g.setColor(Color.red);
                g.drawString(remoteTime, 40, 60);
              } catch (Exception e) {
                remoteServer = null;
              }
            }
          }
        
          public void run() {
            while (die == false) {
              repaint();
              try {
                Thread.sleep(sleep*1000);
              } catch (InterruptedException e) {
                break;
              }
            }
          }
        
          public void stop() {
            die = true;
            if (kicker != null) {
              kicker.stop();
              kicker = null;
              if (remoteServer != null) {
                remoteServer._release();
                remoteServer = null;
              }
            }
          }
        }

Just some good, old-fashioned Java applet coding! First, init() initalizes the HORB runtime. (Please note: Class horb.orb.HORB must exist in a path where, again, the directory directly above /horb/orb is mentioned in a CLASSPATH entry.)
Next, start() is called. In start() the hostname of the HTML document is determined. If the document is a local document (i.e., read from disk), localhost is used. start() also creates a local time server object and then it invokes a thread that calls paint() every one second.
Method paint() firstly displays the local time by calling the local time server. Next, it creates a remote server object, if it does not exist. This is a method to keep a remote server online as possible as we can. Then paint() displays the remote time.
When user leaves the page, method stop() is called. We have to destroy the remote time server and the connection to the server by calling _release().

Here is a compilation procedure.

        
	C:> horbc Server.java
        C:> horbc -c WClock.java

Before you run the applet, start HORB server on the server machine.

        C:> horb -v

Use Java enabled WWW browser, for example Netscape Navigator 2. If you don't have a Netscape Navigator 2.0 or above, Appletviewer is a good tool to see the result.

        C:> appletviewer wclock.htm

You need to restart the WWW browser and the horb server, when you recompile the classes. Because the current Java interpreter does not have reloading feature.

Here is a summary of the directory structure.

        C:\HTTPD\DATA        CLASSPATH entry codebase will default to
        C:\HTTPD\DATA\horb\orb          HORB runtime classes
        C:\HTTPD\DATA\horb\examples\wclock      HTML, wclock classes


4.2 Applet-Applet programming

(See horb/examples/worldClock for example code.)

As mentioned earlier, an applet cannot connect to a machine other than the server. An applet cannot talk to another applet directly. Thus, We use a HORB server as a hub of applets (clients).

World Clock

There are two styles to do inter-applet communication in a HORB server, 1) use of temporary object, and 2) use of daemon object.

1) Temporary object

Each client creates a temporary object of a common class. A temporary object here is a usual remote object described in this document so far. A client can have its dedicated storage (instance variables) in a temporary object. Class variables are used for inter-object, that is, inter-applet communication. This subsection uses this style.

2) Daemon object

Each client connects to a daemon object. A thread corresponds to a client. Thus, the daemon object has the same number of threads as clients. If you need client specific storage, use hash tables that are indexed by Thread, for example:

        hashtable.put(Thread.currentThread(), data);
        data = (Data)hashtable.get(Thread.currentThread());

World Clock described here uses the former style. The below is the server side class.

        // Server
        package horb.examples.worldClock;
        import horb.orb.*;
        import java.util.*;
        
        class Server {
          private String clientName;
          private static Hashtable global = new Hashtable();
        
          /** check in. */
          public Server() {
            try {
              clientName = HORBServer.getClientHostName();
            } catch (Exception e) {}
          }
        
          /** This method is remotely called every second. */
          public synchronized TimeObj[] getTime(TimeObj obj) {
            obj.whoAmI = clientName;
            global.put(obj.whoAmI, obj);
        
            // copy hashtable into an array to carry
            int num = (global.size() > 20) ? 20 : global.size();
            TimeObj[] times = new TimeObj[num];
            Enumeration e = global.elements();
            for (int i = 0; i < num && e.hasMoreElements(); i++) {
              times[i] = (TimeObj)e.nextElement();
            }
            return times;
          }
        
          /** check out. */
          public synchronized void _threadEndHook() {
            global.remove(clientName);
          }
        }

One object (instance) of this class is created when a client (an applet) requests a session. Thus the object can have its own storage, e.g. clientName in this class. A hashtable global is used as a common storage among clients.

When an object of this class is created, contructor Server() is called. Server() initializes clientName by client's hostname.
Each client calls getTime() every one second. A client puts its local time to the hashtable global, then gets the contents of the hashtable.
When a client leaves World Clock, _threadEndHook() is called. This is one of predefined hook methods. If a server class has this method, ORB calls it when a client leaves. _threadEndHook() removes the client from the hashtable.

Time is carried with class TimeObj. You can say this is a small mobile object or a simple agent.

        // TimeObj.java
        package horb.examples.worldClock;
        import java.util.Date;
        
        public class TimeObj {
          String time;
          String whoAmI;
        
          public void setTime() {
            // set current system time
            Date date = new Date();
            int hour = date.getHours();
            int min = date.getMinutes();
            int sec = date.getSeconds();
            time = ""+hour/10+hour%10+":"+min/10+min%10+":"+sec/10+sec%10;
          }
        }

Class WorldClock of the client side is a little bit long, but it's quite similar to WClock.java. This class also tries to make connection persistent.

        package horb.examples.worldClock;
        
        import java.awt.*;
        import java.net.*;
        import horb.orb.*;
        import java.applet.*;
        
        public class WorldClock extends Applet implements Runnable {
          String host;
          int port = 8887;
          Server_Proxy remoteServer;                    // remote object reference
          boolean die;
          Thread kicker;
          int sleep;
          String errMsg;
          Font large = new Font("Helvetica",Font.PLAIN,28);
          Font small = new Font("TimesRoman",Font.ITALIC,12);
          int x = 500;
          int y = 300;
        
          public void init() {
            die = false;
            resize(x+40 ,y);                            // show frame
            setBackground(new Color(220, 220, 255));
          }
        
          public void start() {
            die = false;
            if (kicker != null)
              return;
            try {
              host = getDocumentBase().getHost();       // get server hostname
            } catch (Exception e0) {}
            if (host == null || host.length() == 0)
              host = "localhost";
        
            sleep = 1;
            kicker = new Thread(this);                  // start a timer thread
            kicker.start();
          }
        
          public void update(Graphics g) {
            paint(g);
          }
        
          public void paint(Graphics g) {
            if (remoteServer == null) {
              // create remote server object
              try {
                showStatus("creating object on "+host+"...");
                remoteServer = new Server_Proxy(new HorbURL(host, port, null));
                showStatus("calling server object on "+host);
                sleep = 1;
              } catch (Exception e) {
                showStatus("Connect failed. Firewall? DNS? or server down?");
                System.out.println(e);
                sleep = 10;
              }
            }
            if (remoteServer != null) {
              try {
                // pass local time to server and receive times
                TimeObj local = new TimeObj();
                local.setTime();
                TimeObj[] global = remoteServer.getTime(local); // call server
                for (int i = 0; i < 20; i++) {                  // display times
                  g.clearRect(20+(i%4)*(x/4), (i/4)*(y/5), x/4, y/5);
                  if (i < global.length) {
                    g.setFont(large);
                    g.setColor(Color.blue);
                    g.drawString(global[i].time, 20+(i%4)*(x/4), 30+(i/4)*(y/5));
                    g.setFont(small); 
                    g.setColor(Color.red);
                    String s = global[i].whoAmI.toLowerCase();
                    int beg = s.length()-22;
                    s = s.substring((beg < 0 ? 0 : beg), s.length());
                    g.drawString(s, 20+(i%4)*(x/4), 50+(i/4)*(y/5));
                  }
                }
              } catch (Exception e) {
                remoteServer = null;
              }
            }
          }
        
          public void run() {
            while (die == false) {
              repaint();                                // repaint each 1 sec
              try {
                Thread.sleep(sleep*1000);
              } catch (InterruptedException e) {
                break;
              }
            }
          }
        
          public void stop() {
            die = true;
            if (kicker != null) {
              kicker.stop();
              kicker = null;
              if (remoteServer != null) {
                remoteServer._release();
                remoteServer = null;
              }
            }
          }
        }

Here is an example of compile and run.

        C:> horbc Server
        C:> horbc -c WorldClock.java
        C:> horb                                (in another window)

        C:> appletviewer WorldClock.htm   
                ( or load WorldClock.html in Netscape)