8 存根/框架接口


本节包含由rmic存根编译器生成的存根和框架使用的接口和类。

8.1 RemoteStub类

java.rmi.server.RemoteStub类是远程对象存根的通用超类。存根对象是支持与远程对象的实际实现定义的完全相同的一组远程接口的代理。

package java.rmi.server;

public abstract class RemoteStub extends java.rmi.RemoteObject {
        protected RemoteStub() {...}
        protected RemoteStub(RemoteRef ref) {...}

        protected static void setRef(RemoteStub stub, RemoteRef ref) {...}
}

RemoteStub的第一个构造函数创建一个带有null远程引用的存根。第二个构造函数使用给定的远程引用ref创建一个存根。

setRef方法在Java 2 SDK标准版v1.2中已被弃用(不再受支持)。

8.1.1 存根类的远程对象类型等价性

客户端与存根(代理)对象交互,这些对象具有与远程对象类定义的完全相同的一组远程接口;存根类不包括构成对象类型图的非远程部分的类层次结构。这是因为存根类是从实现一个或多个远程接口的最精细的实现类生成的。例如,如果C扩展B,B扩展A,但只有B实现了一个远程接口,则从B生成一个存根,而不是从C生成。

因为存根实现了与远程对象类相同的一组远程接口,所以存根具有与服务器对象类型图的远程部分相同的类型。因此,客户端可以利用内置的Java编程语言操作来检查远程对象的类型,并将一个远程接口转换为另一个远程接口。

存根是使用rmic编译器生成的。

8.1.2 声明为final的对象方法的语义

以下方法在java.lang.Object类中声明为final,因此不能被任何实现重写:

getClass的默认实现适用于用Java编程语言编写的所有对象,无论是本地对象还是远程对象;因此,对于远程存根,getClass方法报告由rmic生成的存根对象的确切类型。请注意,存根类型仅反映远程对象实现的远程接口,而不是该对象的本地接口。

java.lang.Objectwaitnotify方法处理等待和通知,与Java编程语言的线程模型上下文相关。虽然在远程存根中使用这些方法不会破坏线程模型,但这些方法对于用Java编程语言编写的本地对象的语义与对于客户端的本地引用到远程对象(存根)的实际对象不同。具体来说,这些方法操作客户端对远程对象(存根)的本地引用,而不是远程站点上的实际对象。

8.2 RemoteCall接口

RemoteCall接口是远程对象的存根和框架用于执行对远程对象的调用的抽象。

注意:自Java 2 SDK标准版v1.2起,RemoteCall接口已被弃用。1.2存根协议不再使用此接口。从Java 2 SDK标准版v1.2开始,存根现在使用不需要RemoteCall作为参数的新invoke方法。

package java.rmi.server;

import java.io.*;

public interface RemoteCall {
        ObjectOutput getOutputStream() throws IOException;
        void releaseOutputStream() throws IOException;
        ObjectInput getInputStream() throws IOException;
        void releaseInputStream() throws IOException;
        ObjectOutput getResultStream(boolean success)
                throws IOException, StreamCorruptedException;
        void executeCall() throws Exception;
        void done() throws IOException;
}

getOutputStream方法返回输出流,其中存根将参数编组或框架编组结果。

releaseOutputStream方法释放输出流;在某些传输中,这将释放流。

getInputStream方法返回存根解组结果或框架解组参数的InputStream

releaseInputStream方法释放输入流。这将允许某些传输提前释放连接的输入端。

getResultStream方法返回一个输出流(在写入与调用成功相关的头信息后)。获取结果流应该每个远程调用成功一次。如果successtrue,则要编组的结果是正常返回;否则结果是异常。如果已为此远程调用获取了结果流,则抛出StreamCorruptedException

executeCall方法执行调用所需的所有操作。

done方法允许在远程调用完成后进行清理。

8.3 RemoteRef接口

RemoteRef接口表示远程对象的句柄。每个存根包含一个RemoteRef实例,其中包含引用的具体表示。此远程引用用于对其是引用的远程对象进行远程调用。

package java.rmi.server;

public interface RemoteRef extends java.io.Externalizable {
        Object invoke(Remote obj,
                      java.lang.reflect.Method method,
                      Object[] params,
                      long opnum)
                throws Exception;

        RemoteCall newCall(RemoteObject obj, Operation[] op, int opnum,
                           long hash) throws RemoteException;
        void invoke(RemoteCall call) throws Exception;
        void done(RemoteCall call) throws RemoteException;
        String getRefClass(java.io.ObjectOutput out);
        int remoteHashCode();
        boolean remoteEquals(RemoteRef obj);
        String remoteToString();
}

invoke(Remote,Method,Object[],long)方法将方法调用委托给存根(obj)的远程引用,并允许引用负责建立与远程主机的连接,编组method和参数params的某种表示,然后将方法调用传递给远程主机。此方法要么返回远程主机上的远程对象的方法调用结果,要么如果调用失败或远程调用引发异常则抛出RemoteException。请注意,操作号opnum表示方法签名的哈希值,并且可以用于编码方法以进行传输。

