/*
* ProActive Parallel Suite(TM):
* The Open Source library for parallel and distributed
* Workflows & Scheduling, Orchestration, Cloud Automation
* and Big Data Analysis on Enterprise Grids & Clouds.
*
* Copyright (c) 2007 - 2017 ActiveEon
* Contact: contact@activeeon.com
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation: version 3 of
* the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If needed, contact us to obtain a release under GPL Version 2 or 3
* or a different license than the AGPL.
*/
package org.ow2.proactive.utils.console;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import org.ow2.proactive.authentication.Authentication;
import org.ow2.proactive.authentication.crypto.Credentials;
import org.ow2.proactive.jmx.JMXClientHelper;
/**
* MBeanInfoViewer is used to handle MBean information display
*
* @author The ProActive Team
* @since ProActive Scheduling 1.0
*/
public final class MBeanInfoViewer {
/** The authentication interface */
private final Authentication auth;
/** The connector environment */
private final HashMap<String, Object> env;
/** The name of the MBean to view */
private ObjectName mbeanName;
/** The connection to the MBeanServer */
private MBeanServerConnection connection;
/** The names of the attributes of the MBean */
private String[] names;
/**
* Creates a new instance of MBeanInfoViewer.
*
* @param auth the authentication interface
* @param user the user that wants to connect to the JMX infrastructure
* @param creds the credentials of the user
*/
public MBeanInfoViewer(final Authentication auth, final String user, final Credentials creds) {
this.auth = auth;
this.env = new HashMap<>(2);
// Fill the env with credentials
this.env.put(JMXConnector.CREDENTIALS, new Object[] { (user == null ? "" : user), creds });
}
private void lazyConnect() {
if (this.connection == null) {
// By default try the connector over RMI
final JMXConnector jmxConnector = JMXClientHelper.tryJMXoverRMI(this.auth, this.env);
try {
this.connection = jmxConnector.getMBeanServerConnection();
} catch (Exception e) {
throw new RuntimeException("Unable create the MBeanInfoViewer about " + mbeanName, e);
}
}
}
/**
* Sets the value of a specific attribute of a named MBean. The MBean
* is identified by its object name as a String.
* The first time this method is called it connects to the JMX connector server.
* The default behavior will try to establish a connection using RMI protocol, if it fails
* the RO (Remote Object) protocol is used.
*
* @param mbeanNameAsString the object name of the MBean
* @param attributeName the name of the attribute
* @param value the new value of the attribute
*/
public void setAttribute(final String mbeanNameAsString, final String attributeName, final Object value) {
this.lazyConnect();
try {
// If new name create a new ObjectName
if (this.mbeanName == null || !this.mbeanName.getCanonicalName().equals(mbeanNameAsString)) {
this.mbeanName = new ObjectName(mbeanNameAsString);
}
this.connection.setAttribute(this.mbeanName, new Attribute(attributeName, value));
this.mbeanName = null;
} catch (Exception e) {
throw new RuntimeException("Unable to set the attribute " + attributeName + " of " + mbeanNameAsString, e);
}
}
/**
* Invokes an operation on an MBean.
* The first time this method is called it connects to the JMX connector server.
* The default behavior will try to establish a connection using RMI protocol, if it fails
* the RO (Remote Object) protocol is used.
*
* @param mbeanNameAsString the object name of the MBean
* @param operationName the name of the operation to invoke
* @param params the array of parameters of the operation
*/
public void invoke(final String mbeanNameAsString, final String operationName, final Object[] params) {
this.lazyConnect();
try {
// If new name create a new ObjectName
if (this.mbeanName == null || !this.mbeanName.getCanonicalName().equals(mbeanNameAsString)) {
this.mbeanName = new ObjectName(mbeanNameAsString);
}
this.connection.invoke(this.mbeanName, operationName, params, null);
} catch (Exception e) {
throw new RuntimeException("Unable to invoke " + operationName + " on " + mbeanNameAsString, e);
}
}
/**
* Return the informations about the Scheduler MBean as a formatted string.
* The first time this method is called it connects to the JMX connector server.
* The default behavior will try to establish a connection using RMI protocol, if it fails
* the RO (Remote Object) protocol is used.
*
* @param mbeanNameAsString the object name of the MBean
* @return the informations about the MBean as a formatted string
*/
@Deprecated
public String getInfo(final String mbeanNameAsString) {
// Lazy initial connection
this.lazyConnect();
try {
int padding = 0;
// If new name create a new ObjectName and refresh attribute names
if (this.mbeanName == null || !this.mbeanName.getCanonicalName().equals(mbeanNameAsString)) {
this.mbeanName = new ObjectName(mbeanNameAsString);
final MBeanAttributeInfo[] attrs = this.connection.getMBeanInfo(this.mbeanName).getAttributes();
this.names = new String[attrs.length];
for (int i = 0; i < attrs.length; i++) {
String name = attrs[i].getName();
if (name.length() > padding) {
padding = name.length();
}
this.names[i] = name;
}
padding += 2;
}
// Get the list of attributes in a single JMX call
final AttributeList list = this.connection.getAttributes(this.mbeanName, this.names);
final StringBuilder out = new StringBuilder();
for (int i = 0; i < this.names.length; i++) {
final String attName = this.names[i];
Object value = "Unavailable";
for (int j = 0; j < list.size(); j++) {
final Attribute a = (Attribute) list.get(j);
if (a.getName().equals(attName)) {
value = a.getValue();
break;
}
}
out.append(String.format(" %1$-" + padding + "s" + value + "\n", attName));
}
return out.toString();
} catch (Exception e) {
throw new RuntimeException("Cannot retrieve JMX informations from Selected Bean", e);
}
}
/**
* Return the informations about the Scheduler MBean as a Map.
* The first time this method is called it connects to the JMX connector server.
* The default behavior will try to establish a connection using RMI protocol, if it fails
* the RO (Remote Object) protocol is used.
*
* @param mbeanNameAsString the object name of the MBean
* @return the informations about the MBean as a formatted string
*/
public Map<String, String> getMappedInfo(final String mbeanNameAsString) {
HashMap<String, String> result = new HashMap<>();
// Lazy initial connection
this.lazyConnect();
try {
// If new name create a new ObjectName and refresh attribute names
if (this.mbeanName == null || !this.mbeanName.getCanonicalName().equals(mbeanNameAsString)) {
this.mbeanName = new ObjectName(mbeanNameAsString);
final MBeanAttributeInfo[] attrs = this.connection.getMBeanInfo(this.mbeanName).getAttributes();
List<String> attrNames = new ArrayList<>(attrs.length - 1);
for (int i = 0; i < attrs.length; i++) {
// reading only primitive or string-type attributes
if (isPrimitive(attrs[i].getType()) || String.class.getName().equals(attrs[i].getType())) {
attrNames.add(attrs[i].getName());
}
}
this.names = attrNames.toArray(new String[] {});
}
// Get the list of attributes in a single JMX call
final AttributeList list = this.connection.getAttributes(this.mbeanName, this.names);
for (int i = 0; i < this.names.length; i++) {
final String attName = this.names[i];
Object value = "Unavailable";
for (int j = 0; j < list.size(); j++) {
final Attribute a = (Attribute) list.get(j);
if (a.getName().equals(attName)) {
value = a.getValue();
break;
}
}
result.put(attName, value.toString());
}
return result;
} catch (Exception e) {
throw new RuntimeException("Cannot retrieve JMX informations from Selected Bean", e);
}
}
/**
* Detects if the type is primitive.
*/
private boolean isPrimitive(String type) {
if (type == null)
return false;
if (type.equals("boolean") || type.equals("byte") || type.equals("character") || type.equals("short") ||
type.equals("int") || type.equals("integer") || type.equals("long") || type.equals("float") ||
type.equals("double")) {
return true;
}
return false;
}
}