RMI Stubs:强制客户端的主机值

时间:2017-09-05 15:04:59

标签: java rmi

我们希望从我们网络中的不同主机访问相同的RMI服务器(dev-pc通过ssh-tunnel,jenkins-server通过直接连接)。问题是在不同的客户端主机上以不同的名称知道RMI主机。

当我们连接到注册表时,这不是问题,因为我们可以像这样设置目标主机名:

Registry registry = LocateRegistry.getRegistry("hostname", 10099, new CustomSslRMIClientSocketFactory());

但是当我们查找下面的远程对象时,它包含错误的主机名。

HelloRemote hello = (HelloRemote) registry.lookup(HelloRemote.class.getSimpleName());

在调试器中,我可以观察到主机在Registry对象上是需要的,但在Stub上却没有: debugger view on registry and stub

我们在Stub上调用方法后立即获得连接超时。如果我在调试器中手动将主机值更改为localhost,则方法调用会成功。

我知道我可以在服务器端设置java.rmi.server.hostname但是jenkins的连接不再起作用。 最简单的解决方案是强制RMI使用与注册表相同的主机,从该注册表中检索所有Stub。有没有比通过反射替换Stub中的主机值更好的方法?

2 个答案:

答案 0 :(得分:0)

不幸的是,RMI有一个深深的内在假设,即服务器主机有一个“最公开”的IP地址或主机名。这解释了for j = 0..N-1 swap item #j with item # rand(0..N-1) 惨败。如果你的系统不合规,那你就不走运了。

答案 1 :(得分:0)

正如EJP所指出的,似乎没有优雅的开箱即用解决方案。 我能想到两个不优雅的人:

  1. 更改每个客户端主机上的网络配置,以便将流量重定向到不可访问的IP到localhost。
  2. 通过Reflection更改“hello”对象上的主机值。
  3. 我选择了第二个选项,因为我处于测试环境中,所讨论的代码无论如何都不会有效。我不建议这样做,否则这个代码可能会破坏未来的java版本,如果有安全管理器,它将无法工作。

    但是,这是我的工作代码:

    private static void forceRegistryHostNameOnStub(Object registry, Object stub) {
        try {
            String regHost = getReferenceToInnerObject(registry, "ref", "ref", "ep", "host").toString();
    
            Object stubEp = getReferenceToInnerObject(stub, "h", "ref", "ref", "ep");
            Field fStubHost = getInheritedPrivateField(stubEp, "host");
            fStubHost.setAccessible(true);
            fStubHost.set(stubEp, regHost);
        } catch (Exception e) {
            LOG.error("Applying the registry host to the Stub failed.", e);
        }
    }
    
    private static Object getReferenceToInnerObject(Object from, String... objectHierarchy) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
        Object ref = from;
        for (String fieldname : objectHierarchy) {
            Field f = getInheritedPrivateField(ref, fieldname);
            f.setAccessible(true);
            ref = f.get(ref);
        }
        return ref;
    }
    
    private static Field getInheritedPrivateField(Object from, String fieldname) throws NoSuchFieldException {
        Class<?> i = from.getClass();
        while (i != null && i != Object.class) {
            try {
                return i.getDeclaredField(fieldname);
            } catch (NoSuchFieldException e) {
                // ignore
            }
            i = i.getSuperclass();
        }
        return from.getClass().getDeclaredField(fieldname);
    }
    

    Stub上的方法调用现在成功:

    Registry registry = LocateRegistry.getRegistry("hostname", 10099, new CustomSslRMIClientSocketFactory());
    HelloRemote hello = (HelloRemote) registry.lookup(HelloRemote.class.getSimpleName());
    forceRegistryHostNameOnStub(registry, hello); // manipulate the stub
    hello.doSomething(); // succeeds