📄 tij0170.html
字号:
<html><body>
<table width="100%"><tr>
<td>
<a href="http://www.bruceeckel.com/javabook.html">Bruce Eckel's Thinking in Java</a>
</td>
<td align="right">
<a href="tij_c.html">Contents</a> | <a href="tij0169.html">Prev</a> | <a href="tij0171.html">Next</a>
</td>
</tr></table>
<hr>
<H2 ALIGN=LEFT>
Remote
methods
</H2>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Traditional
approaches to executing code on other machines across a network have been
confusing as well as tedious and error-prone to implement. The nicest way to
think about this problem is that some object happens to live on another
machine, and you can send a message to that object and get a result as if the
object lived on your local machine. This simplification is exactly what Java 1.1<A NAME="Index2865"></A>
<A NAME="Index2866"></A><A NAME="Index2867"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>Remote
Method Invocation
</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
(RMI) allows you to do. This section walks you through the steps necessary to
create your own RMI objects.
</FONT><a name="_Toc408018786"></a><P></DIV>
<A NAME="Heading543"></A><H3 ALIGN=LEFT>
Remote
interfaces
</H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">RMI
makes heavy use of interfaces. When you want to create a remote object, you
mask the underlying implementation by passing around an interface. Thus, when
the client gets a handle to a remote object, what they really get is an
interface handle, which
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>happens</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
to connect to some local stub code that talks across the network. But you
don’t think about this, you just send messages via your interface handle.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">When
you create a <A NAME="Index2868"></A>remote
interface, you must follow these guidelines:
</FONT><P></DIV>
<OL>
<LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> The
remote interface must be
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>public</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
(it cannot have “package access,” that is, it cannot be
“friendly”). Otherwise, a client will get an error when attempting
to load a remote object that implements the remote interface.
</FONT><LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> The
remote interface must extend the interface <A NAME="Index2869"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>java.rmi.Remote</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
</FONT><LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> Each
method in the remote interface must declare <A NAME="Index2870"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>java.rmi.RemoteException</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
in its
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>throws</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
clause in addition to any application-specific exceptions.
</FONT><LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> A
remote object passed as an argument or return value (either directly or
embedded within a local object) must be declared as the remote interface, not
the implementation class.
</FONT></OL><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Here’s
a simple remote interface that represents an accurate time service:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: PerfectTimeI.java</font>
<font color="#009900">// The PerfectTime remote interface</font>
<font color="#0000ff">package</font> c15.ptime;
<font color="#0000ff">import</font> java.rmi.*;
<font color="#0000ff">interface</font> PerfectTimeI <font color="#0000ff">extends</font> Remote {
<font color="#0000ff">long</font> getPerfectTime() <font color="#0000ff">throws</font> RemoteException;
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">It
looks like any other interface except that it extends
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Remote</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and all of its methods throw
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>RemoteException</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
Remember that an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>interface</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and all of its methods are automatically
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>public</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.</FONT><a name="_Toc408018787"></a><P></DIV>
<A NAME="Heading544"></A><H3 ALIGN=LEFT>
Implementing
the remote interface
</H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
server must contain a class that extends <A NAME="Index2871"></A><A NAME="Index2872"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>UnicastRemoteObject</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and implements the remote interface. This class can also have additional
methods, but only the methods in the remote interface will be available to the
client, of course, since the client will get only a handle to the interface,
not the class that implements it.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">You
must explicitly define the constructor for the remote object even if
you’re only defining a default constructor that calls the base-class
constructor. You must write it out since it must throw
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>RemoteException</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Here’s
the implementation of the remote interface
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>PerfectTimeI</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">:</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: PerfectTime.java</font>
<font color="#009900">// The implementation of the PerfectTime </font>
<font color="#009900">// remote object</font>
<font color="#0000ff">package</font> c15.ptime;
<font color="#0000ff">import</font> java.rmi.*;
<font color="#0000ff">import</font> java.rmi.server.*;
<font color="#0000ff">import</font> java.rmi.registry.*;
<font color="#0000ff">import</font> java.net.*;
<font color="#0000ff">public</font> <font color="#0000ff">class</font> PerfectTime
<font color="#0000ff">extends</font> UnicastRemoteObject
<font color="#0000ff">implements</font> PerfectTimeI {
<font color="#009900">// Implementation of the interface:</font>
<font color="#0000ff">public</font> <font color="#0000ff">long</font> getPerfectTime()
<font color="#0000ff">throws</font> RemoteException {
<font color="#0000ff">return</font> System.currentTimeMillis();
}
<font color="#009900">// Must implement constructor to throw</font>
<font color="#009900">// RemoteException:</font>
<font color="#0000ff">public</font> PerfectTime() <font color="#0000ff">throws</font> RemoteException {
<font color="#009900">// super(); // Called automatically</font>
}
<font color="#009900">// Registration for RMI serving:</font>
<font color="#0000ff">public</font> <font color="#0000ff">static</font> <font color="#0000ff">void</font> main(String[] args) {
System.setSecurityManager(
<font color="#0000ff">new</font> RMISecurityManager());
<font color="#0000ff">try</font> {
PerfectTime pt = <font color="#0000ff">new</font> PerfectTime();
Naming.bind(
"<font color="#009900">//colossus:2005/PerfectTime", pt);</font>
System.out.println("Ready to <font color="#0000ff">do</font> time");
} <font color="#0000ff">catch</font>(Exception e) {
e.printStackTrace();
}
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Here,
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>main( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
handles all the details of setting up the server. When you’re serving RMI
objects, at some point in your program you must:
</FONT><P></DIV>
<OL>
<LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> Create
and install a security manager that supports RMI. The only one available for
RMI as part of the Java distribution is <A NAME="Index2873"></A><A NAME="Index2874"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>RMISecurityManager</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.</FONT><LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> Create
one or more instances of a remote object. Here, you can see the creation of the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>PerfectTime</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object.
</FONT><LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> Register
at least one of the remote objects with the RMI <A NAME="Index2875"></A><A NAME="Index2876"></A>remote
object registry for bootstrapping purposes. One remote object can have methods
that produce handles to other remote objects. This allows you to set it up so
the client must go to the registry only once, to get the first remote object.
</FONT></OL><A NAME="Heading545"></A><H4 ALIGN=LEFT>
Setting
up the registry
</H4>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Here,
you see a call to the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>static</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method <A NAME="Index2877"></A><A NAME="Index2878"></A><A NAME="Index2879"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Naming.bind( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
However, this call requires that the registry be running as a separate process
on the computer. The name of the registry server is <A NAME="Index2880"></A><A NAME="Index2881"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>rmiregistry</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
and under 32-bit Windows you say:
</FONT><P></DIV><DIV ALIGN=LEFT><TT><FONT FACE="Courier New" SIZE=3 COLOR="Black">start
rmiregistry
</FONT></TT><P></DIV><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">to
start it in the background. On Unix, it is:
</FONT><P></DIV><DIV ALIGN=LEFT><TT><FONT FACE="Courier New" SIZE=3 COLOR="Black">rmiregistry
&
</FONT></TT><P></DIV><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Like
many network programs, the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>rmiregistry</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is located at the IP address of whatever machine started it up, but it must
also be listening at a port. If you invoke the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>rmiregistry</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
as above, with no argument, the registry’s port will default to 1099. If
you want it to be at some other port, you add an argument on the command line
to specify the port. For this example, the port will be located at 2005, so the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>rmiregistry</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
should be started like this under 32-bit Windows:
</FONT><P></DIV><DIV ALIGN=LEFT><TT><FONT FACE="Courier New" SIZE=3 COLOR="Black">start
rmiregistry 2005
</FONT></TT><P></DIV><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">or
for Unix:
</FONT><P></DIV><DIV ALIGN=LEFT><TT><FONT FACE="Courier New" SIZE=3 COLOR="Black">rmiregistry
2005 &
</FONT></TT><P></DIV><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
information about the port must also be given to the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>bind( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
command, as well as the IP address of the machine where the registry is
located. But this brings up what can be a frustrating problem if you’re
expecting to test RMI programs locally the way the network programs have been
tested so far in this chapter. In the JDK 1.1.1 release, there are a couple of
problems:
</FONT><A NAME="fnB69" HREF="#fn69">[69]</A><P></DIV><DIV ALIGN=LEFT><A NAME="Index2882"></A><P></DIV>
<OL>
<LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> <A NAME="Index2883"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>localhost
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">does
not work with RMI. Thus, to experiment with RMI on a single machine, you must
provide the name of the machine. To find out the name of your machine under
32-bit Windows, go to the control panel and select “Network.”
Select the “Identification” tab, and you’ll see your computer
name. In my case, I called my computer “Colossus” (for all the hard
disks I’ve had to put on to hold all the different development systems).
It appears that capitalization is ignored.
</FONT><LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> RMI
will not work unless your computer has an active <A NAME="Index2884"></A><A NAME="Index2885"></A>TCP/IP
connection, even if all your components are just talking to each other on the
local machine. This means that you must connect to your Internet service
provider before trying to run the program or you’ll get some obscure
exception messages.
</FONT></OL><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Will
all this in mind, the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>bind( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -