首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >远程方法调用(RMI) -强制对导出对象进行“逐值调用”。

远程方法调用(RMI) -强制对导出对象进行“逐值调用”。
EN

Stack Overflow用户
提问于 2014-09-04 16:01:20
回答 1查看 810关注 0票数 2

为了准备考试,我正在编写一个使用RMI的小程序。

RMI服务器上的数据收集应该可以被外部客户端访问.客户端应该能够“锁定”和“解锁”集合。

  • 当集合是解锁的时,客户端应该接收到导出的远程数据对象的引用
  • 当集合被锁定时,客户端应该只接收所述数据对象的序列化副本(即按值计算)(因此数据接口扩展了可序列化和远程接口)。

我已经得到了以下代码,但我不知道如何在()和get()方法中强制对对象进行序列化(然后通过RMI发送)。(但是,当集合isOpen()完美地工作时,修改远程对象)有什么提示、想法、想法吗?

Service-Object:

代码语言:javascript
复制
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;
    }
}

数据对象

代码语言:javascript
复制
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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-09-05 06:36:13

这里发生了几件事。

首先,作为RMI请求或响应的一部分被封送的对象需要可序列化,并且它们作为封送/解封处理过程的一部分被序列化和反序列化。但是,如果这样的对象实现Remote并导出,那么它的存根将被封送,而不是序列化对象本身。这有时是正确的,有时是非常违反直觉的。

第二,这里展示的代码的一个常见样式是Remote对象扩展UnicastRemoteObject。这很方便,但是这种技术的结果是,每个Remote实例都是在构造时隐式导出的

您可以做的一件事是,在集合被锁定时使用UnicastRemoteObject.unexportObject()解导出数据对象,并在解锁集合时使用exportObject()重新导出数据对象。取消导出对象将更改封送处理行为,以便数据对象被序列化,而不是被封送的远程存根。不过,我不推荐这种技术,因为客户端可能已经检索到存根并对其发出调用。如果在未导出对象之后接收到调用,则会发生错误,可能是NoSuchObjectException

相反,我建议的是重构类层次结构,这样Data对象就不再扩展UnicastRemoteObject了。这允许您任意构造Data对象,并且只允许有选择地导出要导出的对象。

特别是,当集合被锁定时,Service上的传入调用可以创建当前Data对象集的副本并返回这些副本。因为它们不会被导出,所以它们将被封送和序列化,并且副本将通过导线发送。原始Data对象将保持导出状态,允许它们继续为具有对它们的远程引用的客户端服务。

顺便说一句,我不确定你在这里试图达到的语义。处理Service的客户端无法判断它将获得远程引用还是本地副本。因此,它对数据对象所做的任何更改都可能更新远程(共享)副本或滞留在本地副本中。如果客户端对服务器上的数据对象有一个远程引用,然后服务器的集合被锁定,我也不确定结果应该是什么。此时客户端还能对服务器上的数据对象进行更新吗?

在任何情况下,显式控制何时导出数据对象和不导出数据对象将控制序列化副本是否被封送,而不是远程存根。

另外,不让远程对象扩展UnicastRemoteObject的另一个原因是它不安全地发布对远程对象的引用(在Java模型的意义上)。UnicastRemoteObject构造函数导出对象,这意味着它将对象绑定到RMI对象表中,从而为其创建存根。然后,子类构造函数运行以初始化对象本身。由于对此对象的引用已经发布,因此在构造函数的完成与读取对象中的值的其他线程之间的关系之前没有发生。因此,其他线程--可能是对远程调用的响应--可以看到远程对象中陈旧的或未初始化的值。因此,我不喜欢让远程对象扩展UnicastRemoteObject,而是构造远程对象,然后显式地导出它们。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25670077

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档