Plan for today:

A. Java RMI

1. A Simple Time Server

I first create a directory where I will put the code, on tucotuco.

Let this directory be called:

/u/dgerman/timeServer
I create it:
tucotuco.cs.indiana.edu% pwd
/nfs/paca/home/user2/dgerman
tucotuco.cs.indiana.edu% ls -l timeServer
timeServer: No such file or directory
tucotuco.cs.indiana.edu% mkdir timeServer
tucotuco.cs.indiana.edu% cd timeServer
tucotuco.cs.indiana.edu% ls 
tucotuco.cs.indiana.edu%
Then we start writing the code. Any class that exports objects must implement an interface that defines the methods that can be accessed via a remote application. (Essentially, a client gets a handle to the interface describing the remote methods of an object.)

tucotuco.cs.indiana.edu% emacs TimeServer.java
tucotuco.cs.indiana.edu% cat TimeServer.java
public interface TimeServer extends java.rmi.Remote {
  public String getTime() throws java.rmi.RemoteException; 
} 
tucotuco.cs.indiana.edu% 
Now that we have a TimeServer interface we need to create a server implementation of it.

tucotuco.cs.indiana.edu% emacs TimeServerImpl.java  
tucotuco.cs.indiana.edu% cat TimeServerImpl.java
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;

public class               TimeServerImpl 
               extends     UnicastRemoteObject
               implements  TimeServer               {
  public String getTime() {
    System.out.println("Sending data..."); 
    return new java.util.Date().toString(); 
  }
  public TimeServerImpl () throws  RemoteException {
    System.out.println("Initializing Timeserver"); 
  }
  public static void main(String arg[]) {
    System.setSecurityManager(new RMISecurityManager()); 
    try {
      TimeServerImpl TSI = new TimeServerImpl(); 
      Naming.rebind("TimeServer", TSI); 
      System.out.println("registered with registry"); 
    } catch (RemoteException e) {
      System.out.println("Error: " + e); 
    } catch (java.net.MalformedURLException e) {
      System.out.println("URL Error: " + e); 
    } 
  } 
} 
tucotuco.cs.indiana.edu%
Essentially the hard part of writing a network distributed application is over. All of the network protocol handling has been taken care of with a few method calls.

Now let's take a look at the client.

tucotuco.cs.indiana.edu% emacs Time.java
tucotuco.cs.indiana.edu% cat Time.java
import java.rmi.*;
public class Time {
  public static void main(String arg[]) {
    String time = null; 
    try {
      TimeServer TS = (TimeServer)Naming.lookup(
                      "rmi://tucotuco.cs.indiana.edu/TimeServer"); 
      time = TS.getTime(); 
    } catch (NotBoundException e) {
      System.out.println("Time Server was not found in registry");
      System.exit(0); 
    } catch (RemoteException e) {
      System.out.println("Time error: " + e);
      System.exit(0); 
    } catch (java.net.MalformedURLException e) {
      System.out.println("URL error: " + e);
      System.exit(0); 
    } 
    if (time != null) System.out.println("The time is: " + time); 
  }
} 
tucotuco.cs.indiana.edu% 
The client must first bootstrap with a registry server to get a handle on the TimeServer object, and then the client will have access to the getTime() method through this handle on the registry. Once the client has this handle, the registry is no longer necessary.

The next step is to compile the three file just created.

