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    }