KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > jmx > export > MBeanExporter


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.jmx.export;
18
19 import java.util.ArrayList JavaDoc;
20 import java.util.Arrays JavaDoc;
21 import java.util.HashMap JavaDoc;
22 import java.util.HashSet JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.Map JavaDoc;
26 import java.util.Set JavaDoc;
27
28 import javax.management.InstanceNotFoundException JavaDoc;
29 import javax.management.JMException JavaDoc;
30 import javax.management.MBeanException JavaDoc;
31 import javax.management.MBeanServer JavaDoc;
32 import javax.management.MalformedObjectNameException JavaDoc;
33 import javax.management.NotificationFilter JavaDoc;
34 import javax.management.NotificationListener JavaDoc;
35 import javax.management.ObjectName JavaDoc;
36 import javax.management.modelmbean.ModelMBean JavaDoc;
37 import javax.management.modelmbean.ModelMBeanInfo JavaDoc;
38 import javax.management.modelmbean.RequiredModelMBean JavaDoc;
39
40 import org.springframework.aop.framework.ProxyFactory;
41 import org.springframework.aop.target.LazyInitTargetSource;
42 import org.springframework.beans.factory.BeanClassLoaderAware;
43 import org.springframework.beans.factory.BeanFactory;
44 import org.springframework.beans.factory.BeanFactoryAware;
45 import org.springframework.beans.factory.DisposableBean;
46 import org.springframework.beans.factory.InitializingBean;
47 import org.springframework.beans.factory.ListableBeanFactory;
48 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
49 import org.springframework.beans.factory.config.BeanDefinition;
50 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
51 import org.springframework.core.Constants;
52 import org.springframework.jmx.export.assembler.AutodetectCapableMBeanInfoAssembler;
53 import org.springframework.jmx.export.assembler.MBeanInfoAssembler;
54 import org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler;
55 import org.springframework.jmx.export.naming.KeyNamingStrategy;
56 import org.springframework.jmx.export.naming.ObjectNamingStrategy;
57 import org.springframework.jmx.export.naming.SelfNaming;
58 import org.springframework.jmx.export.notification.ModelMBeanNotificationPublisher;
59 import org.springframework.jmx.export.notification.NotificationPublisherAware;
60 import org.springframework.jmx.support.JmxUtils;
61 import org.springframework.jmx.support.MBeanRegistrationSupport;
62 import org.springframework.jmx.support.ObjectNameManager;
63 import org.springframework.util.Assert;
64 import org.springframework.util.ClassUtils;
65 import org.springframework.util.CollectionUtils;
66 import org.springframework.util.ObjectUtils;
67
68 /**
69  * JMX exporter that allows for exposing any <i>Spring-managed bean</i>
70  * to a JMX <code>MBeanServer</code>, without the need to define any
71  * JMX-specific information in the bean classes.
72  *
73  * <p>If the bean implements one of the JMX management interfaces,
74  * then MBeanExporter can simply register the MBean with the server
75  * automatically, through its autodetection process.
76  *
77  * <p>If the bean does not implement one of the JMX management interfaces,
78  * then MBeanExporter will create the management information using the
79  * supplied {@link MBeanInfoAssembler} implementation.
80  *
81  * <p>A list of {@link MBeanExporterListener MBeanExporterListeners}
82  * can be registered via the
83  * {@link #setListeners(MBeanExporterListener[]) listeners} property,
84  * allowing application code to be notified of MBean registration and
85  * unregistration events.
86  *
87  * @author Rob Harrop
88  * @author Juergen Hoeller
89  * @author Rick Evans
90  * @since 1.2
91  * @see #setBeans
92  * @see #setAutodetect
93  * @see #setAssembler
94  * @see #setListeners
95  * @see org.springframework.jmx.export.assembler.MBeanInfoAssembler
96  * @see MBeanExporterListener
97  */

98 public class MBeanExporter extends MBeanRegistrationSupport
99         implements MBeanExportOperations, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
100
101     /**
102      * Autodetection mode indicating that no autodetection should be used.
103      */

104     public static final int AUTODETECT_NONE = 0;
105
106     /**
107      * Autodetection mode indicating that only valid MBeans should be autodetected.
108      */

