/******************************************************************************* * Copyright (c) 2011 GigaSpaces Technologies Ltd. All rights reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package org.cloudifysource.usm.jmx; import java.io.IOException; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.logging.Level; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.MBeanServerConnection; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import org.cloudifysource.dsl.utils.IPUtils; /** * Generic fetcher for external-process JMX data. * * @author giladh * @since 8.0.1 * */ public class JmxGenericClient { private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(JmxGenericClient.class.getName()); private static final int DEFAULT_JMX_PORT = 8080; private static final String JMX_URL_FORMAT = "service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi"; private int port = DEFAULT_JMX_PORT; private String host = "127.0.0.1"; private ArrayList<JmxBeanAttributes> targetList = new ArrayList<JmxBeanAttributes>(); private String username; private String password; private int numOfTargets; public void setHost(final String host) { this.host = host.trim(); } public void setPort(final int port) { this.port = port; } /******** * * @author barakme * */ private static class JmxBeanAttributes { private String objectName = ""; private final List<JmxAttribute> attributes = new LinkedList<JmxAttribute>(); private final Map<String, JmxAttribute> attributesByName = new HashMap<String, JmxAttribute>(); public JmxBeanAttributes(final String objectName) { this.objectName = objectName; } public String getObjectName() { return objectName; } public void add(final JmxAttribute att) { this.attributes.add(att); this.attributesByName.put(att.getAttributeName(), att); } public String[] getAttributeNames() { final String[] arr = new String[this.attributes.size()]; int i = 0; for (final JmxAttribute att : this.attributes) { arr[i] = att.getAttributeName(); ++i; } return arr; } public JmxAttribute setValueAndReturnAttribute(final String name, final Object value) { final JmxAttribute att = this.attributesByName.get(name); if (att == null) { throw new java.lang.IllegalStateException( "Attempted to set value of JMX attribute that does not exist. Attribute name: " + name + ", bean name: " + this.objectName); } att.setValue(value); return att; } @Override public String toString() { return "JmxBeanAttributes [beanName=" + objectName + ", attributes=" + attributes + "]"; } } public void setTargets(final List<JmxAttribute> list) { // Sort the input list by bean name and attribute Name Collections.sort(list); this.targetList = new ArrayList<JmxGenericClient.JmxBeanAttributes>(); JmxBeanAttributes current = null; for (final JmxAttribute jmxAttribute : list) { if (current == null || !jmxAttribute.getObjectName().equals(current.getObjectName())) { current = new JmxBeanAttributes(jmxAttribute.getObjectName()); this.targetList.add(current); } current.add(jmxAttribute); } this.numOfTargets = list.size(); } public ArrayList<JmxAttribute> getData() { final JMXServiceURL jmxUrl = createJMXServiceURL(); JMXConnector jmxc = null; final Map<String, Object> env = createEnvironment(); final ArrayList<JmxAttribute> resultList = new ArrayList<JmxAttribute>(this.numOfTargets); try { jmxc = JMXConnectorFactory.connect(jmxUrl, env); final MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); for (final JmxBeanAttributes t : targetList) { handleJMXBean(resultList, mbsc, t); } return resultList; } catch (final Exception e) { final String msg = "Failed to fetch JMX values for " + IPUtils.getSafeIpAddress(host) + ":" + port + ". Error: " + e; logger.severe(msg); } finally { if (jmxc != null) { try { jmxc.close(); } catch (final IOException e) { } } } return null; } protected void handleJMXBean(final ArrayList<JmxAttribute> resultList, final MBeanServerConnection mbsc, final JmxBeanAttributes t) throws MalformedObjectNameException { final ObjectName beanName = new ObjectName(t.getObjectName()); final String[] attributeNames = t.getAttributeNames(); try { // This is the remote call! // Object val = mbsc.getAttribute(beanName, attributeNames[0]); final AttributeList vals = mbsc.getAttributes(beanName, attributeNames); for (Object val : vals) { final Attribute att = (Attribute) val; if (att.getValue() instanceof Exception) { logger.log(Level.WARNING, "Failed to read JMX attribute: " + att.getName() + " in bean: " + t.getObjectName(), att.getValue()); } else { final JmxAttribute result = t.setValueAndReturnAttribute(att.getName(), att.getValue()); resultList.add(result); } } } catch (final Exception e) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Failed to read Attributes for JMX Bean: " + t + ": " + e.getMessage()); } if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Failed to read Attributes for JMX Bean: " + t, e); } } } /** * Create a jmx client. * @return a jmx client */ private JMXServiceURL createJMXServiceURL() { try { return new JMXServiceURL(String.format(JMX_URL_FORMAT, host, port)); } catch (final MalformedURLException e) { // none recoverable final String msg = "Failed to create JMXServiceURL for " + host + "," + port + ". Error: " + e; logger.severe(msg); throw new IllegalArgumentException("Failed to initialize JMX Service URL: " + String.format(JMX_URL_FORMAT, host, port), e); } } private Map<String, Object> createEnvironment() { final Map<String, Object> env = new HashMap<String, Object>(); if (this.hasCredentials()) { final String[] credentials = new String[] { username, password }; env.put("jmx.remote.credentials", credentials); } return env; } private boolean hasCredentials() { return this.username != null || this.password != null; } public Map<String, Object> getAttributes() { final ArrayList<JmxAttribute> resArr = getData(); final Map<String, Object> results = new HashMap<String, Object>(); if (resArr == null) { return results; } String name; for (final JmxAttribute t : resArr) { name = t.getDisplayName(); if (name == null || name.isEmpty()) { name = t.getAttributeName(); } results.put(name, t.getValue()); } return results; } public String getUsername() { return username; } public void setUsername(final String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(final String password) { this.password = password; } }