RMI和JMX Socket工厂

时间:2014-02-04 14:52:36

标签: java rmi jmx

我正在尝试在我的java应用程序中启动嵌入式JMX服务器。我想为RMI注册表和实际的RMI流量使用相同的端口(如果你愿意,可以使用JMX流量)。显然,这可以从RMI Registry is merely a Remote Object itself开始。

增加的难点是我需要使用Socket Factories,因为我需要绑定到特定的NIC。 我从开始:

int registryPort = 3012;
int jmxPort = 3012;    // use the same port

这是我的服务器套接字工厂。很直接的东西:

public class MyRMIServerSocketFactory implements RMIServerSocketFactory {

    private final InetAddress inetAddress;

    public MyRMIServerSocketFactory(InetAddress inetAddress) {
        this.inetAddress = inetAddress;
    }

    @Override
    public ServerSocket createServerSocket(int port) throws IOException {
        return new ServerSocket(port, 0, inetAddress);
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 97 * hash + (this.inetAddress != null ? this.inetAddress.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final FlexibleRMIServerSocketFactory other = (FlexibleRMIServerSocketFactory) obj;
        if (this.inetAddress != other.inetAddress && (this.inetAddress == null || !this.inetAddress.equals(other.inetAddress))) {
            return false;
        }
        return true;
    }    
}

(我的IDE会自动生成equals()hashCode(),不要卡在他们身上)

我像这样创建RMI注册表:

serverSocketFactory = new MyRMIServerSocketFactory(inetAddressBind);
LocateRegistry.createRegistry(
        registryPort,
        RMISocketFactory.getDefaultSocketFactory(),  // client socket factory
        serverSocketFactory // server socket factory
        );       

然后再创建JMXConnectorServer:

JMXServiceURL url = new JMXServiceURL(
     "service:jmx:rmi://localhost:" + jmxPort + 
      "/jndi/rmi://:" + registryPort + "/jmxrmi");

Map env = new HashMap();
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, serverSocketFactory);

connector = JMXConnectorServerFactory.newJMXConnectorServer(
                url, 
                env,
                ManagementFactory.getPlatformMBeanServer());

connector.start();

这导致connector.start()上的绑定错误,表示该地址已被使用。

如果我完全跳过使用Socket Factories:

LocateRegistry.createRegistry(registryPort);

JMXServiceURL url = new JMXServiceURL(
     "service:jmx:rmi://localhost:" + jmxPort + 
      "/jndi/rmi://:" + registryPort + "/jmxrmi");


connector = JMXConnectorServerFactory.newJMXConnectorServer(
                url, 
                null,
                ManagementFactory.getPlatformMBeanServer());

connector.start();

它按预期工作,即只打开一个端口,没有错误。

问题:如何使用Socket Factories使“单一侦听端口方案”工作?

更新 - 最终解决方案

如果您使用空客户端套接字工厂创建注册表,即

,则它可以正常工作
LocateRegistry.createRegistry(
        registryPort,
        null,  // client socket factory (let it default to whatever RMI lib wants)
        serverSocketFactory // server socket factory
        );       

我还必须设置java.rmi.server.hostname System Property我认为在我这样的场景中经常出现这种情况。

2 个答案:

答案 0 :(得分:1)

这应该有效:你的ServerSocketFactory中有一个看起来正确的equals()方法,这是重要的一点。 RMI确实称之为。但是,目前不适用于您的客户端套接字工厂。您需要将null作为客户端套接字工厂传递,而不是RMISocketFactory.getDefaultSocketFactory(),,因为它会为您提供由于某种原因未实现sun.rmi.transport.proxy.RMIMasterSocketFactory,的{​​{1}}。或者使用合理的equals()方法自己实现RMIClientSocketFactory

所以这里发生的事情是RMI首先比较CSF,并且它们出现不平等,所以它甚至不打扰比较SSF:

equals()

所以它尝试在你指定的端口上创建一个新的csf1.equals(csf2) && ssf1.equals(ssf2) ,它与第一个端口相同,所以它失败了。

你可以在equals的开头添加一个快捷方式,如果= =那就返回true。

答案 1 :(得分:0)

您应该搜索JMXMP协议以及包含它的jmxremote_optional.jar。对于JMX来说,这是一个更可控,更有效的协议。