A common example is sharing a word processing
program over a network. The word processor is installed on a server, and anyone
who wants to use it starts it from his or her machine by double clicking an icon
on the desktop or typing at the command line. The invocation sends a request to
a server program for acess to the software, and the server program responds by
making the software available to the requestor.
The RMI API lets you create a publicly accessible remote server object that
enables client and server communications through simple method calls on the
server object. Clients can easily communicate directly with the server object
and indirectly with each other through the server object using Uniform Resource
Locators (URLs) and HyperText Transfer Protocol (HTTP).
This lesson explains how to use the RMI API to
establish client and server communications.
About the Example
This lesson converts the File
Input and Output application from Lesson
6: File Access and Permissions to the RMI API.
Program Behavior
The RMIClient1
program presents a simple user interface and prompts for text input. When you
click the Click Me button, the text is sent to the RMIClient2
program by way of the remote server object. When you click the Click Me
button on the RMIClient2 program, the text sent from RMIClient1
appears.
First Instance of Client 1
If you start a second instance of RMIClient1
and type in some text, that text is sent to RMIClient2 when you
click the Click Me button. To see the text received by RMIClient2 ,
click its Click Me button.
Second Instance of Client 1
File Summary
The example program consists of the RMIClient1
program, remote object and interface, and the RMIClient2 program as illustrated
in the diagram. The corresponding source code files for these executables are
described in the bullet list below.
- RMIClient1.java:
Client program that calls the
sendData method on the RemoteServer
server object.
- RMIClient2.java:
Client program that calls the
getData method on the RemoteServer
server object.
- RemoteServer.java:
Remote server object that implements // de>Send.java and the
sendDa
// ta
and getData remote methods.
- Send.java:
Remote interface that declares the
// >sendData and getData // >
remote server methods.
In addition, the following java.policy
security policy file grants the permissions needed to run the example.
grant {
permission java.net.SocketPermission
"*:1024-65535",
"connect,accept,resolve";
permission java.net.SocketPermission
"*:80", "connect";
permission java.awt.AWTPermission
"accessEventQueue";
permission java.awt.AWTPermission
"showWindowWithoutWarningBanner";
};
Compile the Example
These instructions assume development is in the zelda
home directory. The server program is compiled in the home directory for user zelda ,
but copied to the public_html directory for user zelda
where it runs.
Here is the command sequence for the Unix and
Win32 platforms; an explanation follows.
Unix:
cd /home/zelda/classes
javac Send.java
javac RemoteServer.java
javac RMIClient2.java
javac RMIClient1.java
rmic -d . RemoteServer
cp RemoteServer*.class /home/zelda/public_html/classes
cp Send.class /home/zelda/public_html/classes
Win32:
cd \home\zelda\classes
javac Send.java
javac RemoteServer.java
javac RMIClient2.java
javac RMIClient1.java
rmic -d . RemoteServer
copy RemoteServer*.class \home\zelda\public_html\classes
copy Send.class \home\zelda\public_html\classes
The first two javac commands compile the
RemoteServer and Send class and interface. The third javac
command compiles the RMIClient2 class. The last javac
command compiles the RMIClient1 class.
The next line runs the rmic command
on the RemoteServer server class. This command produces output
class files of the form ClassName_Stub.class and ClassName_Skel.class .
These output classes let clients invoke methods on the RemoteServer
server object.
The first copy command moves the RemoteServer
class file with its associated skel and stub class
files to a publicly accessible location in the /home/zelda/public_html/classes
directory, which is on the server machine, so they can be publicly accessed and
downloaded. They are placed in the public_html directory to be
under the web server running on the server machine because these files are
accessed by client programs using URLs.
The second copy command moves the Send
class file to the same location for the same reason. The RMIClient1
and RMIClient2 class files are not made publicly accessible; they
communicate from their client machines using URLs to access and download the
remote object files in the public_
// html directory.
RMIClient1 is invoked from a
client-side directory and uses the server-side web server and client-side
Java VM to download the publicly accessible files.
RMIClient2 is invoked from a
client-side directory and uses the server-side web server and client-side
Java VM to download the publicly accessible files.
Start the RMI Registry
Before you start the client programs, you must start
the RMI Registry, which is a server-side naming repository that allows remote
clients to get a reference to the remote server object.
Before you start the RMI Registry, make sure the
shell or window in which you run the rmiregistry command does not
have a CLASSPATH environment variable that points to the remote
object classes, including the stub and skel classes,
anywhere on your system. If the RMI Registry finds these classes when it starts,
it will not load them from the server-side Java VM, which will create problems
when clients try to download the remote server classes.
The following commands
unset the CLASSPATH and start the RMI Registry on the default 1099
port. You can specify a different port by adding the port number as follows: rmiregistry
4444 & . If you specify a different port number, you must specify the
same port number in your server-side code as well.
Unix:
cd /home/zelda/public_html/classes
unsetenv CLASSPATH
rmiregistry &
Win32:
cd \home\zelda\public_html\classes
set CLASSPATH=
start rmiregistry
Note: You might want to set
the CLASSPATH back to its original setting at this point.
Run the RemoteServer Server Object
To run the example programs, start RemoteServer
first. If you start either RMIClient1 or RMIClient2
first, they will not be able to establish a connection because the remote server
object is not running.
In this example, RemoteServer is
started from the /home/zelda/public_html/classes directory.
The lines beginning at java should be
all on one line with spaces where the lines break. The properties specified with
the -D option to the java interpreter command are
program attributes that manage the behavior of the program for this invocation.
Unix:
cd /home/zelda/public_html/classes
java
-Djava.rmi.server.codebase=http://kq6py/~zelda/classes
-Djava.rmi.server.hostname=kq6py.eng.sun.com
-Djava.security.policy=java.policy RemoteServer
Win32:
cd \home\zelda\public_html\classes
java -Djava.rmi.server.codebase=file:
c:\home\zelda\public_html\classes
-Djava.rmi.server.hostname=kq6py.eng.sun.com
-Djava.security.policy=java.policy RemoteServer
- The
java.rmi.server.codebase
property specifies where the publicly accessible classes are located.
- The
java.rmi.server.hostname
property is the complete host name of the server where the publicly
accessible classes reside.
- The
java.rmi.security.policy
property specifies the policy
file with the permissions needed to run the remote server object and
access the remote server classes for download.
- The class to execute (
RemoteServer ).
Run the RMIClient1 Program
Here is the command sequence for the Unix and Win32
platforms; an explanation follows.
In this example, RMIClient1 is
started from the /home/zelda/classes directory.
The lines beginning at java should be
all on one line with spaces where the lines break. Properties specified with the
-D option to the java interpreter command are program
attributes that manage the behavior of the program for this invocation.
Unix:
cd /home/zelda/classes
java -Djava.rmi.server.codebase=
http://kq6py/~zelda/classes/
-Djava.security.policy=java.policy
RMIClient1 kq6py.eng.sun.com
Win32:
cd \home\zelda\classes
java -Djava.rmi.server.codebase=
file:c:\home\zelda\classes\
-Djava.security.policy=java.policy
RMIClient1 kq6py.eng.sun.com
- The
java.rmi.server.codebase
property specifies where the publicly accessible classes for downloading are
located.
- The
java.security.policy property
specifies the policy
file with the permissions needed to run the client program and access
the remote server classes.
- The client program class to execute (
RMIClient1 ),
and the host name of the server (Kq6py ) where the remote server
classes are.
Run RMIClient2
Here is the command sequence for the Unix and Win32
platforms; an explanation follows.
In this example, RMIClient2 is
started from the /home/zelda/classes directory.
The lines beginning at java should be
all on one line with spaces where the lines break. The properties specified with
the -D option to the java interpreter command are
program attributes that manage the behavior of the program for this invocation.
Unix:
cd /home/zelda/classes
java -Djava.rmi.server.codebase=
http://kq6py/~zelda/classes
-Djava.security.policy=java.policy
RMIClient2 kq6py.eng.sun.com
Win32:
cd \home\zelda\classes
java -Djava.rmi.server.codebase=
file:c:\home\zelda\public_html\classes
-Djava.security.policy=java.policy
RMIClient2 kq6py.eng.sun.com
- The
java.rmi.server.codebase
property specifies where the publicly accessible classes are located.
- The
java.rmi.server.hostname
property is the complete host name of the server where the publicly
accessible classes reside.
- The
java.rmi.security.policy
property specifies the policy
file with the permissions needed to run the remote server object and
access the remote server classes for download.
- The class to execute (
RMIClient2 ).
RemoteServer Class
The RemoteServer
class extends UnicastRemoteObject and implements the sendData
and getData methods declared in the Send interface.
These are the remotely accessible methods.
UnicastRemoteObject implements a
number of java.lang.Object methods for remote objects and includes
constructors and static methods to make a remote object available to receive
method calls from client programs.
class RemoteServer extends UnicastRemoteObject
implements Send {
String text;
public RemoteServer() throws RemoteException {
super();
}
public void sendData(String gotText){
text = gotText;
}
public String getData(){
return text;
}
The main method installs the RMISecurityManager
and opens a connection with a port on the machine where the server program runs.
The security manager determines whether there is a policy file that lets
downloaded code perform tasks that require permissions. The main
method creates a name for the the RemoteServer object that includes
the server name (kq6py ) where the RMI Registry and remote object
run, and the name, Send .
By default the server name uses port 1099. If you
want to use a different port number, you can add it with a colon as follows: kq6py:4444 .
If you change the port here, you must start the RMI Registry
with the same port number.
The try block creates an instance of
the RemoteServer class and binds the name to the
remote object to the RMI Registry with t
// he Naming.rebind(name,
remoteServer); statement.
public static void main(String[] args){
if(System.getSecurityManager() == null) {
System.setSecurityManager(new
RMISecurityManager());
}
String name = "//kq6py.eng.sun.com/Send";
try {
Send remoteServer = new RemoteServer();
Naming.rebind(name, remoteServer);
System.out.println("RemoteServer bound");
} catch (java.rmi.RemoteException e) {
System.out.println("Cannot create
remote server object");
} catch (java.net.MalformedURLException e) {
System.out.println("Cannot look up
server object");
}
}
}
Note: The remoteServer
object is type Send (see instance declaration at top of class)
because the interface available to clients is the Send interface
and its methods; not the RemoteServer class and its methods.
Send Interface
The Send
interface declares the methods implemented in the RemoteServer
class. These are the remotely accessible methods.
public interface Send extends Remote {
public void sendData(String text)
throws RemoteException;
public String getData() throws RemoteException;
}
RMIClient1 Class
The RMIClient1
class establishes a connection to the remote server program and sends data to
the remote server object. The code to do these things is in the actionPerformed
and main methods.
actionPerformed Method
The actionPerformed method calls the RemoteServer.sendData
method to send text to the remote server object.
public void actionPerformed(ActionEvent event){
Object source = event.getSource();
if(source == button){
//Send data over socket
String text = textField.getText();
try{
send.sendData(text);
} catch (java.rmi.RemoteException e) {
System.out.println("Cannot send data to server");
}
textField.setText(new String(""));
}
}
main Method
The main method installs the RMISecurityManager
and creates a name to use to look up the RemoteServer
server object. The client uses the Naming.lookup method to look up
the RemoteServer object in the RMI Registry running on the server.
The security manager determines whether there is a
policy file that lets downloaded code perform tasks that require permissions.
RMIClient1 frame = new RMIClient1();
if(System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
try {
//args[0] contains name of server where
// Send runs
String name = "//" + args[0] + "/Send";
send = ((Send) Naming.lookup(name));
} catch (java.rmi.NotBoundException e) {
System.out.println("Cannot look up
remote server object");
} catch(java.rmi.RemoteException e){
System.out.println("Cannot look up
remote server object");
} catch(java.net.MalformedURLException e) {
System.out.println("Cannot look up
remote server object");
}
RMIClient2 Class
The RMIClient2
class establishes a connection with the remote server program and gets the data
from the remote server object and displays it. The code to do this is in the actionPerformed
and main methods.
actionPerformed Method
The actionPerformed method calls the RemoteServer.getData
method to retrieve the data sent by the client program. This data is appended to
the TextArea object for display to the end user on the server side.
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if(source == button){
try{
String text = send.getData();
textArea.append(text);
} catch (java.rmi.RemoteException e) {
System.out.println("Cannot send data
to server");
}
}
}
}
main Method
The main method installs the RMISecurityManager
and creates a name to use to look up the RemoteServer
server object. The args[0] parameter provides the name of the
server host. The client uses the Naming.lookup method to look up
the RemoteServer object in the RMI Registry running on the server.
The security manager determines whether there is a
policy file that lets downloaded code perform tasks that require permissions.
RMIClient2 frame = new RMIClient2();
if(System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
try {
String name = "//" + args[0] + "/Send";
send = ((Send) Naming.lookup(name));
} catch (java.rmi.NotBoundException e) {
System.out.println("Cannot look up remote
server object");
} catch(java.rmi.RemoteException e){
System.out.println("Cannot look up remote
server object");
} catch(java.net.MalformedURLException e) {
System.out.println("Cannot look up remote
server object");
}
More Information
You can find more information on the RMI API in the RMI
trail of The
Java Tutorial.
<--BEGIN READER SURVEY-->
Reprinted with permission from the Java
Developer Connection(SM)
Copyright Sun Microsystems Inc.
|