RMI
,远程方法调用(
Remote Method Invocation
)是
Enterprise JavaBeans
的支柱,是建立分布式
Java
应用程序的方便途径。
RMI
是非常容易使用的,但是它非常的强大。
RMI
的基础是
接口
,
RMI
构架基于一个重要的原理:定义接口和定义接口的具体实现是分开的。下面我们通过具体的例子,建立一个简单的远程计算服务和使用它的客户程序
1.
一个正常工作的
RMI
系统由下面几个部分组成:
●
远程服务的接口定义
●
远程服务接口的具体实现
●
桩(
Stub
)和框架(
Skeleton
)文件
●
一个运行远程服务的服务器
●
一个
RMI
命名服务,它允许客户端去发现这个远程服务
●
类文件的提供者(一个
HTTP
或者
FTP
服务器)
●
一个需要这个远程服务的客户端程
下面我们一步一步建立一个简单的
RMI
系统。首先在你的机器里建立一个新的文件夹,以便放置我们创建的文件,为了简单起见,我们只使用一个文件夹存放客户端和服务端代码,并且在同一个目录下运行服务端和客户端。
如果所有的
RMI
文件都已经设计好了,那么你需要下面的几个步骤去生成你的系统:
2.
步骤
1
、
编写并且编译接口的
Java
代码
2
、
编写并且编译接口实现的
Java
代码
3
、
从接口实现类中生成桩(
Stub
)和框架(
Skeleton
)类文件
4
、
编写远程服务的主运行程序
5
、
编写
RMI
的客户端程序
6
、
安装并且运行
RMI
系统
2.1
接口
第一步就是建立和编译服务接口的
Java
代码。这个接口定义了所有的提供远程服务的功能,下面是源程序:
//
Calculator.java
// define the interface
import java.rmi.Remote;
public interface Calculator extends Remote
{
public long add( long a, long b)
throws java.rmi.RemoteException;
public long sub( long a, long b)
throws java.rmi.RemoteException;
public long mul( long a, long b)
throws java.rmi.RemoteException;
public long div( long a, long b)
throws java.rmi.RemoteException;
}
// define the interface
import java.rmi.Remote;
public interface Calculator extends Remote
{
public long add( long a, long b)
throws java.rmi.RemoteException;
public long sub( long a, long b)
throws java.rmi.RemoteException;
public long mul( long a, long b)
throws java.rmi.RemoteException;
public long div( long a, long b)
throws java.rmi.RemoteException;
}
建立这个文件,把它存放在刚才的目录下,并且编译。
>javac Calculator.java
2.2
接口的具体实现
下一步,我们就要写远程服务的具体实现,这是一个
CalculatorImpl
类文件:
//
CalculatorImpl.java
// Implementation
import java.rmi.server.UnicastRemoteObject;
public class CalculatorImpl extends UnicastRemoteObject implements Calculator
{
// 这个实现必须有一个显式的构造函数,并且要抛出一个RemoteException异常
public CalculatorImpl()
throws java.rmi.RemoteException {
super ();
}
public long add( long a, long b)
throws java.rmi.RemoteException {
return a + b;
}
public long sub( long a, long b)
throws java.rmi.RemoteException {
return a - b;
}
public long mul( long a, long b)
throws java.rmi.RemoteException {
return a * b;
}
public long div( long a, long b)
throws java.rmi.RemoteException {
return a / b;
}
}
// Implementation
import java.rmi.server.UnicastRemoteObject;
public class CalculatorImpl extends UnicastRemoteObject implements Calculator
{
// 这个实现必须有一个显式的构造函数,并且要抛出一个RemoteException异常
public CalculatorImpl()
throws java.rmi.RemoteException {
super ();
}
public long add( long a, long b)
throws java.rmi.RemoteException {
return a + b;
}
public long sub( long a, long b)
throws java.rmi.RemoteException {
return a - b;
}
public long mul( long a, long b)
throws java.rmi.RemoteException {
return a * b;
}
public long div( long a, long b)
throws java.rmi.RemoteException {
return a / b;
}
}
同样的,把这个文件保存在你的目录里然后编译他。
这个实现类使用了
UnicastRemoteObject
去联接
RMI
系统。在我们的例子中,我们是直接的从
UnicastRemoteObject
这个类上继承的,事实上并不一定要这样做,如果一个类不是从
UnicastRmeoteObject
上继承,那必须使用它的
exportObject()
方法去联接到
RMI
。
如果一个类继承自
UnicastRemoteObject
,那么它必须提供一个构造函数并且声明抛出一个
RemoteException
对象。当这个构造函数调用了
super()
,它久激活
UnicastRemoteObject
中的代码完成
RMI
的连接和远程对象的初始化。
2.3
桩(
Stubs
)和框架(
Skeletons
)
下一步就是要使用
RMI
编译器
rmic
来生成桩和框架文件,这个编译运行在远程服务实现类文件上。
>rmic CalculatorImpl
在你的目录下运行上面的命令,成功执行完上面的命令你可以发现一个
Calculator_stub.class
文件,如果你是使用的
Java2SDK
,那么你还可以发现
Calculator_Skel.class
文件。
2.4
主机服务器
远程
RMI
服务必须是在一个服务器中运行的。
CalculatorServer
类是一个非常简单的服务器。
//
CalculatorServer.java
import java.rmi.Naming;
public class CalculatorServer {
public CalculatorServer() {
try {
Calculator c = new CalculatorImpl();
Naming.rebind( " rmi://localhost:1099/CalculatorService " , c);
} catch (Exception e) {
System.out.println( " Trouble: " + e);
}
}
public static void main(String args[]) {
new CalculatorServer();
}
}
import java.rmi.Naming;
public class CalculatorServer {
public CalculatorServer() {
try {
Calculator c = new CalculatorImpl();
Naming.rebind( " rmi://localhost:1099/CalculatorService " , c);
} catch (Exception e) {
System.out.println( " Trouble: " + e);
}
}
public static void main(String args[]) {
new CalculatorServer();
}
}
建立这个服务器程序,然后保存到你的目录下,并且编译它。
2.5
客户端
客户端源代码如下:
//
CalculatorClient.java
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.net.MalformedURLException;
import java.rmi.NotBoundException;
public class CalculatorClient {
public static void main(String[] args) {
try {
Calculator c = (Calculator) Naming.lookup( " rmi://localhost:1099/CalculatorService " );
System.out.println( c.sub( 4 , 3 ) );
System.out.println( c.add( 4 , 5 ) );
System.out.println( c.mul( 3 , 6 ) );
System.out.println( c.div( 9 , 3 ) );
}
catch (MalformedURLException murle) {
System.out.println();
System.out.println(
" MalformedURLException " );
System.out.println(murle);
}
catch (RemoteException re) {
System.out.println();
System.out.println(
" RemoteException " );
System.out.println(re);
}
catch (NotBoundException nbe) {
System.out.println();
System.out.println(
" NotBoundException " );
System.out.println(nbe);
}
catch (
java.lang.ArithmeticException
ae) {
System.out.println();
System.out.println(
" java.lang.ArithmeticException " );
System.out.println(ae);
}
}
}
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.net.MalformedURLException;
import java.rmi.NotBoundException;
public class CalculatorClient {
public static void main(String[] args) {
try {
Calculator c = (Calculator) Naming.lookup( " rmi://localhost:1099/CalculatorService " );
System.out.println( c.sub( 4 , 3 ) );
System.out.println( c.add( 4 , 5 ) );
System.out.println( c.mul( 3 , 6 ) );
System.out.println( c.div( 9 , 3 ) );
}
catch (MalformedURLException murle) {
System.out.println();
System.out.println(
" MalformedURLException " );
System.out.println(murle);
}
catch (RemoteException re) {
System.out.println();
System.out.println(
" RemoteException " );
System.out.println(re);
}
catch (NotBoundException nbe) {
System.out.println();
System.out.println(
" NotBoundException " );
System.out.println(nbe);
}
catch (
java.lang.ArithmeticException
ae) {
System.out.println();
System.out.println(
" java.lang.ArithmeticException " );
System.out.println(ae);
}
}
}
保存这个客户端程序到你的目录下(注意这个目录是一开始建立那个,所有的我们的文件都在那个目录下),并且编译他。
2.6
运行
RMI
系统
现在我们建立了所有运行这个简单
RMI
系统所需的文件,现在可以运行这个
RMI
系统啦!
我们是在命令控制台下运行这个系统的,你必须开启三个控制台窗口,一个运行服务器,一个运行客户端,还有一个运行
RMIRegistry
。
首先运行注册程序
RMIRegistry
,你必须在包含你刚写的类的那么目录下运行这个注册程序。
>rmiregistry
好,这个命令成功的话,注册程序已经开始运行了,不要管他,现在切换到另外一个控制台,在第二个控制台里,我们运行服务器
CalculatorService
,因为
RMI
的安全机制将在服务端发生作用
,
所以你必须增加一条安全策略。以下是对应安全策略的例子
grant {
permission java.security.AllPermission "", "";
};
注意
:
这是一条最简单的安全策略
,
它允许任何人做任何事
,
对于你的更加关键性的应用
,
你必须指定更加详细安全策略。
现在为了运行服务端,你需要除客户类
(CalculatorClient.class)
之外的所有的类文件。确认安全策略在
policy.txt
文件之后
,
使用如下命令来运行服务器。
>
java -Djava.security.policy=policy.txt CalculatorServer
这个服务器就开始工作了,把接口的实现加载到内存等待客户端的联接。好现在切换到第三个控制台,启动我们的客户端。
为了在其他的机器运行客户端程序你需要一个远程接口
(Calculator.class)
和一个
stub(CalculatorImpl_Stub.class)
。使用如下命令运行客户端
>
java -Djava.security.policy=policy.txt CalculatorClient
如果所有的这些都成功运行,你应该看到下面的输出:
1
9
18
3