我们希望从网络中的不同主机访问相同的RMI服务器(dev-pc通过ssh隧道,jenkins-server通过直接连接)。问题是RMI主机在不同的客户端主机上以不同的名称已知。
当我们连接到注册表时,这不是一个问题,因为我们可以像这样设置目标主机名:
Registry registry = LocateRegistry.getRegistry("hostname", 10099, new CustomSslRMIClientSocketFactory());但是,当我们像下面这样查找远程对象时,它包含错误的主机名。
HelloRemote hello = (HelloRemote) registry.lookup(HelloRemote.class.getSimpleName());在调试器中,我可以看到主机类似于Registry,而不是Stub上的:

当我们调用Stub上的方法时,我们就会得到一个连接超时。如果我在调试器中手动将主机值更改为localhost,则方法调用将成功。
我知道我可以在服务器端设置java.rmi.server.hostname,但是来自jenkins的连接不再工作了。最简单的解决方案是,我强制RMI使用与从该注册表检索的所有Stub的注册表相同的主机。有比通过反射替换Stub中的主机值更好的方法吗?
发布于 2017-09-05 21:46:06
不幸的是,RMI有一个深层次的内置假设,即服务器主机有一个“最公共的”IP地址或主机名。这就解释了java.rmi.server.hostname的惨败。如果你的系统不符合,你就倒霉了。
发布于 2017-09-07 08:19:28
正如EJP指出的那样,似乎没有优雅的开箱即用的解决方案。我能想到两个不雅的:
我选择了第二种选择,因为我所处的是一个测试环境,所讨论的代码无论如何都不会有效率。我建议不要这样做,因为这段代码可能会与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(); // succeedshttps://stackoverflow.com/questions/46058176
复制相似问题