tucotuco.cs.indiana.edu% pwd
/nfs/paca/home/user2/dgerman/timeServer
tucotuco.cs.indiana.edu% ls -l
total 6
-rw-r--r--   1 dgerman  students     665 Oct 27 00:06 Time.java
-rw-r--r--   1 dgerman  students     117 Oct 26 23:44 TimeServer.java
-rw-r--r--   1 dgerman  students     816 Oct 26 23:53 TimeServerImpl.java
tucotuco.cs.indiana.edu% javac *.java
tucotuco.cs.indiana.edu% ls -l
total 16
-rw-r--r--   1 dgerman  students    1228 Oct 27 00:11 Time.class
-rw-r--r--   1 dgerman  students     665 Oct 27 00:06 Time.java
-rw-r--r--   1 dgerman  students     276 Oct 27 00:11 TimeServer.class
-rw-r--r--   1 dgerman  students     117 Oct 26 23:44 TimeServer.java
-rw-r--r--   1 dgerman  students    1269 Oct 27 00:11 TimeServerImpl.class
-rw-r--r--   1 dgerman  students     816 Oct 26 23:53 TimeServerImpl.java
tucotuco.cs.indiana.edu% 
Once the class files have been created, the stub and skeleton classes must be generated. The stub is the client-side proxy to the remote method, and the skeleton is the server-side proxy. All marshaling of data and method access is done through these two objects.

The stub and skeleton files can be generated directly from from the TimeServer class. Essentially, the RMI stub compiler (rmic) does this for us.

tucotuco.cs.indiana.edu% ls -l
total 16
-rw-r--r--   1 dgerman  students    1228 Oct 27 00:11 Time.class
-rw-r--r--   1 dgerman  students     665 Oct 27 00:06 Time.java
-rw-r--r--   1 dgerman  students     276 Oct 27 00:11 TimeServer.class
-rw-r--r--   1 dgerman  students     117 Oct 26 23:44 TimeServer.java
-rw-r--r--   1 dgerman  students    1269 Oct 27 00:11 TimeServerImpl.class
-rw-r--r--   1 dgerman  students     816 Oct 26 23:53 TimeServerImpl.java
tucotuco.cs.indiana.edu% rmic TimeServerImpl
tucotuco.cs.indiana.edu% ls -l
total 24
-rw-r--r--   1 dgerman  students    1228 Oct 27 00:11 Time.class
-rw-r--r--   1 dgerman  students     665 Oct 27 00:06 Time.java
-rw-r--r--   1 dgerman  students     276 Oct 27 00:11 TimeServer.class
-rw-r--r--   1 dgerman  students     117 Oct 26 23:44 TimeServer.java
-rw-r--r--   1 dgerman  students    1269 Oct 27 00:11 TimeServerImpl.class
-rw-r--r--   1 dgerman  students     816 Oct 26 23:53 TimeServerImpl.java
-rw-r--r--   1 dgerman  students    1495 Oct 27 00:19 TimeServerImpl_Skel.class
-rw-r--r--   1 dgerman  students    1862 Oct 27 00:19 TimeServerImpl_Stub.class
tucotuco.cs.indiana.edu%
We now have everything we need to employ our TimeServer application. All that is left to do is start the registry, start the TimeServerImpl application, and test it with the Time client application.

We start the registry if it's not already running:

tucotuco.cs.indiana.edu% pwd
/nfs/paca/home/user2/dgerman/timeServer
tucotuco.cs.indiana.edu% netstat -a | grep 1099
tucotuco.cs.indiana.edu% rmiregistry &
[1] 28432
tucotuco.cs.indiana.edu% netstat -a | grep 1099
      *.1099               *.*                0      0     0      0 LISTEN
tucotuco.cs.indiana.edu% ps
   PID TTY      TIME CMD
 28432 pts/13   0:03 jre
 23489 pts/13   0:01 csh
tucotuco.cs.indiana.edu% 
Then we start the server application:
tucotuco.cs.indiana.edu% java TimeServerImpl &
[2] 28484
tucotuco.cs.indiana.edu% Initializing Timeserver
registered with registry

tucotuco.cs.indiana.edu% ps 
   PID TTY      TIME CMD
 28432 pts/13   0:04 jre
 28484 pts/13   0:04 java
 23489 pts/13   0:02 csh
tucotuco.cs.indiana.edu%
At this point we have a registry running (on tucotuco) that is listening for Naming.lookup() calls, and a single object registered, the TimeServerImpl object, with the associated name "TimeServerImpl". Now the client needs to be used.

