2017-09-05 118 views
0

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

这不是一个问题,当我们连接到注册表,因为我们可以将目标主机名是这样的:

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

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

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

在调试器中,我可以看到,就像需要注册表对象上的主机,但不能在存根: debugger view on registry and stub

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

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

回答

0

不幸的是,RMI有一个深入的假设,即服务器主机有一个“最公开的”IP地址或主机名。这解释了java.rmi.server.hostname惨败。如果你的系统不符合,你的运气不好。

0

正如EJP指出的那样,似乎没有优雅的开箱即用解决方案。 我能想到2名unelegant的国家:

  1. 更改每个客户端主机上的网络配置,从而将流量重定向到无法访问的IP,而不是本地主机。
  2. 通过反射更改“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); 
} 

存根上的方法调用现在成功:

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