JMX service url cannot connect remote jvm - java

I wrote JVM monitoring with JMX. I connect MBean sever connection with ths following url
service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi
It worked using localhost but I would to connect other machine JVM and I also used this;
service:jmx:rmi:///jndi/rmi://OTHER_SERVER_PORT:9999/jmxrmi
It doesn't work.I goggling about this and some example tell to add the following JVM options to target machine JVM.
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
So where to add the above JVM options? And how to connect other machine JVM using JMX service url? This is my sample class
public class JMXRemote {
public static void main(String[] args) throws Exception {
JMXServiceURL url = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://OTHER_PC_PORT:9999/jmxrmi");
JMXConnector c = JMXConnectorFactory.connect(url);
MBeanServerConnection mbsc = c.getMBeanServerConnection();
Object o = mbsc.getAttribute(new ObjectName("java.lang:type=Memory"),
"HeapMemoryUsage");
CompositeData cd = (CompositeData) o;
System.out.println(cd.get("committed"));
}
}

Related

Is there a way to start JFR monitoring/recording from remote via code?

I am using Jmeter to do load test using Ant plugin. Currently capturing server's JVM metrics during the test using JFR GUI mode, for each node. It is cumbersome to start for each node and sometimes its getting missed to start. Is there a way to start doing them together via code?
Client details
Hotspot JDK 1.8_131,
Mac OS,
Jmeter 4.0
Server details
Hotspot 1.8.0_152,
OEL ,
Tomcat
In internet I am getting the options to start JFR programmatically in server node, which will not work for me.
If you have terminal access, you could create a script and start a recording using the command line tool jcmd.
$ jcmd <class/jarfile> VM.unlock_commercial_features
$ jcmd <class/jarfile> JFR.start
or
$ jcmd (to list available pids)
$ jcmd <pid> VM.unlock_commercial_features
$ jcmd <pid> JFR.start
For more details, see
Example 2-1 Dynamic Interaction Using jcmd
If you can't access the server over SSH, or don't want to do it in the shell, you could use JMX and send the same conmands as diagnostic commands.
In JMC, in the Management/JMX Console you can inspect the MBeans on the server. There you will find the "com.sun.management / DiagnosticCommand" MXBean and the operations which you can write code against. The object name is com.sun.management:type=DiagnosticCommand and you need to call it with jfrStart.
You can set up the connection like this.
String host ="myHost";
int port = 1234;
HashMap map = new HashMap();
String[] credentials = new String[2];
credentials[0] = user;
credentials[1] = password;
map.put("jmx.remote.credentials", credentials);
String s = "/jndi/rmi://" + host + ":" + port + "/jmxrmi";
JMXServiceURL url = new JMXServiceURL("rmi", "", 0, s);
JMXConnector c= JMXConnectorFactory.newJMXConnector(url, map);
c.connect();
MBeanServerConnection conn = c.getMBeanServerConnection();
If you don't care about security, you can set map to null. Then you create an object name and invoke the operation you like to execute, for example:
ObjectName on = new ObjectName("com.sun.management:type=DiagnosticCommand");
Object[] args = new Object[] {
new String[] {
"dumponexit=true",
"filename=/recordings/rec.jfr",
"duration=600s"
}
};
String[] sig = new String[] {"[Ljava.lang.String;"};
conn.invoke(on, "jfrStart", args, sig);
In JDK 9 and later there is a FlightRecorderMXBean that can be used for remote access (including recording download), but if you are stuck on JDK 8, I would do the above. There is a MBean in Oracle JDK 8 as well, but it is unsupported, undocumented and black magic is needed to make it work.

Trigger Snapshots on Remote Server from local

