简体   繁体   English

远程方法调用(RMI)-在导出的对象上强制“按值调用”

[英]Remote Method Invocation (RMI) - Force “Call-by-Value” on an exported object

For exam preparation I am writing a little program that uses RMI. 为了准备考试,我正在编写一个使用RMI的小程序。

A data-collection on a RMI-server should be accessible to external clients. 外部客户端应该可以访问RMI服务器上的数据收集。 Clients should be able to "lock" and "unlock" the collection. 客户应该能够“锁定”和“解锁”集合。

  • When the collection is unlocked , clients should receive a reference to the exported remote Data-object. 解锁集合后,客户端应收到对导出的远程数据对象的引用
  • When the collection is locked clients should only receive a serialized copy (ie by-value) of said Data-object (thus the Data-interface extends both the Serializable and the Remote-Interfaces). 当集合被锁定时,客户端应该只接收所述数据对象的序列化副本 (即按值)(因此,数据接口扩展了可序列化和远程接口)。

I have already got the following code, but I do not know how to force the serialization of the object (and then send it via RMI) inside the close() and get() methods. 我已经有了以下代码,但是我不知道如何在close()和get()方法中强制对象的序列化(然后通过RMI发送)。 (Modifying the remote -object when the collection isOpen() works perfectly, though.) Any hints, thoughts, ideas? (但是,当集合isOpen()正常工作时,修改远程对象 。)有任何提示,想法和想法吗?

Service-Object: 服务对象:

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;
    }
}

Data-Object 数据对象

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;
    }
}

(imports left out by intention) (故意遗漏的进口)

UML UML

UML

There are a couple things going on here. 这里发生了几件事情。

First, objects that are marshalled as part of an RMI request or response need to be serializable, and they are serialized and deserialized as part of the marshaling/unmarshaling process. 首先,作为RMI请求或响应的一部分编组的对象需要可序列化,并且作为编组/解组过程的一部分对其进行序列化和反序列化。 However, if such an object implements Remote and is exported, its stub is marshaled instead of the object itself being serialized. 但是,如果这样的对象实现Remote并被导出,则其存根将被封送,而不是序列化对象本身。 This is sometimes the right thing and is sometimes quite counterintuitive. 这有时是正确的事情,有时是很违反直觉的。

Second, a common style that the code here exhibits is for Remote objects to extend UnicastRemoteObject . 其次,此处代码展示的一种通用样式是Remote对象扩展UnicastRemoteObject This is convenient, but the consequence of this technique is that every Remote instance is implicitly exported at construction time. 这很方便,但是此技术的结果是在构造时隐式导出了每个Remote实例。

One thing you could do would be to unexport the data objects using UnicastRemoteObject.unexportObject() when the collection is locked and re-export them using exportObject() when the collection is unlocked. 您可以做的一件事是,当集合被锁定时,使用UnicastRemoteObject.unexportObject()取消导出数据对象,而当集合被解锁时,使用exportObject()重新导出它们。 Unexporting the object will change the marshaling behavior so that the data objects are serialized instead of the remote stub being marshaled. 取消导出对象将改变封送处理行为,以便对数据对象进行序列化,而不是封送远程存根。 I don't recommend this technique, though, because clients might have retrieved stubs and be issuing calls on them. 不过,我不推荐这种技术,因为客户端可能已经检索到存根并对其发出呼叫。 If a call is received after the object is unexported, an error will occur, probably NoSuchObjectException . 如果在取消导出对象之后收到调用,则将发生错误,可能是NoSuchObjectException

What I'd recommend instead is to refactor your class hierarchy so the Data objects no longer extend UnicastRemoteObject . 相反,我建议您重构类的层次结构,以便Data对象不再扩展UnicastRemoteObject This allows you to construct Data objects at will and only selectively export the ones you want exported. 这使您可以随意构造Data对象,并且仅选择性地导出要导出的对象。

In particular, while the collection is locked, an incoming call on the Service could create copies of the current set of Data objects and return those. 特别是,在锁定集合的同时, Service上的传入呼叫可以创建当前Data对象集的副本并返回这些副本。 Since they won't be exported, they'll be marshaled and serialized and the copies sent across the wire. 由于不会导出它们,因此将对它们进行封送处理和序列化,并通过网络发送副本。 The original Data objects will remain exported, allowing them to continue to service clients that have remote references to them. 原始Data对象将保持导出状态,从而使它们可以继续为具有远程引用的客户端提供服务。

As an aside, I'm unsure of the semantics you're trying to achieve here. 顺便说一句,我不确定您要在此处实现的语义。 A client dealing with the Service can't tell whether it will get a remote reference or a local copy. 处理该Service客户端无法确定它将获得远程引用还是本地副本。 Thus, any changes it makes to the data objects might update the remote (shared) copy or be stranded in a local copy. 因此,对数据对象所做的任何更改都可能会更新远程(共享)副本或被困在本地副本中。 I'm also not sure what the result should be if a client has a remote reference to a data object on the server and then the server's collection is locked. 我也不确定如果客户端对服务器上的数据对象进行远程引用,然后服务器的集合被锁定,结果应该是什么。 Can the client still make updates to the data object on the server at this time? 客户端此时仍可以对服务器上的数据对象进行更新吗?

In any case, explicitly controlling when the data objects are and are not exported will control whether serialized copies are marshaled instead of remote stubs. 在任何情况下,显式控制何时导出数据对象和不导出数据对象将控制是否封送序列化副本而不是远程存根。

As another aside, another reason not to have remote objects extend UnicastRemoteObject is that it unsafely publishes a reference to the remote object (in the sense of the Java Memory Model). 另外,不让远程对象扩展UnicastRemoteObject另一个原因是,它不安全地发布了对远程对象的引用(就Java内存模型而言)。 The UnicastRemoteObject constructor exports the object, which means that it binds the object into the RMI object table creates the stub for it. UnicastRemoteObject构造函数导出对象,这意味着它将对象绑定到RMI对象表中,为此对象创建存根。 Then the subclass constructor runs to initialize the object itself. 然后 ,子类构造函数运行以初始化对象本身。 Since the reference to this object has already been published, there is no happens-before relationship between the completion of the constructor and other threads reading of the values in the object. 由于已经发布了对该对象的引用,因此构造函数的完成与读取该对象中值的其他线程之间没有任何先发生后关系。 Thus, other threads -- possibly in response to remote calls -- can see stale or uninitialized values in the remote object. 因此,其他线程(可能响应远程调用)可以在远程对象中看到陈旧或未初始化的值。 For this reason I prefer not to have remote objects extend UnicastRemoteObject , and instead construct remote objects and then export them explicitly. 因此,我不想让远程对象扩展UnicastRemoteObject ,而是构造远程对象然后显式导出它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM