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                 return assembler.includeBean(beanClass, beanName);
766             }
767         });
768     }
769
770     /**
771      * Attempts to detect any beans defined in the <code>ApplicationContext</code> that are
772      * valid MBeans and registers them automatically with the <code>MBeanServer</code>.
773      */

774     private void autodetectMBeans() {
775         autodetect(new AutodetectCallback() {
776             public boolean include(Class JavaDoc beanClass, String JavaDoc beanName) {
777                 return isMBean(beanClass);
778             }
779         });
780     }
781
782     /**
783      * Performs the actual autodetection process, delegating to an
784      * <code>AutodetectCallback</code> instance to vote on the inclusion of a
785      * given bean.
786      * @param callback the <code>AutodetectCallback</code> to use when deciding
787      * whether to include a bean or not
788      */

789     private void autodetect(AutodetectCallback callback) {
790         String JavaDoc[] beanNames = this.beanFactory.getBeanNamesForType(null);
791         for (int i = 0; i < beanNames.length; i++) {
792             String JavaDoc beanName = beanNames[i];
793             if (!isExcluded(beanName)) {
794                 Class JavaDoc beanClass = this.beanFactory.getType(beanName);
795                 if (beanClass != null && callback.include(beanClass, beanName)) {
796                     boolean lazyInit = isBeanDefinitionLazyInit(this.beanFactory, beanName);
797                     Object JavaDoc beanInstance = (!lazyInit ? this.beanFactory.getBean(beanName) : null);
798                     if (!this.beans.containsValue(beanName) &&
799                             (beanInstance == null || !CollectionUtils.containsInstance(this.beans.values(), beanInstance))) {
800                         // Not already registered for JMX exposure.
801
this.beans.put(beanName, (beanInstance != null ? beanInstance : beanName));
802                         if (logger.isInfoEnabled()) {
803                             logger.info("Bean with name '" + beanName + "' has been autodetected for JMX exposure");
804                         }
805                     }
806                     else {
807                         if (logger.isDebugEnabled()) {
808                             logger.debug("Bean with name '" + beanName + "' is already registered for JMX exposure");
809                         }
810                     }
811                 }
812             }
813         }
814     }
815
816     /**
817      * Indicates whether or not a particular bean name is present in the excluded beans list.
818      */

819     private boolean isExcluded(String JavaDoc beanName) {
820         return (this.excludedBeans != null && this.excludedBeans.contains(beanName));
821     }
822
823
824     //---------------------------------------------------------------------
825
// Management of notification listeners
826
//---------------------------------------------------------------------
827

828     /**
829      * If the supplied managed resource implements the {@link NotificationPublisherAware} an instance of
830      * {@link org.springframework.jmx.export.notification.NotificationPublisher} is injected.
831      */

832     private void injectNotificationPublisherIfNecessary(
833             Object JavaDoc managedResource, ModelMBean JavaDoc modelMBean, ObjectName JavaDoc objectName) {
834         if (managedResource instanceof NotificationPublisherAware) {
835             ((NotificationPublisherAware) managedResource).setNotificationPublisher(
836                     new ModelMBeanNotificationPublisher(modelMBean, objectName, managedResource));
837         }
838     }
839
840     /**
841      * Register the configured {@link NotificationListener NotificationListeners}
842      * with the {@link MBeanServer}.
843      */

844     private void registerNotificationListeners() throws MBeanExportException {
845         for (int i = 0; i < this.notificationListeners.length; i++) {
846             NotificationListenerBean bean = this.notificationListeners[i];
847             NotificationListener JavaDoc listener = bean.getNotificationListener();
848             NotificationFilter JavaDoc filter = bean.getNotificationFilter();
849             Object JavaDoc handback = bean.getHandback();
850             ObjectName JavaDoc[] namesToRegisterWith = getObjectNamesForNotificationListener(bean);
851             for (int j = 0; j < namesToRegisterWith.length; j++) {
852                 ObjectName JavaDoc objectName = namesToRegisterWith[j];
853                 try {
854                     this.server.addNotificationListener(objectName, listener, filter, handback);
855                 }
856                 catch (InstanceNotFoundException JavaDoc ex) {
857                     throw new MBeanExportException("Unable to register NotificationListener for MBean [" +
858                             objectName + "] because that MBean instance does not exist", ex);
859                 }
860             }
861         }
862     }
863
864     /**
865      * Retrieve the {@link javax.management.ObjectName ObjectNames} for which a
866      * {@link NotificationListener} should be registered.
867      */