I am trying to remotely profile alfresco running on a 64-bit linux server running a 1.8 JVM and Apache Tomcat 7.xx from my testing code but can't figure out how to programatically trigger snapshots.
What I want to do is to connect to the remote server, start profiling, and save a snapshot of that server's performance onto my local machine from my testing code which is written in Java.
I've already installed JProfiler 9.2 onto the linux server and can connect and take snapshots via the JProfiler GUI. The server also requires an SSH connection for security. I'd like to do this from my code, similar to how Controller.saveSnapshot(file) works for local JVMs.
Is this possible?
I know I can set triggers and have the remote profiler save snapshots on the server, but this isn't what I want to.
Additionally, I looked into using the command line controller but was unable to get it to connect to the server even with the correct arguments in the remote VM options.
I also tried to use ConnectionFactor.createRemoteConnection(), but don't see an argument that allows for a password to be input, so it fails.
You can access the JProfiler MBean programmatically. Below is an example on how to do that. I would run such a program on the remote machine and start it via SSH, because JMX connnections are difficult to tunnel through SSH.
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
// Shows how to connect to the the JProfiler MBean programatically
// The profiled process has to be in offline profiling mode and the JMX server
// has to be started by passing -Djprofiler.jmxServerPort=[port] to the profiled JVM.
// This will not work in nowait mode because the MBean is not registered in that case.
public class MBeanProgrammaticAccessExample {
public static void main(String[] args) throws Exception {
if (args.length == 0) {
System.out.println("Specify the port as an argument that was passed to " +
"the profiled JVM with the VM parameter " +
"-Djprofiler.jmxServerPort=[port]");
}
String port = args[0];
// In this case the connection is made to a process on localhost, but it could
// be on a remote system as well. Note that the connection is made via JMX which
// does not work well with firewalls
System.out.println("Connecting to localhost:" + port);
JMXServiceURL jmxUrl = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://localhost:" + port + "/jmxrmi");
JMXConnector connector = JMXConnectorFactory.newJMXConnector(jmxUrl,
Collections.<String, Object>emptyMap());
Map<String, Object> env = new HashMap<>();
// If you have protected the JMX server with a JMX password file by passing
// -Djprofiler.jmxPasswordFile=[file] to the profiled JVM, you can specify
// the password like this:
//env.put(JMXConnector.CREDENTIALS, new String[] {"username", "password"});
connector.connect(env);
MBeanServerConnection connection = connector.getMBeanServerConnection();
ObjectName objectName = new ObjectName(
"com.jprofiler.api.agent.mbean:type=RemoteController");
if (!connection.isRegistered(objectName)) {
throw new RuntimeException("JProfiler MBean not found.");
}
RemoteControllerMBean mbeanProxy = JMX.newMBeanProxy(connection,
objectName, RemoteControllerMBean.class, true);
// You can look up all available operations in the javadoc of
// com.jprofiler.api.agent.mbean.RemoteControllerMBean
System.out.println("Recording CPU data for 5 seconds ....");
mbeanProxy.startCPURecording(true);
// If you do not want a dependency on the JProfiler classes
// you can make the above call like this:
//connection.invoke(objectName, "startCPURecording", new Object[] {true},
// new String[] {Boolean.TYPE.getName()});
Thread.sleep(5000);
System.out.println("Saving snapshot to the working directory " +
"of the profiled JVM ....");
mbeanProxy.saveSnapshot("snapshot.jps");
connector.close();
System.out.println("Success");
}
}

Is it possible to enable remote jmx monitoring programmatically?

I need to programmatically start a new java process and dynamically set the JMX port.
So instead of doing this
-Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.port=9995 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
I would like to do the following
System.setProperty("java.rmi.server.hostname", "127.0.0.1" );
System.setProperty("com.sun.management.jmxremote", "true" );
System.setProperty("com.sun.management.jmxremote.authenticate", "false" );
System.setProperty("com.sun.management.jmxremote.ssl", "false" );
System.setProperty("com.sun.management.jmxremote.port", "9995" );
but it doesn't work. Any idea why?
By the time your code is called you've missed your chance to configure the jmxremote connector.
What you need to do is create your own rmi registry and a JMXConnectorServer to listen for rmi calls and pass them to the MBeanServer.
private void createJmxConnectorServer() throws IOException {
LocateRegistry.createRegistry(1234);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://localhost/jndi/rmi://localhost:1234/jmxrmi");
JMXConnectorServer svr = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
svr.start();
}

Issues with connecting to remote windows machine using Jsch

