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
notify
notifyAll
wait
getClass
的默认实现适用于用Java编程语言编写的所有对象,无论是本地对象还是远程对象;因此,对于远程存根,getClass
方法报告由rmic
生成的存根对象的确切类型。请注意,存根类型仅反映远程对象实现的远程接口,而不是该对象的本地接口。
java.lang.Object
的wait
和notify
方法处理等待和通知,与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
方法返回一个输出流(在写入与调用成功相关的头信息后)。获取结果流应该每个远程调用成功一次。如果success为true
,则要编组的结果是正常返回;否则结果是异常。如果已为此远程调用获取了结果流,则抛出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位值为0xB0B1B2B3
和0xB4B5B6B7
,则哈希值将为0xB7B6B5B4B3B2B1B0
。
注意:自Java 2 SDK标准版v1.2起,newCall(RemoteObject,Operation[],int,long)
、invoke(RemoteCall)
和done(RemoteCall)
方法已被弃用。使用1.2存根协议版本生成的rmic
生成的存根不再使用这些方法。由newCall
、invoke
和done
组成的调用序列已被一个新的invoke
方法取代,该方法将Method
对象作为其参数之一。
newCall
方法为对远程对象obj的新远程方法调用创建适当的调用对象。操作数组op包含远程对象上可用的操作。操作号opnum是一个索引,指定此远程调用的特定操作。接口hash是一个64位值,用于强制执行使用v1.1存根协议的存根和框架之间的兼容性。接口哈希是从使用SHA-1的特定字节流的消息摘要的前两个32位值计算得到的。此字节流包含数据,就好像它是使用接口java.io.DataOutput
的writeInt
和writeUTF
方法编写的,包含以下项目:
- (
int
)存根版本号,始终为1 - 对于每个远程方法,按操作号顺序:
- (UTF-8)远程方法名称
- (UTF-8)远程方法描述符(请参阅Java虚拟机规范)
- 对于每个声明的异常,按二进制名称的字典顺序:
- (UTF-8)异常类的名称
接口哈希值与上述用于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
还返回操作描述符的字符串表示形式(通常是方法签名)。