    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&ouml;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    }