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
011package org.picocontainer.gems.jmx;
012
013import org.picocontainer.ComponentAdapter;
014import org.picocontainer.PicoContainer;
015import org.picocontainer.PicoCompositionException;
016import org.picocontainer.behaviors.AbstractBehavior;
017import org.picocontainer.behaviors.Cached;
018
019import java.util.List;
020import java.util.ArrayList;
021import java.lang.reflect.Type;
022import javax.management.InstanceAlreadyExistsException;
023import javax.management.MBeanRegistrationException;
024import javax.management.MBeanServer;
025import javax.management.NotCompliantMBeanException;
026import javax.management.ObjectName;
027import 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")
035public 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}