[英]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.