A. Java RMI
1. A Simple Time Server
I first create a directory where I will put the code, on tucotuco.B. Brief Introduction to Dynamic HTMLLet this directory be called:
I create it:/u/dgerman/timeServerThen 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% 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%
Now that we have atucotuco.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%TimeServerinterface we need to create a server implementation of it.
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.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%Now let's take a look at the client.
The client must first bootstrap with a registry server to get a handle on thetucotuco.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%TimeServerobject, and then the client will have access to thegetTime()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.
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.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%The stub and skeleton files can be generated directly from from the
TimeServerclass. Essentially, the RMI stub compiler (rmic) does this for us.
We now have everything we need to employ ourtucotuco.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%TimeServerapplication. All that is left to do is start the registry, start theTimeServerImplapplication, and test it with theTimeclient application.We start the registry if it's not already running:
Then we start the server application: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%At this point we have a registry running (ontucotuco.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%tucotuco) that is listening forNaming.lookup()calls, and a single object registered, theTimeServerImplobject, with the associated name "TimeServerImpl". Now the client needs to be used.We first move the client on
school:And now we're ready to run the client: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%At the same time on tucotuco:school.cs.indiana.edu%java Time The time is: Tue Oct 27 01:02:22 EST 1998 school.cs.indiana.edu%the server is letting us know that data has been sent (the time).tucotuco.cs.indiana.edu% Sending data...Do not forget that on the server side
we have the registry and the server running.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%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/htdocsFirst we create the remote interface: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%The application will also use twp external files, loosely referred to as the database.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 first file contains a list of employees and passwords.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 second one will collect the transaction logs.
Thetucotuco.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%TimeClockServeruses two additional classes. One is a structure to hold the employee data when it is added to the employeesVectorand the other one holds the password data when it is added to the passwordtucotuco.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%VectorNow we have the complete server part of the code. We compile these withtucotuco.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%javacand produce the stub and skeleton withrmicThe client applet istucotuco.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%We then embed the applet in an HTML document.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 warningAnd after we start the registry and the server we're open for business.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%The applet can be accessed over the web.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
Note a few features.
The logFile has all the complete transactions:
Here's the (general) summary of the steps taken, again.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%
We will go over the most relevant examples of Web Developer's Guide to Dynamic HTML by Steve Holzner.