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ö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    }