We first move the client on school:

school.cs.indiana.edu%pwd   
/nfs/whale/home/user1/dgerman
school.cs.indiana.edu%ls -l timeServer
timeServer not found
school.cs.indiana.edu%mkdir timeServer
school.cs.indiana.edu%cd timeServer
school.cs.indiana.edu%ls
school.cs.indiana.edu%ftp tucotuco.cs.indiana.edu
Connected to tucotuco.cs.indiana.edu.
220 tucotuco.cs.indiana.edu FTP server (Version wu-2.4.2-academ[BETA-18](1) Fri Oct 23 17:20:20 EST 1998) ready.
Name (tucotuco.cs.indiana.edu:dgerman): dgerman
331 Password required for dgerman.
Password:
230-Please read the file README
230-  it was last modified on Wed Aug 26 15:58:43 1998 - 62 days ago
230 User dgerman logged in.
ftp> binary
200 Type set to I.
ftp> cd timeServer     
250 CWD command successful.
ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for file list.
TimeServer.java
TimeServerImpl.java
Time.java
Time.class
TimeServer.class
TimeServerImpl.class
TimeServerImpl_Stub.class
TimeServerImpl_Skel.class
226 Transfer complete.
207 bytes received in 0.021 seconds (9.44 Kbytes/s)
ftp> get Time.class
200 PORT command successful.
150 Opening BINARY mode data connection for Time.class (1228 bytes).
226 Transfer complete.
local: Time.class remote: Time.class
1228 bytes received in 0.93 seconds (1.29 Kbytes/s)
ftp> get TimeServerImpl_Stub.class
200 PORT command successful.
150 Opening BINARY mode data connection for TimeServerImpl_Stub.class (1862 bytes).
226 Transfer complete.
local: TimeServerImpl_Stub.class remote: TimeServerImpl_Stub.class
1862 bytes received in 0.0027 seconds (675.47 Kbytes/s)
ftp> get TimeServer.class
200 PORT command successful.
150 Opening BINARY mode data connection for TimeServer.class (276 bytes).
226 Transfer complete.
local: TimeServer.class remote: TimeServer.class
276 bytes received in 0.0025 seconds (108.16 Kbytes/s)
ftp> bye
221 Goodbye.
school.cs.indiana.edu%ls -l
total 5
-rw-------   1 dgerman      1228 Oct 27 01:00 Time.class
-rw-------   1 dgerman       276 Oct 27 01:00 TimeServer.class
-rw-------   1 dgerman      1862 Oct 27 01:00 TimeServerImpl_Stub.class
school.cs.indiana.edu%
And now we're ready to run the client:
school.cs.indiana.edu%java Time
The time is: Tue Oct 27 01:02:22 EST 1998
school.cs.indiana.edu%
At the same time on tucotuco:
tucotuco.cs.indiana.edu% Sending data...
the server is letting us know that data has been sent (the time).

Do not forget that on the server side

tucotuco.cs.indiana.edu% pwd
/nfs/paca/home/user2/dgerman/timeServer
tucotuco.cs.indiana.edu% ps -f
     UID   PID  PPID  C    STIME TTY      TIME CMD
 dgerman 28432 23489  0 00:27:47 pts/13   0:05 /usr/bin/../java/bin/../bin/sparc/native_threads/jre sun.rmi.registry.RegistryI
 dgerman 28484 23489  0 00:31:07 pts/13   0:06 /usr/bin/../java/bin/../bin/sparc/native_threads/java TimeServerImpl
 dgerman 23489 23485  0 19:39:52 pts/13   0:02 -csh
tucotuco.cs.indiana.edu% ps 
   PID TTY      TIME CMD
 28432 pts/13   0:05 jre
 28484 pts/13   0:06 java
 23489 pts/13   0:02 csh
tucotuco.cs.indiana.edu% 
we have the registry and the server running.

Here's a (general) summary of the steps taken.

