Java RMI存根:在客户端强制主机值

Java RMI存根:在客户端强制主机值,java,rmi,Java,Rmi,我们希望从网络中的不同主机访问相同的RMI服务器(通过ssh隧道访问dev pc,通过直接连接访问jenkins服务器)。问题在于,RMI主机在不同的客户机主机上以不同的名称存在 当我们连接到注册表时,这不是问题,因为我们可以这样设置目标主机名: Registry registry = LocateRegistry.getRegistry("hostname", 10099, new CustomSslRMIClientSocketFactory()); 但是,当我们像下面这样查找远程对象时,

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

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

Registry registry = LocateRegistry.getRegistry("hostname", 10099, new CustomSslRMIClientSocketFactory());
但是,当我们像下面这样查找远程对象时,它包含错误的主机名

HelloRemote hello = (HelloRemote) registry.lookup(HelloRemote.class.getSimpleName());
在调试器中,我可以观察到注册表对象上需要主机,但存根上不需要主机:

只要调用存根上的方法,我们就会得到一个连接超时。如果我在调试器中将主机值手动更改为
localhost
,则方法调用成功

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

不幸的是,RMI有一个根深蒂固的假设,即服务器主机有一个“最公共”的IP地址或主机名。这解释了java.rmi.server.hostname的失败。如果您的系统不符合要求,您就倒霉了。

不幸的是,RMI有一个根深蒂固的假设,即服务器主机只有一个“最公共”的IP地址或主机名。这解释了java.rmi.server.hostname的失败。如果您的系统不符合要求,您就倒霉了。

正如EJP所指出的,似乎没有现成的解决方案。 我可以想出两个不合法的例子:

  • 更改每个客户端主机上的网络配置,以便将流量重定向到不可访问的ip,改为本地主机
  • 通过反射更改“hello”-对象上的主机值
  • 我选择了第二个选项,因为我在一个测试环境中,并且所讨论的代码无论如何都不会产生效果。我不建议这样做,因为这段代码可能会与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);
    }
    

    正如EJP所指出的,似乎没有现成的解决方案。 我可以想出两个不合法的例子:

  • 更改每个客户端主机上的网络配置,以便将流量重定向到不可访问的ip,改为本地主机
  • 通过反射更改“hello”-对象上的主机值
  • 我选择了第二个选项,因为我在一个测试环境中,并且所讨论的代码无论如何都不会产生效果。我不建议这样做,因为这段代码可能会与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);
    }