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 }