I should connect to a java program on localhost jvm using JMX. In other words I want to develop a JMX client to config a java program on localhost.
Don't recommend using JConsole! JConsole is not suitable because it is general JMX client and have negative effect on main program performance.
Samples on oracle site use RMIConnector and host:port params but I don't know:
where should set jmx port?
JConsole have an option to connect to java processes by PID. But I don't find any method in JMX api that have PID as input param.
We use something like the following to programatically connect to our JMX servers. You should run your server with something like the following arguments:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.port=1234
-Dcom.sun.management.jmxremote.ssl=false
To bind to a particular address you'll need to add the following VM arguments:
-Djava.rmi.server.hostname=A.B.C.D
Then you can connect to your server using JMX client code like the following:
String host = "localhost"; // or some A.B.C.D
int port = 1234;
String url = "service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/jmxrmi";
JMXServiceURL serviceUrl = new JMXServiceURL(url);
JMXConnector jmxConnector = JMXConnectorFactory.connect(serviceUrl, null);
try {
MBeanServerConnection mbeanConn = jmxConnector.getMBeanServerConnection();
// now query to get the beans or whatever
Set<ObjectName> beanSet = mbeanConn.queryNames(null, null);
...
} finally {
jmxConnector.close();
}
We also have code that can programatically publish itself to a particular port outside of the VM arguments but that's more fu than you need I think.
In terms of connecting "by pid", you need to be using Java6 to do it from Java land as far as I know. I've not used the following code but it seems to work.
List<VirtualMachineDescriptor> vms = VirtualMachine.list();
for (VirtualMachineDescriptor desc : vms) {
VirtualMachine vm;
try {
vm = VirtualMachine.attach(desc);
} catch (AttachNotSupportedException e) {
continue;
}
Properties props = vm.getAgentProperties();
String connectorAddress =
props.getProperty("com.sun.management.jmxremote.localConnectorAddress");
if (connectorAddress == null) {
continue;
}
JMXServiceURL url = new JMXServiceURL(connectorAddress);
JMXConnector connector = JMXConnectorFactory.connect(url);
try {
MBeanServerConnection mbeanConn = connector.getMBeanServerConnection();
Set<ObjectName> beanSet = mbeanConn.queryNames(null, null);
...
} finally {
jmxConnector.close();
}
}
I've also the author of SimpleJMX package which makes it easy to start a JMX server and publish beans to remote clients.
// create a new server listening on port 8000
JmxServer jmxServer = new JmxServer(8000);
// start our server
jmxServer.start();
// register our lookupCache object defined below
jmxServer.register(lookupCache);
jmxServer.register(someOtherObject);
// stop our server
jmxServer.stop();
It does have a client interface as well but right now it doesn't have any mechanisms to find processes by PID -- only host/port combinations are supported (in 6/2012).
To clarify, if you are only interested in getting local JMX stats, you don't need to use the remote api. Just use java.lang.management.ManagementFactory:
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
memoryMXBean.getHeapMemoryUsage().getMax();
...
List<MemoryPoolMXBean> beans = ManagementFactory.getMemoryPoolMXBeans();
...
List<VirtualMachineDescriptor> vm = new ArrayList<VirtualMachineDescriptor>();
jvmList = new JVMListManager();
vm = jvmList.listActiveVM();
for (VirtualMachineDescriptor vmD : vm)
{
try
{
//importFrom is taking a process ID and returning a service url in a String Format
String ServiceUrl = ConnectorAddressLink.importFrom(Integer.parseInt(vmD.id().trim()));
JMXServiceURL jmxServiceUrl = new JMXServiceURL(ServiceUrl);
jmxConnector = JMXConnectorFactory.connect(jmxServiceUrl, null);
con = jmxConnector.getMBeanServerConnection();
CompilationMXBean compMXBean = ManagementFactory.newPlatformMXBeanProxy(con
, ManagementFactory.COMPILATION_MXBEAN_NAME
, CompilationMXBean.class);
}catch(Exception e)
{
//Do Something
}
}
protected List listActiveVM() {
List<VirtualMachineDescriptor> vm = VirtualMachine.list();
return vm;
}
This requires you to use the jmxremote argument at JVM startup for the process you are trying to read.
TO be able to do it without having to pass a jmxremote argument at startup. You will have to use the attach api(only applicable for Programs using Java 6 and higher.
Simplest means:
import javax.management.Attribute;
import javax.management.AttributeList;
import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;
// set a self JMX connection
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
// set the object name(s) you are willing to query, here a CAMEL JMX object
ObjectName objn = new ObjectName("org.apache.camel:context=*,type=routes,name=\"route*\"");
Set<ObjectName> objectInstanceNames = mBeanServer.queryNames(objn, null);
for (ObjectName on : objectInstanceNames) {
// query a number of attributes at once
AttributeList attrs = mBeanServer.getAttributes(on, new String[] {"ExchangesCompleted","ExchangesFailed"});
// process attribute values (beware of nulls...)
// ... attrs.get(0) ... attrs.get(1) ...
}
This is how you can get a JMX connection to a Java Program with it's PID (for version <= Java 8 only) :
import sun.management.ConnectorAddressLink;
import javax.management.*;
public static MBeanServerConnection getLocalJavaProcessMBeanServer(int javaProcessPID) throws IOException {
String address = ConnectorAddressLink.importFrom(javaProcessPID);
JMXServiceURL jmxUrl = new JMXServiceURL(address);
return JMXConnectorFactory.connect(jmxUrl).getMBeanServerConnection();
}
Related
I have some code connecting to JMX and getting mBean by name. Now I'm writing tests with JUnit for it. I have already done some tests without authentication using something like this:
private static void startJmxServer() throws Exception {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
LocateRegistry.createRegistry(PORT);
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + HOST + ':' + PORT + "/jmxrmi");
JMXConnectorServer connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
Example exampleMBean = new Example();
ObjectName exampleName = new ObjectName(MBEAN_NAME);
mbs.registerMBean(exampleMBean, exampleName);
connectorServer.start();
}
Now I want to do some test with authentication. So I need to specify next JVM properies:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=1234
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.access.file=/somepath/jmxremote.access
-Dcom.sun.management.jmxremote.password.file=/somepath/jmxremote.password
I've already tried passing this properties in JMXConnectorServer environment variable. Also I've tried System.setProperty. But have failed, as connection was available without any credentials.
The only way, that makes it work is:
private static void startJmxServer() throws Exception {
String name = ManagementFactory.getRuntimeMXBean().getName();
VirtualMachine vm = VirtualMachine.attach(name.substring(0, name.indexOf('#')));
String lca = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress");
if (lca == null) {
Path p = Paths.get(System.getProperty("java.home")).normalize();
if (!"jre".equals(p.getName(p.getNameCount() - 1).toString()
.toLowerCase())) {
p = p.resolve("jre");
}
File f = p.resolve("lib").resolve("management-agent.jar").toFile();
if (!f.exists()) {
throw new IOException("Management agent not found");
}
String options = String.format("com.sun.management.jmxremote.port=%d, " +
"com.sun.management.jmxremote.authenticate=true, " +
"com.sun.management.jmxremote.ssl=false, " +
"com.sun.management.jmxremote.access.file=/somepath/jmxremote.access, " +
"com.sun.management.jmxremote.password.file=/somepath/jmxremote.password", PORT);
vm.loadAgent(f.getCanonicalPath(), options);
}
vm.detach();
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
Example exampleMBean = new Example();
ObjectName exampleName = new ObjectName(MBEAN_NAME);
mbs.registerMBean(exampleMBean, exampleName);
}
But as agent was loaded I can not change VM properties to run test without authentication.Also I'm want to avoid such sort of thing, because of need in manual defining tools.jar and want to use common JMX tools. Any idea how to manage this?
Authentication configuration is passed in environment - the second argument to JMXConnectorServerFactory.newJMXConnectorServer.
HashMap<String, Object> env = new HashMap<>();
env.put("jmx.remote.x.password.file", "/somepath/jmxremote.password");
env.put("jmx.remote.x.access.file", "/somepath/jmxremote.access");
JMXConnectorServer connectorServer =
JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
Note that the attribute names here differ from property names.
Consult ConnectorBootstrap.java from JDK sources to see how the default JMXConnectorServer is initialized.
I'm writting a Java (1.7) application to be running on Windows. The application is accessing additional services running on the same host and other ones running in the Internet. The application can be run in two environments where in one, proxy settings must be specified (there is proxy when accessing the Internet); while in the other environment, the proxy settings must not be specified (there is no proxy).
I want the application to be simple and don't want its users bother with specification of the proxy settings on cmd-line (-Dhttp.proxyHost, etc.) - the application should learn the proxy settings from Windows system settings (IE / Tools / Internet Properties / Connections / LAN Settings).
I have written a piece of code that is supposed to learn that settings, see below. The trouble is that this piece of code does not identify localhost, 127.0.0.1 and my-computer-name (where my-computer-name is the name of my computer) as URLs where proxy should be by-passed when being accessed (yes, I do have 'Bypass proxy server for local addresses' checked). As a result, the application tries to access local services through the proxy which is wrong.
So far I've found out that one way to teach JVM not to use proxy for 'local addresses' is to list the strings (localhost, 127.0.0.1, my-computer-name) in Proxy Settings / Exceptions (Do not use proxy server for addresses beginning with). Obviously, this is not a good solution as usually no one is listing these strings there (the first check-box is enough for non-Java applications).
Second (trivial) solution would be just to count with these strings in my piece of code and do not use proxy settings for them even when JVM thinks otherwise. I don't think this is a good solution and if this is the only solution, IMHO, there is a defect in JVM.
I've found many resources in the Internet how to learn System proxy settings. But how to learn the non-proxy settings?
Thanks,
PP
public static final String HTTP_PROXY_HOST_KEY = "http.proxyHost";
public static final String HTTPS_PROXY_HOST_KEY = "https.proxyHost";
public static final String HTTP_PROXY_PORT_KEY = "http.proxyPort";
public static final String HTTPS_PROXY_PORT_KEY = "https.proxyPort";
public static final String NO_PROXY_HOSTS_KEY = "http.nonProxyHosts";
// provide list of urls which are to be accessed by this application and return proxy and non-proxy settings
private Properties getSystemProxyConfiguration(String[] urls) {
log.debug("Getting system proxy");
Properties properties = new Properties();
SortedSet<String> nonProxyHosts = new TreeSet<>();
for (String url : urls) {
URI uri;
try {
uri = new URI(url);
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
}
InetSocketAddress address = getSystemProxy(uri);
if (address != null) {
if (url.toLowerCase().startsWith("https")) {
properties.put(HTTPS_PROXY_HOST_KEY, address.getHostString());
properties.put(HTTPS_PROXY_PORT_KEY, ""+address.getPort());
//todo verify that all previous URLs in this array are using the same proxy
log.debug("HTTPS proxy: " + address.getHostString() + ":" + address.getPort());
} else {
properties.put(HTTP_PROXY_HOST_KEY, address.getHostString());
properties.put(HTTP_PROXY_PORT_KEY, ""+address.getPort());
//todo verify that all previous URLs in this array are using the same proxy
log.debug("HTTP proxy: " + address.getHostString() + ":" + address.getPort());
}
} else { //todo DEFECT -> this does not find the non-proxy hosts (even though specified in IE Internet settings)
nonProxyHosts.add(uri.getHost());
}
}
if (nonProxyHosts.size() > 0) {
String nonProxyHostsString = nonProxyHosts.first();
nonProxyHosts.remove(nonProxyHostsString);
for (String nonProxyHost : nonProxyHosts) {
nonProxyHostsString = nonProxyHostsString + "|" + nonProxyHost;
}
properties.put(NO_PROXY_HOSTS_KEY, nonProxyHostsString);
log.debug("Non HTTP(S) proxy hosts: "+nonProxyHostsString);
} else {
log.debug("No non HTTP(S) proxy hosts set");
}
return properties;
}
private InetSocketAddress getSystemProxy(URI uri) {
List<Proxy> proxyList;
proxyList = ProxySelector.getDefault().select(uri);
if (proxyList != null && proxyList.size() > 0) { //todo DEFECT - this never returns DIRECT proxy for localhost, 127.0.0.1, my-computer-name strings
Proxy proxy = proxyList.get(0);
if (proxyList.size() > 1) {
log.warn("There is more " + proxy.type() + " proxies available. Use "+PROXY_PROPERTIES_FILE_NAME+" to set the right one.");
}
InetSocketAddress address = (InetSocketAddress) proxy.address();
return address;
}
return null;
}
With Tomcat setup behind Apache, how can an id (IP address ideally) of the server be easily determined?
The specific situation is that multiple servers are setup behind a load balancer, thus the incoming request host name is non-unique and insufficient to identify a particular server for logging purposes. Using HttpServletRequest.getLocalAddr() is unfortunately returning the same hostname instead of the IP address as would be expected (I am assuming this is related to this very old issue here: https://issues.apache.org/bugzilla/show_bug.cgi?id=46082).
Is there a way to make getLocalAddr() perform as documented, or are other methods required to query the IP address of the server?
On our project, we use JMX to get all the config information.
It takes a few steps, because it is like navigating down the server.xml file
This link has some info: http://oss.wxnet.org/mbeans.html
It is probably overkill if all you want is the IP, but I thought I'd throw it out there.
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
Set<ObjectName> theConnectors = mbeanServer.queryNames(
new ObjectName("Catalina:type=Connector,*"),
null);
if (theConnectors != null)
{
for (ObjectName nextConnectorName : theConnectors)
{
InetAddress theInetAddress = (InetAddress) mbeanServer.getAttribute(
nextConnectorName,
"address");
if (theInetAddress != null)
{
ipAddress = theInetAddress.getHostAddress();
}
if (!StringUtil.isEmpty(ipAddress))
{
// found the IP address
break;
}
}
}
For my situation, the solution was to get the IP address of the server directly instead of attempting to get the local address via HttpServleRequest.
I cached the IP for use in my filter via:
private static final String serverIp;
static {
String addressString = null;
try
{
InetAddress address = InetAddress.getLocalHost();
addressString = address.getHostAddress();
} catch (Exception e)
{
logger.error("Exception while attempting to determine local ip address",e);
}
if (addressString != null) serverIp = addressString;
else serverIp = "unknown";
}
I had a similar issue recently (a few years after the original question) and found this question and answers. The issue in my case was that the ServletRequest#getLocalAddr() implementation was returning the remote address instead of the local address. The issue was caused by a regression in Tomcat v9.0.22. It was fixed in v9.0.23. See the question and answer here:
https://stackoverflow.com/a/57725039/9602527
I need to connect a java aplication to an MBean server, but I'm having trouble getting JMXConnector to work.
I am getting a NoInitialContextException when I try to run this:
try
{
if(user != null)
{
HashMap environment = new HashMap();
environment.put(JMXConnector.CREDENTIALS, new String[] {user, password});
connector = JMXConnectorFactory.connect(location, environment);
}
else
{
connector = JMXConnectorFactory.connect(location, null);
}
beanServer = connector.getMBeanServerConnection();
}
catch(Exception e)
{
throw new ConnectException("Failed to connect to " + location + ": " + e.getMessage());
}
It happens when I use an username and password, although I cannot test without one because the test server I have has to be authenticated.
Edit: I am using java6 SE. No related jars added.
Any ideas on what I'm doing wrong here? Any help is much appreciated.
You didn't specify which server you are trying to connect, but here is an example for weblogic . I suspect, your JMXServiceURL is not correct, it changes based on Mbeanserver you are trying to connect.
As thinksteep said, you can try that:
Map<String, Object> env = new HashMap<String, Object>();
env.put(JMXConnectorServerFactory.PROTOCOL_PROVIDER_PACKAGES, "com.sun.jmx.remote.protocol");
And later you can connect with:
jmxc = JMXConnectorFactory.connect(new JMXServiceURL(address), env);
I am new to Documentum. I am creating a stand alone java application that needs to connect to a documentum instance (5.3). How would I go about connecting to the instance?
You must install and configure Documentum Foundation Classes (Documentum API) at your client machine, copy dfc.jar at any place in your classpath and at the end read tons of documentation :-)
first your dmcl.ini file must conain the following lines
[DOCBROKER_PRIMARY]
host=<host address or ip of docbroker for your docbase>
port=<port # usually 1489>
then the following code should work for you
String docbaseName = "docbase";
String userName = "user";
String password = "pass";
IDfClientX clientx = new DfClientX();
IDfClient client = clientx.getLocalClient();
IDfSessionManager sMgr = client.newSessionManager();
IDfLoginInfo loginInfoObj = clientx.getLoginInfo();
loginInfoObj.setUser(userName);
loginInfoObj.setPassword(password);
loginInfoObj.setDomain(null);
sMgr.setIdentity(docbaseName, loginInfoObj);
IDfSession session = null;
try
{
session = sMgr.getSession(docbaseName);
// do stuff here
}
finally
{
if(session != null)
{
sMgr.release(session);
}
}