Spring Semester 2003


Lecture Notes Twenty-Four: Java RMI

There are two parts to this lecture (that used to be

Happy Thanksgiving people!

Part One is called: The essence of RMI.

Here's the basic starting point.

In a local directory:

school.cs.indiana.edu%pwd  
/l/www/classes/a348-dger/fall99/lectures/rmi/local
there are two files, a client and a server.
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
They will run on the same host first.

Let's look at the "server". It exports a method that reports the time.

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; 
    } 
  } 
}
That's very simple. All processing is done and reported on a host, too.

The "client" gets a reference to the server and asks it for the time.

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); 
    } 
  } 
} 
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).

So we now compile both the server and the client, to run them.

school.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
We end up with two more files, both .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).

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
 
Checking the date reveals that the answer is probably correct.

school.cs.indiana.edu%date    
Tue Nov 16 17:19:52 EST 1999
school.cs.indiana.edu%
Hopefully this is extremely basic and presents no problems.

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.

school.cs.indiana.edu%pwd
/l/www/classes/a348-dger/fall99/lectures/rmi/network
The files we have there are five:
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
The .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).
school.cs.indiana.edu%diff Client.java.local ../local/Client.java
school.cs.indiana.edu%diff Server.java.local ../local/Server.java
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%cat ServerExports.java
import java.rmi.*; 
 
public interface ServerExports extends Remote {
  public String exportedMethod() throws RemoteException; 
} 
Now let's look at the new server.
school.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); 
    } 
  } 
}
The new things is (essentially) that it has a 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.

school.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); 
    } 
  } 
} 
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 an 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.

school.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
We compile the .java files.

school.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
We then use the RMI compiler on the server class
school.cs.indiana.edu%rmic Server
and that creates two new files that handle the networking.
school.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% 
Let's try it now from 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:

school.cs.indiana.edu%java Server &     
[1] 5511
school.cs.indiana.edu%Initializing Server...
Server Ready...

Then we run the client, by giving it the host where the server is:
school.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%
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) to tucotuco and run the client from there. First, let's stop the server.
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%
And now we do the distributed part of the experiment,
school.cs.indiana.edu%pwd
/l/www/classes/a348-dger/fall99/lectures/rmi/network
We are running from our new directory. We start the server.
school.cs.indiana.edu%java Server &
[1] 5879
school.cs.indiana.edu%Initializing Server...
Server Ready...
 
Now we connect to the host where the client will run on.

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     **
****************************************************************
 
Now we're in. We move to the directory where we will put the client.
tucotuco.cs.indiana.edu% cd rmi/exercise
And we start proceedings to bring the client here, by ftp.
tucotuco.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.
Now we're in. We move to the network directory, make the type of transfer binary and transfer the following files: The relevant commands are emphasized below.
ftp> cd /l/www/classes/a348/fall99/lectures/rmi/network
250 CWD command successful.
Make the transfer type binary.
ftp> binary
200 Type set to I.
Get the first class.
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 second class file.
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)
Get the third class.
ftp> 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)
That's all we need to transfer, now we are ready to close the ftp session.
ftp> 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.
Now we're back at the telnet session prompt.

We run the client (from tucotuco).

The client contacts the server and reports what it reports:

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
 
Our experiment is getting close to an end.
tucotuco.cs.indiana.edu% exit
tucotuco.cs.indiana.edu% logout
Connection closed by foreign host.
We're now back on school where the server is still running.
school.cs.indiana.edu%date
Tue Nov 16 18:35:18 EST 1999
The date is close to what the server reported.

We look for the server process.

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
And we stop it.
school.cs.indiana.edu%kill 5879
school.cs.indiana.edu%
[1]    Terminated           java Server
Then log out.
school.cs.indiana.edu%exit
That was Part One.

Part Two is called: Building A Chat Application in Java

So let's get started again.

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% 
The basic starting point.

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%
Let's see it running.

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% 
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% 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; 
}
Make a note of this link.

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%
We try again, like last time.

It works now. So what's different?

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%
Where did we get this file?

(Many thanks to Rob Henderson 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:

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% 
On the client side:
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% 
So the file is truly important.

Here, however, is another way of doing the same thing.

On the server side (sessions are still intertwined):

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%
On the client side:
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%
So we can bypass the naming convention, but we can't do without doing something.

Now let's try a more sophisticated development.

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 discuss this in class.

We'll test it this way.

Some notes: the RMI recipe. Applets and RMI (from Fall 1999).


Last updated: Apr 17, 2003 by Adrian German for A348/A548