package org.datadog.jmxfetch;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import javax.management.Attribute;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.apache.log4j.Logger;
public class Connection {
private static final long CONNECTION_TIMEOUT = 10000;
private static final long JMX_TIMEOUT = 20;
private final static Logger LOGGER = Logger.getLogger(Connection.class.getName());
private static final ThreadFactory daemonThreadFactory = new DaemonThreadFactory();
private JMXConnector connector;
private MBeanServerConnection mbs;
protected HashMap<String, Object> env;
protected JMXServiceURL address;
private static <T extends Throwable> T initCause(T wrapper, Throwable wrapped) {
wrapper.initCause(wrapped);
return wrapper;
}
public MBeanAttributeInfo[] getAttributesForBean(ObjectName bean_name)
throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException {
return mbs.getMBeanInfo(bean_name).getAttributes();
}
public Set<ObjectName> queryNames(ObjectName name) throws IOException {
String scope = (name != null) ? name.toString() : "*:*";
LOGGER.debug("Querying bean names on scope: " + scope);
return mbs.queryNames(name, null);
}
protected void createConnection() throws IOException {
this.env.put("attribute.remote.x.request.waiting.timeout", CONNECTION_TIMEOUT);
closeConnector();
LOGGER.info("Connecting to: " + this.address);
connector = connectWithTimeout(this.address, this.env);
mbs = connector.getMBeanServerConnection();
}
public Object getAttribute(ObjectName objectName, String attributeName) throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException, IOException {
Object o = mbs.getAttribute(objectName, attributeName);
if (o instanceof javax.management.Attribute){
return ((Attribute)o).getValue();
}
return o;
}
/**
* Connect to a MBean Server with a timeout
* This code comes from this blog post:
* https://weblogs.java.net/blog/emcmanus/archive/2007/05/making_a_jmx_co.html
*/
JMXConnector connectWithTimeout(final JMXServiceURL url, final Map<String, Object> env) throws IOException {
final BlockingQueue<Object> mailbox = new ArrayBlockingQueue<Object>(1);
ExecutorService executor = Executors.newSingleThreadExecutor(daemonThreadFactory);
executor.submit(new Runnable() {
public void run() {
try {
JMXConnector connector = JMXConnectorFactory.connect(url, env);
if (!mailbox.offer(connector)) {
connector.close();
}
} catch (Throwable t) {
mailbox.offer(t);
}
}
});
Object result;
try {
result = mailbox.poll(JMX_TIMEOUT, TimeUnit.SECONDS);
if (result == null) {
if (!mailbox.offer(""))
result = mailbox.take();
}
} catch (InterruptedException e) {
throw initCause(new InterruptedIOException(e.getMessage()), e);
} finally {
executor.shutdown();
}
if (result == null) {
LOGGER.warn("Connection timed out: " + url);
throw new SocketTimeoutException("Connection timed out: " + url);
}
if (result instanceof JMXConnector) {
return (JMXConnector) result;
}
try {
throw (Throwable) result;
} catch (Throwable e) {
throw new IOException(e.toString(), e);
}
}
public void closeConnector() {
if (connector != null) {
try {
connector.close();
} catch (IOException e) {
// ignore
}
}
}
public boolean isAlive() {
if (connector == null) {
return false;
}
try {
connector.getConnectionId();
} catch (IOException e) { // the connection is closed or broken
return false;
}
return true;
}
private static class DaemonThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setDaemon(true);
return t;
}
}
}