Most of what appears below (except the actual Chat example) is
taken directly from (or based on) Sun's Java RMI tutorial site
An Overview of RMI Applications
RMI applications are often comprised of two separate programs: a
server and a client. A typical server application creates some remote objects,
makes references to them accessible, and waits for clients to invoke methods
on these remote objects. A typical client application gets a remote reference
to one or more remote objects in the server and then invokes methods on
them. RMI provides the mechanism by which the server and the client communicate
and pass information back and forth. Such an application is sometimes referred
to as a distributed object application.
Distributed object applications need to
Locate remote objects: Applications can use one of two mechanisms
to obtain references to remote objects. An application can register its remote
objects with RMI's simple naming facility, the rmiregistry, or the application
can pass and return remote object references as part of its normal operation.
Communicate with remote objects: Details of communication between
remote objects are handled by RMI; to the programmer, remote communication
looks like a standard Java method invocation.
Load class byte codes for objects that are passed around: Because
RMI
allows a caller to pass objects to remote objects, RMI provides
the necessary mechanisms for loading an object's code, as well as for
transmitting its data.
The following illustration depicts an RMI distributed application
that uses the registry to obtain a reference to a remote object. The server
calls the registry to associate (or bind) a name with a remote object.
The client looks up the remote object by its name in the server's registry
and then invokes a method on it. The illustration also shows that the RMI
system uses an existing Web server to load class bytecodes, from server
to client and from client to server, for objects when needed.
Advantages of Dynamic Code Loading
One of the central and unique features of RMI is its ability to
download the bytecodes (or simply code) of an object's class if the class
is not defined in the receiver's virtual machine. The types and the behavior
of an object, previously available only in a single virtual machine, can
be transmitted to another, possibly remote, virtual machine. RMI passes
objects by their true type, so the behavior of those objects is not changed
when they are sent to another virtual machine. This allows new types to
be introduced into a remote virtual machine, thus extending the behavior
of an application dynamically. The compute engine example in this chapter
uses RMI's capability to introduce new behavior to a distributed program.
Remote Interfaces, Objects, and Methods
Like any other application, a distributed application built using
Java RMI is made up of interfaces and classes. The interfaces define methods,
and the classes implement the methods defined in the interfaces and, perhaps,
define additional methods as well. In a distributed application some of
the implementations are assumed to reside in different virtual machines.
Objects that have methods that can be called across virtual machines are
remote objects. An object becomes remote by implementing a remote interface,
which has the following characteristics.
A remote interface extends the tagging interface java.rmi.Remote.
Each method of the interface declares java.rmi.RemoteException in
its throws clause, in addition to any application specific exceptions.
RMI treats a remote object differently from a non remote object when
the object is passed from one virtual machine to another. Rather than making
a copy of the implementation object in the receiving virtual machine, RMI
passes a remote stub for a remote object. The stub acts as the local representative,
or proxy, for the remote object and basically is, to the caller, the remote
reference. The caller invokes a method on the local stub, which is responsible
for carrying out the method call on the remote object.
A stub for a remote object implements the same set of remote interfaces
that the remote object implements. This allows a stub to be cast to any
of the interfaces that the remote object implements. However, this also
means that only those methods defined in a remote interface are available
to be called in the receiving virtual machine.
Creating Distributed Applications Using RMI
When you use RMI to develop a distributed application, you follow
these general steps.
- Design and implement the components of your distributed application.
- Compile sources and generate stubs.
- Make classes network accessible.
- Start the application.
- Design and Implement the Application Components
First, decide on your application architecture and determine which
components are local objects and which ones should be remotely accessible.
This step includes:
Defining the remote interfaces: A remote interface specifies the methods
that can be invoked remotely by a client. Clients program to remote
interfaces, not to the implementation classes of those interfaces. Part of the
design of such interfaces is the determination of any local objects that will be
used as parameters and return values for these methods; if any of these interfaces
or classes do not yet exist, you need to define them as well.
Implementing the remote objects: Remote objects must implement one or
more remote interfaces. The remote object class may include implementations
of other interfaces (either local or remote) and other methods (which
are available only locally). If any local classes are to be used as parameters
or return values to any of these methods, they must be implemented as well.
Implementing the clients: Clients that use remote objects can be
implemented at any time after the remote interfaces are defined, including
after the remote objects have been deployed.
Compile Sources and Generate Stubs
This is a two-step process. In the first step you use the javac
compiler to compile the source files, which contain the implementation of the remote
interfaces and implementations, the server classes, and the client classes.
In the second step you use the rmic compiler to create stubs for the remote objects.
RMI uses a remote object's stub class as a proxy in clients so that clients
can communicate with a particular remote object.
Make Classes Network Accessible
In this step you make everything the class files associated with
the remote interfaces, stubs, and other classes that need to be downloaded
to clients--accessible via a Web server.
Start the Application. Starting the application includes running
the RMI remote object registry, the server, and the client.
We will now re-implement the chat from our previous lecture using RMI.
In order to do that we need to implement client and server which
are available to each other via RMI.
We start by creating the following interfaces
As you probably realized, the idea is that we define an interface
and then write server side implementation. (BTW we have to be careful when
we say server in this example, you will soon see why) and then generate a stub
to represent it on the client side. This way, both on the client and server side we
can operate on the remote objects via interfaces. Simple enough, lets now
take a look at the implementation of ChatServer:
That's all! The only important thing to emphasize is that server,
when constructed,
(re)registers itself in registry, this is how it can be looked up
later.
Here is the code for the client, which is event as simpler:
There is one thing we need to clarify -what is client what is server.
Okay, this chat
is not your most typical RMI program. In the regular one, you would
have most
of your real objects sitting on the server side, with client only
having stubs to access them.
In such setup, however, we couldn't do chat! This is because there
would be no way
for the server to invoke methods on client remotely, because it
simply would be
local. So we really have to make clients remote to the server and
make the server
lookup clients via registry.
Okay, now before we can start running this chat, we need to do couple
more things.
First of all, we need to generate Stubs & Skeletons for classes
which implement Remote
interface. What are these? Lets face it, RMI is no magic. What actually
happens behind
our backs is this. When call a method on a client side (by client
side I mean the site which
does not have the remote object, but just has a reference to the
remote object), the Stub,
knowing the method name and arguments, tunnels this information
to the server in some
kind of serialized form. On the server side, Skeleton gets called
first, it deserializes
this information and calls the method on the actual object. The
results are sent back
in similar fashion. We will take a look at the stubs & skeletons
of our classes in a moment,
but for now, let us see how we can generate them. All we have to
do is to run rmic on
the classes which implement Remote. Here is how I did it for this
example:
rmic - keep edu.nyu.sejava.iskold.rmi.chat.ChatClient
rmic - keep edu.nyu.sejava.iskold.rmi.chat.ChatServer
- keep does not remove java files, which are appearing below (in
all their glory:)
Now the Stub:
Yes, some times funny stuff are going on behind our backs...