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 final T componentInstance = componentAdapter.getComponentInstance(container, into); 093 for (DynamicMBeanProvider provider : providers) { 094 final JMXRegistrationInfo info = provider.provide(container, componentAdapter); 095 if (info != null) { 096 Exception exception = null; 097 try { 098 mBeanServer.registerMBean(info.getMBean(), info.getObjectName()); 099 } catch (final InstanceAlreadyExistsException e) { 100 exception = e; 101 } catch (final MBeanRegistrationException e) { 102 exception = e; 103 } catch (final NotCompliantMBeanException e) { 104 exception = e; 105 } 106 if (null == registeredObjectNames) { 107 registeredObjectNames = new ArrayList<ObjectName>(); 108 } 109 registeredObjectNames.add(info.getObjectName()); 110 if (exception != null) { 111 throw new PicoCompositionException("Registering MBean failed", exception); 112 } 113 } 114 } 115 return componentInstance; 116 } 117 118 public String getDescriptor() { 119 return "ExposedJMX"; 120 } 121 122 @Override 123 public void dispose(final Object component) { 124 if( null != registeredObjectNames ) { 125 for (Object registeredObjectName : registeredObjectNames) { 126 try { 127 mBeanServer.unregisterMBean((ObjectName)registeredObjectName); 128 } catch (InstanceNotFoundException e) { 129 throw new JMXRegistrationException(e); 130 } catch (MBeanRegistrationException e) { 131 throw new JMXRegistrationException(e); 132 } 133 } 134 } 135 136 if( super.hasLifecycle( getComponentImplementation( ) ) ) { 137 super.dispose(component); 138 } 139 } 140 141 @Override 142 public boolean hasLifecycle( final Class<?> type ) { 143 return true; 144 } 145 146 }