109     public static final int AUTODETECT_MBEAN = 1;
110
111     /**
112      * Autodetection mode indicating that only the {@link MBeanInfoAssembler} should be able
113      * to autodetect beans.
114      */

115     public static final int AUTODETECT_ASSEMBLER = 2;
116
117     /**
118      * Autodetection mode indicating that all autodetection mechanisms should be used.
119      */

120     public static final int AUTODETECT_ALL = AUTODETECT_MBEAN | AUTODETECT_ASSEMBLER;
121
122
123     /**
124      * Wildcard used to map a {@link javax.management.NotificationListener}
125      * to all MBeans registered by the <code>MBeanExporter</code>.
126      */

127     private static final String JavaDoc WILDCARD = "*";
128
129     /** Constant for the JMX <code>mr_type</code> "ObjectReference" */
130     private static final String JavaDoc MR_TYPE_OBJECT_REFERENCE = "ObjectReference";
131
132     /** Prefix for the autodetect constants defined in this class */
133     private static final String JavaDoc CONSTANT_PREFIX_AUTODETECT = "AUTODETECT_";
134
135
136     /** Constants instance for this class */
137     private static final Constants constants = new Constants(MBeanExporter.class);
138
139     /** The beans to be exposed as JMX managed resources, with JMX names as keys */
140     private Map JavaDoc beans;
141
142     /** The autodetect mode to use for this MBeanExporter */
143     private int autodetectMode = AUTODETECT_NONE;
144
145     /** Indicates whether Spring should modify generated ObjectNames */
146     private boolean ensureUniqueRuntimeObjectNames = true;
147
148     /** Indicates whether Spring should expose the managed resource ClassLoader in the MBean */
149     private boolean exposeManagedResourceClassLoader = false;
150
151     /** A set of bean names that should be excluded from autodetection */
152     private Set JavaDoc excludedBeans;
153
154     /** The MBeanExporterListeners registered with this exporter. */
155     private MBeanExporterListener[] listeners;
156
157     /** The NotificationListeners to register for the MBeans registered by this exporter */
158     private NotificationListenerBean[] notificationListeners = new NotificationListenerBean[0];
159
160     /** Stores the MBeanInfoAssembler to use for this exporter */
161     private MBeanInfoAssembler assembler = new SimpleReflectiveMBeanInfoAssembler();
162
163     /** The strategy to use for creating ObjectNames for an object */
164     private ObjectNamingStrategy namingStrategy = new KeyNamingStrategy();
165
166     /** Stores the ClassLoader to use for generating lazy-init proxies */
167     private ClassLoader JavaDoc beanClassLoader = ClassUtils.getDefaultClassLoader();
168
169     /** Stores the BeanFactory for use in autodetection process */
170     private ListableBeanFactory beanFactory;
171
172
173     /**
174      * Supply a <code>Map</code> of beans to be registered with the JMX
175      * <code>MBeanServer</code>.
176      * <p>The String keys are the basis for the creation of JMX object names.
177      * By default, a JMX <code>ObjectName</code> will be created straight
178      * from the given key. This can be customized through specifying a
179      * custom <code>NamingStrategy</code>.
180      * <p>Both bean instances and bean names are allowed as values.
181      * Bean instances are typically linked in through bean references.
182      * Bean names will be resolved as beans in the current factory, respecting
183      * lazy-init markers (that is, not triggering initialization of such beans).
184      * @param beans Map with JMX names as keys and bean instances or bean names
185      * as values
186      * @see #setNamingStrategy
187      * @see org.springframework.jmx.export.naming.KeyNamingStrategy
188      * @see javax.management.ObjectName#ObjectName(String)
189      */

190     public void setBeans(Map JavaDoc beans) {
191         this.beans = beans;
192     }
193
194     /**
195      * Set whether to autodetect MBeans in the bean factory that this exporter
196      * runs in. Will also ask an <code>AutodetectCapableMBeanInfoAssembler</code>
197      * if available.
198      * <p>This feature is turned off by default. Explicitly specify "true" here
199      * to enable autodetection.
200      * @see #setAssembler
201      * @see AutodetectCapableMBeanInfoAssembler
202      * @see #isMBean
203      * @deprecated in favor of {@link #setAutodetectModeName(String)}
204      */

