header

Torsten Curdt’s weblog

Binding JMX on a dedicated address

Unfortunately in order to get JMX to bind to dedicated address (and not just 0.0.0.0) you have to jump through a few hoops. In order to save you some time – here is how you do it. Create your own RMISocketFactory that only creates server sockets on the specified address

public class RMIServerSocketFactoryImpl implements RMIServerSocketFactory {

    private final InetAddress localAddress;

    public RMIServerSocketFactoryImpl( final InetAddress pAddress ) {
        localAddress = pAddress;
    }

    public ServerSocket createServerSocket(final int pPort) throws IOException  {
        return ServerSocketFactory.getDefault()
            .createServerSocket(pPort, 0, localAddress);
    }

    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }

        return obj.getClass().equals(getClass());
    }

    public int hashCode() {
        return RMIServerSocketFactoryImpl.class.hashCode();
    }
}

Then create the naming service with that factory and create the RMI server using that naming service.

RMIServerSocketFactory serverFactory = new RMIServerSocketFactoryImpl(InetAddress.getByName(address));

LocateRegistry.createRegistry(namingPort, null, serverFactory);

StringBuffer url = new StringBuffer();
url.append("service:jmx:");
url.append("rmi://").append(address).append(':').append(protocolPort).append("/jndi/");
url.append("rmi://").append(address).append(':').append(namingPort).append("/connector");

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

rmiServer = new RMIConnectorServer(
   new JMXServiceURL(url.toString()),
   env,
   ManagementFactory.getPlatformMBeanServer()
   );

rmiServer.start();

Why do some so simple things need to be so overly complicated?

  • The problem was that I startet my app with the "standard" rmi and jmx environment variables
    -Dcom.sun.management.jmxremote \
    -Dcom.sun.management.jmxremote.port=1150 \
    -Djava.rmi.server.hostname=my.host.name \
    ...

    Now I removed this (actually I replaced them by my own, custom variables) and it's working now!

    Thanx && cheers,
    Martin
  • Martin, maybe have a look here to see the code in action.

    http://github.com/tcurdt/jmx2s...
  • Jarrett, I have exactly the same problem right now: Address already in use. Can you share your solution?
  • Jarrett Eriksen
    Actually, looks like I figured it out.
  • Jarrett Eriksen
    I've been working on this for a while now and came across this post. I've been getting an error:

    Caused by: java.rmi.server.ExportException: Port already in use: 3007; nested exception is:
    java.net.BindException: Address already in use
    (this occurs when I call cs.start() )


    I think that I should be getting around the problem mentioned in your first comment by providing a static IP through a system variable. I've also checked that this port is not in use, either before or after the exception gets thrown. I've also tried changing the ports in the url so that they are different, with the same error as a result. Do you have any idea what could be going on here? Some of my code is shown below, though I'm not sure how well it will format. Thanks!



    final String hostname = InetAddress.getLocalHost().getHostName();
    final String ipAddress = System.getProperty("example.rmi.agent.ipAddress", "0.0.0.0");
    String parseIP = ipAddress;
    int a = parseIP.indexOf('.');
    String firstNumber = parseIP.substring(0, a);
    parseIP = parseIP.substring(a+1);
    int b = parseIP.indexOf('.');
    String secondNumber = parseIP.substring(0, b);
    parseIP = parseIP.substring(b+1);
    int c = parseIP.indexOf('.');
    String thirdNumber = parseIP.substring(0, c);
    parseIP = parseIP.substring(c+1);
    String fourthNumber = parseIP;
    byte[] addr = {
    (byte)Integer.parseInt(firstNumber),
    (byte)Integer.parseInt(secondNumber),
    (byte)Integer.parseInt(thirdNumber),
    (byte)Integer.parseInt(fourthNumber)
    };

    RMIServerSocketFactory serverFactory = new RMIServerSocketFactoryImpl(InetAddress.getByAddress(hostname, addr));

    StringBuffer url = new StringBuffer();
    url.append("service:jmx:");
    url.append("rmi://").append(hostname).append(':').append(port).append("/jndi/");
    url.append("rmi://").append(hostname).append(':').append(port).append("/connector");

    env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, serverFactory);

    RMIConnectorServer cs = new RMIConnectorServer(new JMXServiceURL(url.toString()), env, mbs);

    cs.start();
  • As if that were not complicated enough, you also have to set the java.rmi.server.hostname property to be sure it will work. See http://weblogs.java.net/blog/e... and blame it all on RMI.
blog comments powered by Disqus