I am currently working on making one Java application which will execute commands (from command prompt) of remotely located Windows machine.
For developing this I have tried following:
Used Jsch library, the application worked well when connected to Unix machines, I was able to get list of processes using 'prstat -a' command, but when I tried with Windows machine, it gave me ConnectException(Timed out)
private final static String HOSTNAME = "SOMECOMPUTERNAME.XXX.XXX.XXX.COM";//
private final static String USERNAME = "SOMEUSERNAME";
private final static String PASSWORD = "SOMEPASSWORD";
private final static int PORT = 22;
public static void main(String[] args) {
JSch jscc = new JSch();
try {
Session session = jscc.getSession(USERNAME, HOSTNAME, PORT);
session.setPassword(PASSWORD);
Properties localProperties = new Properties();
localProperties.put("StrictHostKeyChecking", "no");
session.setConfig(localProperties);
session.connect(60000);
System.out.println("Session connected");
} catch (JSchException e) {
e.printStackTrace();
}
}
The target machine info:
Windows x64 workstation
Latest version of Java installed
Doesn't use any encryption
Please guide into this, Can it be done with Jsch library, if not that which method you can recommend.
Following things you can assume:
I don't want any code in target machine. So a Java application which resides in my workstation and just executes some simple window commands, like dir or start applicationName in the target window machine
I have username and corresponding passwords
I don't have the ipaddress, just a full computer name in format as shown in the code above(HOSTNAME)
Any suggestions are welcomed.
Regards,
icr
Windows does not have SSH installed by default. You will need a SSH client for windows.
I prefer OpenSSH....its free and works perfectly

Obtain information about jboss

How would one find out the jboss port programatically within application/war that is deployed on that jboss server? Using Java
It is a web service running, we don't have any user interface
I am assuming you want the HTTP port.
JBoss publishes a Tomcat connector MBean for each web listener. The naming convention of the mbeans' ObjectNames is:
Domain: jboss.web
Attributes:
address: The binding address
port: The listening port
type: connector
The trick is, without making any assumptions about the bind address or port (the bind address could be 127.0.0.1, or 0.0.0.0 or a host name), finding the correct MBean. To do this, you can use a JMX Query that specifies:
The known domain name: jboss.web
The known type: connector
A wild card for the address: *****
A wild card for the port: *****
An attribute value expression that specifies you are looking for the HTTP/1.1 protocol port (as opposed to the AJP port): Query.match(Query.attr("protocol"), Query.value("HTTP/1.1"))
Once you have an MBeanServerConnection to the JBoss MBeanServer, this statement will return the correct port:
String port = server.queryNames(
new ObjectName("jboss.web:type=Connector,address=*,port=*"),
Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")))
.iterator().next().getKeyProperty("port");
If you are attempting to determine the port from code running inside the JBoss JVM, acquiring the MBeanServerConnection is trivial since you can make a static call to org.jboss.mx.util.MBeanServerLocator.locateJBoss().
Here is an example of a simple JSP to print the HTTP port number:
<%#page contentType="text/html" import="java.util.*,org.jboss.mx.util.*,javax.management.*" %>
<html><head><title>JBoss Web Server Port</title></head><body>
<%
try {
MBeanServerConnection server = MBeanServerLocator.locateJBoss();
String port = server.queryNames(
new ObjectName("jboss.web:type=Connector,address=*,port=*"),
Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")))
.iterator().next().getKeyProperty("port");
out.println("<p>Port:" + port + "</p>");
} catch (Exception e) {
e.printStackTrace(System.err);
}
%></body></html>
If you need to acquire this remotely, you need to use the JBoss client RMIAdaptor. The reference provided by Nayan Wadekar is a good example of how to do this.
If your JBoss server has a org.jboss.mx.remoting.service.JMXConnectorServerService deployed or you are running JBoss using the platform MBeanServer, you can also use the native JMX remoting. Here's a Groovy example of that:
import javax.management.*;
import javax.management.remote.*;
conn = null;
try {
url = new JMXServiceURL("service:jmx:rmi://njw810/jndi/rmi://njw810:1090/jmxconnector");
conn = JMXConnectorFactory.connect(url);
server = conn.getMBeanServerConnection();
objectName = new ObjectName("jboss.web:type=Connector,address=*,port=*"); // HTTP/1.1
println server.queryNames(objectName, Query.match(Query.attr("protocol"), Query.value("HTTP/1.1"))).iterator().next().getKeyProperty("port");
} finally {
try { conn.close(); println "Connection Closed"; } catch (Exception e) {}
}

Categories