868     private ObjectName JavaDoc[] getObjectNamesForNotificationListener(NotificationListenerBean bean)
869             throws MBeanExportException {
870
871         String JavaDoc[] mappedObjectNames = bean.getMappedObjectNames();
872         if (mappedObjectNames != null) {
873             ObjectName JavaDoc[] objectNames = new ObjectName JavaDoc[mappedObjectNames.length];
874             for (int i = 0; i < mappedObjectNames.length; i++) {
875                 String JavaDoc mappedName = mappedObjectNames[i];
876                 try {
877                     objectNames[i] = ObjectNameManager.getInstance(mappedName);
878                 }
879                 catch (MalformedObjectNameException JavaDoc ex) {
880                     throw new MBeanExportException(
881                             "Invalid ObjectName [" + mappedName + "] specified for NotificationListener [" +
882                             bean.getNotificationListener() + "]", ex);
883                 }
884             }
885             return objectNames;
886         }
887         else {
888             // Mapped to all MBeans registered by the MBeanExporter.
889
return (ObjectName JavaDoc[]) this.registeredBeans.toArray(new ObjectName JavaDoc[this.registeredBeans.size()]);
890         }
891     }
892
893     /**
894      * Called when an MBean is registered. Notifies all registered
895      * {@link MBeanExporterListener MBeanExporterListeners} of the registration event.
896      * <p>Please note that if an {@link MBeanExporterListener} throws a (runtime)
897      * exception when notified, this will essentially interrupt the notification process
898      * and any remaining listeners that have yet to be notified will not (obviously)
899      * receive the {@link MBeanExporterListener#mbeanRegistered(javax.management.ObjectName)}
900      * callback.
901      * @param objectName the <code>ObjectName</code> of the registered MBean
902      */

903     protected void onRegister(ObjectName JavaDoc objectName) {
904         notifyListenersOfRegistration(objectName);
905     }
906
907     /**
908      * Called when an MBean is unregistered. Notifies all registered
909      * {@link MBeanExporterListener MBeanExporterListeners} of the unregistration event.
910      * <p>Please note that if an {@link MBeanExporterListener} throws a (runtime)
911      * exception when notified, this will essentially interrupt the notification process
912      * and any remaining listeners that have yet to be notified will not (obviously)
913      * receive the {@link MBeanExporterListener#mbeanUnregistered(javax.management.ObjectName)}
914      * callback.
915      * @param objectName the <code>ObjectName</code> of the unregistered MBean
916      */

917     protected void onUnregister(ObjectName JavaDoc objectName) {
918         notifyListenersOfUnregistration(objectName);
919     }
920
921
922     /**
923      * Notifies all registered {@link MBeanExporterListener MBeanExporterListeners} of the
924      * registration of the MBean identified by the supplied {@link ObjectName}.
925      */

926     private void notifyListenersOfRegistration(ObjectName JavaDoc objectName) {
927         if (this.listeners != null) {
928             for (int i = 0; i < this.listeners.length; i++) {
929                 this.listeners[i].mbeanRegistered(objectName);
930             }
931         }
932     }
933
934     /**
935      * Notifies all registered {@link MBeanExporterListener MBeanExporterListeners} of the
936      * unregistration of the MBean identified by the supplied {@link ObjectName}.
937      */

938     private void notifyListenersOfUnregistration(ObjectName JavaDoc objectName) {
939         if (this.listeners != null) {
940             for (int i = 0; i < this.listeners.length; i++) {
941                 this.listeners[i].mbeanUnregistered(objectName);
942             }
943         }
944     }
945
946
947     //---------------------------------------------------------------------
948
// Inner classes for internal use
949
//---------------------------------------------------------------------
950

951     /**
952      * Internal callback interface for the autodetection process.
953      */

954     private static interface AutodetectCallback {
955
956         /**
957          * Called during the autodetection process to decide whether
958          * or not a bean should be included.
959          * @param beanClass the class of the bean
960          * @param beanName the name of the bean
961          */

962         boolean include(Class JavaDoc beanClass, String JavaDoc beanName);
963     }
964
965
966     /**
967      * Extension of {@link LazyInitTargetSource} that will inject a
968      * {@link org.springframework.jmx.export.notification.NotificationPublisher}
969      * into the lazy resource as it is created if required.
970      */

971     private class NotificationPublisherAwareLazyTargetSource extends LazyInitTargetSource {
972
973         private ModelMBean JavaDoc modelMBean;
974         
975         private ObjectName JavaDoc objectName;
976
977         public void setModelMBean(ModelMBean JavaDoc modelMBean) {
978             this.modelMBean = modelMBean;
979         }
980
981         public void setObjectName(ObjectName JavaDoc objectName) {
982             this.objectName = objectName;
983         }
984
985         protected void postProcessTargetObject(Object JavaDoc targetObject) {
986             injectNotificationPublisherIfNecessary(targetObject, this.modelMBean, this.objectName);
987         }
988     }
989
990 }
991
Popular Tags