205     public void setAutodetect(boolean autodetect) {
206         this.autodetectMode = (autodetect ? AUTODETECT_ALL : AUTODETECT_NONE);
207     }
208
209     /**
210      * Sets the autodetection mode to use.
211      * @exception IllegalArgumentException if the supplied value is not
212      * one of the <code>AUTODETECT_</code> constants
213      * @see #setAutodetectModeName(String)
214      * @see #AUTODETECT_ALL
215      * @see #AUTODETECT_ASSEMBLER
216      * @see #AUTODETECT_MBEAN
217      * @see #AUTODETECT_NONE
218      */

219     public void setAutodetectMode(int autodetectMode) {
220         if (!constants.getValues(CONSTANT_PREFIX_AUTODETECT).contains(new Integer JavaDoc(autodetectMode))) {
221             throw new IllegalArgumentException JavaDoc("Only values of autodetect constants allowed");
222         }
223         this.autodetectMode = autodetectMode;
224     }
225
226     /**
227      * Sets the autodetection mode to use by name.
228      * @exception IllegalArgumentException if the supplied value is not resolvable
229      * to one of the <code>AUTODETECT_</code> constants or is <code>null</code>
230      * @see #setAutodetectMode(int)
231      * @see #AUTODETECT_ALL
232      * @see #AUTODETECT_ASSEMBLER
233      * @see #AUTODETECT_MBEAN
234      * @see #AUTODETECT_NONE
235      */

236     public void setAutodetectModeName(String JavaDoc constantName) {
237         if (constantName == null || !constantName.startsWith(CONSTANT_PREFIX_AUTODETECT)) {
238             throw new IllegalArgumentException JavaDoc("Only autodetect constants allowed");
239         }
240         this.autodetectMode = constants.asNumber(constantName).intValue();
241     }
242
243     /**
244      * Set the implementation of the <code>MBeanInfoAssembler</code> interface to use
245      * for this exporter. Default is a <code>SimpleReflectiveMBeanInfoAssembler</code>.
246      * <p>The passed-in assembler can optionally implement the
247      * <code>AutodetectCapableMBeanInfoAssembler</code> interface, which enables it
248      * to particiapte in the exporter's MBean autodetection process.
249      * @see org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler
250      * @see org.springframework.jmx.export.assembler.AutodetectCapableMBeanInfoAssembler
251      * @see org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler
252      * @see #setAutodetect
253      */

254     public void setAssembler(MBeanInfoAssembler assembler) {
255         this.assembler = assembler;
256     }
257
258     /**
259      * Set the implementation of the <code>ObjectNamingStrategy</code> interface
260      * to use for this exporter. Default is a <code>KeyNamingStrategy</code>.
261      * @see org.springframework.jmx.export.naming.KeyNamingStrategy
262      * @see org.springframework.jmx.export.naming.MetadataNamingStrategy
263      */

264     public void setNamingStrategy(ObjectNamingStrategy namingStrategy) {
265         this.namingStrategy = namingStrategy;
266     }
267
268     /**
269      * Set the <code>MBeanExporterListener</code>s that should be notified
270      * of MBean registration and unregistration events.
271      * @see MBeanExporterListener
272      */

273     public void setListeners(MBeanExporterListener[] listeners) {
274         this.listeners = listeners;
275     }
276
277     /**
278      * Set the list of names for beans that should be excluded from autodetection.
279      */

280     public void setExcludedBeans(String JavaDoc[] excludedBeans) {
281         this.excludedBeans = (excludedBeans != null ? new HashSet JavaDoc(Arrays.asList(excludedBeans)) : null);
282     }
283
284     /**
285      * Indicates whether Spring should ensure that {@link ObjectName ObjectNames} generated by
286      * the configured {@link ObjectNamingStrategy} for runtime-registered MBeans should be modified
287      * to ensure uniqueness for every instance of managed <code>Class</code>. Default value is
288      * <code>true</code>.
289      * @see JmxUtils#appendIdentityToObjectName(javax.management.ObjectName, Object)
290      */

291     public void setEnsureUniqueRuntimeObjectNames(boolean ensureUniqueRuntimeObjectNames) {
292         this.ensureUniqueRuntimeObjectNames = ensureUniqueRuntimeObjectNames;
293     }
294
295     /**
296      * Indicates whether or not the managed resource should be exposed as the
297      * {@link Thread#getContextClassLoader() thread context ClassLoader} before allowing
298      * any invocations on the MBean to occur. Default value is <code>false</code>.
299      */

