package org.infinispan.cli.connection.jmx;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.infinispan.cli.CommandBuffer;
import org.infinispan.cli.Context;
import org.infinispan.cli.commands.ProcessedCommand;
import org.infinispan.cli.connection.Connection;
public class JMXConnection implements Connection {
private static final QueryExp INTERPRETER_QUERY = createObjectName("*:type=CacheManager,component=Interpreter,name=*");
private JMXConnector jmxConnector;
private Map<String, ObjectInstance> cacheManagers;
private Map<String, String> sessions;
private String activeCacheManager;
private String activeCache;
private final JMXUrl serviceUrl;
private MBeanServerConnection mbsc;
public JMXConnection(final JMXUrl serviceUrl) {
this.serviceUrl = serviceUrl;
}
@Override
public boolean needsCredentials() {
return serviceUrl.needsCredentials();
}
@Override
public void connect(String credentials) throws Exception {
JMXServiceURL url = new JMXServiceURL(serviceUrl.getJMXServiceURL());
try {
jmxConnector = JMXConnectorFactory.connect(url, serviceUrl.getConnectionEnvironment(credentials));
} catch (Throwable t) {
throw new Exception(t);
}
mbsc = jmxConnector.getMBeanServerConnection();
cacheManagers = new TreeMap<String, ObjectInstance>();
for (ObjectInstance mbean : mbsc.queryMBeans(null, INTERPRETER_QUERY)) {
if ("org.infinispan.cli.interpreter.Interpreter".equals(mbean.getClassName())) {
cacheManagers.put(unquote(mbean.getObjectName().getKeyProperty("name")), mbean);
}
}
if (cacheManagers.size() == 0) {
throw new IllegalArgumentException("The remote server does not expose any CacheManagers");
}
sessions = new HashMap<String, String>();
cacheManagers = Collections.unmodifiableMap(cacheManagers);
activeCacheManager = serviceUrl.getContainer();
if (activeCacheManager == null) {
activeCacheManager = cacheManagers.keySet().iterator().next();
} else if (!cacheManagers.containsKey(activeCacheManager)) {
throw new Exception("No such container: " + activeCacheManager);
}
activeCache = serviceUrl.getCache();
if (activeCache != null) {
try {
getSession(cacheManagers.get(activeCacheManager));
} catch (MBeanException e) {
Throwable ue = unwrapException(e);
throw new Exception(ue.getMessage(), ue);
}
}
}
private Throwable unwrapException(MBeanException e) {
Exception te = e.getTargetException();
if (te != null) {
if (te instanceof InvocationTargetException) {
return ((InvocationTargetException) te).getCause();
} else {
return te;
}
} else {
return e;
}
}
@Override
public boolean isConnected() {
return jmxConnector != null;
}
@Override
public void close() throws IOException {
if (jmxConnector != null) {
try {
jmxConnector.close();
} catch (IOException e) {
// Ignore
} finally {
mbsc = null;
jmxConnector = null;
}
}
}
@Override
public String toString() {
return serviceUrl.toString();
}
@Override
public String getActiveCache() {
return activeCache;
}
@Override
public Collection<String> getAvailableContainers() {
return cacheManagers.keySet();
}
@Override
public String getActiveContainer() {
return activeCacheManager;
}
@Override
public void setActiveContainer(String name) {
if (cacheManagers.containsKey(name)) {
activeCacheManager = name;
} else {
throw new IllegalArgumentException(name);
}
}
@Override
public Collection<String> getAvailableCaches() {
ObjectInstance manager = cacheManagers.get(activeCacheManager);
try {
String[] cacheNames = (String[]) mbsc.getAttribute(manager.getObjectName(), "cacheNames");
List<String> cacheList = Arrays.asList(cacheNames);
Collections.sort(cacheList);
return cacheList;
} catch (Exception e) {
return Collections.emptyList();
}
}
@Override
public void execute(Context context, CommandBuffer commandBuffer) {
ObjectInstance manager = cacheManagers.get(activeCacheManager);
// Shallow copy of the commands to avoid being cleared when the buffer is reset
List<ProcessedCommand> bufferedCommands = new ArrayList<ProcessedCommand>(commandBuffer.getBufferedCommands());
try {
String sessionId = getSession(manager);
Map<String, String> response = (Map<String, String>) mbsc.invoke(manager.getObjectName(), "execute", new String[] { sessionId, commandBuffer.toString() },
new String[] { String.class.getName(), String.class.getName() });
if (response.containsKey("OUTPUT")) {
context.result(bufferedCommands, response.get("OUTPUT"), false);
}
if (response.containsKey("ERROR")) {
context.result(bufferedCommands, response.get("ERROR"), true);
}
if (response.containsKey("CACHE")) {
activeCache = response.get("CACHE");
}
} catch (InstanceNotFoundException e) {
context.result(bufferedCommands, e.toString(), true);
} catch (MBeanException e) {
Exception te = e.getTargetException();
context.result(bufferedCommands, te.getCause() != null ? te.getCause().toString() : te.toString(), true);
} catch (ReflectionException e) {
context.result(bufferedCommands, e.toString(), true);
} catch (IOException e) {
context.result(bufferedCommands, e.toString(), true);
}
}
private String getSession(ObjectInstance manager) throws InstanceNotFoundException, MBeanException, ReflectionException, IOException {
String sessionId = sessions.get(activeCacheManager);
if (sessionId == null) {
sessionId = (String) mbsc.invoke(manager.getObjectName(), "createSessionId", new Object[] { activeCache }, new String[] { String.class.getName() });
sessions.put(activeCacheManager, sessionId);
}
return sessionId;
}
private static ObjectName createObjectName(String name) {
try {
return new ObjectName(name);
} catch (MalformedObjectNameException e) {
throw new RuntimeException(e);
}
}
public static String unquote(String s) {
if (s != null && ((s.startsWith("\"") && s.endsWith("\"")) || (s.startsWith("'") && s.endsWith("'")))) {
s = s.substring(1, s.length() - 1);
}
return s;
}
}