MBeanInfoCombiner.java

package com.ziesemer.utils.jmx.server;

import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.openmbean.CompositeData;

/**
 * <p>This class is meant to be called by the customization hooks in
 * 		{@link javax.management.StandardMBean}, useful for filling in missing descriptions,
 * 		parameter names, and impacts from another populated {@link MBeanInfo},
 * 		such as one built by {@link MBeanInfoBuilder} that reads annotations.</p>
 * <p>If an instance of this class is stored in a subclass of {@link javax.management.StandardMBean},
 * 		steps should be taken to allow it and its included {@link MBeanInfo} to
 * 		be garbage collected.
 * 	One way to accomplish this is by setting the stored reference to null after calling
 * 		<code>super</code> in either {@link javax.management.StandardMBean#getMBeanInfo()} or
 * 		{@link javax.management.StandardMBean#cacheMBeanInfo(MBeanInfo)}.</p>
 * <p>Following the current limitations of {@link MBeanInfoBuilder}, methods supporting
 * 		{@link javax.management.MBeanConstructorInfo}s are not currently provided,
 * 		as no current need was seen to support constructors remotely.
 * 	For example, JConsole doesn't appear to support constructing object instances at all,
 * 		with or without parameters.
 * 	If you have a need otherwise, please first verify that this is still the current release,
 * 		then open an issue on the issue tracker.</p>
 * <p>This approach should maintain compatibility with Java 1.5/5.0, 1.6 / 6, and beyond.
 * 		This includes:</p>
 * <ul>
 * 	<li>Not extending {@link javax.management.StandardMBean}, which would result in
 * 			hiding the constructor added in 1.6 / 6 that supports MXBeans.</li>
 * 	<li>Allowing {@link javax.management.Descriptor}s to be used in all the
 * 			{@link javax.management.MBeanFeatureInfo} classes, which aren't usable until
 * 			Java 1.6 / 6 when these classes also implement
 * 			<code>javax.management.DescriptorRead</code>.</li>
 * 	<li>Proper and full support of MXBeans, a limitation of this library's {@link SimpleMBean}.</li>
 * </ul>
 * @author Mark A. Ziesemer
 * 	<a href="http://www.ziesemer.com.">&lt;www.ziesemer.com&gt;</a>
 */
public class MBeanInfoCombiner{
	
	protected final MBeanInfo mBeanInfo;
	
	public MBeanInfoCombiner(final MBeanInfo mBeanInfo){
		this.mBeanInfo = mBeanInfo;
	}
	
	public MBeanInfo getMBeanInfo(){
		return mBeanInfo;
	}
	
	/**
	 * <p>Designed to be called from
	 * 	{@link javax.management.StandardMBean#getDescription(MBeanInfo)}.</p>
	 */
	public String getDescription(final MBeanInfo mbi){
		return mBeanInfo.getDescription();
	}
	
	/**
	 * <p>Designed to be called from
	 * 	{@link javax.management.StandardMBean#getDescription(MBeanAttributeInfo)}.</p>
	 */
	public String getDescription(final MBeanAttributeInfo mbai){
		for(final MBeanAttributeInfo customInfo : mBeanInfo.getAttributes()){
			if(customInfo.getName().equals(mbai.getName())){
				return customInfo.getDescription();
			}
		}
		return mbai.getDescription();
	}
	
	/**
	 * <p>Designed to be called from
	 * 	{@link javax.management.StandardMBean#getDescription(MBeanOperationInfo)}.</p>
	 */
	public String getDescription(final MBeanOperationInfo mboi){
		final MBeanOperationInfo customInfo = findMatching(mboi);
		if(customInfo != null){
			return customInfo.getDescription();
		}
		return mboi.getDescription();
	}
	
	/**
	 * <p>Designed to be called from
	 * 	{@link javax.management.StandardMBean#getImpact(MBeanOperationInfo)}.</p>
	 */
	public int getImpact(final MBeanOperationInfo mboi){
		final MBeanOperationInfo customInfo = findMatching(mboi);
		if(customInfo != null){
			return customInfo.getImpact();
		}
		return mboi.getImpact();
	}
	
	/**
	 * <p>Designed to be called from
	 * 	{@link javax.management.StandardMBean#getParameterName(MBeanOperationInfo, MBeanParameterInfo, int)}.</p>
	 */
	public String getParameterName(final MBeanOperationInfo mboi,
			final MBeanParameterInfo mbpi, final int sequence){
		final MBeanOperationInfo customInfo = findMatching(mboi);
		if(customInfo != null){
			return customInfo.getSignature()[sequence].getName();
		}
		return mbpi.getName();
	}
	
	/**
	 * <p>Designed to be called from
	 * 	{@link javax.management.StandardMBean#getDescription(MBeanOperationInfo, MBeanParameterInfo, int)}.</p>
	 */
	public String getDescription(final MBeanOperationInfo mboi,
			final MBeanParameterInfo mbpi, final int sequence){
		final MBeanOperationInfo customInfo = findMatching(mboi);
		if(customInfo != null){
			return customInfo.getSignature()[sequence].getDescription();
		}
		return mbpi.getDescription();
	}
	
	protected MBeanOperationInfo findMatching(final MBeanOperationInfo mboi){
		for(final MBeanOperationInfo customInfo : mBeanInfo.getOperations()){
			if(customInfo.getName().equals(mboi.getName())
					&& (customInfo.getReturnType().equals(mboi.getReturnType())
						|| CompositeData.class.getName().equals(mboi.getReturnType()))){
				final MBeanParameterInfo[] customParams = customInfo.getSignature();
				final MBeanParameterInfo[] params = mboi.getSignature();
				// Can't simply call .equals, as it takes the names and descriptions into account.
				if(customParams.length == params.length){
					boolean matches = true;
					for(int i=0; i < customParams.length && matches; i++){
						matches = customParams[i].getType().equals(params[i].getType());
					}
					if(matches){
						return customInfo;
					}
				}
			}
		}
		return null;
	}
	
}