Friday, July 2, 2010

JMX - jconsole connection / TCP Port redirection,Fireware issue

JMX provides a standard way to instrument the Java runtime environment and applications, and the JMX Remote API allows that instrumentation to be accessed remotely. there are couple system properties that you need to setup.

To enable the JMX agent and configure its operation using jconsole, your must set some specific system properties when you start the JVM. For local access, set the property com.sun.management.jmxremote as follows when starting the JVM:

prompt> java -Dcom.sun.management.jmxremote AppName

And, to enable monitoring and management from remote systems [here we disable the authentication and ssl]  , set the property:

-Dcom.sun.management.jmxremote.port=portNumber
-Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false

When we talk about the firewall settings, here is one quick question, Is the PortNumber pretty much all we needed to put it into our exception list? if you answer yes like me. you need read the following parts. or maybe that’s why you are redirected to this blog. 

lets do a simple test, Just open you notepad and write a helloworld Application.

public class MyApp
{
    public static void main(String[] args)  throws java.io.IOException
    {
        System.out.println("hello,world, press to continue");
         int i=System.in.read();
    }
}

then Compile and Run the application.

C:\temp>javac MyApp.java

C:\temp>java -cp "." -Dcom.sun.management.jmxremote.port=2222 -Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false   MyApp

hello,world, press to continue

Keep the application running, and Open Jconsole, put localost:2222 to the remote process field, and click to go. nothing special here, you will be able to see the Mbeans .

So, How does the jconsole communicate with our JMXRemote Port? your may run netstat or download a utility offered from microsoft called tcpview.  when you filter to jconsole process, you may find the following sessions. besides, the port 2222, it open one extra tcp port . the port number looks like it just pick it up randomly

image

at the end of one day, if somebody enforce the firewall policy by adding port 2222 to the only allowed port,  then Nobody can use the jconsole to monitor this jvm.

So how can we Restrict the JMX listening Port/TCP to use the predefined Port? here is the steps.
basic Idea,  when the application startup, just start one JMXConnectorServer service explicitly.  the server will expose jmxjmi service to the client. In java, there is one serverfactory called JMXConnectorServerFactory

public static JMXConnectorServer newJMXConnectorServer(JMXServiceURL serviceURL, Map<String,?> environment, MBeanServer mbeanServer) throws IOException

The connector server will generate an RMIServerImpl based on the protocol (rmi or iiop) and, for rmi, the port if any. When the connector server is started, it will derive a stub from this object using its toStub method and store the object using the given jndi-name. The properties defined by the JNDI API are consulted as usual.

For example, if the JMXServiceURL is:

service:jmx:rmi://ignoredhost/jndi/rmi://myhost/myname

then the connector server will generate an RMIJRMPServerImpl and store its stub using the JNDI name

rmi://myhost/myname

then we just change the myapp.java to loadup the connecterserver directly.

finally code:

import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;

public class MyApp
{
    public static void main(String[] args)  throws java.io.IOException
    {
        int port=3333;
        LocateRegistry.createRegistry(port);
        JMXServiceURL url =
            new JMXServiceURL("service:jmx:rmi://localhost:"+port+"/jndi/rmi://:"+port+"/jmxrmi");
               JMXConnectorServer cs =
            JMXConnectorServerFactory.newJMXConnectorServer(url, null, ManagementFactory.getPlatformMBeanServer());
        cs.start();

        System.out.println("hello,world, press to continue");

        int i=System.in.read();
    }
}

Recompile and restart the app, run jconsole. check the tcp session. Only TCP Port 3333 is open and required this time now.

image

In this case, we owned the source code of our application. if you are using the commercial package or third party application,  you can’t change their source code then.
not a big problem, the Insturment package allow Java programming language agents to instrument programs running on the JVM.  we just inject the save logic by writing a customeragent , and use the javaagent parameter to load our customeAgent.

we define another class, named JconsoleAgent

import java.lang.instrument.Instrumentation;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.rmi.registry.LocateRegistry; import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL; public class JconsoleAgent {
    public static void premain(String agentArgs, Instrumentation inst)
    {
        try
        {
            int port= Integer.parseInt(
                    System.getProperty("MyJMXPORT","5000"));
             LocateRegistry.createRegistry(port);
              String hostname = InetAddress.getLocalHost().getHostName();
            JMXServiceURL url =new JMXServiceURL("service:jmx:rmi://"+hostname+":"+port+"/jndi/rmi://"+hostname+":"+port+"/jmxrmi");
             JMXConnectorServer cs =
                JMXConnectorServerFactory.newJMXConnectorServer(url, null, ManagementFactory.getPlatformMBeanServer());
            cs.start();
        }
        catch (Exception ex)
        {
            System.out.println("SETUP MyJMXPORT Failed" + ex.getMessage());
        }
    }
}

then export the compiled class to a jar file named Jconsoleagent.jar . Edit the MANIFEST.MF file inclued in the jar, and put one more line.

Manifest-Version: 1.0
Premain-Class: JconsoleAgent

then startup the application like

C:\temp>java -cp "." -DMyJMXPORT=3333 -javaagent:Jconsoleagent.jar MyApp
you will be able to use the port 3333 only here to do the jmx monitoring.

image

Hope it Helps.

Another question, if you have multi NICS on the server, you can specify which RMI server that will server the request by adding parameters like

-Djava.rmi.server.hostname=MyInternalNIC

Also, make sure you jconsole client can resmove the MyInternalNIC to the same IPaddress as you defined on the server. otherwise, you can’t conenct to the remote server

here is my great reference link, http://www.cs.washington.edu/education/courses/cse341/98au/java/jdk1.2beta4/docs/guide/rmi/rmiNetworkingFAQ.html

 
Locations of visitors to this page