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 }