[英]Remote Method Invocation (RMI) - Force “Call-by-Value” on an exported object
为了准备考试,我正在编写一个使用RMI的小程序。
外部客户端应该可以访问RMI服务器上的数据收集。 客户应该能够“锁定”和“解锁”集合。
我已经有了以下代码,但是我不知道如何在close()和get()方法中强制对象的序列化(然后通过RMI发送)。 (但是,当集合isOpen()
正常工作时,修改远程对象 。)有任何提示,想法和想法吗?
服务对象:
public interface Service extends Remote
{
public Data open() throws RemoteException;
public Data close() throws RemoteException;
public Data get() throws RemoteException;
public boolean isOpen() throws RemoteException;
}
public class ServiceImpl extends UnicastRemoteObject implements Service
{
private boolean open;
private DataImpl data;
public ServiceImpl() throws RemoteException
{
open = true;
data = new DataImpl();
}
@Override
public synchronized Data open() throws RemoteException
{
open = true;
//by-reference
return data;
}
@Override
public synchronized Data close() throws RemoteException
{
open = false;
//force by-value
//TODO: How to force serialization???
return null;
}
@Override
public synchronized Data get() throws RemoteException
{
if (open)
{
//by-reference
return data;
}
else
{
//force by-value
//TODO: How to force serialization???
return null;
}
}
@Override
public synchronized boolean isOpen() throws RemoteException
{
return open;
}
}
数据对象
public interface Data extends Remote, Serializable
{
public void append(String s) throws RemoteException;
public ArrayList<String> getValues() throws RemoteException;
public String getString() throws RemoteException;
}
public class DataImpl extends UnicastRemoteObject implements Data
{
private ArrayList<String> data;
public DataImpl() throws RemoteException
{
data = new ArrayList<String>();
}
@Override
public synchronized void append(String s) throws RemoteException
{
data.add(s);
}
@Override
public synchronized ArrayList<String> getValues() throws RemoteException
{
return data;
}
@Override
public synchronized String getString() throws RemoteException
{
String s = "";
for (String d : data)
{
s += d + ", ";
}
return s;
}
}
(故意遗漏的进口)
UML
这里发生了几件事情。
首先,作为RMI请求或响应的一部分编组的对象需要可序列化,并且作为编组/解组过程的一部分对其进行序列化和反序列化。 但是,如果这样的对象实现Remote
并被导出,则其存根将被封送,而不是序列化对象本身。 这有时是正确的事情,有时是很违反直觉的。
其次,此处代码展示的一种通用样式是Remote
对象扩展UnicastRemoteObject
。 这很方便,但是此技术的结果是在构造时隐式导出了每个Remote
实例。
您可以做的一件事是,当集合被锁定时,使用UnicastRemoteObject.unexportObject()
取消导出数据对象,而当集合被解锁时,使用exportObject()
重新导出它们。 取消导出对象将改变封送处理行为,以便对数据对象进行序列化,而不是封送远程存根。 不过,我不推荐这种技术,因为客户端可能已经检索到存根并对其发出呼叫。 如果在取消导出对象之后收到调用,则将发生错误,可能是NoSuchObjectException
。
相反,我建议您重构类的层次结构,以便Data
对象不再扩展UnicastRemoteObject
。 这使您可以随意构造Data
对象,并且仅选择性地导出要导出的对象。
特别是,在锁定集合的同时, Service
上的传入呼叫可以创建当前Data
对象集的副本并返回这些副本。 由于不会导出它们,因此将对它们进行封送处理和序列化,并通过网络发送副本。 原始Data
对象将保持导出状态,从而使它们可以继续为具有远程引用的客户端提供服务。
顺便说一句,我不确定您要在此处实现的语义。 处理该Service
客户端无法确定它将获得远程引用还是本地副本。 因此,对数据对象所做的任何更改都可能会更新远程(共享)副本或被困在本地副本中。 我也不确定如果客户端对服务器上的数据对象进行远程引用,然后服务器的集合被锁定,结果应该是什么。 客户端此时仍可以对服务器上的数据对象进行更新吗?
在任何情况下,显式控制何时导出数据对象和不导出数据对象将控制是否封送序列化副本而不是远程存根。
另外,不让远程对象扩展UnicastRemoteObject
另一个原因是,它不安全地发布了对远程对象的引用(就Java内存模型而言)。 UnicastRemoteObject
构造函数导出对象,这意味着它将对象绑定到RMI对象表中,为此对象创建存根。 然后 ,子类构造函数运行以初始化对象本身。 由于已经发布了对该对象的引用,因此构造函数的完成与读取该对象中值的其他线程之间没有任何先发生后关系。 因此,其他线程(可能响应远程调用)可以在远程对象中看到陈旧或未初始化的值。 因此,我不想让远程对象扩展UnicastRemoteObject
,而是构造远程对象然后显式导出它们。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.