    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
    011    package org.picocontainer.gems.jmx;
    012
    013    import java.util.HashSet;
    014    import java.util.Set;
    015
    016    import javax.management.DynamicMBean;
    017    import javax.management.JMException;
    018    import javax.management.MBeanServer;
    019    import javax.management.ObjectInstance;
    020    import javax.management.ObjectName;
    021
    022    import org.picocontainer.ComponentAdapter;
    023    import org.picocontainer.PicoContainer;
    024    import org.picocontainer.visitors.TraversalCheckingVisitor;
    025
    026
    027    /**
    028     * A {@link org.picocontainer.PicoVisitor} to register JMX components for components of a {@link PicoContainer} tree in
    029     * a {@link MBeanServer}.
    030     * @author Michael Ward
    031     * @author J&ouml;rg Schaible
    032     */
    033    public class JMXVisitor extends TraversalCheckingVisitor {
    034        private final DynamicMBeanProvider[] mBeanProviders;
    035        private final MBeanServer mBeanServer;
    036        private final Set visited;
    037        private final Set registeredInfo;
    038        private PicoContainer picoContainer;
    039
    040        /**
    041         * Construct a JMXVisitor. This instance will register by default any component in the {@link MBeanServer}, that is
    042         * already a {@link DynamicMBean}. The {@link ObjectName} will use the default domain of the MBeanServer and has a
    043         * <em>type</em> key with the class name (without package name) as value.
    044         * @param server The {@link MBeanServer}to use for registering the MBeans.
    045         */
    046        public JMXVisitor(final MBeanServer server) {
    047            this(server, new DynamicMBeanProvider[]{new DynamicMBeanComponentProvider()});
    048        }
    049
    050        /**
    051         * Construct a JMXVisitor.
    052         * @param server The {@link MBeanServer} to use for registering the MBeans.
    053         * @param providers The providers to deliver the DynamicMBeans.
    054         */
    055        public JMXVisitor(final MBeanServer server, final DynamicMBeanProvider[] providers) {
    056            if (server == null) {
    057                throw new NullPointerException("MBeanServer may not be null");
    058            }
    059            if (providers == null) {
    060                throw new NullPointerException("DynamicMBeanProvider[] may not be null");
    061            }
    062            if (providers.length == 0) {
    063                throw new IllegalArgumentException("DynamicMBeanProvider[] may not be empty");
    064            }
    065            mBeanServer = server;
    066            mBeanProviders = providers;
    067            visited = new HashSet();
    068            registeredInfo = new HashSet();
    069        }
    070
    071        /**
    072         * Entry point for the visitor traversal.
    073         * @return Returns a {@link Set} with all ObjectInstance instances retrieved from the {@link MBeanServer} for the
    074         *         registered MBeans.
    075         * @see org.picocontainer.visitors.AbstractPicoVisitor#traverse(java.lang.Object)
    076         */
    077        @Override
    078            public Object traverse(final Object node) {
    079            super.traverse(node);
    080            picoContainer = null;
    081            final Set set = new HashSet(registeredInfo);
    082            registeredInfo.clear();
    083            return set;
    084        }
    085
    086        /**
    087         * Provides the PicoContainer, that can resolve the components to register as MBean.
    088         * @see org.picocontainer.PicoVisitor#visitContainer(org.picocontainer.PicoContainer)
    089         */
    090        @Override
    091            public boolean visitContainer(final PicoContainer pico) {
    092            super.visitContainer(pico);
    093            picoContainer = pico;
    094            visited.clear();
    095            return CONTINUE_TRAVERSAL;
    096        }
    097
    098        /**
    099         * Register the component as MBean. The implementation uses the known DynamicMBeanProvider instances to get the
    100         * MBean from the component.
    101         * @see org.picocontainer.PicoVisitor#visitComponentAdapter(org.picocontainer.ComponentAdapter)
    102         */
    103        @Override
    104            public void visitComponentAdapter(final ComponentAdapter componentAdapter) {
    105            super.visitComponentAdapter(componentAdapter);
    106            if (picoContainer == null) {
    107                throw new JMXRegistrationException("Cannot start JMXVisitor traversal with a ComponentAdapter");
    108            }
    109            if (!visited.contains(componentAdapter.getComponentKey())) {
    110                visited.add(componentAdapter.getComponentKey());
    111                for (final DynamicMBeanProvider provider : mBeanProviders) {
    112                    final JMXRegistrationInfo info = provider.provide(picoContainer, componentAdapter);
    113                    if (info != null) {
    114                        registeredInfo.add(register(info.getMBean(), info.getObjectName()));
    115                        break;
    116                    }
    117                }
    118            }
    119
    120        }
    121
    122        /**
    123         * Register a MBean in the MBeanServer.
    124         * @param dynamicMBean the {@link DynamicMBean} to register.
    125         * @param objectName the {@link ObjectName} of the MBean registered the {@link MBeanServer}.
    126         * @return Returns the {@link ObjectInstance} returned from the MBeanServer after registration.
    127         * @throws JMXRegistrationException Thrown if MBean cannot be registered.
    128         */
    129        protected ObjectInstance register(final DynamicMBean dynamicMBean, final ObjectName objectName)
    130                throws JMXRegistrationException {
    131            try {
    132                return mBeanServer.registerMBean(dynamicMBean, objectName);
    133            } catch (final JMException e) {
    134                throw new JMXRegistrationException("Unable to register MBean to MBean Server", e);
    135            }
    136        }
    137    }