2017-09-05 113 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