300     public void setExposeManagedResourceClassLoader(boolean exposeManagedResourceClassLoader) {
301         this.exposeManagedResourceClassLoader = exposeManagedResourceClassLoader;
302     }
303
304     /**
305      * Set the {@link NotificationListenerBean NotificationListenerBeans} containing the
306      * {@link javax.management.NotificationListener NotificationListeners} that will be registered
307      * with the {@link MBeanServer}.
308      * @see #setNotificationListenerMappings(java.util.Map)
309      * @see NotificationListenerBean
310      */

311     public void setNotificationListeners(NotificationListenerBean[] notificationListeners) {
312         this.notificationListeners = notificationListeners;
313     }
314
315     /**
316      * Set the {@link NotificationListener NotificationListeners} to register with the
317      * {@link javax.management.MBeanServer}. The key of each entry in the <code>Map</code> is
318      * a String representation of the {@link javax.management.ObjectName} of the MBean the
319      * listener should be registered for. Specifying an asterisk (<code>*</code>) will cause
320      * the listener to be associated with all MBeans registered by this class at startup time.
321      * <p>The value of each entry is the {@link javax.management.NotificationListener} to register.
322      * For more advanced options such as registering
323      * {@link javax.management.NotificationFilter NotificationFilters} and
324      * handback objects see {@link #setNotificationListeners(NotificationListenerBean[])}.
325      */

326     public void setNotificationListenerMappings(Map JavaDoc listeners) {
327         Assert.notNull(listeners, "Property 'notificationListenerMappings' must not be null");
328         List JavaDoc notificationListeners = new ArrayList JavaDoc(listeners.size());
329
330         for (Iterator JavaDoc iterator = listeners.entrySet().iterator(); iterator.hasNext();) {
331             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iterator.next();
332
333             // Get the listener from the map value.
334
Object JavaDoc value = entry.getValue();
335             if (!(value instanceof NotificationListener JavaDoc)) {
336                 throw new IllegalArgumentException JavaDoc(
337                         "Map entry value [" + value + "] is not a NotificationListener");
338             }
339             NotificationListenerBean bean = new NotificationListenerBean((NotificationListener JavaDoc) value);
340
341             // Get the ObjectName from the map key.
342
Object JavaDoc key = entry.getKey();
343             if (key != null && !WILDCARD.equals(key)) {
344                 // This listener is mapped to a specific ObjectName.
345
bean.setMappedObjectName(entry.getKey().toString());
346             }
347
348             notificationListeners.add(bean);
349         }
350
351         this.notificationListeners = (NotificationListenerBean[])
352                 notificationListeners.toArray(new NotificationListenerBean[notificationListeners.size()]);
353     }
354
355     public void setBeanClassLoader(ClassLoader JavaDoc classLoader) {
356         this.beanClassLoader = classLoader;
357     }
358
359     /**
360      * This callback is only required for resolution of bean names in the "beans"
361      * <code>Map</code> and for autodetection of MBeans (in the latter case,
362      * a <code>ListableBeanFactory</code> is required).
363      * @see #setBeans
364      * @see #setAutodetect
365      * @see org.springframework.beans.factory.ListableBeanFactory
366      */

367     public void setBeanFactory(BeanFactory beanFactory) {
368         if (beanFactory instanceof ListableBeanFactory) {
369             this.beanFactory = (ListableBeanFactory) beanFactory;
370         }
371         else {
372             logger.info("MBeanExporter not running in a ListableBeanFactory: Autodetection of MBeans not available.");
373         }
374     }
375
376
377     //---------------------------------------------------------------------
378
// Lifecycle in bean factory: automatically register/unregister beans
379
//---------------------------------------------------------------------
380

381     /**
382      * Start bean registration automatically when deployed in an
383      * <code>ApplicationContext</code>.
384      * @see #registerBeans()
385      */

386     public void afterPropertiesSet() {
387         logger.info("Registering beans for JMX exposure on startup");
388         registerBeans();
389     }
390
391     /**
392      * Unregisters all beans that this exported has exposed via JMX
393      * when the enclosing <code>ApplicationContext</code> is destroyed.
394      */