2. A Punch Clock Applet

This application mimics the first one, only that client distribution is automatic.

We decide to keep everything in one directory inside ~/httpd/htdocs

tucotuco.cs.indiana.edu% pwd
/nfs/paca/home/user2/dgerman/httpd/htdocs
tucotuco.cs.indiana.edu% ls -l punchClock
punchClock: No such file or directory
tucotuco.cs.indiana.edu% mkdir punchClock
tucotuco.cs.indiana.edu% cd punchClock
tucotuco.cs.indiana.edu% pwd
/nfs/paca/home/user2/dgerman/httpd/htdocs/punchClock
tucotuco.cs.indiana.edu% 
First we create the remote interface:
tucotuco.cs.indiana.edu% emacs TimeClock.java
tucotuco.cs.indiana.edu% cat TimeClock.java
import java.rmi.*;

public interface TimeClock extends Remote {
        public String punch(String id, String passwd) 
                      throws RemoteException;
} 
tucotuco.cs.indiana.edu%
The application will also use twp external files, loosely referred to as the database.
tucotuco.cs.indiana.edu% emacs authFile
tucotuco.cs.indiana.edu% cat authFile
Troy sloopy
Bob foobar
Joe doh
Adrian A348tucotuco.cs.indiana.edu% cat > logFile
^D
tucotuco.cs.indiana.edu% 
The first file contains a list of employees and passwords.

The second one will collect the transaction logs.

tucotuco.cs.indiana.edu% emacs TimeClockServer.java
tucotuco.cs.indiana.edu% cat TimeClockServer.java
import java.rmi.*;
import java.rmi.server.*;
import java.io.*;
import java.util.*;

public class TimeClockServer 
       extends UnicastRemoteObject
       implements TimeClock {

       Vector employees = new Vector();
       Vector passwd = null;
       RandomAccessFile logFile = null;

       //Class Constructor---
       public TimeClockServer() throws RemoteException {
          try {
             logFile = new RandomAccessFile("logFile","rw");
             logFile.seek(logFile.length());
          } catch (IOException e) { 
                System.out.println("logfile: " + e);
          }
          System.out.println("TimeClockServer starting at " + 
                              new Date().toString());
          readPasswords();

       }

       //Remote Method implementation---
       public String punch( String empID, String passwd ) {
          if(isValidID(new passWord(empID,passwd))){
             if(onTheClock(empID)) 
                return removeEmployee(empID);
             else
                return addEmployee(empID);
          }
          return "Sorry, Invalid employee or passwd";
        }

        private String addEmployee(String empID){ 
           //adds an employee to the cache of employees
           //that are "on the clock"
           empRecord emp;
           employees.addElement(emp = new empRecord(empID));
           return "Employee# " + empID + " punched in at: " +
                  emp.timeOn.toString();
        }
        private String removeEmployee(String empID) { 
           //punch out employees and update the log
           empRecord emp;
           for(int i = 0; i < employees.size(); i++) {
              emp = (empRecord)employees.elementAt(i);
              if(emp.id.equals(empID)) {
                emp.timeOff = new Date();
                employees.removeElementAt(i);
                try {
                logFile.writeChars(emp.id + " " +
                                   emp.timeOn.toString() + " " +
                                   emp.timeOff.toString() + "\n");
                } catch (IOException e) {
                  System.out.println(e);
                }

                return "Employee# " + emp.id + "\npunched in at: " +
                       emp.timeOn.toString() + "\npunched out at: " +
                       emp.timeOff.toString();
              }
           }
           return "Employee not found";
        }
        private boolean isValidID(passWord pw) { 
           passWord pass;
           for(int i = 0; i < passwd.size(); i++) {
              pass = (passWord)passwd.elementAt(i);
              if(pass.isEqual(pw))
                 return true;
           }
           return false;
        }
        private boolean onTheClock(String empID) {
          for(int i = 0; i < employees.size(); i++)
                if(((empRecord)employees.elementAt(i)).id.equals(empID))
                   return true;
          return false;
        }

        public static void main(String arg[]) {
           System.setSecurityManager(new RMISecurityManager());
           try {
                   TimeClockServer TCS = new TimeClockServer();
                   Naming.rebind("TimeClockServer",TCS);
                      System.out.println("Server Ready");
               } catch (Exception e) {
                 System.out.println("TimeClockServer error: " + e);
               }
        }

        void readPasswords() {
           String s;
           passwd = new Vector();
           BufferedReader authFile = null;

           try {
              authFile = new BufferedReader(new 
                        FileReader("authFile"));
              while(true) {
                 if((s = authFile.readLine())==null){
                        authFile=null;
                        return;
                 }
                 passwd.addElement(new passWord(s.substring(0,s.indexOf(' ')),
                        s.substring(s.indexOf(' ')+1)));
              } 
           } catch (FileNotFoundException e) { 
                   System.out.println("authfile not found: " + e);
           } catch (IOException e) {
                authFile = null;
           }
       }
}
tucotuco.cs.indiana.edu% pwd
/nfs/paca/home/user2/dgerman/httpd/htdocs/punchClock
tucotuco.cs.indiana.edu% ls -l
total 12
-rw-r--r--   1 dgerman  students     140 Oct 27 01:30 TimeClock.java
-rw-r--r--   1 dgerman  students    3307 Oct 27 01:50 TimeClockServer.java
-rw-r--r--   1 dgerman  students      42 Oct 27 01:42 authFile
-rw-r--r--   1 dgerman  students       0 Oct 27 01:42 logFile
tucotuco.cs.indiana.edu% 
The TimeClockServer uses two additional classes. One is a structure to hold the employee data when it is added to the employees Vector
tucotuco.cs.indiana.edu% emacs empRecord.java
tucotuco.cs.indiana.edu% cat empRecord.java
import java.util.*;

