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.
(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
(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).
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)