用于opnum参数的方法哈希是从使用国家标准与技术研究所(NIST)安全哈希算法(SHA-1)的特定字节流的消息摘要的前两个32位值计算得到的64位(long)整数。此字节流包含一个字符串,就好像它是使用java.io.DataOutput.writeUTF方法编写的,包含远程方法的名称,后跟其方法描述符(请参阅Java虚拟机规范中的方法描述符描述)。

例如,如果远程接口的方法具有以下名称和签名:

void myRemoteMethod(int count, Object obj, boolean flag)

包含远程方法名称和描述符的字符串将如下所示:

myRemoteMethod(ILjava/lang/Object;Z)V

64位哈希值是一个八字节序列的小端构成,其中前四个字节是消息摘要的第一个32位值,按大端字节顺序排列,最后四个字节是消息摘要的第二个32位值,按大端字节顺序排列。例如,如果消息摘要的前两个32位值为0xB0B1B2B30xB4B5B6B7,则哈希值将为0xB7B6B5B4B3B2B1B0

注意:自Java 2 SDK标准版v1.2起,newCall(RemoteObject,Operation[],int,long)invoke(RemoteCall)done(RemoteCall)方法已被弃用。使用1.2存根协议版本生成的rmic生成的存根不再使用这些方法。由newCallinvokedone组成的调用序列已被一个新的invoke方法取代,该方法将Method对象作为其参数之一。

newCall方法为对远程对象obj的新远程方法调用创建适当的调用对象。操作数组op包含远程对象上可用的操作。操作号opnum是一个索引,指定此远程调用的特定操作。接口hash是一个64位值,用于强制执行使用v1.1存根协议的存根和框架之间的兼容性。接口哈希是从使用SHA-1的特定字节流的消息摘要的前两个32位值计算得到的。此字节流包含数据,就好像它是使用接口java.io.DataOutputwriteIntwriteUTF方法编写的,包含以下项目:

接口哈希值与上述用于invoke方法中的方法哈希的计算方式相同。

方法invoke(RemoteCall)执行远程调用。 invoke将引发任何“用户”异常,这些异常应该通过而不被存根捕获。 如果在远程调用期间引发任何异常,则invoke应在引发“用户异常”或RemoteException之前负责清理连接。

方法done允许远程引用清理(或重用)连接。 只有在invoke调用成功返回(非异常情况)到存根时才应调用done

方法getRefClass返回要序列化到流out上的引用类型的非包限定类名。

方法remoteHashCode为远程对象返回哈希码。 引用同一远程对象的两个远程对象存根将具有相同的哈希码(以支持将远程对象用作哈希表键)。 RemoteObject将其hashCode方法的调用转发到远程引用的remoteHashCode方法。

方法remoteEquals比较两个远程对象是否相等。 如果它们引用同一远程对象,则两个远程对象相等。 例如,如果它们引用同一远程对象,则两个存根相等。 RemoteObject将其equals方法的调用转发到远程引用的remoteEquals方法。

方法remoteToString返回表示此远程对象引用的String

8.4 ServerRef接口

接口ServerRef表示远程对象实现的服务器端句柄。

package java.rmi.server;

public interface ServerRef extends RemoteRef {
        RemoteStub exportObject(java.rmi.Remote obj, Object data)
                throws java.rmi.RemoteException;
        String getClientHost() throws ServerNotActiveException;
}

方法exportObject查找或创建所提供的Remote对象实现obj的客户端存根对象。参数data包含导出对象所需的信息(例如端口号)。

方法getClientHost返回当前客户端的主机名。 当从主动处理远程方法调用的线程调用时,将返回调用该调用的客户端的主机名。 如果当前未正在服务远程方法调用,则将调用ServerNotActiveException

8.5 Skeleton接口

接口Skeleton仅由rmic编译器生成的骨架实现使用。 远程对象的骨架是一个在服务器端分派调用到实际远程对象实现的实体。

注意:自Java 2 SDK标准版v 1.2起,Skeleton接口已被弃用。 rmic存根编译器生成的每个1.1(以及使用rmic -vcompat生成的与1.1兼容的版本1.2)骨架类都实现了此接口。 自Java 2 SDK标准版v1.2兼容版本起,不再需要骨架来分派远程方法调用。 要生成与1.2或更高版本兼容的存根,请使用带有选项-v1.2的命令rmic

package java.rmi.server;

public interface Skeleton {
        void dispatch(Remote obj, RemoteCall call, int opnum, long hash)
                throws Exception;
        Operation[] getOperations();
}

dispatch方法从call对象获取的输入流中取消任何参数,调用实际远程对象实现obj上的方法(由操作编号opnum指示),并将返回值编组或在调用期间发生异常时引发异常。

getOperations方法返回包含远程对象方法的操作描述符的数组。

8.6 Operation

Operation保存Java编程语言中远程对象方法的描述。

注意:自Java 2 SDK标准版v1.2起,Operation接口已被弃用。 1.2存根协议不再使用旧的接受Operation作为参数的RemoteRef.invoke方法。 自Java 2 SDK标准版v1.2起,存根现在使用不需要Operation作为参数的新invoke方法。

package java.rmi.server;

public class Operation {
        public Operation(String op) {...}
        public String getOperation() {...}
        public String toString() {...}
}

通常使用方法签名构造Operation对象。

方法getOperation返回操作描述符的内容(初始化时的值)。

方法toString还返回操作描述符的字符串表示形式(通常是方法签名)。