395     public void destroy() {
396         logger.info("Unregistering JMX-exposed beans on shutdown");
397         unregisterBeans();
398     }
399
400
401     //---------------------------------------------------------------------
402
// Implementation of MBeanExportOperations interface
403
//---------------------------------------------------------------------
404

405     public ObjectName JavaDoc registerManagedResource(Object JavaDoc managedResource) throws MBeanExportException {
406         Assert.notNull(managedResource, "Managed resource must not be null");
407         try {
408             ObjectName JavaDoc objectName = getObjectName(managedResource, null);
409             if (this.ensureUniqueRuntimeObjectNames) {
410                 objectName = JmxUtils.appendIdentityToObjectName(objectName, managedResource);
411             }
412             registerManagedResource(managedResource, objectName);
413             return objectName;
414         }
415         catch (MalformedObjectNameException JavaDoc ex) {
416             throw new MBeanExportException("Unable to generate ObjectName for MBean [" + managedResource + "]", ex);
417         }
418     }
419
420     public void registerManagedResource(Object JavaDoc managedResource, ObjectName JavaDoc objectName) throws MBeanExportException {
421         Assert.notNull(managedResource, "Managed resource must not be null");
422         Assert.notNull(objectName, "ObjectName must not be null");
423         Object JavaDoc mbean = null;
424         if (isMBean(managedResource.getClass())) {
425             mbean = managedResource;
426         }
427         else {
428             mbean = createAndConfigureMBean(managedResource, managedResource.getClass().getName());
429         }
430         try {
431             doRegister(mbean, objectName);
432         }
433         catch (JMException JavaDoc ex) {
434             throw new UnableToRegisterMBeanException(
435                     "Unable to register MBean [" + managedResource + "] with object name [" + objectName + "]", ex);
436         }
437     }
438
439
440     //---------------------------------------------------------------------
441
// Exporter implementation
442
//---------------------------------------------------------------------
443

444     /**
445      * Registers the defined beans with the <code>MBeanServer</code>. Each bean is exposed
446      * to the <code>MBeanServer</code> via a <code>ModelMBean</code>. The actual implemetation
447      * of the <code>ModelMBean</code> interface used depends on the implementation of the
448      * <code>ModelMBeanProvider</code> interface that is configured. By default the
449      * <code>RequiredModelMBean</code> class that is supplied with all JMX implementations
450      * is used.
451      * <p>The management interface produced for each bean is dependent on the
452      * <code>MBeanInfoAssembler</code> implementation being used.
453      * The <code>ObjectName</code> given to each bean is dependent on the implementation
454      * of the <code>ObjectNamingStrategy</code> interface being used.
455      */

456     protected void registerBeans() {
457         // If no server was provided then try to find one.
458
// This is useful in an environment such as JDK 1.5, Tomcat
459
// or JBoss where there is already an MBeanServer loaded.
460
if (this.server == null) {
461             this.server = JmxUtils.locateMBeanServer();
462         }
463
464         // The beans property may be <code>null</code>, for example
465
// if we are relying solely on autodetection.
466
if (this.beans == null) {
467             this.beans = new HashMap JavaDoc();
468         }
469
470         // Perform autodetection, if desired.
471
if (this.autodetectMode != AUTODETECT_NONE) {
472             if (this.beanFactory == null) {
473                 throw new MBeanExportException("Cannot autodetect MBeans if not running in a BeanFactory");
474             }
475
476             if (isAutodetectModeEnabled(AUTODETECT_MBEAN)) {
477                 // Autodetect any beans that are already MBeans.
478
logger.info("Autodetecting user-defined JMX MBeans");
479                 autodetectMBeans();
480             }
481
482             // Allow the assembler a chance to vote for bean inclusion.
483
if (isAutodetectModeEnabled(AUTODETECT_ASSEMBLER) &&
484                     this.assembler instanceof AutodetectCapableMBeanInfoAssembler) {
485                 autodetectBeans((AutodetectCapableMBeanInfoAssembler) this.assembler);
486             }
487         }
488
489         // Check we now have at least one bean.
490
if (this.beans.isEmpty()) {
491             throw new IllegalArgumentException JavaDoc("Must specify at least one bean for registration");
492         }
493
494         try {
495             for (Iterator JavaDoc it = this.beans.entrySet().iterator(); it.hasNext();) {
496                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
497                 String JavaDoc beanKey = (String JavaDoc) entry.getKey();
498                 Object JavaDoc value = entry.getValue();
499                 registerBeanNameOrInstance(value, beanKey);
500             }
501
502             // All MBeans are now registered successfully - go ahead and register the notification listeners.
503
registerNotificationListeners();
504         }
505         catch (MBeanExportException ex) {
506             // Unregister beans already registered by this exporter.
507
unregisterBeans();
508             throw ex;
509         }
510     }
511
512     /**
513      * Return whether the specified bean definition should be considered as lazy-init.
514      * @param beanFactory the bean factory that is supposed to contain the bean definition
515      * @param beanName the name of the bean to check
516      * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#getBeanDefinition
517      * @see org.springframework.beans.factory.config.BeanDefinition#isLazyInit
518      */

