All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.integration.jmx.DefaultMBeanObjectConverter Maven / Gradle / Ivy

/*
 * Copyright 2013-2016 the original author or authors.
 *
 * 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.springframework.integration.jmx;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.RuntimeMBeanException;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author Stuart Williams
 * @since 3.0
 *
 */
public class DefaultMBeanObjectConverter implements MBeanObjectConverter {

	private static final Log log = LogFactory.getLog(DefaultMBeanObjectConverter.class);

	private final MBeanAttributeFilter filter;

	public DefaultMBeanObjectConverter() {
		this(new DefaultMBeanAttributeFilter());
	}

	public DefaultMBeanObjectConverter(MBeanAttributeFilter filter) {
		this.filter = filter;
	}

	@Override
	public Object convert(MBeanServerConnection connection, ObjectInstance instance) {
		Map attributeMap = new HashMap();

		try {
			ObjectName objName = instance.getObjectName();
			if (!connection.isRegistered(objName)) {
				return attributeMap;
			}

			MBeanInfo info = connection.getMBeanInfo(objName);
			MBeanAttributeInfo[] attributeInfos = info.getAttributes();

			for (MBeanAttributeInfo attrInfo : attributeInfos) {
				// we don't need to repeat name of this as an attribute
				if ("ObjectName".equals(attrInfo.getName()) || !this.filter.accept(objName, attrInfo.getName())) {
					continue;
				}

				Object value;
				try {
					value = connection.getAttribute(objName, attrInfo.getName());
				}
				catch (RuntimeMBeanException e) {
					// N.B. standard MemoryUsage MBeans will throw an exception when some
					// measurement is unsupported. Logging at trace rather than debug to
					// avoid confusion.
					if (log.isTraceEnabled()) {
						log.trace("Error getting attribute '" + attrInfo.getName() + "' on '" + objName + "'", e);
					}

					// try to unwrap the exception somewhat; not sure this is ideal
					Throwable t = e;
					while (t.getCause() != null) {
						t = t.getCause();
					}
					value = String.format("%s[%s]", t.getClass().getName(), t.getMessage());
				}

				attributeMap.put(attrInfo.getName(), checkAndConvert(value));
			}

		}
		catch (Exception e) {
			throw new IllegalArgumentException(e);
		}

		return attributeMap;
	}

	/**
	 * @param input
	 * @return recursively mapped object
	 */
	private Object checkAndConvert(Object input) {
		if (input == null) {
			return input;
		}
		else if (input.getClass().isArray()) {

			if (CompositeData.class.isAssignableFrom(input.getClass().getComponentType())) {
				List converted = new ArrayList();
				int length = Array.getLength(input);
				for (int i = 0; i < length; i++) {
					Object value = checkAndConvert(Array.get(input, i));
					converted.add(value);
				}
				return converted;
			}
			if (TabularData.class.isAssignableFrom(input.getClass().getComponentType())) {
				// TODO haven't hit this yet, but expect to
				log.warn("TabularData.isAssignableFrom(getComponentType) for " + input.toString());
			}
		}
		else if (input instanceof CompositeData) {
			CompositeData data = (CompositeData) input;

			if (data.getCompositeType().isArray()) {
				// TODO? I haven't found an example where this gets thrown - but need to test it on Tomcat/Jetty or
				// something
				log.warn("(data.getCompositeType().isArray for " + input.toString());
			}
			else {
				Map returnable = new HashMap();
				Set keys = data.getCompositeType().keySet();
				for (String key : keys) {
					// we don't need to repeat name of this as an attribute
					if ("ObjectName".equals(key)) {
						continue;
					}
					Object value = checkAndConvert(data.get(key));
					returnable.put(key, value);
				}
				return returnable;
			}
		}
		else if (input instanceof TabularData) {
			TabularData data = (TabularData) input;

			if (data.getTabularType().isArray()) {
				// TODO? I haven't found an example where this gets thrown, so might not be required
				log.warn("TabularData.isArray for " + input.toString());
			}
			else {

				Map returnable = new HashMap();
				@SuppressWarnings("unchecked")
				Set> keySet = (Set>) data.keySet();
				for (List keys : keySet) {
					CompositeData cd = data.get(keys.toArray());
					Object value = checkAndConvert(cd);

					if (keys.size() == 1 && (value instanceof Map) && ((Map) value).size() == 2) {

						Object actualKey = keys.get(0);
						Map valueMap = (Map) value;

						if (valueMap.containsKey("key") && valueMap.containsKey("value")
								&& actualKey.equals(valueMap.get("key"))) {
							returnable.put(valueMap.get("key"), valueMap.get("value"));
						}
						else {
							returnable.put(actualKey, value);
						}
					}
					else {
						returnable.put(keys, value);
					}
				}

				return returnable;
			}
		}

		return input;
	}
}