Java远程方法调用(2)

来源:百度文库 编辑:神马文学网 时间:2024/05/01 21:23:27
Java远程方法调用(2)

· 传递属性


   前面我们讲到,RMI可以传递属性,并简单介绍了一下一个有关开支报告程序的情况。下面我们将深入讨论如何设计这样的系统。这样介绍的目的是使您能够利用RMI的功能将属性从一个系统传递到另一个系统,并随心所欲地安排当前的计算地点,并便于将来的改变。下面的例子并未涉及真实世界可能发生的所有问题,但可帮助读者了解处理问题的方法。

· 服务器定义的策略

   客户机向用户显示图形用户界面(GUI),用户填写开支报告。客户机程序使用RMI与服务器进行通信。服务器使用JDBC( Java关系数据库连接包)将开支报告存储在数据库中。至此,这看起来与其它多层次系统大同小异,但有一个重大区别-- RMI能下载属性。
假定公司关于开支报告的政策发生改变。例如,目前公司只要求对超过20美元的开支需开具发票。但到明天,公司认为这太宽松了,便决定除不超过20美元的餐费以外,任何开支均需开具发票。如果不能下载属性的话,那么在设计便于修改的系统时您可选择下列方法之一:

   用客户端安装与政策有关的程序。政策改变时,必须更新包含此政策的所有客户端程序。您可在若干服务器上安装客户程序,并要求所有用户从这些服务器之一运行客户程序,从而减少问题。但这仍不能彻底解决问题-- 那些让程序运行好几天的用户就无法使程序更新,而总是会有一些用户为了方便而把软件复制到本地磁盘上。

   您可要求服务器在每次向开支报告添加项目时检查政策。但这样就会在客户机和服务器之间产生大量数据流,并增加服务器的工作量。这还会使系统变得更加脆弱--网络故障会立即妨碍用户,而不仅仅是只在其呈交开支报告或启动新的报告时对其产生影响。同时,添加项目的速度也会变慢,因为这需要穿越整个网络往返一圈才能到达(不堪重负的)服务器。

您可在呈交报告时要求服务器对政策进行检查。这样就会使用户创建很多必须待批报告的错误项目,而不是立刻捕捉到第一个错误,从而使用户有机会停止制造错误。为避免浪费时间,用户需要立刻得到有关错误的反馈。

   有了RMI,您就能以简单的方法调用程序从服务器得到属性,从而提供了一种灵活的方式,将计算任务从服务器卸载到客户机上,同时为用户提供速度更快的反馈。当用户准备编写一份新的开支报告时,客户机就会向服务器要求一个对象,该对象嵌入了适用于该开支报告的当前政策,就如同通过用Java编写的政策接口所表示的那样。该对象可以以任何方式实现当前政策。如果这是客户机RMI首次看到这种专门执行的政策,就会要求服务器提供一份执行过程的副本。如果执行过程将来发生变化,则一种新的政策对象将被返回给客户机,而RMI运行时则会要求得到新的执行过程。

   这表明,政策永远是动态的。您要想修改政策,就只需简单地编写通用政策接口的新的执行程序,把它安装在服务器上,并对服务器进行配置以返回这种新类型的对象即可。这样,每台客户机都会根据新的政策对新的开支报告进行检查。

这是一种比任何静态方法都更好的方法,原因如下:

   所有客户机不必暂停或用新的软件来升级--软件可根据需要在不工作时加以更新。
   服务器不必参与项目检查工作,该工作可在本地完成。
   允许动态限制,因为对象执行程序(而不仅仅是数据)是在客户机和服务器之间进行传递的。
   使用户能立刻看到错误。


使客户机在服务器上所能调用的方法的远程接口定义如下:
import java.rmi.*;
public interface ExpenseServer extends Remote {
Policy getPolicy() throws RemoteException;
void submitReport(ExpenseReport report)
throws RemoteException, InvalidReportException;
}
import语句输入Java RMI包。所有RMI类型均在包java.rmi或其子包内定义。接口ExpenseServer是一般的Java接口,具有如下两种有趣的特点:

它扩展了名为Remote的RMI接口,这使该接口标记为可用于远程调用。

它的所有方法均抛出RemoteException,后者用来表示网络或信息故障。

   远程方法还可抛出您所需要的任何其他例外,但至少必须抛出RemoteException,这样您才能处理只会在分布式系统中发生的错误状态。该接口本身支持两个方法:getPolicy (返回一个实现政策接口的对象),和submitReport (提交一个完成的开支请求,并在报告无论因何种原因使表格出现错误时,抛出一个例外)。
政策接口本身可声明一种使客户机知道能否在开支报告中添加一个项目的方法:
public interface Policy {
void checkValid (Expenseentry entry)
throws PolicyViolationException;
}
如果该项目有效--即符合当前政策,则该方法可正常返回。否则就会抛出一个描述该错误的例外。政策接口是本地的(而非远程的),所以将通过本机对象在客户机上执行--在客户机的虚拟机上,而非整个网络上运行的对象。客户机可能运行下列程序:
Policy curPolicy = server.getPolicy();
start a new expense report
show the GUI to the user
while (user keeps adding entries) {
try {
curPolicy.checkValid(entry); // throws exception if not OK
add the entry to the expense report
} catch (PolicyViolationException e) {
show the error to the user
}
}
server.submitReport(report);


   当用户请求客户机软件启动一份新的开支报告时,客户机就调用server.getPolicy,并要求服务器返回一个包含当前开支政策的对象。添加的每个项目首先都被提交给该政策对象,以获得批准。如果政策对象报告无错误,则该项目就被添加到报告中;否则错误就被显示给用户,而后者可采取修正措施。当用户完成向报告中添加项目时,整个报告就被呈交。服务程序如下:
import java.rmi. *;
import java.rmi.server. *;
class ExpenseServerImpl
extends UnicastRemoteObject
implements ExpenseServer
{
ExpenseServerImpl() throws RemoteException {
// . . . set up server state . . .
}
public Policy getPolicy() {
return new TodaysPolicy();
}
public void submitReport(ExpenseReport report) {
// . . . write the report into the db . . .
}
}
除基本程序包外,我们还输入RMI的服务程序包。类型UnicastRemoteObject 定义了此服务程序远程对象的类型,在本例中,应为单一服务程序而非复制服务(下面还会详细介绍)。Java类ExpenseSeverImpl实现远程接ExpenseServer的方法。远程主机的客户机可使用RMI将信息发送给ExpenseServerImpl对象。