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 java.util.HashMap;
014import java.util.Map;
015
016import javax.management.DynamicMBean;
017import javax.management.MBeanInfo;
018import javax.management.ObjectName;
019
020import org.picocontainer.ComponentAdapter;
021import org.picocontainer.PicoContainer;
022
023
024/**
025 * A DynamicMBeanProvider, that creates DynamicMBeans for registered Pico components on the fly.
026 * @author Michael Ward
027 * @author Jörg Schaible
028 */
029public class RegisteredMBeanConstructingProvider implements DynamicMBeanProvider {
030
031    private final DynamicMBeanFactory factory;
032    private final Map registry;
033
034    /**
035     * Construct a RegisteredMBeanConstructingProvider with a {@link StandardMBeanFactory} as default.
036     */
037    public RegisteredMBeanConstructingProvider() {
038        this(new StandardMBeanFactory());
039    }
040
041    /**
042     * Construct a RegisteredMBeanConstructingProvider, that uses a specific {@link DynamicMBeanFactory}.
043     * @param factory
044     */
045    public RegisteredMBeanConstructingProvider(final DynamicMBeanFactory factory) {
046        this.factory = factory;
047        this.registry = new HashMap();
048    }
049
050    /**
051     * Provide a DynamicMBean for the given Pico component. The implementation will lookup the component's key in the
052     * internal registry. Only components that were registered with additional information will be considered and a
053     * {@link DynamicMBean} will be created for them using the {@link DynamicMBeanFactory}. If the component key is of
054     * type class, it is used as management interface.
055     * @see org.picocontainer.gems.jmx.DynamicMBeanProvider#provide(PicoContainer, ComponentAdapter)
056     */
057    public JMXRegistrationInfo provide(final PicoContainer picoContainer, final ComponentAdapter componentAdapter) {
058        final Object key = componentAdapter.getComponentKey();
059        final MBeanInfoWrapper wrapper = (MBeanInfoWrapper)registry.get(key);
060        if (wrapper != null) {
061            final Object instance = componentAdapter.getComponentInstance(picoContainer, ComponentAdapter.NOTHING.class);
062            final Class management = wrapper.getManagementInterface() != null
063                                                                             ? wrapper.getManagementInterface()
064                                                                             : key instanceof Class
065                                                                                                   ? (Class)key
066                                                                                                   : instance
067                                                                                                           .getClass();
068            final DynamicMBean mBean = factory.create(instance, management, wrapper.getMBeanInfo());
069            return new JMXRegistrationInfo(wrapper.getObjectName(), mBean);
070        }
071        return null;
072    }
073
074    /**
075     * Register a specific Pico component by key with an MBeanInfo and an ObjectName.
076     * @param componentKey The key of the Pico component.
077     * @param objectName The {@link ObjectName} of the MBean.
078     * @param management The management interface.
079     * @param mBeanInfo The {@link MBeanInfo} of the MBean.
080     */
081    public void register(
082            final Object componentKey, final ObjectName objectName, final Class management, final MBeanInfo mBeanInfo) {
083        registry.put(componentKey, new MBeanInfoWrapper(mBeanInfo, objectName, management));
084    }
085
086    /**
087     * Register a specific Pico component by key with an MBeanInfo and an ObjectName.
088     * @param componentKey The key of the Pico component.
089     * @param objectName The {@link ObjectName} of the MBean.
090     * @param mBeanInfo The {@link MBeanInfo} of the MBean.
091     */
092    public void register(final Object componentKey, final ObjectName objectName, final MBeanInfo mBeanInfo) {
093        register(componentKey, objectName, null, mBeanInfo);
094    }
095
096    /**
097     * Register a specific Pico component with an MBeanInfo and an ObjectName. The implementation class of the
098     * {@link DynamicMBean} must be the key of the Pico component.
099     * @param objectName The {@link ObjectName} of the MBean.
100     * @param mBeanInfo The {@link MBeanInfo} of the MBean.
101     */
102    public void register(final ObjectName objectName, final MBeanInfo mBeanInfo) {
103        try {
104            register(getClass().getClassLoader().loadClass(mBeanInfo.getClassName()), objectName, mBeanInfo);
105        } catch (final ClassNotFoundException e) {
106            throw new JMXRegistrationException("Cannot access class " + mBeanInfo.getClassName() + " of MBean", e);
107        }
108    }
109
110    /**
111     * Register a specific Pico component by key with an ObjectName.
112     * @param componentKey The key of the Pico component.
113     * @param objectName The {@link ObjectName} of the MBean.
114     */
115    public void register(final Object componentKey, final ObjectName objectName) {
116        registry.put(componentKey, new MBeanInfoWrapper(null, objectName, null));
117    }
118
119    /**
120     * Simple wrapper to tie a MBeanInfo to an ObjectName
121     */
122    private static class MBeanInfoWrapper {
123        private final MBeanInfo mBeanInfo;
124        private final ObjectName objectName;
125        private final Class managementInterface;
126
127        MBeanInfoWrapper(final MBeanInfo mBeanInfo, final ObjectName objectName, final Class management) {
128            this.mBeanInfo = mBeanInfo;
129            this.objectName = objectName;
130            this.managementInterface = management;
131        }
132
133        MBeanInfo getMBeanInfo() {
134            return mBeanInfo;
135        }
136
137        ObjectName getObjectName() {
138            return objectName;
139        }
140
141        Class getManagementInterface() {
142            return managementInterface;
143        }
144    }
145
146}