RMI和JMX套接字工厂 [英] RMI and JMX Socket Factories

查看:127
本文介绍了RMI和JMX套接字工厂的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在我的Java应用程序中启动嵌入式JMX服务器.我想为RMI注册中心和实际RMI流量(或JMX流量,如果您愿意)使用相同的端口.显然这是可能的,因为 RMI注册表本身仅仅是一个远程对象. /p>

增加的困难是我需要使用套接字工厂,因为我需要绑定到特定的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;
    }    
}

(equals()hashCode()由我的IDE自动生成,不会卡在它们上)

我这样创建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()上的绑定错误,表明该地址已被使用.

如果我完全跳过使用套接字工厂:

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();

它可以按预期工作,即仅打开一个端口且没有错误.

问题:如何在套接字工厂中使用单侦听端口方案"?

更新-最终解决方案

如果您使用空客户端套接字工厂(即

)创建注册表,则该方法有效

LocateRegistry.createRegistry(
        registryPort,
        null,  // client socket factory (let it default to whatever RMI lib wants)
        serverSocketFactory // server socket factory
        );       

我还必须设置 java.rmi.server.hostname系统属性,我猜在像我这样的场景中经常会出现这种情况.

解决方案

这应该起作用:ServerSocketFactory中有一个外观正确的equals()方法,这一点很重要. RMI确实会这样称呼.但是,当前不适用于您的客户端套接字工厂.您需要传递null作为客户端套接字工厂,而不是RMISocketFactory.getDefaultSocketFactory(),,因为这会给您一个sun.rmi.transport.proxy.RMIMasterSocketFactory,,由于某种原因它没有实现equals().否则,您可以使用合理的equals()方法来实现自己的RMIClientSocketFactory.

因此,这里发生的是RMI首先比较了CSF,而它们之间是不相等的,因此比较SSF根本不用费心:

csf1.equals(csf2) && ssf1.equals(ssf2)

,因此它尝试在您指定的端口(与第一个端口相同)上创建新的ServerSocket,因此失败.

您可以在equals的开头添加一个快捷方式,如果此== that,则返回true.

I'm trying to start an embedded JMX server in my java app. I want to use the same port for the RMI Registry and for the actual RMI traffic (or JMX traffic if you like). Apparently this is possible since the RMI Registry is merely a Remote Object itself.

The added difficulty is that I need to use Socket Factories because I need to bind to a specific NIC. I start off by:

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

and here's my server socket factory. Pretty straight-forward stuff:

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;
    }    
}

(the equals() and hashCode() are auto-generated by my IDE, don't get stuck on them)

I create the RMI Registry like this:

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

and then on to creating the 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();

This results in a bind error on the connector.start() saying that the address is already in use.

If I skip using Socket Factories altogether:

LocateRegistry.createRegistry(registryPort);

and

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


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

connector.start();

it works as expected, i.e. only a single port will be opened and no errors.

Question: How to make the 'single-listening-port-scenario' work with Socket Factories ?

UPDATE - FINAL SOLUTION

It works if you create the Registry with a null client socket factory, i.e.

LocateRegistry.createRegistry(
        registryPort,
        null,  // client socket factory (let it default to whatever RMI lib wants)
        serverSocketFactory // server socket factory
        );       

I also had to set the java.rmi.server.hostname System Property which I guess will often be the case in a scenario like mine.

解决方案

This should work: you have a correct-looking equals() method in your ServerSocketFactory, which is the important bit. RMI does call that. However the same doesn't apply currently to your client socket factory. You need to pass null as the client socket factory, not RMISocketFactory.getDefaultSocketFactory(), as that gives you a sun.rmi.transport.proxy.RMIMasterSocketFactory, which doesn't implement equals() for some reason. Or else your own implementation of RMIClientSocketFactory with a plausible equals() method.

So what is happening here is that RMI is comparing the CSFs first, and they are coming out unequal, so it doesn't even bother comparing the SSFs:

csf1.equals(csf2) && ssf1.equals(ssf2)

so it tries to create a new ServerSocket on the port you specifed, which is the same as the first port, so it fails.

You could add a shortcut at the beginning of equals that returns true if this == that.

这篇关于RMI和JMX套接字工厂的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