CSDN博客

img morningbird

RMI规范(5)

发表于2001/7/24 20:29:00  820人阅读

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 中的其它方法保留了它们的原始实现。

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 方法允许一个活动方法确定当前线程中活动的远程方法是由哪
台主机初始化的。如果当前线程中没有活动的远程方法,则抛出异常 ServerNot
ActiveException。
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。如果不允许进行该项设置,则将抛出 Security
Exception。

静态方法 getSocketFactory 返回由 RMI 使用的套接字工厂。如果未设置套接字
工厂,则返回值为 null。

当传输需要创建套接字时,传输层将在 getSocketFactory 方法返回的 RMISock
etFactory 上调用 createSocket 和 createServerSocket 方法。例如:

RMISocketFactory.getSocketFactory().createSocket(myhost, myport)

方法 createSocket 应创建一个连接到指定 host 和 port 的客户机套接字。 c
reateServerSocket 方法应在指定 port 上创建服务器套接字。

缺省的 RMISocketFactory 传输实现使用 HTTP 通过防火墙提供透明的 RMI,如
下所述:

在 createSocket 中,工厂将自动尝试与无法用套接字直接联系的主机建立
HTTP 连接。
在 createServerSocket 中,工厂将返回用于自动检测新接受的连接是否为 HTT
P POST 请求的服务器套接字。如果是,则返回仅将请求主体透明地展示给传输然
后将其输出格式化为 HTTP 响应的套接字。
方法 setFailureHandler 设置失败句柄。在创建服务器套接字失败时,该句柄将
由 RMI 运行时调用。该句柄返回一个布尔值,用于指示是否应重试。缺省的失败
句柄返回值为 false,意味着缺省情况下运行时将不再尝试创建套接字。

方法 getFailureHandler 在套接字创建失败时返回当前句柄。失败句柄未设置时
将为 null。

5.8.2 RMIServerSocketFactory 接口
为了支持与远程对象的自定义通信,可以在导出远程对象时为其指定一个 RMISe
rverSocketFactory 实例。这一点既可通过相应的 UnicastRemoteObject 构造函
数或 exportObject 方法完成,也可通过相应的 java.rmi.activation.Activat
able 构造函数或 exportObject 方法完成。如果该服务器套接字工厂在导出远程
对象时与之关联,则 RMI 运行时将使用远程对象的服务器套接字工厂来创建 Se
rverSocket(使用 RMIServerSocketFactory.createServerSocket 方法),以接
受远程客户机的连接。

package java.rmi.server;
public interface RMIServerSocketFactory
{

public java.net.ServerSocket createServerSocket(int port)
throws IOException;
}

5.8.3 RMIClientSocketFactory 接口
要自定义与远程对象的通信,可在导出时远程对象为其指定一个 RMIClientSock
etFactory 的实例。这一点既可通过相应的 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 运行时等待片刻后尝试再创建 ServerSock
et。

注意,当 ServerSocket 首次导出对象时如果创建 ServerSocket 失败,就不会
调用 RMIFailureHandler。而当 ServerSocket 接受请求失败后再创建该 Serve
rSocket 时,即调用 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 解释器

0 0

相关博文

我的热门文章

img
取 消
img