epubjs
Version:
Render ePub documents in the browser, across many devices
425 lines (415 loc) • 62.1 kB
HTML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Remote Method Invocation</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body><div class="sect1" title="Remote Method Invocation"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-13-SECT-4"/>Remote Method Invocation</h1></div></div></div><p><a id="idx10792" class="indexterm"/>The most fundamental means of communication in Java is
method invocation. Mechanisms such as the Java event model are built on
simple method invocations between objects in the same virtual machine.
Therefore, when we want to communicate between virtual machines on
different hosts, it’s natural to want a mechanism with similar
capabilities and semantics—to run a method “over there.” Java’s RMI
mechanism does just that. It lets us get a reference to an object on a
remote host and use it almost as if it were in our own virtual machine.
RMI lets us invoke methods on remote objects, passing real Java objects as
arguments and getting real Java objects as returned values.</p><p>Remote invocation is nothing new. For many years, <a id="I_indexterm13_id769377" class="indexterm"/><a id="I_indexterm13_id769382" class="indexterm"/>C programmers have used remote procedure calls (RPC) to
execute a C function on a remote host and return the results. The primary
difference between RPC in other languages and RMI is that RPC is usually
primarily concerned with data structures. It’s relatively easy to pack up
data and ship it around, but RMI tries to do one better. In Java, we don’t
just work with data structures; we work with objects that contain both
data and methods for operating on the data. Not only do we have to be able
to ship the state of an object (the data) over the wire, but the recipient
has to be able to interact with the object (use its methods) after
receiving it. With Java RMI, you can work with network services in an
object-oriented fashion, using real, extensible types and pass “live”
references between client and server.</p><p>It should be no surprise that RMI uses object serialization, which
allows us to send graphs of objects (objects and the tree of all the
connected objects that they reference). When necessary, RMI can also use
dynamic class loading and the security manager to transport Java classes
safely. In addition to making remote method calls almost as easy to use as
local calls, RMI makes it possible to ship both data and behavior (code)
around the Net.</p><div class="sect2" title="Real-World Usage"><div class="titlepage"><div><div><h2 class="title"><a id="id2226727"/>Real-World Usage</h2></div></div></div><p><a id="I_indexterm13_id769409" class="indexterm"/> <a id="I_indexterm13_id769420" class="indexterm"/>Now that the introduction has you all excited, we should
put things in a little more context. While Java RMI has proven to be
very powerful, it has never really caught on as a way to build general
applications. Instead, RPC-like web services using XML and HTTP to
transfer data using standardized network protocols have ruled for many
years. The reason for this is primarily that they are cross-platform and
can be easily consumed by JavaScript running within web browsers. Web
services that run over HTTP are also generally immune to firewall issues
since they use the same mechanism as all web pages. Since the tools to
develop applications using web services have become mature and easy to
use, developers tend to use them even when building applications purely
in Java, where RMI might otherwise be more powerful. In this section
we’ll go ahead and show you what can be done with RMI; however, you will
definitely want to check out the chapters on web services and web
applications later in this book as well.</p></div><div class="sect2" title="Remote and Nonremote Objects"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-13-SECT-4.1"/>Remote and Nonremote Objects</h2></div></div></div><p><a id="idx10776" class="indexterm"/> Before an object can be used remotely through RMI, it
must be serializable. But that’s not sufficient. Remote objects in RMI
are real distributed objects. As the name suggests, a remote object can
be an object on a different machine or an object on the local host. The
term <span class="emphasis"><em>remote</em></span> means that the object is used through a
special kind of object interface that can be passed over the network.
Like normal Java objects, remote objects are passed by reference.
Regardless of where the reference is used, the method invocation occurs
on the original object, which still lives on its original host. If a
remote host returns a reference to one of its remote objects to you, you
can call the object’s methods; the actual method invocations happen on
the remote host where the underlying object resides.</p><p>Nonremote objects are simpler; they’re just normal serializable
objects. (You can pass these over the network as we did in the previous
section.) The catch is that when you pass a nonremote object over the
network, it is simply copied, so references to the object on one host
are not the same as those on the remote host. Nonremote objects are
passed by value (copying) as opposed to by reference. This may be
acceptable for many kinds of data holder objects on your host, such as
the client requests and server responses in our previous example. These
types of objects are sometimes called value objects or <span class="emphasis"><em>data
transfer objects</em></span> (DTOs).</p><div class="sect3" title="Remote interfaces"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-13-SECT-4.1.2"/>Remote interfaces</h3></div></div></div><p><a id="idx10798" class="indexterm"/>Remote objects implement a special <span class="emphasis"><em>remote
interface</em></span> that specifies which of the object’s methods can
be invoked remotely. The remote interface is part of the application
that you create by extending the <a id="I_indexterm13_id769529" class="indexterm"/><code class="literal">java.rmi.Remote</code>
interface. Your remote object then implements its remote interface as it
would any other Java interface. In your client-side code, you should
then refer to the remote object as an instance of the remote interface—not as an instance of its
implementation class. Because both the real object and stub that the
client receives implement the remote interface, they are equivalent as
far as we are concerned (for method invocation); locally, we never
have to worry about whether we have a reference to a stub or to an
actual object. This <span class="emphasis"><em>type equivalence</em></span> means that
we can use normal language features such as casting with remote
objects. Of course, public fields (variables) of the remote object are
not accessible through an interface, so you must make accessor methods
if you want to manipulate the remote object’s fields.</p><p>One additional requirement for remote objects distinguishes them
from local objects. All methods in the remote interface must declare
that they can throw the exception <a id="I_indexterm13_id769568" class="indexterm"/><code class="literal">java.rmi.RemoteException</code>. This exception (or
one of its subclasses) is thrown when any kind of networking error
happens (for example, a server crash, network failure, or timeout).
Some people see this as a limitation and try to paper over it in
various ways. However, the <code class="literal">RemoteException</code> is there for a reason—remote
objects can behave differently from local objects and your code needs
to deal with that issue explicitly. There is no magic bullet
(automatic retries, transactions) that truly makes the difference go
away.</p><p>Here’s a simple example of the remote interface that defines the
behavior of <code class="literal">RemoteObject</code>; we give
it two methods that can be invoked remotely, both of which return some
kind of <code class="literal">Value</code> object:<a id="I_indexterm13_id769607" class="indexterm"/></p><a id="I_13_tt870"/><pre class="programlisting"> <code class="kn">import</code> <code class="nn">java.rmi.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">interface</code> <code class="nc">RemoteObject</code> <code class="kd">extends</code> <code class="n">Remote</code> <code class="o">{</code>
<code class="kd">public</code> <code class="n">Value</code> <code class="nf">doSomething</code><code class="o">()</code> <code class="kd">throws</code> <code class="n">RemoteException</code><code class="o">;</code>
<code class="kd">public</code> <code class="n">Value</code> <code class="nf">doSomethingElse</code><code class="o">()</code> <code class="kd">throws</code> <code class="n">RemoteException</code><code class="o">;</code>
<code class="o">}</code></pre></div><div class="sect3" title="Exporting remote objects"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-13-SECT-4.1.3"/>Exporting remote objects</h3></div></div></div><p><a id="idx10797" class="indexterm"/>You make a remote object available to the outside world
by using the <a id="I_indexterm13_id769648" class="indexterm"/><code class="literal">java.rmi.server.UnicastRemoteObject</code> class.
One way is simply to have the implementation of your remote object
extend <code class="literal">UnicastRemoteObject</code>. When a
subclass of <code class="literal">UnicastRemoteObject</code> is
constructed, the RMI runtime system automatically “exports” it to
start listening for network connections from clients. Like <code class="literal">java.lang.Object</code>, this superclass also
provides implementations of <code class="literal">equals()</code>, <code class="literal">hashcode()</code>, and <code class="literal">toString()</code> that make sense for a remote
object.</p><p>Here’s a remote object class that implements the <code class="literal">RemoteObject</code> interface we showed earlier and
extends <code class="literal">UnicastRemoteObject</code>; we
haven’t shown implementations for the two methods or the
constructor:</p><a id="I_13_tt871"/><pre class="programlisting"> <code class="kd">public</code> <code class="kd">class</code> <code class="nc">MyRemoteObject</code> <code class="kd">implements</code> <code class="n">RemoteObject</code>
<code class="kd">extends</code> <code class="n">java</code><code class="o">.</code><code class="na">rmi</code><code class="o">.</code><code class="na">UnicastRemoteObject</code>
<code class="o">{</code>
<code class="kd">public</code> <code class="nf">MyRemoteObject</code><code class="o">()</code> <code class="kd">throws</code> <code class="n">RemoteException</code> <code class="o">{...}</code>
<code class="kd">public</code> <code class="n">Value</code> <code class="nf">doSomething</code><code class="o">()</code> <code class="kd">throws</code> <code class="n">RemoteException</code> <code class="o">{...}</code>
<code class="kd">public</code> <code class="n">Value</code> <code class="nf">doSomethingElse</code><code class="o">()</code> <code class="kd">throws</code> <code class="n">RemoteException</code> <code class="o">{...}</code>
<code class="c1">// nonremote methods</code>
<code class="kd">private</code> <code class="kt">void</code> <code class="nf">doSomethingInternal</code><code class="o">()</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code>
<code class="o">}</code></pre><p>Note that we have to supply a constructor that can throw a
<code class="literal">RemoteException</code> (even if it does
nothing) because <code class="literal">UnicastRemoteObject</code>’s default constructor
throws <code class="literal">RemoteException</code> and, even if
it’s not shown, the Java language always delegates to the superclass
constructor. This class can have as many additional methods as it
needs (presumably most of them will be <code class="literal">private</code>, but that isn’t strictly necessary),
but these nonremote methods are not required to throw the remote
exception.</p><p>Now, what if we can’t or don’t want to make our remote object
implementation a subclass of <code class="literal">UnicastRemoteObject</code>? Suppose, for example,
that it has to be a subclass of <code class="literal">BankAccount</code> or some other special base type
for our system. Well, we can simply take over the job of exporting the
object ourselves, using the static method <a id="I_indexterm13_id769771" class="indexterm"/><code class="literal">exportObject()</code> of
<code class="literal">UnicastRemoteObject</code>. The <code class="literal">exportObject()</code> method takes as an argument a
<code class="literal">Remote</code> interface and accomplishes
what the <code class="literal">UnicastRemoteObject</code>
constructor normally does for us. It returns as a value the remote
object’s client stub. However, you will normally not do anything with
this directly. In the next section, we’ll discuss how clients actually
find your service, through the RMI registry (a lookup service).</p><p>Normally, exported objects listen on individual <a id="I_indexterm13_id769812" class="indexterm"/><span class="emphasis"><em>ephemeral</em></span> (randomly assigned) port
numbers by default. (This is implementation-dependent.) You can
control the port number allocation
explicitly by exporting your objects using another form of <code class="literal">Unicast</code><code class="literal">RemoteObject.exportObject()</code>, which takes
both a <code class="literal">Remote</code> interface and a
port number as arguments.</p><p>Finally, the name <code class="literal">UnicastRemoteObject</code> begs the question, “What
other kinds of remote objects are there?” Right now, few. There is
another type of object called <code class="literal">Activatable</code> that is for RMI objects that
require persistence over time. We’ll say a few more words about RMI
activation later in this chapter, but it’s not something we will get
into in detail.<a id="I_indexterm13_id769866" class="indexterm"/></p></div><div class="sect3" title="The RMI registry"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-13-SECT-4.1.4"/>The RMI registry</h3></div></div></div><p><a id="idx10799" class="indexterm"/>The registry is RMI’s phone book. You use the registry
to look up a reference to a registered remote object on another host,
using an application-specified name. We’ve already described how
remote references can be passed back and forth by remote method calls.
The registry is needed to bootstrap the process by allowing the client
to look up an initial object on the remote host.</p><p>The registry is implemented by a class called <code class="literal">Naming</code> and an application called <a id="I_indexterm13_id769909" class="indexterm"/><code class="literal">rmiregistry</code>. The
<code class="literal">rmiregistry</code> application must be
running on a host before you start a Java program that wants to
advertise in the registry. You can then create instances of remote
objects and bind them to particular names in the registry. A registry
name can be anything you choose; it takes the form of a
slash-separated path. When a client object wants to find your object,
it constructs a special URL with the <a id="I_indexterm13_id769931" class="indexterm"/><code class="literal">rmi:</code> protocol, the
hostname, and the object name. On the client, the RMI <code class="literal">Naming</code> class then talks to the registry and
returns the remote object reference.</p><p>So, which objects need to register themselves with the registry?
Initially, this can be any object that the client has no other way of
finding. After that, a call to a remote method can return another
remote object without using the registry. Likewise, a call to a remote
method can have another remote object as its argument, without
requiring the registry. You could design your system such that only
one object registers itself and then serves as a factory for any other
remote objects you need. In other words, it wouldn’t be hard to build
a simple object request factory that returns references to all the
remote objects your application uses. Depending on how you structure
your application, this may happen naturally anyway.</p><p>The RMI registry is just one implementation of a lookup
mechanism for remote objects. It is not very sophisticated, and
lookups tend to be slow. It is not intended to be a general-purpose
directory service, but simply to bootstrap RMI communications. More
generally, the Java Naming and Directory Interface (JNDI) is a Java
API allowing access to other widely used name services that can
provide this kind of functionality. JNDI is used with RMI as part of
the Enterprise JavaBeans APIs.<a id="I_indexterm13_id769971" class="indexterm"/><a id="I_indexterm13_id769978" class="indexterm"/></p></div></div><div class="sect2" title="An RMI Example"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-13-SECT-4.2"/>An RMI Example</h2></div></div></div><p><a id="idx10775" class="indexterm"/> In our first example using RMI, we duplicate the simple
serialized object protocol from the previous section. We make a remote
RMI object called <code class="literal">MyServer</code> on which
we can invoke methods to get a <code class="literal">Date</code>
object or execute a <code class="literal">WorkRequest</code>
object. First, we define our <code class="literal">Remote</code>
interface:</p><a id="I_13_tt872"/><pre class="programlisting"> <code class="c1">//file: ServerRemote.java</code>
<code class="kn">import</code> <code class="nn">java.rmi.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.util.*</code><code class="o">;</code>
<code class="err"> </code>
<code class="kd">public</code> <code class="kd">interface</code> <code class="nc">ServerRemote</code> <code class="kd">extends</code> <code class="n">Remote</code> <code class="o">{</code>
<code class="n">Date</code> <code class="nf">getDate</code><code class="o">()</code> <code class="kd">throws</code> <code class="n">RemoteException</code><code class="o">;</code>
<code class="n">Object</code> <code class="nf">execute</code><code class="o">(</code> <code class="n">WorkRequest</code> <code class="n">work</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">RemoteException</code><code class="o">;</code>
<code class="o">}</code></pre><p>The <code class="literal">ServerRemote</code> interface
extends the <code class="literal">java.rmi.Remote</code>
interface, which identifies objects that implement it as remote objects.
We supply two methods that take the place of our old protocol: <code class="literal">getDate()</code> and <code class="literal">execute()</code>.</p><p>Next, we implement this interface in a class called <code class="literal">MyServer</code> that defines the bodies of these
methods. (Another common convention for naming the implementation of
remote interfaces is to append <code class="literal">Impl</code>
to the class name. Using that convention, <code class="literal">MyServer</code> would instead be named something like
<code class="literal">ServerImpl</code>.)</p><a id="I_13_tt873"/><pre class="programlisting"> <code class="c1">//file: MyServer.java</code>
<code class="kn">import</code> <code class="nn">java.rmi.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.util.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">MyServer</code>
<code class="kd">extends</code> <code class="n">java</code><code class="o">.</code><code class="na">rmi</code><code class="o">.</code><code class="na">server</code><code class="o">.</code><code class="na">UnicastRemoteObject</code>
<code class="kd">implements</code> <code class="n">ServerRemote</code> <code class="o">{</code>
<code class="kd">public</code> <code class="nf">MyServer</code><code class="o">()</code> <code class="kd">throws</code> <code class="n">RemoteException</code> <code class="o">{</code> <code class="o">}</code>
<code class="c1">// implement the ServerRemote interface</code>
<code class="kd">public</code> <code class="n">Date</code> <code class="nf">getDate</code><code class="o">()</code> <code class="kd">throws</code> <code class="n">RemoteException</code> <code class="o">{</code>
<code class="k">return</code> <code class="k">new</code> <code class="nf">Date</code><code class="o">();</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="n">Object</code> <code class="nf">execute</code><code class="o">(</code> <code class="n">WorkRequest</code> <code class="n">work</code> <code class="o">)</code>
<code class="kd">throws</code> <code class="n">RemoteException</code> <code class="o">{</code>
<code class="k">return</code> <code class="n">work</code><code class="o">.</code><code class="na">execute</code><code class="o">();</code>
<code class="o">}</code>
<code class="err"> </code>
<code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code><code class="n">String</code> <code class="n">args</code><code class="o">[])</code> <code class="o">{</code>
<code class="k">try</code> <code class="o">{</code>
<code class="n">ServerRemote</code> <code class="n">server</code> <code class="o">=</code> <code class="k">new</code> <code class="n">MyServer</code><code class="o">();</code>
<code class="n">Naming</code><code class="o">.</code><code class="na">rebind</code><code class="o">(</code><code class="s">"NiftyServer"</code><code class="o">,</code> <code class="n">server</code><code class="o">);</code>
<code class="o">}</code> <code class="k">catch</code> <code class="o">(</code><code class="n">java</code><code class="o">.</code><code class="na">io</code><code class="o">.</code><code class="na">IOException</code> <code class="n">e</code><code class="o">)</code> <code class="o">{</code>
<code class="c1">// problem registering server</code>
<code class="o">}</code>
<code class="o">}</code>
<code class="o">}</code></pre><p><code class="literal">MyServer</code> extends <a id="I_indexterm13_id770122" class="indexterm"/><code class="literal">UnicastRemoteObject</code> so
that when we create an instance of <code class="literal">MyServer</code>, it is automatically exported and
starts listening to the network. We start by providing a constructor
that must throw <a id="I_indexterm13_id770140" class="indexterm"/><code class="literal">RemoteException</code>, which
accommodates errors that might occur in exporting an instance. Next,
<code class="literal">MyServer</code> implements the methods of
the remote interface <code class="literal">ServerRemote</code>.
These methods are straightforward.</p><p>The last method in this class is <code class="literal">main()</code>. This method lets the object set itself
up as a server. <code class="literal">main()</code> creates an
instance of the <code class="literal">MyServer</code> object and
then calls the static method <code class="literal">Naming.rebind()</code> to place the object in the
registry. The arguments to <code class="literal">rebind()</code>
include the name of the remote object in the registry (<code class="literal">NiftyServer</code>)—which clients use to look up the
object—and a reference to the server object itself. We could have called
<code class="literal">bind()</code> instead, but <code class="literal">rebind()</code> handles the case where there’s
already a <code class="literal">NiftyServer</code> registered by
replacing it.</p><p>We wouldn’t need the <code class="literal">main()</code>
method or this <code class="literal">Naming</code> business if we
weren’t expecting clients to use the registry to find the server—that
is, we could omit <code class="literal">main()</code> and still
use this object as a remote object. We would just be limited to passing
the object in method invocations or returning it from method
invocations—but that could be part of a factory pattern, as we discussed
before.</p><p>Now we need our client:</p><a id="I_13_tt874"/><pre class="programlisting"> <code class="c1">//file: MyClient.java</code>
<code class="kn">import</code> <code class="nn">java.rmi.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.util.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">MyClient</code> <code class="o">{</code>
<code class="err"> </code>
<code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code><code class="n">String</code> <code class="o">[]</code> <code class="n">args</code><code class="o">)</code>
<code class="kd">throws</code> <code class="n">RemoteException</code> <code class="o">{</code>
<code class="k">new</code> <code class="nf">MyClient</code><code class="o">(</code> <code class="n">args</code><code class="o">[</code><code class="mi">0</code><code class="o">]</code> <code class="o">);</code>
<code class="o">}</code>
<code class="err"> </code>
<code class="kd">public</code> <code class="nf">MyClient</code><code class="o">(</code><code class="n">String</code> <code class="n">host</code><code class="o">)</code> <code class="o">{</code>
<code class="k">try</code> <code class="o">{</code>
<code class="n">ServerRemote</code> <code class="n">server</code> <code class="o">=</code> <code class="o">(</code><code class="n">ServerRemote</code><code class="o">)</code>
<code class="n">Naming</code><code class="o">.</code><code class="na">lookup</code><code class="o">(</code><code class="s">"rmi://"</code><code class="o">+</code><code class="n">host</code><code class="o">+</code><code class="s">"/NiftyServer"</code><code class="o">);</code>
<code class="n">System</code><code class="o">.</code><code class="na">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code> <code class="n">server</code><code class="o">.</code><code class="na">getDate</code><code class="o">()</code> <code class="o">);</code>
<code class="n">System</code><code class="o">.</code><code class="na">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code>
<code class="n">server</code><code class="o">.</code><code class="na">execute</code><code class="o">(</code> <code class="k">new</code> <code class="n">MyCalculation</code><code class="o">(</code><code class="mi">2</code><code class="o">)</code> <code class="o">)</code> <code class="o">);</code>
<code class="o">}</code> <code class="k">catch</code> <code class="o">(</code><code class="n">java</code><code class="o">.</code><code class="na">io</code><code class="o">.</code><code class="na">IOException</code> <code class="n">e</code><code class="o">)</code> <code class="o">{</code>
<code class="c1">// I/O Error or bad URL</code>
<code class="o">}</code> <code class="k">catch</code> <code class="o">(</code><code class="n">NotBoundException</code> <code class="n">e</code><code class="o">)</code> <code class="o">{</code>
<code class="c1">// NiftyServer isn't registered</code>
<code class="o">}</code>
<code class="o">}</code>
<code class="o">}</code></pre><p>When we run <code class="literal">MyClient</code>, we pass
it the hostname of the server on which the registry is running. The
<code class="literal">main()</code> method creates an instance of
the <code class="literal">MyClient</code> object, passing the
hostname from the command line as an argument to the constructor.</p><p>The constructor for <code class="literal">MyClient</code>
uses the hostname to construct a URL for the object. The URL looks like
this: <span class="emphasis"><em>rmi://hostname/NiftyServer</em></span>. (Remember,
<code class="literal">NiftyServer</code> is the name under which
we registered our <code class="literal">ServerRemote</code>.) We
pass the URL to the static <code class="literal">Naming.lookup()</code> method. If all goes well, we
get back a reference to a <code class="literal">ServerRemote</code> (the remote interface). The
registry has no idea what kind of object it will return; <code class="literal">lookup()</code> therefore returns an <code class="literal">Object</code>, which we must cast to <code class="literal">ServerRemote</code>, the remote interface
type.</p><div class="sect3" title="Running the example"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-13-SECT-4.2.1"/>Running the example</h3></div></div></div><p><a id="idx10796" class="indexterm"/>You can run the client and server on the same machine or
on different machines. First, make sure all the classes are in your
classpath (or the current directory if there is no classpath) and then
start the <a id="I_indexterm13_id770358" class="indexterm"/><code class="literal">rmiregistry</code> and
<code class="literal">MyServer</code> on your server
host:</p><a id="I_13_tt875"/><pre class="programlisting"> <code class="o">%</code> <strong class="userinput"><code><code class="n">rmiregistry</code> <code class="o">&</code></code></strong><em class="lineannotation"><span class="lineannotation">(on Windows:</span></em><strong class="userinput"><code><code class="n">start</code> <code class="n">rmiregistry</code></code></strong><em class="lineannotation"><span class="lineannotation">)</span></em>
<code class="o">%</code><strong class="userinput"><code><code class="n">java</code> <code class="n">MyServer</code></code></strong></pre><p>Next, run the client, passing the name of the server host (or
“localhost” for the local machine):</p><a id="I_13_tt876"/><pre class="programlisting"> <code class="o">%</code> <strong class="userinput"><code><code class="n">java</code> <code class="n">MyClient</code></code></strong><strong class="userinput"><code><em class="replaceable"><code>myhost</code></em></code></strong></pre><p>The client should print the date and the number 4, which the
server graciously calculated. Hooray! With just a few lines of code,
you have created a powerful client/server application.<a id="I_indexterm13_id770428" class="indexterm"/></p></div><div class="sect3" title="Dynamic class loading"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-13-SECT-4.2.2"/>Dynamic class loading</h3></div></div></div><p><a id="idx10766" class="indexterm"/> <a id="idx10794" class="indexterm"/>Before running the example, we told you to distribute
all of the class files to both the client and server machines.
However, RMI was designed to ship classes in addition to data around
the network; you shouldn’t have to distribute all the classes in
advance. Let’s go a step further and have RMI load classes for us as
needed. This involves a few extra steps.</p><p>First, we need to tell RMI where to find any other classes it
needs. We can use the system property <a id="I_indexterm13_id770478" class="indexterm"/><code class="literal">java.rmi.server.codebase</code> to specify a URL on
a web server (or FTP server) when we run our client or server. This
URL specifies the location of a JAR file or a base directory where RMI
begins its search for classes. When RMI sends a serialized object
(i.e., an object’s data) to a client, it also sends this URL. If the
recipient needs the class file in addition to the data, it fetches the
file at the specified URL. In addition to stub classes, other classes
referenced by remote objects in the application can be loaded
dynamically. Therefore, we don’t have to distribute many class files
to the client; we can let the client download them as necessary. In
<a class="xref" href="ch13s04.html#learnjava3-CHP-13-FIG-3" title="Figure 13-3. RMI applications and dynamic class loading">Figure 13-3</a>, we see an example of
<code class="literal">MyClient</code> going to the registry to
get a reference to the <code class="literal">ServerRemote</code>
object. Once there, <code class="literal">MyClient</code>
dynamically downloads the stub class for <code class="literal">MyServer</code> from a web server running on the
server object’s host.</p><p>We can now split our class files more logically between the
server and client machines. For example, we could withhold the
<code class="literal">MyCalculation</code> class from the server
because it really belongs to the client. Instead, we can make the
<code class="literal">MyCalculation</code> class available via a
web server on some machine (probably our client’s) and specify the URL
when we run <code class="literal">MyClient</code>:</p><a id="I_13_tt878"/><pre class="programlisting"> <code class="o">%</code> <strong class="userinput"><code><code class="n">java</code> <code class="o">-</code><code class="n">Djava</code><code class="o">.</code><code class="na">rmi</code><code class="o">.</code><code class="na">server</code><code class="o">.</code><code class="na">codebase</code><code class="o">=</code></code></strong><strong class="userinput"><code><em class="replaceable"><code>'http://myserver/foo/'</code></em></code></strong><code class="o">...</code></pre><p>The trailing slash in the codebase URL is important: it says
that the location is a base directory that contains the class files.
In this case, we would expect that <code class="literal">MyCalculation</code> would be accessible at the URL
<span class="emphasis"><em>http://myserver/foo/MyCalculation.class</em></span>.</p><p>Next, we have to set up security. Since we are loading class
files over the network and executing their methods, we must have a
security manager in place to restrict the kinds of things those
classes may do, at least when they are not coming from a trusted code
source. RMI will not load any classes dynamically unless a security
manager is installed. One easy way to meet this condition is to
install the <a id="I_indexterm13_id770593" class="indexterm"/><code class="literal">RMISecurityManager</code> as
the system security manager for your application. It is an example
security manager that works with the default system policy and imposes
some basic restrictions on what downloaded classes can do. To install
the <code class="literal">RMISecurityManager</code>, simply add
the following line to the beginning of the <code class="literal">main()</code> method of both the client and server
applications (yes, we’ll be sending code both ways in the next
section):</p><a id="I_13_tt880"/><pre class="programlisting"> <code class="n">main</code><code class="o">()</code> <code class="o">{</code>
<code class="n">System</code><code class="o">.</code><code class="na">setSecurityManager</code><code class="o">(</code> <code class="k">new</code> <code class="n">RMISecurityManager</code><code class="o">()</code> <code class="o">);</code>
<code class="o">...</code></pre><div class="figure"><a id="learnjava3-CHP-13-FIG-3"/><div class="figure-contents"><div class="mediaobject"><a id="I_13_tt879"/><img src="httpatomoreillycomsourceoreillyimages1707646.png" alt="RMI applications and dynamic class loading"/></div></div><p class="title">Figure 13-3. RMI applications and dynamic class loading</p></div><p>The <a id="I_indexterm13_id770650" class="indexterm"/><code class="literal">RMISecurityManager</code>
works with the system security policy file to enforce restrictions.
You have to provide a policy file that allows the client and server to
do basic operations like make network connections. Unfortunately,
allowing all the operations needed to load classes dynamically
requires listing a lot of permission information and we don’t want to
get into that here. We suggest that for this example, you simply grant
the code all permissions. Here is an example policy file—call it
<span class="emphasis"><em>mysecurity.policy</em></span>:</p><a id="I_13_tt881"/><pre class="programlisting"> <code class="n">grant</code> <code class="o">{</code>
<code class="n">permission</code> <code class="n">java</code><code class="o">.</code><code class="na">security</code><code class="o">.</code><code class="na">AllPermission</code> <code class="o">;</code>
<code class="o">};</code></pre><p>(It’s exceedingly lame, not to mention risky, to install a
security manager and then tell it to enforce no real security, but
we’re more interested in looking at the networking code at the
moment.)</p><p>To run our <code class="literal">MyServer</code>
application, we would use a command such as:</p><a id="I_13_tt882"/><pre class="programlisting"> <code class="o">%</code> <strong class="userinput"><code><code class="n">java</code> <code class="o">-</code><code class="n">Djava</code><code class="o">.</code><code class="na">rmi</code><code class="o">.</code><code class="na">server</code><code class="o">.</code><code class="na">codebase</code><code class="o">=</code></code></strong><strong class="userinput"><code><em class="replaceable"><code>'http://myserver/foo/'</code></em></code></strong> <code class="err">\</code>
<strong class="userinput"><code><code class="o">-</code><code class="n">Djava</code><code class="o">.</code><code class="na">security</code><code class="o">.</code><code class="na">policy</code><code class="o">=</code></code></strong><strong class="userinput"><code><em class="replaceable"><code>mysecurity.policy MyServer</code></em></code></strong></pre><p>Finally, one last trick is required to enable dynamic class
loading. As of the current implementation, the <a id="I_indexterm13_id770729" class="indexterm"/><code class="literal">rmiregistry</code> must be
run without the classes that are to be loaded in its classpath. If the
classes are in the classpath of <code class="literal">rmiregistry</code>, it does not annotate the
serialized objects with the URLs of their class files, and no classes
are dynamically loaded. This limitation is really annoying; all we can
say is to heed the warning for now.</p><p>If you follow these directions, you should be able to run our
client with only the <code class="literal">MyClient</code> class
and the <code class="literal">ServerRemote</code> remote
interface in its classpath. All the other classes are loaded
dynamically from the specified server as needed.<a id="I_indexterm13_id770763" class="indexterm"/><a id="I_indexterm13_id770770" class="indexterm"/></p></div><div class="sect3" title="Passing remote object references"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-13-SECT-4.2.3"/>Passing remote object references</h3></div></div></div><p><a id="idx10795" class="indexterm"/>So far, we haven’t done anything that we couldn’t have
done with the simple object protocol. We used only one remote object,
<code class="literal">MyServer</code>, and we got its reference
from the RMI registry. Now we extend our example to pass some remote
references between the client and server, allowing additional remote
calls in both directions. We’ll add two methods to our remote <code class="literal">ServerRemote</code>interface:</p><a id="I_13_tt883"/><pre class="programlisting"> <code class="kd">public</code> <code class="kd">interface</code> <code class="nc">ServerRemote</code> <code class="kd">extends</code> <code class="n">Remote</code> <code class="o">{</code>
<code class="o">...</code>
<code class="n">StringIterator</code> <code class="nf">getList</code><code class="o">()</code> <code class="kd">throws</code> <code class="n">RemoteException</code><code class="o">;</code>
<code class="kt">void</code> <code class="nf">asyncExecute</code><code class="o">(</code> <code class="n">WorkRequest</code> <code class="n">work</code><code class="o">,</code> <code class="n">WorkListener</code>
<code class="n">listener</code> <code class="o">)</code>
<code class="kd">throws</code> <code class="n">RemoteException</code><code class="o">;</code>
<code class="o">}</code></pre><p><code class="literal">getList()</code> retrieves a new
kind of object from the server: a <code class="literal">StringIterator</code>. The <code class="literal">StringIterator</code> we’ve created is a simple
list of strings with some methods for accessing the strings in order.
We make it a remote object so that implementations of <code class="literal">StringIterator</code> stay on the server.</p><p>Next, we spice up our work request feature by adding an <code class="literal">asyncExecute()</code> method. <code class="literal">asyncExecute()</code> lets us hand off a <code class="literal">WorkRequest</code> object as before, but it does
the calculation on its own time. The return type for <code class="literal">asyncExecute()</code> is <code class="literal">void</code> because it doesn’t actually return a
value; we get the result later. Along with the request, our client
passes a reference to a <code class="literal">WorkListener</code> object that is to be notified
when the <code class="literal">WorkRequest</code> is done. We’ll
have our client implement <code class="literal">WorkListener</code> itself.</p><p>Because this is to be a remote object, our interface must extend
<code class="literal">Remote</code> and its methods must throw
<code class="literal">RemoteException</code>s:</p><a id="I_13_tt884"/><pre class="programlisting"> <code class="c1">//file: StringIterator.java</code>
<code class="kn">import</code> <code class="nn">java.rmi.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">interface</code> <code class="nc">StringIterator</code> <code class="kd">extends</code> <code class="n">Remote</code> <code class="o">{</code>
<code class="kd">public</code> <code class="kt">boolean</code> <code class="nf">hasNext</code><code class="o">()</code> <code class="kd">throws</code> <code class="n">RemoteException</code><code class="o">;</code>
<code class="kd">public</code> <code class="n">String</code> <code class="nf">next</code><code class="o">()</code> <code class="kd">throws</code> <code class="n">RemoteException</code><code class="o">;</code>
<code class="o">}</code></pre><p>Next, we provide a simple implementation of <code class="literal">StringIterator</code>, called <code class="literal">MyString</code><code class="literal">Iterator</code>:</p><a id="I_13_tt885"/><pre class="programlisting"> <code class="c1">//file: MyStringIterator.java</code>
<code class="kn">import</code> <code class="nn">java.rmi.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">MyStringIterator</code>
<code class="kd">extends</code> <code class="n">java</code><code class="o">.</code><code class="na">rmi</code><code class="o">.</code><code class="na">server</code><code class="o">.</code><code class="na">UnicastRemoteObject</code>
<code class="kd">implements</code> <code class="n">StringIterator</code> <code class="o">{</code>
<code class="err"> </code>
<code class="n">String</code> <code class="o">[]</code> <code class="n">list</code><code class="o">;</code>
<code class="kt">int</code> <code class="n">index</code> <code class="o">=</code> <code class="mi">0</code><code class="o">;</code>
<code class="kd">public</code> <code class="nf">MyStringIterator</code><code class="o">(</code> <code class="n">String</code> <code class="o">[]</code> <code class="n">list</code> <code class="o">)</code>
<code class="kd">throws</code> <code class="n">RemoteException</code> <code class="o">{</code>
<code class="k">this</code><code class="o">.</code><code class="na">list</code> <code class="o">=</code> <code class="n">list</code><code class="o">;</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kt">boolean</code> <code class="nf">hasNext</code><code class="o">()</code> <code class="kd">throws</code> <code class="n">RemoteException</code> <code class="o">{</code>
<code class="k">return</code> <code class="n">index</code> <code class="o"><</code> <code class="n">list</code><code class="o">.</code><code class="na">length</code><code class="o">;</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="n">String</code> <code class="nf">next</code><code class="o">()</code> <code class="kd">throws</code> <code class="n">RemoteException</code> <code class="o">{</code>
<code class="k">return</code> <code class="n">list</code><code class="o">[</code><code class="n">index</code><code class="o">++];</code>
<code class="o">}</code>
<code class="o">}</code></pre><p><code class="literal">MyStringIterator</code> extends
<code class="literal">UnicastRemoteObject</code>. Its methods
are simple: it can give you the next string in the list, and it can
tell you if there are any strings you haven’t seen yet.</p><p>Next, we discuss the <a id="I_indexterm13_id770983" class="indexterm"/><code class="literal">WorkListener</code> remote
interface that defines how an object should listen for a completed
<a id="I_indexterm13_id770994" class="indexterm"/><code class="literal">WorkRequest</code>. It has
one method, <a id="I_indexterm13_id771005" class="indexterm"/><code class="literal">workCompleted()</code>,
which the server executing a <code class="literal">WorkRequest</code> calls when the job is
done:</p><a id="I_13_tt886"/><pre class="programlisting"> <code class="c1">//file: WorkListener.java</code>
<code class="kn">import</code> <code class="nn">java.rmi.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">interface</code> <code class="nc">WorkListener</code> <code class="kd">extends</code> <code class="n">Remote</code> <code class="o">{</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">workCompleted</code><code class="o">(</code><code class="n">WorkRequest</code> <code class="n">request</code><code class="o">,</code> <code class="n">Object</code> <code class="n">result</code> <code class="o">)</code>
<code class="kd">throws</code> <code class="n">RemoteException</code><code class="o">;</code>
<code class="o">}</code></pre><p>Let’s add the new features to <code class="literal">MyServer</code>. We need to add implementations of
the <code class="literal">getList()</code> and <code class="literal">asyncExecute()</code> methods, which we just added
to the <code class="literal">ServerRemote</code> interface:</p><a id="I_13_tt887"/><pre class="programlisting"> <code class="kd">public</code> <code class="kd">class</code> <code class="nc">MyServer</code> <code class="kd">extends</code> <code class="n">java</code><code class="o">.</code><code class="na">rmi</code><code class="o">.</code><code class="na">server</code><code class="o">.</code><code class="na">UnicastRemoteObject</code>
<code class="kd">implements</code> <code class="n">ServerRemote</code> <code