- 浏览: 103496 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
chifanbua:
我基本上在iteye上很少留言的 不过你的这个写的确实不错 用 ...
Oracle decode函数说明 -
shaoxuexue86:
谢谢啊
RMI 开发步骤 -
huxiaojun_198213:
RMIRegistHelper只是负责注册远程对象,与客户端所 ...
RMI 开发步骤 -
shaoxuexue86:
请问客户端程序ClientTest 与 RMIRegistHe ...
RMI 开发步骤 -
huxiaojun_198213:
本章节可能有一些细节方面翻译得不是很好,有出入的地方,还请各 ...
RMI动态类加载
服务器接口
java.rmi.server 包包含通常用于实现远程对象的接口与类。
主题:
RemoteObject 类
RemoteServer 类
UnicastRemoteObject 类
Unreferenced 接口
RMISecurityManager 类
RMIClassLoader 类
LoaderHandler 接口
RMI 套接字工厂
RMIFailureHandler 接口
LogStream 类
stub 和 skeleton 编译器
5.1 RemoteObject 类
类 java.rmi.server.RemoteObject 将 java.lang.Object 行为实现于远程对象。实现方法 hashCode 和 equals 将允许将远程对象引用存储在散列表中进行比较。
如果两个 Remote 对象引用同一个远程对象,则方法 equals 的返回值为 true。它负责比较远程对象的远程对象引用。
方法 toString 返回一个说明远程对象的字符串。该字符串的内容和语法与实现有关且可变。
java.lang.Object 中的其它方法保留了它们的原始实现。
因为 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 中读取其外部形式。
2 RemoteServer 类
java.rmi.server.RemoteServer 类是服务器实现类java.rmi.server.UnicastRemoteObject和 java.rmi.activation.Activatable 的通用超类。
因为 RemoteServer 是抽象类,所以将无法实例化。因此,必须从子类实现中调用某一 RemoteServer 的构造函数。
第一个 RemoteServer 构造函数将创建带空远程引用的 RemoteServer。
第二个 RemoteServer 构造函数将创建带给定远程引用 ref 的 RemoteServer。
getClientHost 方法允许一个活动方法确定当前线程中活动的远程方法是由哪台主机初始化的。如果当前线程中没有活动的远程方法,则抛出异常 ServerNotActiveException。
setLog 方法将 RMI 调用记录到指定输出流中。如果输出流为空,则关闭调用日志。getLog 方法返回 RMI 调用日志流,从而使特定于应用程序的信息以同步方式写到调用日志中。
5.3 UnicastRemoteObject 类
类 java.rmi.server.UnicastRemoteObject 支持创建并导出远程对象。该类实现的远程服务器对象具有下列特征:
对这种对象的引用至多仅在创建该远程对象的进程生命期内有效。
通过 TCP 传输与远程对象通信。
调用、参数和结果使用流协议在客户机和服务器之间进行通信。
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 接口
java.rmi.server.Unreferenced 接口允许服务器对象通知,告诉它没有客户机对它进行远程引用。
分布式垃圾收集机制将为每个远程对象维护一个持有该远程对象引用的客户虚拟机集合。
只要某个客户机持有该远程对象的远程引用,RMI 运行时就会保存该远程对象的本地引用。
当“引用”集合为空时,即调用Unreferenced.unreferenced 方法(如果服务器实现 Unreferenced 接口)。远程对象不需要支持 Unreferenced 接口。
只要存在远程对象的某个本地引用,它就可以在远程调用中传递或返给客户机。接收引用的进程将被添加到远程对象的引用集合中。
当引用集合为空时,即调用远程对象的 unreferenced 方法。这样,Unreferenced 方法可以进行多次调用(每当集合为空时)。当不再有引用(本地引用或客户机持有的引用)时,才会收集远程对象。
5.5 RMISecurityManager 类
RMISecurityManager 提供与 java.lang.SecurityManager 相同的安全特性,但它覆盖 checkPackageAcess 方法。
在 RMI 应用程序中,如果没有设置安全管理器,则只能从本地类路径加载stub 和类。这可确保应用程序不受由远程方法调用所下载的代码的侵害。
5.6 RMIClassLoader 类
java.rmi.server.RMIClassLoader 类提供一套公共静态方法,用于支持 RMI中基于网络的类加载。
这些方法由 RMI 的内部编组流调用,用于实现 RMI 参数和返回值类型的动态类加载。
但为了模拟 RMI 的类加载行为,也可由应用程序直接对其进行调用。RMIClassLoader 类没有可以公共访问的构造函数,因此无法实例化。
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 接口
--------------------------------------------------------------
注意 - 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。
静态方法 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 方法),以接受远程客户机的连接。
5.8.3 RMIClientSocketFactory 接口
要自定义与远程对象的通信,可在导出时远程对象为其指定一个 RMIClientSocketFactory 的实例。
这一点既可通过相应的 UnicastRemoteObject 构造函数或exportObject 方法完成,也可通过相应的 java.rmi.activation.Activatable的构造函数或 exportObject 方法完成。
如果该客户机套接字工厂在导出远程对象时与之相关联,则客户机套接字工厂将同远程对象的引用一起下载到远程虚拟机。
随后,RMI 运行时将使用 RMIClientSocketFactory.createSocket 方法来建立从客户机到远程对象的连接。
5.RMIFailureHandler 接口
java.rmi.server.RMIFailureHandler 接口提供一种方法指明服务器套接字创建失败时指定 RMI 运行时如何响应(除非对象正在导出)。
当出现了防止 RMI 运行时创建 java.net.Socket 的异常时将调用 failure 方法。
如果运行时试图重试,则该方法返回值为 true;否则将返回 false。
调用该方法前,需要通过调用 RMISocketFactory.setFailureHandler 来注册失败句柄。
如果该句柄未设置,则 RMI 运行时等待片刻后尝试再创建 ServerSocket。
注意,当 ServerSocket 首次导出对象时如果创建 ServerSocket 失败,就不会调用 RMIFailureHandler。
而当 ServerSocket 接受请求失败后再创建该 ServerSocket 时,即调用 RMIFailureHandler。
5.10 LogStream 类
类 LogStream 提供一种记录错误的机制。对系统进行监控的人可能会对这些错误感兴趣。该类在内部用于日志服务器调用。
----------------------------------------------------------------------
----------
注意 -JDK1.2 中不鼓励使用 LogStream 类
----------------------------------------------------------------------
----------
方法 log 返回由给定名称标识的 LogStream。如果某名称所对应的日志不存在,即创建一个使用缺省流的日志。
方法 getDefaultStream 返回用于新日志的当前缺省流。
方法 setDefaultStream 为新日志设置缺省流。
方法 getOutputStream 返回当前日志输出到的流。
方法 setOutputStream 设置日志的输出流。
方法 write 的第一种形式将向该流写一个字节数据。如果不是新起一行,则将把
该字节添加到内部缓冲区。如果是新起一行,则当前缓冲区中的行将按相应的日志前缀发送到日志的输出流中。方法 write 的第二种形式将写字节子数组。
方法 toString 以字符串形式返回日志名。
方法 parseLevel 将日志等级的字符串名转换为内部整型表示。
5.11 stub 和 skeleton 编译器
rmic 的 stub 和 skeleton 编译器用于为特定的远程对象实现编译相应的 stub和 skeleton。该编译器将由远程对象类的类全名调用。
该类必须在先前已成功编译过。
导入类的位置由环境变量 CLASSPATH 或参数 -classpath 指定。
除非指定参数 -d,否则编译好的类文件将放在当前目录下。
参数 -keepgenerated (或 -keep)为 stub 和 skeleton 保留生成的 java 源文件。
也可指定 stub 协议的版本:
- -v1.1 创建符合 JDK 1.1 stub 协议版本的 stub/skeleton
- -vcompat(JDK 1.2 中为缺省值)创建同时兼容 JDK 1.1 和 1.2 stub 协议版本的 stub/skeleton
- -v1.2 创建仅符合 JDK 1.2 stub 协议版本的 stub(注意,JDK 1.2 stub协议中并不需要 skeleton)
-show 选项为程序显示一个图形用户界面。
大多数 javac 命令行参数均可用(-O 除外)且可与 rmic 一起使用:
- -g 生成调试信息
- -depend 反复编译过期文件
- -nowarn 不生成警告信息
- -verbose 输出有关编译器所执行的操作的消息
- -classpath 指定查找输入源及类文件的位置
- -d 指定放置生成类文件的位置
- -J< runtime flag> 将参数传给 java 解释器
java.rmi.server 包包含通常用于实现远程对象的接口与类。
主题:
RemoteObject 类
RemoteServer 类
UnicastRemoteObject 类
Unreferenced 接口
RMISecurityManager 类
RMIClassLoader 类
LoaderHandler 接口
RMI 套接字工厂
RMIFailureHandler 接口
LogStream 类
stub 和 skeleton 编译器
5.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 中读取其外部形式。
2 RemoteServer 类
java.rmi.server.RemoteServer 类是服务器实现类java.rmi.server.UnicastRemoteObject和 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.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 运行时等待片刻后尝试再创建 ServerSocket。
注意,当 ServerSocket 首次导出对象时如果创建 ServerSocket 失败,就不会调用 RMIFailureHandler。
而当 ServerSocket 接受请求失败后再创建该 ServerSocket 时,即调用 RMIFailureHandler。
5.10 LogStream 类
类 LogStream 提供一种记录错误的机制。对系统进行监控的人可能会对这些错误感兴趣。该类在内部用于日志服务器调用。
package java.rmi.server; public class LogStream extends java.io.PrintStream { public static LogStream log(String name); public static synchronized PrintStream getDefaultStream(); public static synchronized void setDefaultStream( PrintStream newDefault); public synchronized OutputStream getOutputStream(); public synchronized void setOutputStream(OutputStream out); public void write(int b); public void write(byte b[], int off, int len); public String toString(); public static int parseLevel(String s); // 日志等级常数 public static final int SILENT = 0; public static final int BRIEF = 10; public static final int VERBOSE = 20; }
----------------------------------------------------------------------
----------
注意 -JDK1.2 中不鼓励使用 LogStream 类
----------------------------------------------------------------------
----------
方法 log 返回由给定名称标识的 LogStream。如果某名称所对应的日志不存在,即创建一个使用缺省流的日志。
方法 getDefaultStream 返回用于新日志的当前缺省流。
方法 setDefaultStream 为新日志设置缺省流。
方法 getOutputStream 返回当前日志输出到的流。
方法 setOutputStream 设置日志的输出流。
方法 write 的第一种形式将向该流写一个字节数据。如果不是新起一行,则将把
该字节添加到内部缓冲区。如果是新起一行,则当前缓冲区中的行将按相应的日志前缀发送到日志的输出流中。方法 write 的第二种形式将写字节子数组。
方法 toString 以字符串形式返回日志名。
方法 parseLevel 将日志等级的字符串名转换为内部整型表示。
5.11 stub 和 skeleton 编译器
rmic 的 stub 和 skeleton 编译器用于为特定的远程对象实现编译相应的 stub和 skeleton。该编译器将由远程对象类的类全名调用。
该类必须在先前已成功编译过。
导入类的位置由环境变量 CLASSPATH 或参数 -classpath 指定。
除非指定参数 -d,否则编译好的类文件将放在当前目录下。
参数 -keepgenerated (或 -keep)为 stub 和 skeleton 保留生成的 java 源文件。
也可指定 stub 协议的版本:
- -v1.1 创建符合 JDK 1.1 stub 协议版本的 stub/skeleton
- -vcompat(JDK 1.2 中为缺省值)创建同时兼容 JDK 1.1 和 1.2 stub 协议版本的 stub/skeleton
- -v1.2 创建仅符合 JDK 1.2 stub 协议版本的 stub(注意,JDK 1.2 stub协议中并不需要 skeleton)
-show 选项为程序显示一个图形用户界面。
大多数 javac 命令行参数均可用(-O 除外)且可与 rmic 一起使用:
- -g 生成调试信息
- -depend 反复编译过期文件
- -nowarn 不生成警告信息
- -verbose 输出有关编译器所执行的操作的消息
- -classpath 指定查找输入源及类文件的位置
- -d 指定放置生成类文件的位置
- -J< runtime flag> 将参数传给 java 解释器
发表评论
-
RMI中的安全策略
2012-07-13 15:58 3564以下翻译来自Java RMI的Ch ... -
RMI动态类加载
2012-07-13 15:14 1508以下翻译自Java RMI的Chapter 19.Dynami ... -
RMI运行时说明
2012-07-06 12:25 2957RMI运行时环境在客户端和服务端都扮演了重要的角色.在这种架构 ... -
RMI中的属性说明
2012-07-03 18:06 1370服务端属性 下面的表 ... -
O'reilly<<Java RMI>> 第18章:使用定制Socket (翻译)
2012-06-30 16:15 1530以下翻译来自:o'reilly ... -
RMI规范--第一章
2012-06-29 10:52 919原文网址: http://docs.or ... -
RMI规范--第十章
2012-06-29 10:50 757RMI 通信协议 10.1 概述 ... -
RMI规范--第九章
2012-06-29 10:29 843本章中的接口和类用于 ... -
RMI规范--第八章
2012-06-29 10:13 610本章包含 rmic stub 编译 ... -
RMI规范--第七章
2012-06-29 09:38 863远程对象激活 主题: 概述 激活协议 “可激活 ... -
RMI规范--第六章
2012-06-28 00:17 900注册服务程序接口 RMI 系统使用 java.rmi.re ... -
RMI规范--第四章
2012-06-28 00:15 838客户机接口 程序员在编写使用远程对象的 applet 或应 ... -
RMI规范--第三章
2012-06-28 00:14 800主题: Stub 与 skeleton ... -
RMI规范--第二章
2012-06-28 00:13 977Java 分布式对象模型 2.1 分布式对象应用程序 ... -
J2SE1.6 RMI官方指南翻译四
2012-06-27 18:20 1015Compiling and Running the Examp ... -
J2SE1.6 RMI官方指南翻译三
2012-06-27 18:16 825Creating a Client Program The c ... -
J2SE1.6 RMI官方指南翻译二
2012-06-27 18:13 1108Implementing a Remote Interface ... -
J2SE1.6 RMI官方指南翻译一
2012-06-27 16:37 880最近在学习分布式 ... -
RMI 开发步骤
2012-06-25 18:36 1425开发RMI步骤: 1.编写服务接口,此接口须直接或间接继承至R ... -
JDK1.6 rmic命令使用
2012-06-25 18:24 1459rmic称为rmi编译命令,用于生成rmi远程实现类的stub ...
相关推荐
#### 第5章 TongWeb主要监控参数 - 为了确保TongWeb的正常运行和高效监控,本章提供了关于监控JVM垃圾收集、开启监控功能、设置持久化监控、监控JVM、WEB容器、JDBC连接池等方面的指导。 - 还特别介绍了快照服务及其...
本复习资料主要针对第15章的内容,涵盖了分布式程序设计的期末考试重点,主要包括Java EE的基本概念和体系结构、JSF开发以及EJB应用的基础知识。复习内容详细列举了可能出现在考试中的题型,如判断题、选择题、填空...
Java Management Extensions(JMX)1.4规范是Java平台中用于管理系统和应用程序的重要技术。它提供了管理和监控Java应用程序、设备和服务的标准框架。本章节主要介绍了JMX的基本概念、架构以及核心组件。 ### 1. ...
### 第5章 数据库编程 - **内容概览**:涵盖JDBC数据库连接技术、Java访问数据库的基本步骤、高级特性及应用示例。 - **教学要求**:学生需掌握JDBC数据库连接方法。 - **重点与难点**:重点在于JDBC技术及数据库...
第5 章 XML 简介 5.1 XML 简介及其语法规则 5.2 DTD 的书写及实例 5.3 CSS 与XSL 及其实例 5.4 XHTML 简介 5.5 WML 简介 5.6 本章小结 第6 章 JSP 与XML 联合开发技术 6.1 XML 与JSP 技术联合 6.2 在JSP 中应用XML ...
第5章涉及Java XML技术,XML是可扩展标记语言,用于存储和传输数据,Java提供了强大的XML处理能力,包括DOM、SAX、StAX等解析方式以及XSLT转换技术。 第6章内容是JSP技术,JSP即JavaServer Pages,是一种允许开发者...
第5章 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:不能接触 ...
#### 第五章 Java Server Page **5.1 概述** JSP(JavaServer Pages)是一种基于Java技术的Web开发技术,用于快速生成动态网页。 **5.2 JSP和其他类似技术比较** 与其他Web开发技术相比,JSP具有易用性高、开发...
#### 第一章 SQL Server 基本操作 ##### SQL Server 操作须知 1. **安装路径**: 安装文件及其安装路径都不能包含中文字符,否则可能会导致安装失败或出现其他错误。 2. **版本有效期**: 如果安装后一段时间内能够...
5. **RMI(远程方法调用)**:RMI是一种用于Java环境下的远程过程调用技术,允许Java对象通过网络调用另一个Java虚拟机上的对象的方法。 - 实现过程包括定义接口、实现接口、编译产生桩和框架、发布服务和调用服务...
第5章 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:不能接触 ...
第5章 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:不能接触 ...
第5章 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:不能接触 ...
第5章 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:...
第5章 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:不能接触 ...
第5章 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:不能接触 ...
第5章 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:不能接触 ...
第5章 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:不能接触 ...
第5章 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:不能接触 ...