![]() |
![]() Spring Semester 2003 |
There are two parts to this lecture (that used to be
Part One is called: The essence of RMI.
Here's the basic starting point.
In a local
directory:
there are two files, a client and a server.school.cs.indiana.edu%pwd /l/www/classes/a348-dger/fall99/lectures/rmi/local
They will run on the same host first.school.cs.indiana.edu%ls -l total 2 -rw------- 1 dgerman 386 Nov 16 17:14 Client.java -rw------- 1 dgerman 271 Nov 16 17:17 Server.java
Let's look at the "server". It exports a method that reports the time.
That's very simple. All processing is done and reported on a host, too.school.cs.indiana.edu%cat Server.java public class Server { public String exportedMethod() { try { return " the time is " + new java.util.Date() + "\n " + " on " + java.net.InetAddress.getLocalHost() + "\n"; } catch (Exception e) { return "Error... " + e; } } }
The "client" gets a reference to the server and asks it for the time.
The client reports the host it's running on, followed by the message that the server's exported method is returning (and we know what that is, having looked at the server already).school.cs.indiana.edu%cat Client.java public class Client { public static void main(String[] args) { try { String time = null; Server timeServer = new Server(); System.out.println( "Client reporting from: " + java.net.InetAddress.getLocalHost() + ":\n " + timeServer.exportedMethod()); } catch (Exception e) { System.out.println("Error... " + e); } } }
So we now compile both the server and the client, to run them.
We end up with two more files, bothschool.cs.indiana.edu%javac *.java school.cs.indiana.edu%ls -l total 4 -rw------- 1 dgerman 956 Nov 16 17:19 Client.class -rw------- 1 dgerman 386 Nov 16 17:14 Client.java -rw------- 1 dgerman 798 Nov 16 17:19 Server.class -rw------- 1 dgerman 271 Nov 16 17:17 Server.java
.class
as expected.
We run the client. From school
the server reports the
time and the message is reported by the client
that is running on school
(same host, for now).
Checking the date reveals that the answer is probably correct.school.cs.indiana.edu%java Client Client reporting from: school.cs.indiana.edu/129.79.252.113: the time is Tue Nov 16 17:19:45 EST 1999 on school.cs.indiana.edu/129.79.252.113
Hopefully this is extremely basic and presents no problems.school.cs.indiana.edu%date Tue Nov 16 17:19:52 EST 1999 school.cs.indiana.edu%
Now let's set a goal:
we want to run the client from a different host than the one the server
runs on. For example we'll start the server on
school
, and
move the client to (and then run it from) tucotuco
, and
have it contact the server running on school
and invoke its
exported method to find out what time is on the host the server's running
on (in our case, school
).
The constraint is: we are not allowed to edit, change, or otherwise modify in any way the server's exported method. In other words if we implement a networking scheme for this method to be available over the network, whatever we do must be around the method itself.
We are not allowed to build any kind of networking in it. The method must remain completely, entirely, 100% unchanged, exactly as we have it now.
A few other things obviously will have to change (but the changes will be simple).
So we start from scratch, in a separate directory, called network
to
emphasize the fact that the resulting programs will be used in a distributed
environment unlike the first experiment that was purely local
.
The files we have there are five:school.cs.indiana.edu%pwd /l/www/classes/a348-dger/fall99/lectures/rmi/network
Theschool.cs.indiana.edu%ls -l total 5 -rw------- 1 dgerman 495 Nov 16 18:18 Client.java -rw------- 1 dgerman 386 Nov 16 17:22 Client.java.local -rw------- 1 dgerman 958 Nov 16 18:17 Server.java -rw------- 1 dgerman 271 Nov 16 17:22 Server.java.local -rw------- 1 dgerman 130 Nov 16 18:16 ServerExports.java
.local
files are the ones we developed before. They are listed here
for reference only. We won't really use them. (Originally I thought I'd diff
them
with the ones that use RMI but then I thought I'd actually present the RMI versions on their own
so that we can see what we have in its entirety).
There's an interface file there, that describes what the server will make available over the network. This file is the first of the new things we need to do when setting up an RMI framework, to describe the exported methods in an interface.school.cs.indiana.edu%diff Client.java.local ../local/Client.java school.cs.indiana.edu%diff Server.java.local ../local/Server.java
Now let's look at the new server.school.cs.indiana.edu%cat ServerExports.java import java.rmi.*; public interface ServerExports extends Remote { public String exportedMethod() throws RemoteException; }
The new things is (essentially) that it has aschool.cs.indiana.edu%cat Server.java import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; public class Server extends UnicastRemoteObject implements ServerExports { public String exportedMethod() { try { return " the time is " + new java.util.Date() + "\n " + " on " + java.net.InetAddress.getLocalHost() + "\n"; } catch (Exception e) { return "Error... " + e; } } public Server () throws RemoteException { System.out.println("Initializing Server..."); } public static void main(String arg[]) { Registry reg; try { System.setSecurityManager(new RMISecurityManager()); Server server = new Server(); reg = LocateRegistry.createRegistry(39904); // your port here... reg.bind("TimeServer", server); System.out.println("Server Ready..."); } catch (Exception e) { System.out.println("Error..." + e); } } }
main()
method. The
server will be started on the server side. The client will just get a reference
to the server and invoke the exported method through the reference.
In its main()
method the server class sets a security manager to export
the method, creates a server object, creates a registry (to export the object and its
exportable method) and binds the newly created object with the registry.
Now the server is ready for any client to connect and invoke the server object's exported method.
Let's look at the client.
The client uses a variable that is of the type of the interface the server implements and stores in this variable a reference to the object that it locates through the use of anschool.cs.indiana.edu% school.cs.indiana.edu%cat Client.java import java.rmi.*; public class Client { public static void main(String[] args) { try { String time = null; ServerExports timeServer = (ServerExports)Naming.lookup( "rmi://" + args[0] + ":39904/TimeServer"); System.out.println( "Client reporting from: " + java.net.InetAddress.getLocalHost() + ":\n " + timeServer.exportedMethod()); } catch (Exception e) { System.out.println("Error... " + e); } } }
rmi://
URL. The rest is unchanged,
as is the source code of the method that the server exports. Now we're ready to show how this works, a two stage experiment.
These, then, are the files that we have.
We compile theschool.cs.indiana.edu% school.cs.indiana.edu%ls -l total 5 -rw------- 1 dgerman 495 Nov 16 18:18 Client.java -rw------- 1 dgerman 386 Nov 16 17:22 Client.java.local -rw------- 1 dgerman 958 Nov 16 18:17 Server.java -rw------- 1 dgerman 271 Nov 16 17:22 Server.java.local -rw------- 1 dgerman 130 Nov 16 18:16 ServerExports.java
.java
files.
We then use the RMI compiler on the server classschool.cs.indiana.edu%javac *.java school.cs.indiana.edu%ls -l total 10 -rw------- 1 dgerman 1105 Nov 16 18:24 Client.class -rw------- 1 dgerman 495 Nov 16 18:18 Client.java -rw------- 1 dgerman 386 Nov 16 17:22 Client.java.local -rw------- 1 dgerman 1561 Nov 16 18:24 Server.class -rw------- 1 dgerman 958 Nov 16 18:17 Server.java -rw------- 1 dgerman 271 Nov 16 17:22 Server.java.local -rw------- 1 dgerman 289 Nov 16 18:24 ServerExports.class -rw------- 1 dgerman 130 Nov 16 18:16 ServerExports.java
and that creates two new files that handle the networking.school.cs.indiana.edu%rmic Server
Let's try it now fromschool.cs.indiana.edu%ls -l total 14 -rw------- 1 dgerman 1105 Nov 16 18:24 Client.class -rw------- 1 dgerman 495 Nov 16 18:18 Client.java -rw------- 1 dgerman 386 Nov 16 17:22 Client.java.local -rw------- 1 dgerman 1561 Nov 16 18:24 Server.class -rw------- 1 dgerman 958 Nov 16 18:17 Server.java -rw------- 1 dgerman 271 Nov 16 17:22 Server.java.local -rw------- 1 dgerman 289 Nov 16 18:24 ServerExports.class -rw------- 1 dgerman 130 Nov 16 18:16 ServerExports.java -rw------- 1 dgerman 1542 Nov 16 18:24 Server_Skel.class -rw------- 1 dgerman 1838 Nov 16 18:24 Server_Stub.class school.cs.indiana.edu%
school
to school
. That is, both
the client and the server are on the same host, but communicate over the network. We start the server:
Then we run the client, by giving it the host where the server is:school.cs.indiana.edu%java Server & [1] 5511 school.cs.indiana.edu%Initializing Server... Server Ready...
So at this point we are able (at least) to say we have done as much as we had before. But we are ready to do even more. We will move the client (and some client-side files) toschool.cs.indiana.edu%java Client school.cs.indiana.edu Client reporting from: school.cs.indiana.edu/129.79.252.113: the time is Tue Nov 16 18:24:44 EST 1999 on school.cs.indiana.edu/129.79.252.113 school.cs.indiana.edu%
tucotuco
and run the client from there. First, let's stop the server.
And now we do the distributed part of the experiment,school.cs.indiana.edu%/usr/bin/ps -ef | grep dgerman dgerman 5244 5225 0 18:20:48 pts/50 0:00 -csh dgerman 5511 4438 0 18:24:35 pts/43 0:01 /bin/../java/bin/../bin/sparc/native_threads/java Server dgerman 4438 4434 0 18:12:59 pts/43 0:00 -csh dgerman 5540 4438 0 18:24:54 pts/43 0:00 grep dgerman dgerman 5264 5244 0 18:21:20 pts/50 0:01 emacs lecture24.html school.cs.indiana.edu%kill 5511 school.cs.indiana.edu% [1] Terminated java Server school.cs.indiana.edu%
We are running from our new directory. We start the server.school.cs.indiana.edu%pwd /l/www/classes/a348-dger/fall99/lectures/rmi/network
Now we connect to the host where the client will run on.school.cs.indiana.edu%java Server & [1] 5879 school.cs.indiana.edu%Initializing Server... Server Ready...
Now we're in. We move to the directory where we will put the client.school.cs.indiana.edu%telnet tucotuco.cs.indiana.edu Trying 129.79.251.110... Connected to tucotuco.cs.indiana.edu. Escape character is '^]'. tucotuco.cs.indiana.edu login: dgerman Password: Last login: Tue Nov 16 18:26:04 from school.cs.indian **************************************************************** ** Indiana University Computer Science Department ** ** ** For Authorized Use Only ** ** **************************************************************** ** For general IU CS Computing Information, please see: ** ** http://www.cs.indiana.edu/csg ** ** ** ** For the latest CS Computing Information News, please see: ** ** http://www.cs.indiana.edu/csg/notices/current.html ** ** ** ** To submit a problem report or question, please see: ** ** http://www.cs.indiana.edu/csg/help/reporting.html ** ****************************************************************
And we start proceedings to bring the client here, bytucotuco.cs.indiana.edu% cd rmi/exercise
ftp
.
Now we're in. We move to thetucotuco.cs.indiana.edu% ftp school.cs.indiana.edu Connected to school.cs.indiana.edu. 220 school.cs.indiana.edu FTP server (Version wu-2.6.0(1) Wed Oct 20 09:24:41 EST 1999) ready. Name (school.cs.indiana.edu:dgerman): dgerman 331 Password required for dgerman. Password: 230 User dgerman logged in.
network
directory, make
the type of transfer binary and transfer the following files: Client.class
ServerExports.class
Server_Stub.class
Make the transfer typeftp> cd /l/www/classes/a348/fall99/lectures/rmi/network 250 CWD command successful.
binary
.
Get the first class.ftp> binary 200 Type set to I.
Get the second class file.ftp> get Client.class 200 PORT command successful. 150 Opening BINARY mode data connection for Client.class (1105 bytes). 226 Transfer complete. local: Client.class remote: Client.class 1105 bytes received in 0.0092 seconds (117.08 Kbytes/s)
Get the third class.ftp> get ServerExports.class 200 PORT command successful. 150 Opening BINARY mode data connection for ServerExports.class (289 bytes). 226 Transfer complete. local: ServerExports.class remote: ServerExports.class 289 bytes received in 0.066 seconds (4.26 Kbytes/s)
That's all we need to transfer, now we are ready to close theftp> get Server_Stub.class 200 PORT command successful. 150 Opening BINARY mode data connection for Server_Stub.class (1838 bytes). 226 Transfer complete. local: Server_Stub.class remote: Server_Stub.class 1838 bytes received in 0.012 seconds (154.03 Kbytes/s)
ftp
session.
Now we're back at theftp> bye 221-You have transferred 3232 bytes in 3 files. 221-Total traffic for this session was 4100 bytes in 3 transfers. 221-Thank you for using the FTP service on school.cs.indiana.edu. 221 Goodbye.
telnet
session prompt.
We run the client (from tucotuco
).
The client contacts the server and reports what it reports:
Our experiment is getting close to an end.tucotuco.cs.indiana.edu% java Client school.cs.indiana.edu Client reporting from: tucotuco.cs.indiana.edu/129.79.251.110: the time is Tue Nov 16 18:35:08 EST 1999 on school.cs.indiana.edu/129.79.252.113
We're now back ontucotuco.cs.indiana.edu% exit tucotuco.cs.indiana.edu% logout Connection closed by foreign host.
school
where the server is still running.
The date is close to what the server reported.school.cs.indiana.edu%date Tue Nov 16 18:35:18 EST 1999
We look for the server process.
And we stop it.school.cs.indiana.edu%/usr/bin/ps -ef | grep dgerman dgerman 5921 4438 0 18:35:36 pts/43 0:00 grep dgerman dgerman 5244 5225 0 18:20:48 pts/50 0:00 -csh dgerman 5879 4438 0 18:33:32 pts/43 0:01 /bin/../java/bin/../bin/sparc/native_threads/java Server dgerman 4438 4434 0 18:12:59 pts/43 0:00 -csh dgerman 5264 5244 0 18:21:20 pts/50 0:01 emacs lecture24.html
Then log out.school.cs.indiana.edu%kill 5879 school.cs.indiana.edu% [1] Terminated java Server
That was Part One.school.cs.indiana.edu%exit
Part Two is called: Building A Chat Application in Java
So let's get started again.
The basic starting point.burrowww.cs.indiana.edu% pwd /nfs/paca/home/user1/dgerman burrowww.cs.indiana.edu% mkdir rmi2001 burrowww.cs.indiana.edu% cd rmi2001 burrowww.cs.indiana.edu% ls burrowww.cs.indiana.edu%
Let's see it running.burrowww.cs.indiana.edu% pwd /nfs/paca/home/user1/dgerman/rmi2001 burrowww.cs.indiana.edu% ls -ld * -rw-r--r-- 1 dgerman faculty 386 Nov 13 09:48 Client.java -rw-r--r-- 1 dgerman faculty 263 Nov 13 09:48 Server.java burrowww.cs.indiana.edu% cat Server.java public class Server { public String remoteMethod() { try { return " the time is " + new java.util.Date() + "\n" + " on " + java.net.InetAddress.getLocalHost() + "\n"; } catch (Exception e) { return "Server error: " + e + " ...\n"; } } } burrowww.cs.indiana.edu% cat Client.java public class Client { public static void main(String[] args) { try { String time = null; Server remote = new Server(); System.out.println( "Client reporting from: (" + java.net.InetAddress.getLocalHost() + ") \n" + remote.remoteMethod()); } catch (Exception e) { System.out.println("Client error: " + e + "...\n"); } } } burrowww.cs.indiana.edu%
Easy, but notice one thing: the client controls everything. We do not need to start the server first. But in a networked context that will change, and we will have to bring the server up before we can run the client. Let's now write the networked version.burrowww.cs.indiana.edu% javac *.java burrowww.cs.indiana.edu% java Client Client reporting from: (burrowww.cs.indiana.edu/129.79.245.98) the time is Tue Nov 13 09:51:10 EST 2001 on burrowww.cs.indiana.edu/129.79.245.98 burrowww.cs.indiana.edu% java Client Client reporting from: (burrowww.cs.indiana.edu/129.79.245.98) the time is Tue Nov 13 09:51:14 EST 2001 on burrowww.cs.indiana.edu/129.79.245.98 burrowww.cs.indiana.edu%
Make a note of this link.burrowww.cs.indiana.edu% pwd /nfs/paca/home/user1/dgerman/rmi2001 burrowww.cs.indiana.edu% ls -ld N*.java -rw-r--r-- 1 dgerman faculty 474 Nov 13 10:28 NClient.java -rw-r--r-- 1 dgerman faculty 889 Nov 13 10:27 NServer.java -rw-r--r-- 1 dgerman faculty 122 Nov 13 10:30 NServerAd.java burrowww.cs.indiana.edu% cat NServerAd.java public interface NServerAd extends java.rmi.Remote { public String remoteMethod() throws java.rmi.RemoteException; }
We try again, like last time.burrowww.cs.indiana.edu% cat NServer.java import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; public class NServer extends UnicastRemoteObject implements NServerAd { public String remoteMethod() { try { return " the time is " + new java.util.Date() + "\n" + " on " + java.net.InetAddress.getLocalHost() + "\n"; } catch (Exception e) { return "Server error: " + e + " ...\n"; } } public NServer() throws RemoteException { System.out.println("Server initialized... "); } public static void main(String[] args) { Registry reg; try { System.setSecurityManager(new RMISecurityManager()); NServer richard = new NServer(); reg = LocateRegistry.createRegistry(31287); reg.bind("Feynman", richard); System.out.println("Server ready... "); } catch (Exception e) { System.out.println("Server error " + e + "...\n"); } } } burrowww.cs.indiana.edu% cat NClient.java import java.rmi.*; public class NClient { public static void main(String[] args) { try { String time = null; NServerAd farAway = (NServerAd)Naming.lookup( "rmi://" + args[0] + ":31287/Feynman"); System.out.println( "Client reporting from: (" + java.net.InetAddress.getLocalHost() + ") \n" + farAway.remoteMethod() ); } catch (Exception e) { System.out.println("Client error: " + e + "...\n"); } } } burrowww.cs.indiana.edu%
It works now. So what's different?
Where did we get this file?burrowww.cs.indiana.edu% pwd /nfs/paca/home/user1/dgerman/rmi2001 burrowww.cs.indiana.edu% ls -ld ~/.java* -rw-r--r-- 1 dgerman faculty 72 Nov 9 23:08 /u/dgerman/.java.policy burrowww.cs.indiana.edu% cat ~/.java* grant { permission java.net.SocketPermission "*", "connect,accept"; };burrowww.cs.indiana.edu%
(Many thanks to
Rob Henderson
for researching, and helping us with, this question).
If you ever wondered what's
the advantage of being part of a team you now have the answer.
So the question now is: where did we get this file?
Well, things have changed since November 1999, the date of the previous tutorial.
For one thing, as before, loading classes remotely needs a security manager. The default Java security policy, however, does not allow all the networking operations required to resolve a class from a remote host. (See Flanagan, see Oaks) So we need to have this specified somewhere.
Let's investigate this more closely. We work with other
burrow
machines.
These sessions are intertwined, so check the time stamps to see what happened.
On the server side:
On the client side:burrowww.cs.indiana.edu% pwd /nfs/paca/home/user1/dgerman/rmi2001 burrowww.cs.indiana.edu% date Tue Nov 13 11:16:27 EST 2001 burrowww.cs.indiana.edu% ls -ld ~/.java* -rw-r--r-- 1 dgerman faculty 72 Nov 9 23:08 /u/dgerman/.java.policy burrowww.cs.indiana.edu% cat ~/.java.policy grant { permission java.net.SocketPermission "*", "connect,accept"; };burrowww.cs.indiana.edu% date Tue Nov 13 11:16:59 EST 2001 burrowww.cs.indiana.edu% java NServer Server initialized... Server ready... ^Cburrowww.cs.indiana.edu% burrowww.cs.indiana.edu% date Tue Nov 13 11:17:59 EST 2001 burrowww.cs.indiana.edu% mv ~/.java.policy ~/trickortreat burrowww.cs.indiana.edu% date Tue Nov 13 11:19:01 EST 2001 burrowww.cs.indiana.edu% java NServer Server initialized... Server ready... ^Cburrowww.cs.indiana.edu% burrowww.cs.indiana.edu% date Tue Nov 13 11:19:25 EST 2001 burrowww.cs.indiana.edu%
So the file is truly important.tucotuco.cs.indiana.edu% pwd /nfs/paca/home/user1/dgerman/rmi2001 tucotuco.cs.indiana.edu% date Tue Nov 13 11:16:34 EST 2001 tucotuco.cs.indiana.edu% java NClient burrowww.cs.indiana.edu Client reporting from: (tucotuco.cs.indiana.edu/129.79.245.110) the time is Tue Nov 13 11:17:53 EST 2001 on burrowww.cs.indiana.edu/129.79.245.98 tucotuco.cs.indiana.edu% date Tue Nov 13 11:19:08 EST 2001 tucotuco.cs.indiana.edu% java NClient burrowww.cs.indiana.edu Client error: java.rmi.ConnectException: Connection refused to host: 127.0.0.1; nested exception is: java.net.ConnectException: Connection refused... tucotuco.cs.indiana.edu%
Here, however, is another way of doing the same thing.
On the server side (sessions are still intertwined):
On the client side:burrowww.cs.indiana.edu% pwd /nfs/paca/home/user1/dgerman/rmi2001 burrowww.cs.indiana.edu% ls -ld ../.java* No match burrowww.cs.indiana.edu% ls -ld ../trick* -rw-r--r-- 1 dgerman faculty 72 Nov 9 23:08 ../trickortreat burrowww.cs.indiana.edu% cat ../trick* grant { permission java.net.SocketPermission "*", "connect,accept"; };burrowww.cs.indiana.edu% date Tue Nov 13 11:33:20 EST 2001 burrowww.cs.indiana.edu% date; java -Djava.security.policy=../trickortreat NServer Tue Nov 13 11:34:10 EST 2001 Server initialized... Server ready... ^Cburrowww.cs.indiana.edu% date; java NServer Tue Nov 13 11:34:48 EST 2001 Server initialized... Server ready... ^Cburrowww.cs.indiana.edu%
So we can bypass the naming convention, but we can't do without doing something.tucotuco.cs.indiana.edu% pwd; date /nfs/paca/home/user1/dgerman/rmi2001 Tue Nov 13 11:33:27 EST 2001 tucotuco.cs.indiana.edu% date; java NClient burrowww.cs.indiana.edu Tue Nov 13 11:34:15 EST 2001 Client reporting from: (tucotuco.cs.indiana.edu/129.79.245.110) the time is Tue Nov 13 11:34:36 EST 2001 on burrowww.cs.indiana.edu/129.79.245.98 tucotuco.cs.indiana.edu% date; java NClient burrowww.cs.indiana.edu Tue Nov 13 11:34:58 EST 2001 Client error: java.rmi.ConnectException: Connection refused to host: 127.0.0.1; nested exception is: java.net.ConnectException: Connection refused... tucotuco.cs.indiana.edu%
Now let's try a more sophisticated development.
We'll discuss this in class.burrowww.cs.indiana.edu% pwd /nfs/paca/home/user1/dgerman/chat2001 burrowww.cs.indiana.edu% ls -ld *.java -rw-r--r-- 1 dgerman faculty 1469 Nov 13 14:17 ChatClient.java -rw-r--r-- 1 dgerman faculty 1138 Nov 13 16:08 ChatServer.java -rw-r--r-- 1 dgerman faculty 135 Nov 13 16:07 ClientExports.java -rw-r--r-- 1 dgerman faculty 208 Nov 13 16:08 ServerExports.java burrowww.cs.indiana.edu% cat ClientExports.java import java.rmi.*; public interface ClientExports extends Remote { public void update(String message) throws RemoteException; } burrowww.cs.indiana.edu% cat ServerExports.java import java.rmi.*; public interface ServerExports extends Remote { public void register(ClientExports client) throws RemoteException; public void broadcast(String message) throws RemoteException; } burrowww.cs.indiana.edu% cat ChatClient.java import java.rmi.*; import java.rmi.server.*; import java.io.*; public class ChatClient implements ClientExports { String name; public ChatClient(String name) throws RemoteException { System.out.println("Starting up client..."); this.name = name; } public void update(String message) { System.out.print(message); } public static void main(String[] args) { try { ServerExports serverFarAway = (ServerExports) Naming.lookup( "rmi://" + args[1] + ".cs.indiana.edu:" + args[2] + "/Dirac"); ChatClient thisGuyHere = new ChatClient(args[0]); UnicastRemoteObject.exportObject(thisGuyHere); serverFarAway.register(thisGuyHere); serverFarAway.broadcast("***(" + thisGuyHere.name + ")*** has just connected.\n"); thisGuyHere.talkAndRelayTo(serverFarAway); } catch (Exception e) { System.out.println("Client: error in main..." + e); } } public void talkAndRelayTo(ServerExports remoteServer) { try { InputStreamReader reader = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(reader); String line = br.readLine(); while (! line.equals("quit")) { System.out.println("You typed: " + line); remoteServer.broadcast(name + "> " + line + "\n"); line = br.readLine(); } } catch (Exception e) { System.out.println("Client: error in talk..." + e); } } } burrowww.cs.indiana.edu% cat ChatServer.java import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; import java.util.*; public class ChatServer extends UnicastRemoteObject implements ServerExports { Vector chatters = new Vector(); public ChatServer() throws RemoteException { System.out.println("Server being initialized..."); } public void register(ClientExports remote) throws RemoteException { chatters.addElement(remote); } public void broadcast(String message) throws RemoteException { for (int i = 0; i < chatters.size(); i++) { try { ((ClientExports)(chatters.elementAt(i))).update(message); } catch(Exception e) { System.out.println("Client unavailable..."); } } } public static void main(String[] args) { System.setSecurityManager(new RMISecurityManager()); try { ChatServer pam = new ChatServer(); Registry catalogue = LocateRegistry.createRegistry(Integer.parseInt(args[0])); catalogue.bind("Dirac", pam); System.out.println("Server is ready..."); } catch (Exception e) { System.out.println("Server error: " + e + "..."); } } } burrowww.cs.indiana.edu% javac *.java burrowww.cs.indiana.edu% ls -ld *.java -rw-r--r-- 1 dgerman faculty 1469 Nov 13 14:17 ChatClient.java -rw-r--r-- 1 dgerman faculty 1138 Nov 13 16:08 ChatServer.java -rw-r--r-- 1 dgerman faculty 135 Nov 13 16:07 ClientExports.java -rw-r--r-- 1 dgerman faculty 208 Nov 13 16:08 ServerExports.java burrowww.cs.indiana.edu% rmic ChatServer burrowww.cs.indiana.edu% rmic ChatClient burrowww.cs.indiana.edu% exit burrowww.cs.indiana.edu%
We'll test it this way.
Some notes: the RMI recipe. Applets and RMI (from Fall 1999).