本文主要是介绍RMI规范–第一章 (转贴),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1.1概述
在目前的程序设计中,分布计算常常会被提及。所谓分布计算,它代表着一种程序设计模式,其中的程序,处理的数据和实际的计算都 可以广泛的分布于网络之上,换言之,分布计算允许远程获得商业逻辑(logic)和数据,这既是为了平衡各个计算机的处理能力,也归因于应用程序本身的特 点。分布式对象技术是近几年发展起来的技术,目前流行的几种分布式对象技术有CORBA,Java RMI和MS的DCOM技术。
Java Remote Method Invocation ( RMI — Java远程方法调用)允许您使用Java编写分布式对象,不同的Java虚拟机(JVM)之间进行对象间的通讯。这些JVM可以在一台者多台计算机上, 使得应用程序(Application)可以远程调用方法,共享各个系统的资源和处理能力。 RMI为采用Java对象的分布式计算提供了简单而直接的途径。它将Java的安全性和可移植性等强大功能带给了分布式计算。
1.2优点
从最基本的角度看,RMI是Java的远程过程调用(RPC)机制。与传统的RPC系统相比,RMI具有若干优点,因为它是Java面向对象方法的一部分。
RMI的主要优点如下:
面向对象:
RMI 可将完整的对象作为参数和返回值进行传递,而不仅仅是预定义的数据类型。对象传递功能使您可以在分布式计算中充分利用面向对象技术的强大功能。您可以将类 似Java HashTable这样的复杂类型作为一个参数进行传递。而在目前的RPC系统中,您只能依靠客户机将此类对象分解成基本数据类型,然后传递这些数据类 型,最后在服务器端重新创建对象。
可移动属性:
RMI可将属性(类实现程序)从客户机移动到服务器,或者从服务器移到客户机。例 如,您可以定义一个检查雇员开支报告的接口,以便察看雇员是否遵守了公司目前实行的政策。在开支报告创建后,客户机就会从服务器端获得实现该接口的对象。 如果Policy发生变化,服务器端就会开始返回使用了新政策的该接口的另一个实现程序。您不必在用户系统上安装任何新的软件.
安全:
RMI使用Java内置的安全机制保证下载执行程序时用户系统的安全。 RMI使用专门为保护系统免遭恶意小应用程序侵害而设计的安全管理程序,可保护您的系统和网络免遭潜在的恶意下载程序的破坏。在情况严重时,服务器可拒绝下载任何执行程序。
便于编写和使用:
RMI使得Java远程服务程序和访问这些服务程序的Java客户程序的编写工作变得轻松、简单。服务程序大约用三行指令宣布本身是服务程序,其它方面则与任何其它Java对象类似。
可连接现有/原有的系统:
RMI 可通过Java的本机方法接口JNI与现有系统进行进行交互。利用RMI和JNI,您就能用Java语言编写客户端程序,还能使用现有的服务器端程序。在 使用RMI/JNI与现有服务器连接时,您可以有选择地用Java重新编写服务程序的任何部分,并使新的程序充分发挥Java的功能。类似地, RMI可利用JDBC、在不修改使用数据库的现有非Java源代码的前提下与现有关系数据库进行交互。
分布式垃圾收集:
RMI采用其分布式垃圾收集功能收集不再被网络中任何客户程序所引用的远程服务对象。与Java虚拟机内部的垃圾收集类似,分布式垃圾收集功能允许用户根据自己的需要定义服务器对象,并且明确这些对象在不再被客户机引用时会被删除。
并行计算:
RMI 采用多线程处理方法,可使您的服务器利用这些Java线程更好地并行处理客户端的请求。 Java分布式计算解决方案:RMI从JDK 1.1开始就是Java平台的核心部分,因此,它存在于任何一台1.1 Java虚拟机中。所有RMI系统均采用相同的公开协议,所以,所有Java系统均可直接相互对话,而不必事先对协议进行转换。
1.3简单介绍CORBA和DCOM
1.3.1 COBRA:
CORBA 是OMG(Object Management Group)提出的一个分布式对象技术的规范。 COBRA标准主要分为3个层次:对象请求代理、公共对象服务和公共设施。最底层是对象请求代理ORB,规定了分布对象的定义(接口)和语言映射,实现对 象间的通讯和互操作,是分布对象系统中的“软总线”;在ORB之上定义了很多公共服务,可以提供诸如并发服务、名字服务、事务(交易)服务、安全服务等各 种各样的服务;最上层的公共设施则定义了组件框架,提供可直接为业务对象使用的服务,规定业务对象有效协作所需的协定规则。总之,CORBA的特点是大而 全,互操作性和开放性非常好。目前CORBA的最新版本是2.3。 CORBA 3.0也已基本完成,增加了有关Internet集成和QoS控制等内容。 CORBA的缺点是庞大而复杂,并且技术和标准的更新相对较慢.
1.3.2 DCOM
DCOM 是Microsoft与其他业界厂商合作提出的一种分布构件对象模型(Distributed Component Object Model).DCOM起源于动态数据交换(DDE)技术,对象连接与嵌入OLE就是从DDE引伸而来的。随后,Microsoft引入了构件对象模型 COM,形成了COM对象之间实现互操作的二进制标准。基于COM,微软进一步将OLE技术发展到OLE2。在OLE2中,出现了我们今天熟知的拖-放技 术以及OLE自动化。 DCOM是COM在分布计算方面的自然延续,它为分布在网络不同节点的两个COM构件提供了互操作的基础结构。
当然,按照微软一贯的产品开发逻辑,微妙的思想都退到了幕后,而提供给开发者的是一个以Wizard方式生成各种应用的可视化开发环境.
本文來源于:<a href="http://blog.chaoskey.com/2008/01/14/73/ " rel="bookmark" title=" RMI规范–第一章 (转贴) | 夢想混沌"> RMI规范–第一章 (转贴) | 夢想混沌</a>
RMI规范–第二章 Java 分布式对象模型(转贴)
作者: 混沌
2.1 分布式对象应用程序
RMI 应用程序通常包括两个独立的程序:服务器程序和客户机程序。典型的服务器应用程序将创建多个远程对象,使这些远程对象能够被引用,然后等待客户机调用那些 远程对象上的方法。而典型的客户机程序则从服务器中得到一个或多个远程对象的引用,然后调用远程对象的方法。RMI 为服务器和客户机进行通讯和信息传递提供了一种机制。这样的应用程序有时被称为分布式对象应用程序。
分布式对象应用程序需要:
定位远程对象
应用程序可使用两种机制中的一种得到对远程对象的引用。它既可用 RMI 的简单命名工具 rmiregistry 来注册它的远程对象;也可将远程对象引用作为常规操作的一部分来进行传递和返回。
与远程对象通讯
远 程对象间通讯的细节由 RMI 处理;对于程序员来说,远程通讯看起来就象标准的 Java 方法调用。给作为参数或返回值传递的对象加载类字节码因为 RMI允许调用程序将纯 Java 对象传给远程对象,所以 RMI 将提供必要的机制,既可以加载对象的代码又可以传输对象的数据。
服务器调用注册服务程序以使名字与远程对象相关联。客户机在服务器注册服务程序中用远程对象的名字查找该远程对象,然后调用它的方法。RMI 能用 Java系统支持的任何 URL 协议(例如 HTTP、FTP、file 等)加载类字节码。
2.2 术语的定义
在 Java 分布式对象模型中,remote object 是这样一种对象:它的方法可以从其它 Java 虚拟机(可能在不同的主机上)中调用。该类型的对象由一种或多种 remote interfaces(它是声明远程对象方法的 Java 接口)描述。
远程方法调用 (RMI) 就是调用远程对象上远程接口的方法的动作。更为重要的是,远程对象的方法调用与本地对象的方法调用语法相同。
2.3 分布式和非分布式模型的比较
Java 分布式对象模型在以下几方面与 Java 对象模型相似:
远程对象的引用在任一种方法调用中(本地或远程)都能以参数形式传递或以结果形式返回。
远程对象可以被强制转换成任何远程界面,只要该界面为使用内置 Java 语法进行强制类型转换的实现所支持。
内置 Java 操作符 instanceof 可用来测试远程对象所支持的远程接口。
Java 分布式对象模型在以下几方面与 Java 对象模型不同:
远程对象的客户机与远程接口发生交互,而从不与这些接口的实现类交互。
远程方法的非远程参数和返回结果是通过复制而非引用的方式传递的。这是因为对象的引用只在单个虚拟机中才有用。
远程对象以引用的方式进行传递,而不是复制实际的远程实现。
某些 java.lang.Object 类定义的方法的语义专用于远程对象。
因为调用远程对象的失败模式本来就比调用本地对象的失败模式复杂,所以客户机必须处理远程方法调用期间发生的额外异常。
2.4 RMI 接口和类概述
2.4.1 java.rmi.Remote 接口
在 RMI 中,远程接口是声明了可从远程 Java 虚拟机中调用的方法集。远程接口必须满足下列要求:
远程接口至少必须直接或间接扩展 java.rmi.Remote 接口。
远程接口中的方法声明必须满足下列远程方法声明的要求:
远 程方法声明在其 throws 子句中除了要包含与应用程序有关的异常(注意与应用程序有关的异常无需扩展 java.rmi.RemoteException )之外,还必须包括 java.rmi.RemoteException 异常(或它的超类,例如 java.io.IOException 或 java.lang.Exception )。
远程方法声明中,作为参数或返回值声明的(在参数表中直接声明或嵌入到参数的非远程对象中)远程对象必须声明为远程接口,而非该接口的实现类。
java.rmi.Remote 接口是一个不定义方法的标记接口:
public interface Remote远程接口必须至少扩展 java.rmi.Remote 接口(或其它扩展java.rmi.Remote 的远程接口)。然而,远程接口在下列情况中可以扩展非远程接口:
远程接口也可扩展其它非远程接口,只要被扩展接口的所有方法(如果有)满足远程方法声明的要求。
例如,下面的接口 BankAccount 即为访问银行帐户定义了一个远程接口。它包含往帐户存款、使帐户收支平衡和从帐户取款的远程方法:
public interface BankAccount extends java.rmi.Remote {
public void deposit(float amount) throws java.rmi.RemoteException;
public void withdraw(float amount) throws OverdrawnException, java.rmi.RemoteException;
public float getBalance() throws java.rmi.RemoteException;
}
下例说明了有效的远程接口 Beta。它扩展非远程接口 Alpha(有远程方法)和接口 java.rmi.Remote:
public interface Alpha {
public final String okay = "constants are okay too";
public Object foo(Object obj) throws java.rmi.RemoteException;
public void bar() throws java.io.IOException;
public int baz() throws java.lang.Exception;
}
public interface Beta extends Alpha, java.rmi.Remote {
public void ping() throws java.rmi.RemoteException;
}
2.4.2 RemoteException 类
java.rmi.RemoteException 类是在远程方法调用期间由 RMI 运行时所抛出的异常的超类。为确保使用 RMI 系统的应用程序的健壮性,远程接口中声明的远程方法在其 throws 子句中必须指定 java.rmi.RemoteException
(或它的超类,例如 java.io.IOException 或 java.lang.Exception)。
当远程方法调用由于某种原因失败时,将抛出 java.rmi.RemoteException 异常。远程方法调用失败的原因包括:
通讯失败(远程服务器不可达或拒绝连接;连接被服务器关闭等。)
参数或返回值传输或读取时失败
协议错误
RemoteException 类是一个已检验的异常(必须由远程方法的调用程序处理并经编译器检验的异常),而不是 RuntimeException。
2.4.3 RemoteObject 类及其子类
RMI 服务器函数由 java.rmi.server.RemoteObject 及其子类 java.rmi.server.RemoteServer、java.rmi.server.UnicastRemoteObject和 java.rmi.activation.Activatable 提供。
java.rmi.server.RemoteObject 为对远程对象敏感的 java.lang.Object 方法、hashCode、 equals 和 toString 提供实现。
创建远程对象并将其导出(使它们可为远程客户机利用)所需的方法由类UnicastRemoteObject 和 Activatable 提供。子类可以识别远程引用的语义,例如服务器是简单的远程对象还是可激活的远程对象(调用时将执行的远程对象)。
java.rmi.server.UnicastRemoteObject 类定义了单体(单路传送)远程对象,其引用只有在服务器进程活着时才有效。类 java.rmi.activation.Activatable 是抽象类,它定义的 activatable远程对象在其远程方法被调用时开始执行并在必要时自己关闭。
2.5 实现远程接口
实现远程接口的类的一般规则如下:
该类通常扩展 java.rmi.server.UnicastRemoteObject,因而将继承类 java.rmi.server.RemoteObject 和java.rmi.server.RemoteServer 提供的远程行为。
该类能实现任意多的远程接口。
该类能扩展其它远程实现类。
该类能定义远程接口中不出现的方法,但这些方法只能在本地使用而不能在远程使用。
例如,下面的类 BankAcctImpl 实现 BankAccount 远程接口并扩展java.rmi.server.UnicastRemoteObject 类:
package mypackage;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class BankAccountImpl extends UnicastRemoteObject implements BankAccount {
private float balance = 0.0;
public BankAccountImpl(float initialBalance) throws RemoteException {
balance = initialBalance;
}
public void deposit(float amount) throws RemoteException {
…
}
public void withdraw(float amount) throws OverdrawnException, RemoteException {
…
}
public float getBalance() throws RemoteException {
…
}
}
注 意:必要时,实现远程接口的类能扩展除java.rmi.server.UnicastRemoteObject 类以外的其它一些类。但实现类此时必须承担起一定的责任,即导出对象(由 UnicastRemoteObject 构造函数负责)和实现从 java.lang.Object 类继承的 hashCode、 equals 和 toString 方法的正确远程语义(如果需要)。
2.6 远程方法调用中的参数传递
传给远程对象的参数或源于它的返回值可以是任意可序列化的 Java 对象。这包括 Java 基本类型, 远程?Java 对象和实现 java.io.Serializable 接口的非远程 Java 对象。有关如何使类序列化的详细信息,参见 Java“对象序列化规范”。本地得不到的作为参数或返回值的类,可通过 RMI 系统进行动态下载。
有关 RMI 读取参数、返回值和异常时如何下载参数和返回值类的详细信息,参见“动态类加载”(3.4)一节。
2.6.1 传递非远程对象
非远程对象将作为远程方法调用的参数传递或作为远程方法调用的结果返回时,是通过复制传递的;也就是使用 Java 对象序列化机制将该对象序列化。 因此,在远程对象调用过程中,当非远程对象作为参数或返回值传递时,非远程对象的内容在调用远程对象之前将被复制。
从远程方法调用返回非远程对象时,将在调用的虚拟机中创建新对象。
2.6.2 传递远程对象
当将远程对象作为远程方法调用的参数或返回值传递时,远程对象的 stub 程序即被传递出去。作为参数传递的远程对象仅能实现远程接口。
2.6.3 引用的完整性
如果一个对象的两个引用在单个远程方法调用中以参数形式(或返回值形式)从一个虚拟机传到另一个虚拟机中,并且它们在发送虚拟机中指向同一对象,则两个引 用在接收虚拟机中将指向该对象的同一副本。进一步说就是:在单个远程方法调用中,RMI 系统将在作为调用参数或返回值传递的对象中保持引用的完整性。
2.6.4 类注解
当对象在远程调用中被从一个虚拟机发送到另一个虚拟机中时,RMI 系统在调用流中用类的信息 (URL) 给类描述符加注解,以便该类能在接收器上加载。在远程方法调用期间,调用可随时下载类。
2.6.5 参数传输
为将 RMI 调用的参数序列化到远程调用的目的文件里,需要将该参数写入作为java.io.ObjectOutputStream 类的子类的流中。 ObjectOutputStream 子类将覆盖 replaceObject 方法,目的是用其相应的 stub 类取代每个远程对象。
对象参数将通过 ObjectOutputStream 的 writeObject 方法写入流中。而ObjectOutputStream 则通过 writeObject 方法为每个写入流中的对象(包含所写对象所引用的对象)调用 replaceObject 方法。 RMIObjectOutputStream子类的 replaceObject 方法返回下列值:
如果传给 replaceObject 的对象是 java.rmi.Remote 的实例,则返回远程对象的 stub 程序。远程对象的 stub 程序通过对java.rmi.server.RemoteObject.toStub 方法的调用而获得。
如果传给 replaceObject 的对象不是 java.rmi.Remote 的实例,则只返回该对象。
RMI 的 ObjectOutputStream 子类也实现 annotateClass 方法,该方法用类的位置注解调用流以便能在接收器中下载该类。有关如何使用 annotateClass的详细信息,参见“动态类加载”一节。
因为参数只写入一个 ObjectOutputStream,所以指向调用程序同一对象的引用将在接收器那里指向该对象的同一副本。在接收器上,参数将被单个ObjectInputStream 所读取。
用 于写对象的 ObjectOutputStream(类似的还有用于读对象的ObjectInputStream )的所有其它缺省行为将保留在参数传递中。例如,写对象时对 writeReplace 的调用及读对象时对 readResolve 的调用就是由 RMI 的参数编组与解编流完成的。
与上述 RMI 参数传递方式类似,返回值(或异常)将被写入 ObjectOutputStream的子类并和参数传输的替代行为相同。
2.7 定位远程对象
我们专门提供了一种简单的引导名字服务器,用于存储对远程对象的已命名引用。使用类 java.rmi.Naming 的基于 URL 的方法可以存储远程对象引用。
客 户机要调用远程对象的方法,则必须首先得到该对象的引用。对远程对象的引用通常是在方法调用中以返回值的形式取得。RMI 系统提供一种简单的引导名字服务器,通过它得到给定主机上的远程对象。java.rmi.Naming 类提供基于统一资源定位符 (URL) 的方法,用来绑定、再绑定、解开和列出位于某一主机及端口上的名字-对象对。
本文來源于:<a href="http://blog.chaoskey.com/2008/01/14/74/ " rel="bookmark" title=" RMI规范–第二章 Java 分布式对象模型(转贴) | 夢想混沌"> RMI规范–第二章 Java 分布式对象模型(转贴) | 夢想混沌</a>
RMI规范–第三章 (转贴)
作者: 混沌
3.1 Stub 与 skeleton
在与远程对象的通信过程中,RMI 将使用标准机制(用于 RPC 系统):stub 与 skeleton。远程对象的 stub 担当远程对象的客户本地代表或代理人角色。
调用程序将调用本地 stub 的方法,而本地 stub 将负责执行对远程对象的方法调用。在 RMI 中,远程对象的 stub 与该远程对象所实现的远程接口集相同。调用 stub 的方法时,将执行下列操作:
初始化与包含远程对象的远程虚拟机的连接。
对远程虚拟机参数的进行编组(写入并传输)
等待方法调用结果
解编(读取)返回值或返回的异常
将值返给调用程序
为向调用程序展示比较简单的调用机制,stub 将参数的序列化和网络级通信隐 藏了起来。
在远程虚拟机中,每个远程对象都可以有相应的 skeleton(纯 JDK1.2 环境中不需要 skeleton)。skeleton 负责将调用分配给实际的远程对象实现。它在接收入进入方法调用时执行下列操作:
解编(读取)远程方法的参数
调用实际远程对象实现上的方法
将结果(返回值或异常)编组(写入并传输)给调用程序
由于推出于 JDK1.2 及附加的 stub 协议,使得在纯 JDK1.2 环境中无需使用 skeleton。相反,应使用通用代码代替 JDK1.1 中的 skeleton 履行其职责。
stub 和 skeleton 由 rmic 编译器生成。
3.2 远程方法调用中的线程使用
RMI 运行时分配给远程对象实现的方法可能在也可能不在独立的线程中执行。
RMI 运行时将无法担保远程对象与线程的映射关系。因为同一个远程对象的远程方法调用可能会同时执行,所以远程对象实现需确保其实现是线程安全的。
3.3 远程对象的垃圾收集
与 在本地系统中相同,在分布式系统中自动删除那些不再被任何客户机引用的远程对象是令人满意的。这可以将程序员从跟踪远程对象客户机以便适时终止的任务中解 脱出来。RMI 使用与 Modula-3 网络对象相似的引用计数的垃圾收集算法(参见 1994 年 5 月数字设备公司系统研究中心技术报告 115 中 Birrell、Nelson 和 Owicki 的“网络对象”)。
要实现引用计数垃圾收集,RMI 运行时需要跟踪每个 Java 虚拟机内的所有活动引用。当活动引用进入 Java 虚拟机时,其引用计数将加 1。首次引用某对象时会向该对象的服务器发送“referenced”消息。当发现活动引用在本地虚拟机中并未被引用时,该数将减 1。放弃最后的引用时,未被引用的消息将被发送到服务器。协议中存在很多微妙之处,其中大部分都与维护引用或未引用消息的次序有关,可确保对象不被过早地 收集。
当某远程对象不被任何客户机所引用时,RMI 运行时将对其进行弱引用。如果不存在该对象的其它本地引用,则弱引用将允许 Java 虚拟机的垃圾收集器放弃该对象。
通过保持对对象的常规引用或弱引用,分布式垃圾收集算法可与本地 Java 虚拟机的垃圾收集器以常规方式进行交互。
只 要存在对远程对象的本地引用,就不能将远程对象当作垃圾进行收集,而且该远程对象也可在远程调用中传送或返回客户机。传递远程对象也将同时把目标虚拟机的 标识符添加到被引用集中。需要未引用通知的远程对象必须实现java.rmi.server.Unreferenced 接口。当这些引用不再存在时,将调用 unreferenced 方法。当发现引用集为空时,也将调用 unreferenced。因此,unreferenced 方法可能会被多次调用。只有当没有本地和远程引用时,才可收集远程对象。
注意, 如果在客户机和远程服务器对象之间存在网络分区,则可能会过早地收集、远程对象(因为传输可能认为客户机已失效)。由于可能会出现过早收集的现象,因此远 程引用将不能保证引用的完整性。换句话说,远程引用实际上可能指向不存在的对象。使用此类引用时将抛出必须由应用程序处理的 RemoteException。
3.4 动态类加载
RMI 允许传入 RMI 调用中的参数、返回值和异常为任何可序列化对象。RMI 使用对象序列化机制将数据从一个虚拟机传输到另一个虚拟机,同时用相应的位置信息注释调用流,以便在接收端上加载类定义文件。
当 解编远程方法调用的参数和返回值以使之成为接收虚拟机中的有效对象时,流中所有类型的对象都需要类定义。解编进程将首先尝试通过本地类加载上下文(当前线 程的上下文类加载器)中的名称来解析类。RMI 也提供动态加载作为参数和返回值传送的实际对象类型的类定义的手段(其中远程方法调用的参数和返回值来自传送终点所指定的网络位置)。这包括远程 stub 类的动态下载 - 该类对应于特定远程对象实现类(用于包含远程引用)及 RMI 调用中通过值传送的任何其它类型,例如在解编端的类加载上下文中尚不可用的,声明参数类型的子类。
要支持动态类加载,RMI 运行时应使用用于编组、解编 RMI 参数和返回值的编组流的特定 java.io.ObjectOutputStream 和 java.io.ObjectInputStream子类。这些子类覆盖了 ObjectOutputStream 的 annotateClass 方法和ObjectInputStream 的 resolveClass 方法,以便就何处定位包含对应于流中类描述符的类定义的类文件交换信息。
对于每个写入 RMI 编组流的类描述符,annotateClass 方法将把类对象调用java.rmi.server.RMIClassLoader.getClassAnnotation 的结果添加到流中。
该结果可能为空,也可能是表示 codebase URL 路径(以空格分隔的 URL 列表)的 String 对象。利用该 codebase URL 路径,远程终点可下载所给类的定义文件。
对 于从 RMI 编组流中读取的每个类描述符,resolveClass 方法将从流中读取单个对象。如果该对象是 String(且 java.rmi.server.useCodebaseOnly 属性不是 true),则 resolveClass 将返回调用 RMIClassLoader.loadClass 的结果,并以所注解的 String 对象作为第一个参数,以类描述符中所需类名作为第二个参数。否则,resolveClass 将返回调用 RMIClassLoader.loadClass 的结果,并以所需的类名作为唯一参数。
有关 RMI 中类加载的详细信息,参见“RMIClassLoader 类”(5.6)一节。
3.5 通过代理服务器透过防火墙的 RMI
RMI 传输层通常试图将直接套接字在Internet的主机上打开。然而,许多Intranet的防火墙不允许这样做。因此,缺省 RMI 传输提供两种基于 HTTP 的机制,可使防火墙后的客户机调用驻留在防火墙外的远程对象方法。
3.5.1 如何将 RMI 调用包装在 HTTP 协议内
要透过防火墙,传输层可在防火墙信任的 HTTP 协议范围内嵌入 RMI 调用。将 RMI 调用数据作为 HTTP POST 请求的主体发送出去后,反馈信息将返回到 HTTP 响应主体内。传输层可通过以下两种方法构造 POST 请求:
1. 如果防火墙代理服务器可以把 HTTP 请求定向到主机的任意端口,HTTP 请求就会被直接转发到 RMI 服务器正在监听的端口上。目标计算机上的缺省 RMI 传输层可通过能识别并解码 POST 请求内的 RMI 调用的服务器套接字进行监听。
2. 如果防火墙代理服务器只能把 HTTP 请求定向到某个已知的 HTTP 端口, 该调用就会被转发到正在主机端口 80 上监听的 HTTP 服务器,而且将执行 CGI脚本以转发对同一计算机上目标 RMI 服务器端口的调用。
3.5.2 缺省套接字工厂
RMI 传输扩展 java.rmi.server.RMISocketFactory 类以提供作为客户机和服务器套接字源提供者的套接字工厂的缺省实现。该缺省套接字工厂可创建套接字以透明地提供防火墙通道机制,如下所示:
客户机套接字将自动尝试与无法用直接套接字联系的主机进行 HTTP 连接。
服务器套接字将自动检测新近接收的连接是否 HTTP POST 请求,如果是,则只将请求主体送给传输层,同时将其输出格式转化为 HTTP 响应。
工 厂的 java.rmi.server.RMISocketFactory.createSocket 方法将提供带有此缺省行为的客户机端套接字。工厂的 java.rmi.server.RMISocketFactory.createServerSocket 方法将提供带有此缺省行为的服务器端套接字。
3.5.3 配置客户机
无需特别配置即可使客户机透过防火墙发送 RMI 调用。
但如果将 java.rmi.server.disableHttp 属性的布尔值设置为“true”,客户机即可禁止将 RMI 调用包装为 HTTP 请求。
3.5.4 配置服务器
——————————————————————
注意 - 主机名不应为主机的 IP 地址,因为某些防火墙代理服务器不传送这种主机名。
————————————————————
1. 服务器主机域外的客户机要想调用服务器远程对象的方法,则必须找到该服务器。因此,服务器导出的远程引用必须包含服务器主机的全名。
本信息可否用于运行服务器的 Java 虚拟机,取决于服务器平台和网络环境。
如果不可用,则启动服务器时必须通过 java.rmi.server.hostname 属性指定主机的全名。
例如,在 chatsubo.javasoft.com 上可用以下命令启动 RMI 服务器类 ServerImpl:
java -Djava.rmi.server.hostname=chatsubo.javasoft.com ServerImpl
2. 如果服务器不支持防火墙后可传送到随意端口的 RMI 客户机,则可使用
如下配置:
a. HTTP 服务器在端口 80 上监听。
b. CGI 脚本的位置为别名 URL 路径
/cgi-bin/java-rmi.cgi
该脚本:
- 调用本地 Java 解释程序以执行可将请求传送到适当 RMI 服务器端口的传 输层内部类。
- 在 Java 虚拟机中,以与 CGI 1.0 环境变量相同的名称和值定义属性。
用于 Solaris 和 Windows 32 操作系统的 RMI 分布式版本中提供了示例脚本。注意,脚本必须指定服务器上 Java 解释程序的完整路径。
3.5.5 性能问题与局限
在不考虑代理服务器传送延迟的情况下,由 HTTP 请求传送调用至少要比通过直接套接字传送慢一个数量级。
因为透过防火墙只能在一个方向初始化 HTTP 请求,同时防火墙外的主机也无法回调客户机的方法调用,所以客户机无法将其自身的远程对象导到防火墙以外。
本文來源于:<a href="http://blog.chaoskey.com/2008/01/14/75/ " rel="bookmark" title=" RMI规范–第三章 (转贴) | 夢想混沌"> RMI规范–第三章 (转贴) | 夢想混沌</a>
RMI规范–第四章 客户机接口(转贴)
作者: 混沌
程序员在编写使用远程对象的 applet 或应用程序时,需要注意 java.rmi 包中可用的 RMI 系统客户机可视接口。
4.1 远程接口
package java.rmi;
public interface Remote {}
java.rmi.Remote 接口用来识别所有远程接口;所有远程对象必须直接或间接实现此接口。实现类可以实现任意数目的远程接口,并可扩展其它远程实现类。RMI 提供一些远程对象实现可以扩展的类,有助于远程对象的创建。这些类是 java.rmi.server.UnicastRemoteObject 和 java.rmi.activation.Activatable。有关如何定义远程接口的详细信息,参见“java.rmi.Remote 接口”(2.4.1)一节。
4.2 RemoteException 类
类 java.rmi.RemoteException 是许多在执行远程方法调用时可能发生的、与通信有关的异常的通用超类。远程接口中的每种方法(也是一个接口)必须在其 throws 子句中列出 RemoteException(或其超类,如java.io.IOException 或 java.lang.Exception)。
package java.rmi;
public class RemoteException extends java.io.IOException {
public Throwable detail;
public RemoteException();
public RemoteException(String s);
public RemoteException(String s, Throwable ex);
public String getMessage();
public void printStackTrace();
public void printStackTrace(java.io.PrintStream ps);
public void printStackTrace(java.io.PrintWriter pw);
}
RemoteException 可用详细消息(即 s)和一个嵌套异常(即 ex,Throwable)进行构造。嵌套异常 ex 在构造函数的第三种形式中被指定为参数,通常是 RMI调用过程中发生的基本 I/O 异常。getMessage 方法返回异常的详细消息,包括嵌套异常(如果有)中的消息。
printStackTrace 方法在类 java.lang.Throwable 中将被覆盖掉,以打印嵌套异常的堆栈跟踪。
4.3 Naming 类
java.rmi.Naming 类提供存储和获得对远程对象注册服务程序中远程对象进行引用的方法。Naming 类中的方法以如下形式的,URL 格式的 java.lang.String作为其中的一个参数:
//host:port/name
其 中 host 是注册服务程序所在的主机(远程或本地),port 是注册服务程序接收调用的端口号,name 是注 册表未作出解释的简单字符串。 host 和 port是可选的。如果省略了 host,则主机缺省值为本地 主机。如果省略了 port,则端口缺省值为 1099,即 RMI 系统注册服务程序 rmiregistry 所用的“众所周知”的端口。
为远程对象绑定名称即为稍后使用的远程对象关联或注册名称,可用于查询该远程对象。可以使用 Naming 类的 bind 或 rebind 方法将远程对象与名称相关联。
当远程对象已用 RMI 注册服务程序在本地主机上进行过注册(绑定)后,远程(或本地)主机上的调用程序就可以按名称查询远程对象、获得其引用,然后在对象上调用远程方法。必要时,某一主机上运行的服务器可以共享一个注册服务程序。
服务器的各个进程也可创建和使用自己的注册服务程序(详细信息,参见 java.rmi.registry.LocateRegistry.createRegistry 方法)。
package java.rmi;
public final class Naming {
public static Remote lookup(String url)
throws NotBoundException, java.net.MalformedURLException, RemoteException;
public static void bind(String url, Remote obj) throws AlreadyBoundException,
java.net.MalformedURLException, RemoteException;
public static void rebind(String url, Remote obj)
throws RemoteException, java.net.MalformedURLException
;
public static void unbind(String url) throws RemoteException, NotBoundException,
java.net.MalformedURLException;
public static String[] list(String url)
throws RemoteException, java.net.MalformedURLException
;
}
lookup 方法返回与名称的文件部分相关联的远程对象。如果名称未绑定到对象上,则抛出NotBoundException。
bind 方法将把指定名称绑定到远程对象上。如果该名称已绑定到某一对象上,则抛出 AlreadyBoundException。
rebind 方法总将名称绑定到对象上,无论该名称是否已绑定。原有绑定将丢失。
unbind 方法将取消名称和远程对象间的绑定。如果没有绑定,则抛出 NotBoundException。
list 方法返回一个 String 对象的数组,该对象包含注册服务程序中绑定 URL 的快照。
为了向注册服务程序查询其内容列表,只需要 URL 上的主机名和端口信息;因此,URL 的“file”部分将被忽略。
——————————————————————————–
注 意 - 这些方法也可能抛出 java.rmi.AccessException。AccessException 表示调用程序无执行特定操作的权限。例如,只有运行注册服务程序的主机上的本地客户机才允许执行 bind、rebind 和 unbind 操作。但任何非本地客户机都可调用 lookup 操作。
本文來源于:<a href="http://blog.chaoskey.com/2008/01/14/76/ " rel="bookmark" title=" RMI规范–第四章 客户机接口(转贴) | 夢想混沌"> RMI规范–第四章 客户机接口(转贴) | 夢想混沌</a>
RMI规范–第五章 服务器接口(转贴)
作者: 混沌
java.rmi.server 包包含通常用于实现远程对象的接口与类。
.1 RemoteObject 类
类 java.rmi.server.RemoteObject 将 java.lang.Object 行为实现于远程对象。实现方法 hashCode 和 equals 将允许将远程对象引用存储在散列表中进行比较。如果两个 Remote 对象引用同一个远程对象,则方法 equals 的返回值为 true。它负责比较远程对象的远程对象引用。
方法 toString 返回一个说明远程对象的字符串。该字符串的内容和语法与实现有关且可变。
java.lang.Object 中的其它方法保留了它们的原始实现。
package java.rmi.server;
public abstract class RemoteObject implements java.rmi.Remote, java.io.Serializable {
protected transient RemoteRef ref;
protected RemoteObject();
protected RemoteObject(RemoteRef ref);
public RemoteRef getRef();
public static Remote toStub(java.rmi.Remote obj) throws java.rmi.NoSuchObjectException;
public int hashCode();
public boolean equals(Object obj);
public String toString();
}
因 为 RemoteObject 是抽象类,所以无法实例化。因此,RemoteObject 的构造函数必须从子类实现中调用。第一个 RemoteObject 构造函数将创建带空的远程引用的 RemoteObject。第二个 RemoteObject 构造函数将创建带给定远程引用 ref 的 RemoteObject。
方法 getRef 返回该远程对象的远程引用。
方法 toStub 返回一个远程对象 obj 的 stub 并作为参数传送。该操作仅在已经导出远程对象实现后才有效。如果找不到远程对象的 stub,该方法就抛出 NoSuchObjectException。
5.1.1 RemoteObject 类覆盖的对象方法
java.lang.Object 类中用于方法 equals、hashCode 和 toString 的缺省实现不适用于远程对象。因此,RemoteObject 类提供了这些方法在语义上更合适于远程对象的实现。
equals 和 hashCode 方法
为将远程对象用作散列表中的主键,我们必须在远程对象实现中覆盖 equals 和 hashCode 方法,这些方法是由类 java.rmi.server.RemoteObject 覆盖的:
java.rmi.server.RemoteObject 类实现 equals 方法决定了两个对象的引用是否相等,而不是两个对象的内容是否相等。这是因为决定内容是否相等时需要远程方法调用,而 equals 的签名不允许抛出远程异常。
对于所有引用同一底层远程对象的远程引用,java.rmi.server.RemoteObject 类实现的 hashCode 方法返回同一个值(因为对相同对象的引用被认为是相等的)。
toString 方法
toString 方法被定义为返回表示对象的远程引用的字符串。字符串的内容视引用的类型而定。单体(单路传送)对象的当前实现一个对象标识符以及与传输层有关的该对象的其他信息(例如主机名和端口号)。
clone 方法
只有在对象支持 java.lang.Cloneable 接口时才能用 Java 语言的缺省机制来复制。由 rmic 编译器生成的远程对象的 stub 将被声明为终态,且不实现Cloneable 接口,因此无法复制 stub。
5.1.2 序列化形式
RemoteObject 类实现专门的(私用)方法 writeObject 和方法 readObject,它们由对象序列化机制调用来处理向 java.io.ObjectOutputStream 中序列化数据。RemoteObject 的序列化形式由下列方法写入:
private void writeObject(java.io.ObjectOutputStream out)
throws java.io.IOException, java.lang.ClassNotFoundException;
如果 RemoteObject 的远程引用域 ref 为空,则该方法抛出 java.rmi.MarshalException。
如果远程引用 ref 为非空:
ref 的类通过调用其 getRefClass 方法来获得,该方法通常返回远程引用类的非打包全名。
如果返回的类名为非空:
ref 的类名将以 UTF 格式写到流 out 中。调用 ref 的方法 writeExternal,传递的参数为流 out,从而使 ref 可以将其外部表示法写到流中。
如果 ref.getRefClass 返回的类名为空:
则将一个 UTF 格式的空字符串写到流 out 中。ref 被序列化到流 out(即利用 writeObject)。
序列化恢复时,RemoteObject 的状态将由 ObjectInputStream 调用该方法利用其序列化形式进行重构:
private void readObject(java.io.ObjectInputStream in)
throws java.io.IOException, java.lang.ClassNotFoundException;
首先,ref 的类名(UTF 字符串)将从流 in 中读出。如果类名为空字符串:
则从流中读出对象,然后将 ref 初始化为该对象(即通过调用 in.readObject)如果类名为非空:
则 ref 的完整类名由字符串 java.rmi.server.RemoteRef.packagePrefix 的值和“.”加上从流中读取的类名相连接而成。
创建 ref 类的实例(利用上述完整类名)。
该新实例(成为 ref 域)从流 in 中读取其外部形式。
5.2. RemoteServer 类
java.rmi.server.RemoteServer 类是服务器实现类 java.rmi.server.UnicastR
emoteObject
和 java.rmi.activation.Activatable 的通用超类。
package java.rmi.server;
public abstract class RemoteServer extends RemoteObject {
protected RemoteServer();
protected RemoteServer(RemoteRef ref);
public static String getClientHost() throws ServerNotActiveException;
public static void setLog(java.io.OutputStream out);
public static java.io.PrintStream getLog();
}
因 为 RemoteServer 是抽象类,所以将无法实例化。因此,必须从子类实现中调用某一 RemoteServer 的构造函数。第一个 RemoteServer 构造函数将创建带空远程引用的 RemoteServer。第二个 RemoteServer 构造函数将创建带给定远程引用 ref 的 RemoteServer。
getClientHost 方法允许一个活动方法确定当前线程中活动的远程方法是由哪台主机初始化的。如果当前线程中没有活动的远程方法,则抛出异常 ServerNotActiveException。
setLog 方法将 RMI 调用记录到指定输出流中。如果输出流为空,则关闭调用日志。getLog 方法返回 RMI 调用日志流,从而使特定于应用程序的信息以同步方式写到调用日志中。
5.3 UnicastRemoteObject 类
类 java.rmi.server.UnicastRemoteObject 支持创建并导出远程对象。该类实现的远程服务器对象具有下列特征:
对这种对象的引用至多仅在创建该远程对象的进程生命期内有效。
通过 TCP 传输与远程对象通信。
调用、参数和结果使用流协议在客户机和服务器之间进行通信。
package java.rmi.server;
public class UnicastRemoteObject extends RemoteServer {
protected UnicastRemoteObject() throws java.rmi.RemoteException;
protected UnicastRemoteObject(int port) throws java.rmi.RemoteException;
protected UnicastRemoteObject(int port, RMIClientSocketFactory csf,
RMIServerSocketFactory ssf) throws java.rmi.RemoteException;
public Object clone()throws java.lang.CloneNotSupportedException;
public static RemoteStub exportObject(java.rmi.Remote obj)
throws java.rmi.RemoteException;
public static Remote exportObject(java.rmi.Remote obj, int port)
throws java.rmi.RemoteException;
public static Remote exportObject(Remote obj, int port, RMIClientSocketFactory csf,
RMIServerSocketFactory ssf) throws java.rmi.RemoteException;
public static boolean unexportObject(java.rmi.Remote obj, boolean force)
throws java.rmi.NoSuchObjectException;
}
5.3.1 构造新远程对象
远 程对象实现(实现一个或多个远程接口的实现)必须被创建和导出。导出远程对象使得对象能接受来自客户机的到来的调用。作为 UnicastRemoteObject 导出的远程对象,其导出涉及在 TCP 端口监听(注意,多个远程对象可以接受同一端口的到来的调用,因此没必要在新的端口上监听)。远程对象实现可以扩展类 UnicastRemoteObject 以使用其导出对象的构造函数,或者扩展其它类或者根本不扩展)并通过 UnicastRemoteObject 的 exportObject 方法导出对象。
无参数的构造函数将创建远程对象并在匿名(或任意)端口 上导出,而这将在运行时进行选择。第二种形式的构造函数带单个参数(即 port),它指定远程对象接受到来的调用的端口号。第三种构造函数创建的远程对象在指定端口上通过 RMIServerSocketFactory 创建的 ServerSocket 接受到来的调用;客户机通过由 RMIClientSocketFactory 提供的 Socket 与远程对象建立连接。
5.3.2 导出并非由 RemoteObject 扩展而来的实现
exportObject 方法(任何形式)可用于导出不是由扩展 UnicastRemoteObject类实现的简单对等远程对象。第一种形式的 exportObject 方法带单个参数(即 obj),它是接受到来的 RMI 调用的远程对象;该 exportObject 方法在匿名(或任意)端口上导出远程对象,而这将在运行时进行选择。第二种形式的 exportObject 方法带两个参数,分别是远程对象 obj 和 port。 port 是远程对象接受到来的调用的端口号。第三种 exportObject 方法用指定的RMIClientSocketFactory、csf 和 RMIServerSocketFactory、ssf 在指定port 上导出对象 obj。
在作为参数或返回值传入 RMI 调用前,必须导出对象,否则当试图把“未导出的”对象作为参数或返回值传递给一个远程调用时,将会抛出java.rmi.server.StubNotFoundException。
导出后,对象既可作为参数传入 RMI 调用,也可作为 RMI 调用的结果返回。
exportObject 方法返回 Remote stub。它是远程对象的 stub 对象 obj,它将替代远程对象被传入 RMI 调用。
5.3.3 在 RMI 调用中传递 UnicastRemoteObject
如 上所述,当类型为 UnicastRemoteObject 的对象作为参数或返回值传入RMI 调用中时,该对象将由远程对象的 stub 所代替。远程对象实现保留在创建它的虚拟机中,且不会移出(包括其值)。换言之,远程对象通过引用传入RMI 调用;远程对象实现不能通过值进行传递。
5.3.4 序列化 UnicastRemoteObject
如 果 UnicastRemoteObject 类型的对象写入用户定义的 ObjectOutputStream例如,该对象写入使用序列化的文件),则其中所含的信息将是瞬态的且未予保存。但如果对象是用户定义的 UnicastRemoteObject 子类实例,它就能拥有非瞬态数据并可在序列化对象时予以保存。
当 UnicastRemoteObject 从 ObjectInputStream 读出时,它将自动导出到RMI 运行时,以便接收 RMI 调用。如果由于某种原因而导致导出失败,则序列化恢复对象过程将予以终止,同时抛出异常。
5.3.5 逆导出 UnicastRemoteObject
unexportObject 方法使远程对象 obj 无法接受到来的调用。如果强制参数为真,则即使有对远程对象的待定调用或当前调用,该远程对象仍将被强制逆导出。
如 果强制参数为假,则仅在无对该对象的待定调用和当前调用时才逆导出该对象。如果对象被成功地逆导出,则运行时将把该对象从内部表中删除。以这种强制方式逆 导出对象可能导致客户机持有该远程对象的过期远程引用。如果远程对象先前并未导出到 RMI 运行时中,则该方法将抛出异常 java.rmi.NoSuchObjectException。
5.3.6 clone 方法
只有支持 java.lang.Cloneable 接口的对象才可使用 Java 语言的缺省机制复制。类 java.rmi.server.UnicastRemoteObject 并不实现该接口,但它实现 clone 方法以便当子类需要实现 Cloneable 时远程对象可以正确地进行复制。clone 方法可由子类用于创建一个初始内容相同的复制的远程对象。但是可以被导出接受远程调用且与原对象有所不同。
5.4 Unreferenced 接口
package java.rmi.server;
public interface Unreferenced {
public void unreferenced();
}
java.rmi.server.Unreferenced 接口允许服务器对象通知,告诉它没有客户机对它进行远程引用。分布式垃圾收集机制将为每个远程对象维护一个持有该远程对象引用的客户虚拟机集合。只要某个 客户机持有该远程对象的远程引用,RMI 运行时就会保存该远程对象的本地引用。当“引用” 集合为空时,即调用Unreferenced.unreferenced 方法(如果服务器实现 Unreferenced 接口)。远程对象不需要支持 Unreferenced 接口。
只要存在远程对象的某个本地引用,它就可以在远程调用中传递或返给客户机。接收 引用的进程将被添加到远程对象的引用集合中。当引用集合为空时,即调用远程对象的 unreferenced 方法。这样,Unreferenced 方法可以进行多次调用(每当集合为空时)。当不再有引用(本地引用或客户机持有的引用)时,才会收集远程对象。
5.5 RMISecurityManager 类
package java.rmi;
public class RMISecurityManager extends java.lang.SecurityManager {
public RMISecurityManager();
public synchronized void checkPackageAccess(String pkg)
throws RMISecurityException;
}
RMISecurityManager 提供与 java.lang.SecurityManager 相同的安全特性,但它覆盖 checkPackageAcess 方法。在 RMI 应用程序中,如果没有设置安全管理器,则只能从本地类路径加载stub 和类。这可确保应用程序不受由远程方法调用所下载的代码的侵害。
5.6 RMIClassLoader 类
java.rmi.server.RMIClassLoader 类提供一套公共静态方法,用于支持 RMI中基于网络的类加载。这些方法由 RMI 的内部编组流调用,用于实现 RMI 参数和返回值类型的动态类加载。但为了模拟 RMI 的类加载行为,也可由应用程序直接对其进行调用。 RMIClassLoader 类没有可以公共访问的构造函数,因此无法实例化。
package java.rmi.server;
public class RMIClassLoader {
public static String getClassAnnotation(Class cl);
public static Object getSecurityContext(ClassLoader loader);
public static Class loadClass(String name)
throws java.net.MalformedURLException, ClassNotFoundException;
public static Class loadClass(String codebase, String name)
throws java.net.MalformedURLException, ClassNotFoundException;
public static Class loadClass(URL codebase, String name)
throws java.net.MalformedURLException, ClassNotFoundException;
}
getClassAnnotation 方法将返回一个 String,该 String 代表网络codebase 路径,远程端点通过此路径下载指定类的定义。RMI 运行时在内部编组流中将使用由该方法返回的 String 对象作为类描述符的注解。该 codebase 字符串的格式是由空格界定的 codebase URL 字符串路径。
返回的 codebase 字符串将依赖于所提供类的类加载器:
如果类加载器是下列之一:
“ 系统类加载器”(用于加载应用程序“类路径”中指定的类并从方法ClassLoader.getSystemClassLoader 返回的类加载器),“系统类加载器”的父类,例如用于已安装方式扩展的类加载器,空值(用于加载虚拟机类的“自举类加载器”),<则返回 java.rmi.server.codebase 属性的值。如果该属性未设置则返回值为null。
否则,如果类加载器是类 java.net.URLClassLoader 的实例,则返回的codebase 字符串是一个以空格间隔的外部形式的 URL 列表,它由调用类加载器上的 getURLs 方法返回。 如果 URLClassLoader 由 RMI 运行时创建用来服务于一个 RMIClassLoader.loadClass 方法的调用,则无需任何许可就可获得相关的 codebase 字符串。 如果它是一个任意的 URLClassLoader 实例,则调用程序必须拥有权限去连接 codebase 路径中所有的 URL,在每个由getURLs 方法返回的 URL 实例上调用 openConnection().getPermission()来决定权限。
最后,如果类加载器不是 URLClassLoader 的实例,则 java.rmi.server.codebase
属 性值被返回,如果属性值未设置,则返回 null。因为 getSecurityContext 方法不再适用于 JDK1.2 安全模型,所以不鼓励使用它;它用于 JDK1.1 内部,用来实现基于类加载器的安全检查。 如果指定的类加载器是由 RMI 运行时创建用来服务于一个 RMIClassLoader.loadClass方法的调用,则返回类加载器 codebase 路径中第一个 URL;否则返回 null。
这三种 loadClass 方法都试图通过使用当前线程的上下文类加载器,利用指定的名称加载类并且在设有安全管理器时为特定 codebase 路径加载内部的URLClassLoader:
只 带一个参数(类 name)的 loadClass 方法隐式使用java.rmi.server.codebase 属性值作为 codebase 路径。我们不鼓励使用该版的 loadClass 方法,因为我们已不再鼓励使用 java.rmi.server.codebase属性。用户应使用下列更通用的版本。
带有 String codebase 参数的 loadClass 方法将它用作 codebase 路径;
codebase 字符串必须是以空格间隔的其形式和 getClassAnnotation 方法返回的相同 URL 列表。
带有 java.net.URL codebase 参数的 loadClass 方法将单个 URL 用作codebase。
对于所有 loadClass 方法,codebase 路径将与当前线程的上下文类加载器
(通 过在当前线程上调用 getContextClassLoader 来确定)一起使用,以确定试图用来加载类的内部类加载器实例。RMI 运行时将维持一个内部类加载器实例表,以父类加载器和加载器的 codebase 路径(一个有序 URL 列表)作为键值。loadClass 方法以所需的 codebase 路径和当前线程的上下文类加载器为其父类,在表中查询 URLClassLoader 实例。如果不存在该加载器,就会创建一个并添加到表中。最后,将在所选类加载器上用指定的类 name 调用loadClass 方法。
如果设有 安全管理器(System.getSecurityManager 不返回 null),则loadClass 的调用程序必须拥有能连到 codebase 路径中所有 URL 的权限。否则将抛出异常 ClassNotFoundException。为防止不受信任的代码被加载到没有安全管理器的 Java 虚拟机中,在未设置安全管理器的情况下,所有loadClass 方法都应忽略特定的 codebase 路径,而只加载当前线程上下文类加载器中指定 name 的类。
5.7 LoaderHandler 接口
package java.rmi.server;
public interface LoaderHandler {
Class loadClass(String name) throws MalformedURLException, ClassNotFoundException;
Class loadClass(URL codebase,String name)
throws MalformedURLException, ClassNotFoundException;
Object getSecurityContext(ClassLoader loader);
}
————————————————————–
注意 - JDK1.2 中不鼓励使用 LoaderHandler 接口。
————————————————————–
LoaderHandler 接口仅由 JDK1.1 的内部 RMI 实现使用。
5.8 RMI 套接字工厂
当 RMI 运行时实现需要 java.net.Socket 和 java.net.ServerSocket 的实例以用于连接时,它并非直接实例化这些类的对象,而是在当前 RMISocketFactory 对象(该对象由静态方法 RMISocketFactory.getSocketFactory 返回)上调用 createSocket 和 createServerSocket 方法。这将使应用程序可以用钩子来自定义 RMI 传输所用的套接字类型,例如 java.net.Socket 和
java.net.ServerSocket 类的子类。所用的 RMISocketFactory 实例可由可信任的系统代码设置一次。在 JDK 1.1 中,这种自定义被限制为关于套接字类型的相对全局的决策,因为提供给工厂方法的参数只有主机和端口(对于createSocket)及端口号(对于 createServerSocket)。
在 JDK 1.2 中,我们引入了新的接口 RMIServerSocketFactory 和 RMIClientSocketFactory,可更加灵活地自定义与远程对象通讯所用的协议。
为 使使用 RMI 的应用程序能利用这些新的套接字工厂接口,我们在UnicastRemoteObject 和 java.rmi.activation.Activatable 中添加了几个新构造函数和 exportObject 方法,它们使用客户机和服务器套接字工厂做为附加参数。
由新构造函数或 exportObject 方法(以 RMIClientSocketFactory 和 RMIServerSocketFactory 为参数)导出的远程对象将被 RMI 运行时区别对待。在这种远程对象的生命期内,运行时将用自定义 RMIServerSocketFactory 来创建 ServerSocket 以接受对远程对象的到来的调用,同时用自定义 RMIClientSocketFactory 来创建 Socket 以连接客户机和远程对象。
由 自定义套接字工厂导出的远程对象 stub 和 skeleton 所用的 RemoteRef和 ServerRef 实现分别是 UnicastRef2 和 UnicastServerRef2。UnicastRef2 类型的线表示法包含与 UnicastRef 类型不同的联系“端点”的表示法(仅用一个 UTF 格式的主机名字符串,后跟一个整型端口号表示)。对于UnicastRef2,该端点的线表示则包括一个格式字节,用来指定端点表示的其余
内容 (允许将来扩充),后跟的是指定格式的数据。当前,这些数据可包含UTF 格式的主机名、端口号及可选的(由端点格式字节指定) RMIClientSocketFactory 对象序列化表示。它可被客户机用于在该端点生成到远程对象的套接字连接。端点表示不包括在远程对象导出时指定的 RMIServerSocketFactory 对象。
当通过 UnicastRef2 类型的引用进行调用时,运行时将在创建远程对象的套接字连接时使用端点中 RMIClientSocketFactory 对象的 createSocket 方法。同样,当运行时为了特定远程对象进行 DGC "dirty" 和 "clean" 调用时,它必须在远程虚拟机上调用 DGC,方法是使用远程引用中指定的同一 RMIClientSocketFactory 对象所生成的连接。服务器端的 DGC 实现应负责验证结果是否正确。
如果远程对象是由老的构造函数或 UnicastRemoteObject 中没有将自定义套接字工厂作为参数的方法导出,则和以前一样拥有 UnicastRef 和UnicastServerRef 类型的 RemoteRef 和 ServerRef,并且其端点也将使用老式线表示,即一个 UTF 格式的主机字符串跟一个指定端口号的整数。这样那些不使用 JDK 1.2 新特性的 RMI 服务器可以与老式 RMI 客户机进行互操作。
5.8.1 RMISocketFactory 类
java.rmi.server.RMISocketFactory 抽象类提供一个接口来指定传输中如何获得套接字。注意,下面的类使用 java.net 包中的 Socket 和 ServerSocket。
package java.rmi.server;
public abstract class RMISocketFactory
implements RMIClientSocketFactory, RMIServerSocketFactory {
public abstract Socket createSocket(String host, int port)throws IOException;
public abstract ServerSocket createServerSocket(int port) throws IOException;
public static void setSocketFactory(RMISocketFactory fac) throws IOException;
public static RMISocketFactory getSocketFactory();
public static void setFailureHandler(RMIFailureHandler fh);
public static RMIFailureHandler getFailureHandler();
}
静 态方法 setSocketFactory 可用于设置套接字工厂,而 RMI 将从中获得套接字。应用程序用自己的实例 RMISocketFactory 仅能调用该方法一次。例如,应用程序定义的 RMISocketFactory 实现在所要求的连接上做一些基本的过滤并抛出异常,或者返回其对 java.net.Socket 或 java.net.ServerSocket 类的扩展(例如提供安全信道的扩展)。注意,只有在当前安全管理器允许设置套接字工厂时才可设置 RMISocketFactory。如果不允许进行该项设置,则将抛出 SecurityException。
静态方法 getSocketFactory 返回由 RMI 使用的套接字工厂。如果未设置套接字工厂,则返回值为 null。当传输需要创建套接字时,传输层将在 getSocketFactory 方法返回的 RMISocketFactory 上调用 createSocket 和 createServerSocket 方法。
例 如:RMISocketFactory.getSocketFactory().createSocket(myhost, myport) 方法 createSocket 应创建一个连接到指定 host 和 port 的客户机套接字。 createServerSocket 方法应在指定 port 上创建服务器套接字。缺省的 RMISocketFactory 传输实现使用 HTTP 通过防火墙提供透明的 RMI,如下所述:
在 createSocket 中,工厂将自动尝试与无法用套接字直接联系的主机建立 HTTP 连接。
在 createServerSocket 中,工厂将返回用于自动检测新接受的连接是否为 HTTP POST 请求的服务器套接字。如果是,则返回仅将请求主体透明地展示给传输然后将其输出格式化为 HTTP 响应的套接字。
方法 setFailureHandler 设置失败句柄。在创建服务器套接字失败时,该句柄将由 RMI 运行时调用。该句柄返回一个布尔值,用于指示是否应重试。缺省的失败句柄返回值为 false,意味着缺省情况下运行时将不再尝试创建套接字。
方法 getFailureHandler 在套接字创建失败时返回当前句柄。失败句柄未设置时将为 null。
5.8.2 RMIServerSocketFactory 接口
为 了支持与远程对象的自定义通信,可以在导出远程对象时为其指定一个 RMIServerSocketFactory 实例。这一点既可通过相应的 UnicastRemoteObject 构造函数或 exportObject 方法完成,也可通过相应的 java.rmi.activation.Activatable 构造函数或 exportObject 方法完成。如果该服务器套接字工厂在导出远程对象时与之关联,则 RMI 运行时将使用远程对象的服务器套接字工厂来创建 ServerSocket(使用 RMIServerSocketFactory.createServerSocket 方法),以接受远程客户机的连接。
package java.rmi.server;
public interface RMIServerSocketFactory {
public java.net.ServerSocket createServerSocket(int port) throws IOException;
}
5.8.3 RMIClientSocketFactory 接口
要 自定义与远程对象的通信,可在导出时远程对象为其指定一个 RMIClientSocketFactory 的实例。这一点既可通过相应的 UnicastRemoteObject 构造函数或exportObject 方法完成,也可通过相应的 java.rmi.activation.Activatable 构造函数或 exportObject 方法完成。如果该客户机套接字工厂在导出远程对象时与之相关联,则客户机套接字工厂将同远程对象的引用一起下载到远程虚拟机。随后,RMI 运行时将使用 RMIClientSocketFactory.createSocket 方法来建立
从客户机到远程对象的连接。
package java.rmi.server;
public interface RMIClientSocketFactory {
public java.net.Socket createSocket(String host, int port) throws IOException;
}
5.9 RMIFailureHandler 接口
java.rmi.server.RMIFailureHandler 接口提供一种方法指明服务器套接字创建失败时指定 RMI 运行时如何响应(除非对象正在导出)。
package java.rmi.server;
public interface RMIFailureHandler {
public boolean failure(Exception ex);
}
当出现了防止 RMI 运行时创建 java.net.Socket 的异常时将调用 failure 方法。如果运行时试图重试,则该方法返回值为 true;否则将返回 false。
调用该方法前,需要通过调用 RMISocketFactory.setFailureHandler 来注册失败句柄。如
这篇关于RMI规范–第一章 (转贴)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!