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.HashSet;
014import java.util.Set;
015
016import javax.management.DynamicMBean;
017import javax.management.JMException;
018import javax.management.MBeanServer;
019import javax.management.ObjectInstance;
020import javax.management.ObjectName;
021
022import org.picocontainer.ComponentAdapter;
023import org.picocontainer.PicoContainer;
024import 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 */
033public 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}