class empRecord {        
  String id;
  Date timeOn;
  Date timeOff;

  empRecord(String id){
    this.id = id;
    timeOn = new Date();
  }
}
tucotuco.cs.indiana.edu% 
and the other one holds the password data when it is added to the password Vector
tucotuco.cs.indiana.edu% cat passWord.java
class passWord {
  String id; 
  String passwd;

  passWord(String id, String passwd) {
    this.id = id;
    this.passwd = passwd;
  }

  public boolean isEqual(passWord pw) {
    if(pw.id.equals(this.id)&&pw.passwd.equals(this.passwd))
      return true;
    else return false;
  }
}
tucotuco.cs.indiana.edu%
Now we have the complete server part of the code. We compile these with javac and produce the stub and skeleton with rmic
tucotuco.cs.indiana.edu% pwd
/nfs/paca/home/user2/dgerman/httpd/htdocs/punchClock
tucotuco.cs.indiana.edu% ls -l
total 16
-rw-r--r--   1 dgerman  students     140 Oct 27 01:30 TimeClock.java
-rw-r--r--   1 dgerman  students    3307 Oct 27 01:50 TimeClockServer.java
-rw-r--r--   1 dgerman  students      42 Oct 27 01:42 authFile
-rw-r--r--   1 dgerman  students     153 Oct 27 02:03 empRecord.java
-rw-r--r--   1 dgerman  students       0 Oct 27 01:42 logFile
-rw-r--r--   1 dgerman  students     281 Oct 27 02:03 passWord.java
tucotuco.cs.indiana.edu% javac *.java               
tucotuco.cs.indiana.edu% rmic TimeClockServer
tucotuco.cs.indiana.edu% ls -l
total 40
-rw-r--r--   1 dgerman  students     308 Oct 27 02:08 TimeClock.class
-rw-r--r--   1 dgerman  students     140 Oct 27 01:30 TimeClock.java
-rw-r--r--   1 dgerman  students    3778 Oct 27 02:08 TimeClockServer.class
-rw-r--r--   1 dgerman  students    3307 Oct 27 01:50 TimeClockServer.java
-rw-r--r--   1 dgerman  students    1905 Oct 27 02:08 TimeClockServer_Skel.class
-rw-r--r--   1 dgerman  students    2212 Oct 27 02:08 TimeClockServer_Stub.class 
-rw-r--r--   1 dgerman  students      42 Oct 27 01:42 authFile
-rw-r--r--   1 dgerman  students     423 Oct 27 02:08 empRecord.class
-rw-r--r--   1 dgerman  students     153 Oct 27 02:03 empRecord.java
-rw-r--r--   1 dgerman  students       0 Oct 27 01:42 logFile
-rw-r--r--   1 dgerman  students     540 Oct 27 02:08 passWord.class
-rw-r--r--   1 dgerman  students     281 Oct 27 02:03 passWord.java
tucotuco.cs.indiana.edu%
The client applet is
  
