001/*****************************************************************************
002 * Copyright (C) NanoContainer Organization. All rights reserved.            *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD      *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file.                                                     *
007 *                                                                           *
008 * Original code by Michael Ward                                                 *
009 *****************************************************************************/
010
011package org.picocontainer.gems.jmx;
012
013import javax.management.DynamicMBean;
014import javax.management.InstanceNotFoundException;
015import javax.management.MBeanException;
016import javax.management.MBeanInfo;
017import javax.management.NotCompliantMBeanException;
018import javax.management.RuntimeOperationsException;
019import javax.management.StandardMBean;
020import javax.management.modelmbean.InvalidTargetObjectTypeException;
021import javax.management.modelmbean.ModelMBean;
022import javax.management.modelmbean.ModelMBeanInfo;
023import javax.management.modelmbean.RequiredModelMBean;
024
025
026/**
027 * A factory for DynamicMBeans, that creates MBean instances using the classes {@link StandardMBean} and
028 * {@link ModelMBean} provided by the JMX specification. The implementation offers special support for StandardMBeans
029 * following the naming convention for their management interface using the class name of the component with an appended
030 * <em>MBean</em>.
031 * @author Michael Ward
032 * @author J&ouml;rg Schaible
033 */
034public class StandardMBeanFactory implements DynamicMBeanFactory {
035
036    /**
037     * Create a StandardMBean for the component.
038     * @param componentInstance {@inheritDoc}
039     * @param management The management interface. If <code>null</code> the implementation will use the interface
040     *            complying with the naming convention for management interfaces.
041     * @param mBeanInfo The {@link MBeanInfo} to use. If <code>null</code> the {@link StandardMBean} will use an
042     *            automatically generated one.
043     * @return Returns a {@link StandardMBean}. If the <strong>mBeanInfo</strong> was not null, it is an instance of a
044     *         {@link StandardNanoMBean}.
045     * @see org.picocontainer.gems.jmx.DynamicMBeanFactory#create(java.lang.Object, java.lang.Class,
046     *      javax.management.MBeanInfo)
047     */
048    public DynamicMBean create(final Object componentInstance, final Class management, final MBeanInfo mBeanInfo) {
049        try {
050            if (mBeanInfo == null) {
051                final Class managementInterface = getManagementInterface(componentInstance.getClass(), management, null);
052                return new StandardMBean(componentInstance, managementInterface);
053            } else if (mBeanInfo instanceof ModelMBeanInfo) {
054                final ModelMBean mBean = new RequiredModelMBean((ModelMBeanInfo)mBeanInfo);
055                try {
056                    mBean.setManagedResource(componentInstance, "ObjectReference");
057                } catch (final InvalidTargetObjectTypeException e) {
058                    // N/A: "ObjectReference" is a valid reference type
059                } catch (final InstanceNotFoundException e) {
060                    // N/A: the instance was a valid object
061                }
062                return mBean;
063            } else {
064                final Class<?> managementInterface = getManagementInterface(
065                        componentInstance.getClass(), management, mBeanInfo);
066                return new StandardNanoMBean(componentInstance, managementInterface, mBeanInfo);
067            }
068        } catch (final ClassNotFoundException e) {
069            throw new JMXRegistrationException("Cannot load management interface for StandardMBean", e);
070        } catch (final NotCompliantMBeanException e) {
071            throw new JMXRegistrationException("Cannot create StandardMBean", e);
072        } catch (final RuntimeOperationsException e) {
073            throw new JMXRegistrationException("Cannot create ModelMBean", e);
074        } catch (final MBeanException e) {
075            throw new JMXRegistrationException("Cannot create ModelMBean", e);
076        }
077    }
078
079    private Class getManagementInterface(final Class type, final Class management, final MBeanInfo mBeanInfo)
080            throws ClassNotFoundException {
081        final Class managementInterface;
082        if (management == null) {
083            managementInterface = getDefaultManagementInterface(type, mBeanInfo);
084        } else {
085            managementInterface = management;
086        }
087        return managementInterface;
088    }
089
090    /**
091     * Determin the management interface for the given type. The class name of the given type is used as class name of
092     * the mBean unless the caller has provided a {@link MBeanInfo}, the class name of the MBean is retrieved a
093     * MBeanInfo that defines this name. Following the naming conventions is the name of the management interface the
094     * same as the class name of the MBean with an appended <em>MBean</em>. The {@link ClassLoader} of the type is
095     * used to load the interface type.
096     * @param type The class of the MBean.
097     * @param mBeanInfo The {@link MBeanInfo} for the MBean. May be <code>null</code>.
098     * @return Returns the default management interface.
099     * @throws ClassNotFoundException If the management interface cannot be found.
100     */
101    public Class getDefaultManagementInterface(final Class type, final MBeanInfo mBeanInfo)
102            throws ClassNotFoundException {
103        final ClassLoader classLoader = type.getClassLoader() != null ? type.getClassLoader() : Thread.currentThread()
104                .getContextClassLoader();
105        return classLoader.loadClass((mBeanInfo == null ? type.getName() : mBeanInfo.getClassName()) + "MBean");
106    }
107}