519     protected boolean isBeanDefinitionLazyInit(ListableBeanFactory beanFactory, String JavaDoc beanName) {
520         if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
521             return false;
522         }
523         try {
524             BeanDefinition bd = ((ConfigurableListableBeanFactory) beanFactory).getBeanDefinition(beanName);
525             return bd.isLazyInit();
526         }
527         catch (NoSuchBeanDefinitionException ex) {
528             // Probably a directly registered singleton.
529
return false;
530         }
531     }
532
533     /**
534      * Registers an individual bean with the <code>MBeanServer</code>. This method
535      * is responsible for deciding <strong>how</strong> a bean should be exposed
536      * to the <code>MBeanServer</code>. Specifically, if the <code>mapValue</code>
537      * is the name of a bean that is configured for lazy initialization, then
538      * a proxy to the resource is registered with the <code>MBeanServer</code>
539      * so that the the lazy load behavior is honored. If the bean is already an
540      * MBean then it will be registered directly with the <code>MBeanServer</code>
541      * without any intervention. For all other beans or bean names, the resource
542      * itself is registered with the <code>MBeanServer</code> directly.
543      * @param beanKey the key associated with this bean in the beans map
544      * @param mapValue the value configured for this bean in the beans map.
545      * May be either the <code>String</code> name of a bean, or the bean itself.
546      * @return the <code>ObjectName</code> under which the resource was registered
547      * @throws MBeanExportException if the export failed
548      * @see #setBeans
549      * @see #registerBeanInstance
550      * @see #registerLazyInit
551      */

552     protected ObjectName JavaDoc registerBeanNameOrInstance(Object JavaDoc mapValue, String JavaDoc beanKey) throws MBeanExportException {
553         try {
554             if (mapValue instanceof String JavaDoc) {
555                 // Bean name pointing to a potentially lazy-init bean in the factory.
556
if (this.beanFactory == null) {
557                     throw new MBeanExportException("Cannot resolve bean names if not running in a BeanFactory");
558                 }
559                 String JavaDoc beanName = (String JavaDoc) mapValue;
560                 if (isBeanDefinitionLazyInit(this.beanFactory, beanName)) {
561                     return registerLazyInit(beanName, beanKey);
562                 }
563                 else {
564                     Object JavaDoc bean = this.beanFactory.getBean(beanName);
565                     return registerBeanInstance(bean, beanKey);
566                 }
567             }
568             else {
569                 // Plain bean instance -> register it directly.
570
return registerBeanInstance(mapValue, beanKey);
571             }
572         }
573         catch (JMException JavaDoc ex) {
574             throw new UnableToRegisterMBeanException(
575                     "Unable to register MBean [" + mapValue + "] with key '" + beanKey + "'", ex);
576         }
577     }
578
579     /**
580      * Registers an existing MBean or an MBean adapter for a plain bean
581      * with the <code>MBeanServer</code>.
582      * @param bean the bean to register, either an MBean or a plain bean
583      * @param beanKey the key associated with this bean in the beans map
584      * @return the <code>ObjectName</code> under which the bean was registered
585      * with the <code>MBeanServer</code>
586      */

