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 Joerg Schaible *
009 *****************************************************************************/
010
011 package org.picocontainer.gems.jmx;
012
013 import org.picocontainer.ComponentAdapter;
014 import org.picocontainer.PicoContainer;
015 import org.picocontainer.PicoCompositionException;
016 import org.picocontainer.behaviors.AbstractBehavior;
017 import org.picocontainer.behaviors.Cached;
018
019 import java.util.List;
020 import java.util.ArrayList;
021 import java.lang.reflect.Type;
022 import javax.management.InstanceAlreadyExistsException;
023 import javax.management.MBeanRegistrationException;
024 import javax.management.MBeanServer;
025 import javax.management.NotCompliantMBeanException;
026 import javax.management.ObjectName;
027 import javax.management.InstanceNotFoundException;
028
029
030 /**
031 * {@link ComponentAdapter} that is exposing a component as MBean in a MBeanServer.
032 * @author Jörg Schaible
033 */
034 @SuppressWarnings("serial")
035 public class JMXExposed<T> extends AbstractBehavior<T> {
036
037
038 private final MBeanServer mBeanServer;
039 private final DynamicMBeanProvider[] providers;
040 private List<ObjectName> registeredObjectNames;
041
042 /**
043 * Construct a JMXExposed behaviour
044 * @param delegate The delegated {@link ComponentAdapter}.
045 * @param mBeanServer The {@link MBeanServer} used for registering the MBean.
046 * @param providers An array with providers for converting the component instance into a
047 * {@link javax.management.DynamicMBean}.
048 * @throws NullPointerException Thrown if the {@link MBeanServer} or the array with the {@link DynamicMBeanProvider}
049 * instances is null.
050 */
051 public JMXExposed(
052 final ComponentAdapter<T> delegate, final MBeanServer mBeanServer, final DynamicMBeanProvider[] providers)
053 throws NullPointerException {
054 super(delegate);
055 if (mBeanServer == null || providers == null) {
056 throw new NullPointerException();
057 }
058 this.mBeanServer = mBeanServer;
059 this.providers = providers;
060 }
061
062 /**
063 * Construct a JMXExposed behaviour. This instance uses a {@link DynamicMBeanComponentProvider} as default to
064 * register any component instance in the {@link MBeanServer}, that is already a
065 * {@link javax.management.DynamicMBean}.
066 * @param delegate The delegated {@link ComponentAdapter}.
067 * @param mBeanServer The {@link MBeanServer} used for registering the MBean.
068 * @throws NullPointerException Thrown if the {@link MBeanServer} or the array with the {@link DynamicMBeanProvider}
069 * instances is null.
070 */
071 public JMXExposed(final ComponentAdapter<T> delegate, final MBeanServer mBeanServer)
072 throws NullPointerException {
073 this(delegate, mBeanServer, new DynamicMBeanProvider[]{new DynamicMBeanComponentProvider()});
074 }
075
076 /**
077 * Retrieve the component instance. The implementation will automatically register it in the {@link MBeanServer},
078 * if a provider can return a {@link javax.management.DynamicMBean} for it.
079 * <p>
080 * Note, that you will have to wrap this {@link ComponentAdapter} with a {@link Cached} to avoid
081 * the registration of the same component again.
082 * </p>
083 * @throws PicoCompositionException Thrown by the delegate or if the registering of the
084 * {@link javax.management.DynamicMBean} in the {@link MBeanServer } fails.
085 * @see AbstractBehavior#getComponentInstance(org.picocontainer.PicoContainer, java.lang.Class)
086 */
087 @Override
088 public T getComponentInstance(final PicoContainer container, final Type into)
089 throws PicoCompositionException
090 {
091 final ComponentAdapter<T> componentAdapter = new Cached<T>(getDelegate());
092
093 final T componentInstance = componentAdapter.getComponentInstance(container, into);
094
095 for (DynamicMBeanProvider provider : providers) {
096 final JMXRegistrationInfo info = provider.provide(container, componentAdapter);
097 if (info != null) {
098 Exception exception = null;
099 try {
100 mBeanServer.registerMBean(info.getMBean(), info.getObjectName());
101 } catch (final InstanceAlreadyExistsException e) {
102 exception = e;
103 } catch (final MBeanRegistrationException e) {
104 exception = e;
105 } catch (final NotCompliantMBeanException e) {
106 exception = e;
107 }
108 if (null == registeredObjectNames) {
109 registeredObjectNames = new ArrayList<ObjectName>();
110 }
111 registeredObjectNames.add(info.getObjectName());
112 if (exception != null) {
113 throw new PicoCompositionException("Registering MBean failed", exception);
114 }
115 }
116 }
117 return componentInstance;
118 }
119
120 public String getDescriptor() {
121 return "ExposedJMX";
122 }
123
124 @Override
125 public void dispose(final Object component) {
126 if( null != registeredObjectNames ) {
127 for (Object registeredObjectName : registeredObjectNames) {
128 try {
129 mBeanServer.unregisterMBean((ObjectName)registeredObjectName);
130 } catch (InstanceNotFoundException e) {
131 throw new JMXRegistrationException(e);
132 } catch (MBeanRegistrationException e) {
133 throw new JMXRegistrationException(e);
134 }
135 }
136 }
137
138 if( super.hasLifecycle( getComponentImplementation( ) ) ) {
139 super.dispose(component);
140 }
141 }
142
143 @Override
144 public boolean hasLifecycle( final Class<?> type ) {
145 return true;
146 }
147
148 }