tucotuco.cs.indiana.edu% emacs TimeClockApplet.java
tucotuco.cs.indiana.edu% cat TimeClockApplet.java
import java.rmi.*;
import java.applet.*;
import java.awt.*;

public class TimeClockApplet extends Applet {

   private TextField id;
   private TextField passwd;
   private TextArea status;
   TimeClock TC;

   public void init() {
      add(id = new TextField(20));
      add(passwd = new TextField(20));
      add(status = new TextArea(5,50));
      add(new Button("Punch"));
      status.setEditable(false);
      status.setText("Starting Connection\n");
      try {

         TC = (TimeClock)Naming.lookup("rmi://" +
                         getCodeBase().getHost() + 
                         "/TimeClockServer");

      } catch (Exception e) {
         System.out.println("Error: " + e.toString());
         status.append("Error:" + e.toString());
      }
   }

   public boolean handleEvent(Event e) {
      if(e.target instanceof Button && e.id==Event.ACTION_EVENT) {
         try {
            status.append(TC.punch(id.getText(), passwd.getText())+"\n");
            return true;
         } catch (RemoteException ex) {
           status.append("Error:"+ex.toString());
         }
      }
      return false;
   }
}
%tucotuco.cs.indiana.edu% javac TimeClockApplet.java
Note: TimeClockApplet.java uses a deprecated API.  Recompile with "-deprecation" for details.
1 warning
We then embed the applet in an HTML document.
tucotuco.cs.indiana.edu% emacs timeclock.html
tucotuco.cs.indiana.edu% cat timeclock.html
<HTML>
<HEAD>
<TITLE>TimeClock Applet</TITLE>
</HEAD>
<BODY>
Please enter your ID and password to punch in or out. <p>
<APPLET code=TimeClockApplet.class
        width=400
        height=300>
</APPLET>
</BODY>
</HTML>
tucotuco.cs.indiana.edu% 
And after we start the registry and the server we're open for business.
tucotuco.cs.indiana.edu% ps
   PID TTY      TIME CMD
    84 pts/13   0:03 jre
 23489 pts/13   0:02 csh
tucotuco.cs.indiana.edu% java TimeClockServer
TimeClockServer starting at Tue Oct 27 03:14:09 EST 1998
Server Ready
The applet can be accessed over the web.

Note a few features.

The logFile has all the complete transactions:

tucotuco.cs.indiana.edu% cat logFile
Adrian Tue Oct 27 03:17:31 EST 1998 Tue Oct 27 03:17:33 EST 1998
Joe Tue Oct 27 03:17:45 EST 1998 Tue Oct 27 03:18:06 EST 1998
Adrian Tue Oct 27 03:17:54 EST 1998 Tue Oct 27 03:18:14 EST 1998
tucotuco.cs.indiana.edu% 
Here's the (general) summary of the steps taken, again.

B. Brief Introduction to Dynamic HTML

We will go over the most relevant examples of Web Developer's Guide to Dynamic HTML by Steve Holzner.