587     private ObjectName JavaDoc registerBeanInstance(Object JavaDoc bean, String JavaDoc beanKey) throws JMException JavaDoc {
588         ObjectName JavaDoc objectName = getObjectName(bean, beanKey);
589         if (isMBean(bean.getClass())) {
590             if (logger.isDebugEnabled()) {
591                 logger.debug("Located MBean '" + beanKey + "': registering with JMX server as MBean [" +
592                         objectName + "]");
593             }
594             doRegister(bean, objectName);
595         }
596         else {
597             if (logger.isDebugEnabled()) {
598                 logger.debug("Located simple bean '" + beanKey + "': registering with JMX server as MBean [" +
599                         objectName + "]");
600             }
601             ModelMBean JavaDoc mbean = createAndConfigureMBean(bean, beanKey);
602             doRegister(mbean, objectName);
603             injectNotificationPublisherIfNecessary(bean, mbean, objectName);
604         }
605         return objectName;
606     }
607
608     /**
609      * Registers beans that are configured for lazy initialization with the
610      * <code>MBeanServer<code> indirectly through a proxy.
611      * @param beanName the name of the bean in the <code>BeanFactory</code>
612      * @param beanKey the key associated with this bean in the beans map
613      * @return the <code>ObjectName</code> under which the bean was registered
614      * with the <code>MBeanServer</code>
615      */

616     private ObjectName JavaDoc registerLazyInit(String JavaDoc beanName, String JavaDoc beanKey) throws JMException JavaDoc {
617         ProxyFactory proxyFactory = new ProxyFactory();
618         proxyFactory.setProxyTargetClass(true);
619         proxyFactory.setFrozen(true);
620
621         if (isMBean(this.beanFactory.getType(beanName))) {
622             // A straight MBean... Let's create a simple lazy-init CGLIB proxy for it.
623
LazyInitTargetSource targetSource = new LazyInitTargetSource();
624             targetSource.setTargetBeanName(beanName);
625             targetSource.setBeanFactory(this.beanFactory);
626             proxyFactory.setTargetSource(targetSource);
627
628             Object JavaDoc proxy = proxyFactory.getProxy(this.beanClassLoader);
629             ObjectName JavaDoc objectName = getObjectName(proxy, beanKey);
630             if (logger.isDebugEnabled()) {
631                 logger.debug("Located MBean '" + beanKey + "': registering with JMX server as lazy-init MBean [" +
632                         objectName + "]");
633             }
634             doRegister(proxy, objectName);
635             return objectName;
636         }
637
638         else {
639             // A simple bean... Let's create a lazy-init ModelMBean proxy with notification support.
640
NotificationPublisherAwareLazyTargetSource targetSource = new NotificationPublisherAwareLazyTargetSource();
641             targetSource.setTargetBeanName(beanName);
642             targetSource.setBeanFactory(this.beanFactory);
643             proxyFactory.setTargetSource(targetSource);
644
645             Object JavaDoc proxy = proxyFactory.getProxy(this.beanClassLoader);
646             ObjectName JavaDoc objectName = getObjectName(proxy, beanKey);
647             if (logger.isDebugEnabled()) {
648                 logger.debug("Located simple bean '" + beanKey + "': registering with JMX server as lazy-init MBean [" +
649                         objectName + "]");
650             }
651             ModelMBean JavaDoc mbean = createAndConfigureMBean(proxy, beanKey);
652             targetSource.setModelMBean(mbean);
653             targetSource.setObjectName(objectName);
654             doRegister(mbean, objectName);
655             return objectName;
656         }
657     }
658
659     /**
660      * Retrieve the <code>ObjectName</code> for a bean.
661      * <p>If the bean implements the <code>SelfNaming</code> interface, then the
662      * <code>ObjectName</code> will be retrieved using <code>SelfNaming.getObjectName()</code>.
663      * Otherwise, the configured <code>ObjectNamingStrategy</code> is used.
664      * @param bean the name of the bean in the <code>BeanFactory</code>
665      * @param beanKey the key associated with the bean in the beans map
666      * @return the <code>ObjectName</code> for the supplied bean
667      * @throws javax.management.MalformedObjectNameException
668      * if the retrieved <code>ObjectName</code> is malformed
669      */

670     protected ObjectName JavaDoc getObjectName(Object JavaDoc bean, String JavaDoc beanKey) throws MalformedObjectNameException JavaDoc {
671         if (bean instanceof SelfNaming) {
672             return ((SelfNaming) bean).getObjectName();
673         }
674         else {
675             return this.namingStrategy.getObjectName(bean, beanKey);
676         }
677     }
678
679     /**
680      * Determine whether the given bean class qualifies as an MBean as-is.
681      * <p>The default implementation delegates to {@link JmxUtils#isMBean},
682      * which checks for {@link javax.management.DynamicMBean} classes as well
683      * as classes with corresponding "*MBean" interface (Standard MBeans).
684      * This can be overridden in subclasses, for example to check for
685      * JDK 1.6 MXBeans as well.
686      * @param beanClass the bean class to analyze
687      * @see org.springframework.jmx.support.JmxUtils#isMBean(Class)
688      */

689     protected boolean isMBean(Class JavaDoc beanClass) {
690         return JmxUtils.isMBean(beanClass);
691     }
692
693     /**
694      * Creates an MBean that is configured with the appropriate management
695      * interface for the supplied managed resource.
696      * @param managedResource the resource that is to be exported as an MBean
697      * @param beanKey the key associated with the managed bean
698      * @see #createModelMBean()
699      * @see #getMBeanInfo(Object, String)
700      */

701     protected ModelMBean JavaDoc createAndConfigureMBean(Object JavaDoc managedResource, String JavaDoc beanKey)
702             throws MBeanExportException {
703         try {
704             ModelMBean JavaDoc mbean = createModelMBean();
705             mbean.setModelMBeanInfo(getMBeanInfo(managedResource, beanKey));
706             mbean.setManagedResource(managedResource, MR_TYPE_OBJECT_REFERENCE);
707             return mbean;
708         }
709         catch (Exception JavaDoc ex) {
710             throw new MBeanExportException("Could not create ModelMBean for managed resource [" +
711                     managedResource + "] with key '" + beanKey + "'", ex);
712         }
713     }
714
715     /**
716      * Create an instance of a class that implements <code>ModelMBean</code>.
717      * <p>This method is called to obtain a <code>ModelMBean</code> instance to
718      * use when registering a bean. This method is called once per bean during the
719      * registration phase and must return a new instance of <code>ModelMBean</code>
720      * @return a new instance of a class that implements <code>ModelMBean</code>
721      * @throws javax.management.MBeanException if creation of the ModelMBean failed
722      */

723     protected ModelMBean JavaDoc createModelMBean() throws MBeanException JavaDoc {
724         return (this.exposeManagedResourceClassLoader ? new SpringModelMBean() : new RequiredModelMBean JavaDoc());
725     }
726
727     /**
728      * Gets the <code>ModelMBeanInfo</code> for the bean with the supplied key
729      * and of the supplied type.
730      */

731     private ModelMBeanInfo JavaDoc getMBeanInfo(Object JavaDoc managedBean, String JavaDoc beanKey) throws JMException JavaDoc {
732         ModelMBeanInfo JavaDoc info = this.assembler.getMBeanInfo(managedBean, beanKey);
733         if (logger.isWarnEnabled() && ObjectUtils.isEmpty(info.getAttributes()) &&
734                 ObjectUtils.isEmpty(info.getOperations())) {
735             logger.warn("Bean with key '" + beanKey +
736                     "' has been registered as an MBean but has no exposed attributes or operations");
737         }
738         return info;
739     }
740
741
742     //---------------------------------------------------------------------
743
// Autodetection process
744
//---------------------------------------------------------------------
745

746     /**
747      * Returns <code>true</code> if the particular autodetect mode is enabled
748      * otherwise returns <code>false</code>.
749      */

750     private boolean isAutodetectModeEnabled(int mode) {
751         return (this.autodetectMode & mode) == mode;
752     }
753
754     /**
755      * Invoked when using an <code>AutodetectCapableMBeanInfoAssembler</code>.
756      * Gives the assembler the opportunity to add additional beans from the
757      * <code>BeanFactory</code> to the list of beans to be exposed via JMX.
758      * <p>This implementation prevents a bean from being added to the list
759      * automatically if it has already been added manually, and it prevents
760      * certain internal classes from being registered automatically.
761      */

762     private void autodetectBeans(final AutodetectCapableMBeanInfoAssembler assembler) {
763         autodetect(new AutodetectCallback() {
764             public boolean include(Class JavaDoc beanClass, String JavaDoc beanName) {
765