1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.mx.server.registry;
23
24 import java.util.ArrayList;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Vector;
29 import javax.management.Descriptor;
30 import javax.management.DynamicMBean;
31 import javax.management.InstanceAlreadyExistsException;
32 import javax.management.InstanceNotFoundException;
33 import javax.management.MBeanException;
34 import javax.management.MBeanInfo;
35 import javax.management.MBeanRegistration;
36 import javax.management.MBeanRegistrationException;
37 import javax.management.MBeanServer;
38 import javax.management.MBeanServerDelegate;
39 import javax.management.MBeanServerNotification;
40 import javax.management.MalformedObjectNameException;
41 import javax.management.NotCompliantMBeanException;
42 import javax.management.ObjectInstance;
43 import javax.management.ObjectName;
44 import javax.management.ReflectionException;
45 import javax.management.RuntimeErrorException;
46 import javax.management.RuntimeMBeanException;
47 import javax.management.RuntimeOperationsException;
48 import javax.management.loading.ClassLoaderRepository;
49 import javax.management.modelmbean.ModelMBeanInfo;
50 import javax.management.modelmbean.RequiredModelMBean;
51
52 import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
53 import EDU.oswego.cs.dl.util.concurrent.SynchronizedLong;
54
55 import org.jboss.classloading.spi.RealClassLoader;
56 import org.jboss.logging.Logger;
57 import org.jboss.mx.loading.LoaderRepository;
58 import org.jboss.mx.loading.RepositoryClassLoader;
59 import org.jboss.mx.metadata.MBeanCapability;
60 import org.jboss.mx.modelmbean.ModelMBeanConstants;
61 import org.jboss.mx.modelmbean.RequiredModelMBeanInvoker;
62 import org.jboss.mx.modelmbean.XMBean;
63 import org.jboss.mx.modelmbean.XMBeanConstants;
64 import org.jboss.mx.server.AbstractMBeanInvoker;
65 import org.jboss.mx.server.MBeanInvoker;
66 import org.jboss.mx.server.RawDynamicInvoker;
67 import org.jboss.mx.server.ServerConfig;
68 import org.jboss.mx.server.ServerObjectInstance;
69 import org.jboss.mx.util.ObjectNamePatternHelper;
70 import org.jboss.mx.util.ObjectNamePatternHelper.PropertyPattern;
71 import org.jboss.util.NestedRuntimeException;
72
73 /**
74 * The registry for object name - object reference mapping in the
75 * MBean server.
76 * <p>
77 * The implementation of this class affects the invocation speed
78 * directly, please check any changes for performance.
79 *
80 * @todo JMI_DOMAIN isn't very protected
81 *
82 * @see org.jboss.mx.server.registry.MBeanRegistry
83 *
84 * @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>.
85 * @author <a href="mailto:trevor@protocool.com">Trevor Squires</a>.
86 * @author <a href="mailto:Adrian.Brock@HappeningTimes.com">Adrian Brock</a>.
87 * @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>.
88 * @author <a href="mailto:thomas.diesler@jboss.com">Thomas Diesler</a>.
89 *
90 * @version $Revision: 64035 $
91 */
92 public class BasicMBeanRegistry
93 implements MBeanRegistry
94 {
95 // Constants -----------------------------------------------------
96
97 /** The server config */
98 private static ServerConfig serverConfig = ServerConfig.getInstance();
99
100 /** The default domain */
101 private static String JMI_DOMAIN = serverConfig.getJMIDomain();
102
103 // Attributes ----------------------------------------------------
104
105 /**
106 * A map of domain name to another map containing object name canonical
107 * key properties to registry entries.
108 * domain -> canonicalKeyProperties -> MBeanEntry
109 */
110 private Map domainMap = new ConcurrentReaderHashMap();
111
112 /**
113 * The default domain for this registry
114 */
115 private String defaultDomain;
116
117 /**
118 * The MBeanServer for which we are the registry.
119 */
120 private MBeanServer server;
121
122 /**
123 * The loader repository for loading classes
124 */
125 private LoaderRepository loaderRepository;
126
127 /**
128 * Sequence number for the MBean server registration notifications.
129 */
130 protected final SynchronizedLong registrationNotificationSequence = new SynchronizedLong (1);
131
132 /**
133 * Sequence number for the MBean server unregistration notifications.
134 */
135 protected final SynchronizedLong unregistrationNotificationSequence = new SynchronizedLong (1);
136
137 /**
138 * Direct reference to the mandatory MBean server delegate MBean.
139 */
140 protected MBeanServerDelegate delegate;
141
142 protected Vector fMbInfosToStore;
143 private ObjectName mbeanInfoService;
144
145
146 // Static --------------------------------------------------------
147
148 /**
149 * The logger
150 */
151 protected static Logger log = Logger.getLogger(BasicMBeanRegistry.class);
152
153
154 // Constructors --------------------------------------------------
155
156 /**
157 * Constructs a new BasicMBeanRegistry.<p>
158 */
159 public BasicMBeanRegistry(MBeanServer server, String defaultDomain, ClassLoaderRepository clr)
160 {
161 // Store the context
162 this.server = server;
163 this.defaultDomain = defaultDomain;
164
165 try
166 {
167 loaderRepository = (LoaderRepository) clr;
168 mbeanInfoService = new ObjectName("user:service=MBeanInfoDB");
169 }
170 catch (Exception e)
171 {
172 throw new NestedRuntimeException("Error instantiating registry", e);
173 }
174 }
175
176
177 // MBeanRegistry Implementation ----------------------------------
178
179 public ObjectInstance registerMBean(Object object, ObjectName name, Map valueMap)
180 throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException
181 {
182 ObjectName regName = name;
183 boolean registrationDone = true;
184 boolean invokedPreRegister = false;
185 String magicToken = null;
186 MBeanInvoker invoker = null;
187
188 if (object == null)
189 throw new RuntimeOperationsException(
190 new IllegalArgumentException("Attempting to register null object"));
191
192 // get mbean type, dynamic or standard
193 MBeanCapability mbcap = MBeanCapability.of(object.getClass());
194
195 try
196 {
197
198 if (valueMap != null)
199 magicToken = (String) valueMap.get(JMI_DOMAIN);
200
201 // TODO: allow custom factory for diff invoker types
202 int mbeanType = mbcap.getMBeanType();
203 if (mbeanType == MBeanCapability.STANDARD_MBEAN)
204 {
205 invoker = new XMBean(object, XMBeanConstants.STANDARD_MBEAN);
206 }
207 else if (object instanceof MBeanInvoker)
208 {
209 invoker = (MBeanInvoker)object;
210 }
211 else if (mbeanType == MBeanCapability.DYNAMIC_MBEAN)
212 {
213 if( object instanceof RequiredModelMBean )
214 invoker = new RequiredModelMBeanInvoker((DynamicMBean)object);
215 else
216 invoker = new RawDynamicInvoker((DynamicMBean)object);
217 }
218
219 // Perform the pregistration
220 MBeanEntry entry = new MBeanEntry(regName, invoker, object, valueMap);
221 AbstractMBeanInvoker.setMBeanEntry(entry);
222 regName = invokePreRegister(invoker, regName, magicToken);
223 invokedPreRegister = true;
224
225 try
226 {
227 MBeanInfo info = invoker.getMBeanInfo();
228 verifyMBeanInfo(info, name);
229 entry.setResourceClassName(info.getClassName());
230
231 // Register the mbean
232
233 // Update the registered name to the final value
234 entry.setObjectName(regName);
235
236 add(entry);
237
238 try
239 {
240 // Add the classloader to the repository
241 if (object instanceof ClassLoader)
242 registerClassLoader((ClassLoader)object);
243
244 try
245 {
246 if (delegate != null)
247 sendRegistrationNotification (regName);
248 else if (serverConfig.getMBeanServerDelegateName().equals(name))
249 delegate = (MBeanServerDelegate) object;
250
251 ServerObjectInstance serverObjInst = new ServerObjectInstance
252 (regName, entry.getResourceClassName(), delegate.getMBeanServerId());
253
254 persistIfRequired(invoker.getMBeanInfo(), regName);
255
256 return serverObjInst;
257
258 }
259 catch (Throwable t)
260 {
261 // Problem, remove a classloader from the repository
262 if (object instanceof ClassLoader)
263 loaderRepository.removeClassLoader((ClassLoader)object);
264
265 throw t;
266 }
267 }
268 catch (Throwable t)
269 {
270 // Problem, remove the mbean from the registry
271 remove(regName);
272 throw t;
273 }
274 }
275 // Throw for null MBeanInfo
276 catch (NotCompliantMBeanException e)
277 {
278 throw e;
279 }
280 // Thrown by the registry
281 catch (InstanceAlreadyExistsException e)
282 {
283 throw e;
284 }
285 catch (Throwable t)
286 {
287 // Something is broken
288 log.error("Unexpected Exception:", t);
289 throw t;
290 }
291 }
292 catch (NotCompliantMBeanException e)
293 {
294 registrationDone = false;
295 throw e;
296 }
297 catch (InstanceAlreadyExistsException e)
298 {
299 // It was already registered
300 registrationDone = false;
301 throw e;
302 }
303 catch (MBeanRegistrationException e)
304 {
305 // The MBean cancelled the registration
306 registrationDone = false;
307 log.warn(e.toString());
308 throw e;
309 }
310 catch (RuntimeOperationsException e)
311 {
312 // There was a problem with one the arguments
313 registrationDone = false;
314 throw e;
315 }
316 catch (Exception ex)
317 {
318 // any other exception is mapped to NotCompliantMBeanException
319 registrationDone = false;
320 NotCompliantMBeanException ncex = new NotCompliantMBeanException("Cannot register MBean: " + name);
321 ncex.initCause(ex);
322 throw ncex;
323 }
324 catch (Throwable t)
325 {
326 // Some other error
327 log.error("Cannot register MBean", t);
328 registrationDone = false;
329 return null;
330 }
331 finally
332 {
333 // Tell the MBean the result of the registration
334 if (invoker != null)
335 {
336 try
337 {
338 invoker.postRegister(new Boolean(registrationDone));
339 }
340 catch(Exception e)
341 {
342 // Only throw this if preRegister succeeded
343 if( invokedPreRegister == true )
344 {
345 if( e instanceof RuntimeException )
346 throw new RuntimeMBeanException((RuntimeException) e);
347 else
348 throw new MBeanRegistrationException(e);
349 }
350 }
351 }
352 AbstractMBeanInvoker.setMBeanEntry(null);
353 }
354 }
355
356 /**
357 * Verifies the MBeanInfo and throws an exception if something is wrong.
358 * @param info a MBeanInfo
359 * @param name a ObjectName
360 * @throws NotCompliantMBeanException when something is wrong with the MBean info
361 */
362 private void verifyMBeanInfo(MBeanInfo info, ObjectName name)
363 throws NotCompliantMBeanException
364 {
365 try
366 {
367 if (info == null)
368 throw new NotCompliantMBeanException("MBeanInfo cannot be null, for: " + name);
369
370 if (info.getClassName() == null)
371 throw new NotCompliantMBeanException("Classname returned from MBeanInfo cannot be null, for: " + name);
372 }
373 catch (NotCompliantMBeanException ncex)
374 {
375 throw ncex;
376 }
377 catch (Throwable t)
378 {
379 NotCompliantMBeanException ncex = new NotCompliantMBeanException("Cannot verify MBeanInfo, for: " + name);
380 ncex.initCause(t);
381 throw ncex;
382 }
383 }
384
385 /**
386 * send a MBeanServerNotification.REGISTRATION_NOTIFICATION notification
387 * to regName
388 *
389 * @param regName
390 */
391 protected void sendRegistrationNotification (ObjectName regName)
392 {
393 long sequence = registrationNotificationSequence.increment ();
394 delegate.sendNotification (
395 new MBeanServerNotification (
396 MBeanServerNotification.REGISTRATION_NOTIFICATION,
397 delegate, sequence, regName));
398 }
399
400 /**
401 * subclasses can override to provide their own pre-registration pre- and post- logic for
402 * <tt>preRegister</tt> and must call preRegister on the MBeanRegistration instance
403 *
404 * @param registrationInterface
405 * @param regName
406 * @return object name
407 * @throws Exception
408 */
409 protected ObjectName handlePreRegistration (MBeanRegistration registrationInterface, ObjectName regName)
410 throws Exception
411 {
412 ObjectName mbean = registrationInterface.preRegister (server, regName);
413 if (regName == null)
414 {
415 return mbean;
416 }
417 else
418 {
419 return regName;
420 }
421 }
422
423
424 /**
425 * subclasses can override to provide any custom preDeregister logic
426 * and must call preDregister on the MBeanRegistration instance
427 *
428 * @param registrationInterface
429 * @throws Exception
430 */
431 protected void handlePreDeregister (MBeanRegistration registrationInterface)
432 throws Exception
433 {
434 registrationInterface.preDeregister ();
435 }
436
437 /**
438 * Subclasses can override if they wish to control the classloader
439 * registration to loader repository.
440 *
441 * @param cl classloader
442 */
443 protected void registerClassLoader(ClassLoader cl)
444 {
445 if( (cl instanceof RealClassLoader) == false )
446 {
447 // Only register non-UCLs as UCLs already have a repository
448 loaderRepository.addClassLoader(cl);
449 }
450 }
451
452
453 public void unregisterMBean(ObjectName name)
454 throws InstanceNotFoundException, MBeanRegistrationException
455 {
456 name = qualifyName(name);
457 if (name.getDomain().equals(JMI_DOMAIN))
458 throw new RuntimeOperationsException(new IllegalArgumentException(
459 "Not allowed to unregister: " + name.toString()));
460
461 MBeanEntry entry = get(name);
462 Object resource = entry.getResourceInstance();
463
464 try
465 {
466 // allow subclasses to perform their own pre- and post- pre-deregister logic
467 handlePreDeregister (entry.getInvoker());
468
469 }
470 catch (Exception e)
471 {
472 // don't double wrap MBeanRegistrationException
473 if (e instanceof MBeanRegistrationException)
474 throw (MBeanRegistrationException)e;
475
476 throw new MBeanRegistrationException(e, "preDeregister");
477 }
478
479 // Remove any classloader
480 if (resource instanceof ClassLoader)
481 loaderRepository.removeClassLoader((ClassLoader)resource);
482
483 // It is no longer registered
484 remove(name);
485
486 sendUnRegistrationNotification (name);
487
488 entry.getInvoker().postDeregister();
489 }
490
491 /**
492 * send MBeanServerNotification.UNREGISTRATION_NOTIFICATION notification to
493 * name
494 *
495 * @param name
496 */
497 protected void sendUnRegistrationNotification (ObjectName name)
498 {
499 long sequence = unregistrationNotificationSequence.increment ();
500
501 delegate.sendNotification (
502 new MBeanServerNotification (
503 MBeanServerNotification.UNREGISTRATION_NOTIFICATION,
504 delegate,
505 sequence,
506 name
507 )
508 );
509 }
510
511 public MBeanEntry get(ObjectName name)
512 throws InstanceNotFoundException
513 {
514 if (name == null)
515 throw new RuntimeOperationsException(new IllegalArgumentException("null object name"));
516
517 // Determine the domain and retrieve its entries
518 String domain = name.getDomain();
519
520 if (domain.length() == 0)
521 domain = defaultDomain;
522
523 String props = name.getCanonicalKeyPropertyListString();
524 Map mbeanMap = getMBeanMap(domain, false);
525
526 // Retrieve the mbean entry
527 Object o = null;
528 if (null == mbeanMap || null == (o = mbeanMap.get(props)))
529 throw new InstanceNotFoundException(name + " is not registered.");
530
531 // We are done
532 return (MBeanEntry) o;
533 }
534
535 public String getDefaultDomain()
536 {
537 return defaultDomain;
538 }
539
540 public String[] getDomains()
541 {
542 ArrayList domains = new ArrayList(domainMap.size());
543 for (Iterator iterator = domainMap.entrySet().iterator(); iterator.hasNext();)
544 {
545 Map.Entry entry = (Map.Entry) iterator.next();
546 String domainName = (String) entry.getKey();
547 Map mbeans = (Map) entry.getValue();
548 if (mbeans != null && mbeans.isEmpty() == false)
549 domains.add(domainName);
550 }
551 return (String[]) domains.toArray(new String[domains.size()]);
552 }
553
554 public ObjectInstance getObjectInstance(ObjectName name)
555 throws InstanceNotFoundException
556 {
557 if (!contains(name))
558 throw new InstanceNotFoundException(name + " not registered.");
559
560 return new ServerObjectInstance(qualifyName(name),
561 get(name).getResourceClassName(), delegate.getMBeanServerId());
562 }
563
564 public Object getValue(ObjectName name, String key)
565 throws InstanceNotFoundException
566 {
567 return get(name).getValue(key);
568 }
569
570 public boolean contains(ObjectName name)
571 {
572 // null safety check
573 if (name == null)
574 return false;
575
576 // Determine the domain and retrieve its entries
577 String domain = name.getDomain();
578
579 if (domain.length() == 0)
580 domain = defaultDomain;
581
582 String props = name.getCanonicalKeyPropertyListString();
583 Map mbeanMap = getMBeanMap(domain, false);
584
585 // Return the result
586 return (null != mbeanMap && mbeanMap.containsKey(props));
587 }
588
589 public int getSize()
590 {
591 int retval = 0;
592 for (Iterator iterator = domainMap.values().iterator(); iterator.hasNext();)
593 {
594 retval += ((Map)iterator.next()).size();
595 }
596 return retval;
597 }
598
599 public List findEntries(ObjectName pattern)
600 {
601 ArrayList retval = new ArrayList();
602
603 // There are a couple of shortcuts we can employ to make this a
604 // bit faster - they're commented.
605
606 // First, if pattern == null or pattern.getCanonicalName() == "*:*" we want the
607 // set of all MBeans.
608 if (pattern == null || pattern.getCanonicalName().equals("*:*"))
609 {
610 for (Iterator domainIter = domainMap.values().iterator(); domainIter.hasNext();)
611 retval.addAll(((Map)domainIter.next()).values());
612 }
613 // Next, if !pattern.isPattern() then we are doing a simple get (maybe defaultDomain).
614 else if (!pattern.isPattern())
615 {
616 // simple get
617 try
618 {
619 retval.add(get(pattern));
620 }
621 catch (InstanceNotFoundException e)
622 {
623 // we don't care
624 }
625 }
626 // Now we have to do a brute force, oh well.
627 else
628 {
629 String patternDomain = pattern.getDomain();
630 if (patternDomain.length() == 0)
631 patternDomain = defaultDomain;
632 PropertyPattern propertyPattern = new PropertyPattern(pattern);
633
634 // Here we go, step through every domain and see if our pattern matches before optionally checking
635 // each ObjectName's properties for a match.
636 for (Iterator domainIter = domainMap.entrySet().iterator(); domainIter.hasNext();)
637 {
638 Map.Entry mapEntry = (Map.Entry) domainIter.next();
639 Map value = (Map) mapEntry.getValue();
640 if (value != null && value.isEmpty() == false)
641 {
642 if (ObjectNamePatternHelper.patternMatch((String) mapEntry.getKey(), patternDomain))
643 {
644 for (Iterator mbeanIter = value.values().iterator(); mbeanIter.hasNext();)
645 {
646 MBeanEntry entry = (MBeanEntry) mbeanIter.next();
647 if (propertyPattern.patternMatch(entry.getObjectName()))
648 retval.add(entry);
649 }
650 }
651 }
652 }
653 }
654
655 return retval;
656 }
657
658
659 /**
660 * Cleans up the registry before the MBean server is released.
661 */
662 public void releaseRegistry()
663 // This is based on patch by Rod Burgett (Bug report: 763378)
664 // Modified. Server is calling the registry.
665 {
666 server = null;
667 delegate = null;
668
669 // clear each value element from the domainMap
670 for (Iterator iterator = domainMap.keySet().iterator(); iterator.hasNext();)
671 {
672 Map nextMap = (Map) domainMap.get(iterator.next());
673
674 if ( nextMap.size() > 0 )
675 {
676 nextMap.clear();
677 }
678 }
679
680 domainMap.clear();
681 domainMap = null;
682 }
683
684
685 // Protected -----------------------------------------------------
686
687 protected ObjectName invokePreRegister(MBeanInvoker invoker, ObjectName regName, String magicToken)
688 throws MBeanRegistrationException, NotCompliantMBeanException
689 {
690
691 // if we were given a non-null object name for registration, qualify it
692 // and expand default domain
693 if (regName != null)
694 regName = qualifyName(regName);
695
696 // store the name returned by preRegister() here
697 ObjectName mbeanName = null;
698
699 try
700 {
701 // invoke preregister on the invoker, it will delegate to the resource
702 // if needed
703 mbeanName = invoker.preRegister(server, regName);
704 }
705 // if during pre registration, the mbean turns out to be not compliant
706 catch (NotCompliantMBeanException ncex)
707 {
708 throw ncex;
709
710 }
711 // catch all exceptions cause by preRegister, these will abort registration
712 catch (Exception e)
713 {
714 if (e instanceof MBeanRegistrationException)
715 {
716 throw (MBeanRegistrationException)e;
717 }
718
719 throw new MBeanRegistrationException(e,
720 "preRegister() failed: " +
721 "[ObjectName='" + regName +
722 "', Class=" + invoker.getResource().getClass().getName() +
723 " (" + invoker.getResource() + ")]"
724 );
725 }
726 catch (Throwable t)
727 {
728 log.warn("preRegister() failed for " + regName + ": ", t);
729
730 if (t instanceof Error)
731 throw new RuntimeErrorException((Error)t);
732 else
733 throw new RuntimeException(t.toString());
734 }
735
736
737 // if registered with null name, use the default name returned by
738 // the preregister implementation
739 if (regName == null)
740 regName = mbeanName;
741
742 return validateAndQualifyName(regName, magicToken);
743 }
744
745 /**
746 * Adds an MBean entry<p>
747 *
748 * WARNING: The object name should be fully qualified.
749 *
750 * @param entry the MBean entry to add
751 * @exception InstanceAlreadyExistsException when the MBean's object name
752 * is already registered
753 */
754 protected synchronized void add(MBeanEntry entry)
755 throws InstanceAlreadyExistsException
756 {
757 // Determine the MBean's name and properties
758 ObjectName name = entry.getObjectName();
759 String domain = name.getDomain();
760 String props = name.getCanonicalKeyPropertyListString();
761
762 // Create a properties -> entry map if we don't have one
763 Map mbeanMap = getMBeanMap(domain, true);
764
765 // Make sure we aren't already registered
766 if (mbeanMap.get(props) != null)
767 throw new InstanceAlreadyExistsException(name + " already registered.");
768
769 // Ok, we are registered
770 mbeanMap.put(props, entry);
771 }
772
773 /**
774 * Removes an MBean entry
775 *
776 * WARNING: The object name should be fully qualified.
777 *
778 * @param name the object name of the entry to remove
779 * @exception InstanceNotFoundException when the object name is not
780 * registered
781 */
782 protected synchronized void remove(ObjectName name)
783 throws InstanceNotFoundException
784 {
785 // Determine the MBean's name and properties
786 String domain = name.getDomain();
787 String props = name.getCanonicalKeyPropertyListString();
788 Map mbeanMap = getMBeanMap(domain, false);
789
790 // Remove the entry, raise an exception when it didn't exist
791 if (null == mbeanMap || null == mbeanMap.remove(props))
792 throw new InstanceNotFoundException(name + " not registered.");
793 }
794
795 /**
796 * Validates and qualifies an MBean<p>
797 *
798 * Validates the name is not a pattern.<p>
799 *
800 * Adds the default domain if no domain is specified.<p>
801 *
802 * Checks the name is not in the reserved domain JMImplementation when
803 * the magicToken is not {@link org.jboss.mx.server.ServerConstants#JMI_DOMAIN JMI_DOMAIN}
804 *
805 * @param name the name to validate
806 * @param magicToken used to get access to the reserved domain
807 * @return the original name or the name prepended with the default domain
808 * if no domain is specified.
809 * @exception RuntimeOperationsException containing an
810 * IllegalArgumentException for a problem with the name
811 */
812 protected ObjectName validateAndQualifyName(ObjectName name,
813 String magicToken)
814 {
815 // Check for qualification
816 ObjectName result = qualifyName(name);
817
818 // Make sure the name is not a pattern
819 if (result.isPattern())
820 throw new RuntimeOperationsException(
821 new IllegalArgumentException("Object name is a pattern:" + name));
822
823 // Check for reserved domain
824 if (magicToken != JMI_DOMAIN &&
825 result.getDomain().equals(JMI_DOMAIN))
826 throw new RuntimeOperationsException(new IllegalArgumentException(
827 "Domain " + JMI_DOMAIN + " is reserved"));
828
829 // I can't think of anymore tests, we're done
830 return result;
831 }
832
833 /**
834 * Qualify an object name with the default domain<p>
835 *
836 * Adds the default domain if no domain is specified.
837 *
838 * @param name the name to qualify
839 * @return the original name or the name prepended with the default domain
840 * if no domain is specified.
841 * @exception RuntimeOperationsException containing an
842 * IllegalArgumentException when there is a problem
843 */
844 protected ObjectName qualifyName(ObjectName name)
845 {
846 if (name == null)
847 throw new RuntimeOperationsException(
848 new IllegalArgumentException("Null object name"));
849 try
850 {
851 if (name.getDomain().length() == 0)
852 return new ObjectName(defaultDomain + ":" +
853 name.getCanonicalKeyPropertyListString());
854 else
855 return name;
856 }
857 catch (MalformedObjectNameException e)
858 {
859 throw new RuntimeOperationsException(
860 new IllegalArgumentException(e.toString()));
861 }
862 }
863
864 /**
865 * Adds the given MBean Info object to the persistence queue if it explicity denotes
866 * (via metadata) that it should be stored.
867 * @todo -- add notification of registration of MBeanInfoDb.
868 * It is possible that some MBeans whose MBean Info should be stored are
869 * registered before the MBean Info Storage delegate is available. These
870 * MBeans are remembered by the registry and should be added to the storage delegate
871 * as soon as it is available. In the current mechanism, they are added only if another
872 * MBean requesting MBean info persistence is registered after the delegate is registered.
873 * Someone more familiar with the server could make this more robust by adding
874 * a notification mechanism such that the queue is flushed as soon as the
875 * delegate is available. - Matt Munz
876 * @todo does this code need to be here? can't a notification listener be
877 * registered with the MBeanServerDelegate that stores a backlog
878 * until the service becomes available?
879 * @todo the mbInfoStores is a memory leak if the service is never registered
880 * @todo mbInfoStores is not synchronized correctly
881 * Thread1 adds
882 * Thread1 clones and invokes
883 * Thread2 adds
884 * Thread1 clears
885 * Thread2's add is lost
886 * @todo Don't use Vector, performs too fine grained synchronization,
887 * probably not important in this case.
888 */
889 protected void persistIfRequired(MBeanInfo info, ObjectName name)
890 throws
891 MalformedObjectNameException,
892 InstanceNotFoundException,
893 MBeanException,
894 ReflectionException
895 {
896 if(!(info instanceof ModelMBeanInfo))
897 {
898 return;
899 }
900 ModelMBeanInfo mmbInfo = (ModelMBeanInfo) info;
901 Descriptor descriptor;
902 try
903 {
904 descriptor = mmbInfo.getMBeanDescriptor();
905 }
906 catch(MBeanException cause)
907 {
908 log.error("Error trying to get descriptors.", cause);
909 return;
910 }
911 if (descriptor == null)
912 return;
913 String persistInfo = (String) descriptor.getFieldValue(ModelMBeanConstants.PERSIST_INFO);
914 if (persistInfo == null)
915 return; // use default -- no persistence
916 log.debug("persistInfo: " + persistInfo);
917 Boolean shouldPersist = new Boolean(persistInfo);
918 if(!shouldPersist.booleanValue())
919 {
920 return;
921 }
922 mbInfosToStore().add(name);
923 // see if MBeanDb is available
924 if(contains(mbeanInfoService))
925 {
926 // flush queue to the MBeanDb
927 log.debug("flushing queue");
928 server.invoke(
929 mbeanInfoService,
930 "add",
931 new Object[] { mbInfosToStore().clone() },
932 new String[] { mbInfosToStore().getClass().getName() });
933 log.debug("clearing queue");
934 mbInfosToStore().clear();
935 }
936 else
937 {
938 log.debug("service is not registered. items remain in queue");
939 }
940 }
941
942 /**
943 * ObjectName objects bound to MBean Info objects that are waiting to be stored in the
944 * persistence store.
945 */
946 protected Vector mbInfosToStore()
947 {
948 if(fMbInfosToStore == null)
949 {
950 fMbInfosToStore = new Vector(10);
951 }
952 return fMbInfosToStore;
953 }
954
955 /**
956 * The <code>getMBeanMap</code> method provides synchronized access
957 * to the mbean map for a domain. This is actually a solution to a
958 * bug that resulted in wiping out the jboss domain mbeanMap for no
959 * apparent reason.
960 *
961 * @param domain a <code>String</code> value
962 * @param createIfMissing a <code>boolean</code> value
963 * @return a <code>Map</code> value
964 */
965 private Map getMBeanMap(String domain, boolean createIfMissing)
966 {
967 Map mbeanMap = (Map) domainMap.get(domain);
968 if (mbeanMap == null && createIfMissing)
969 {
970 mbeanMap = new ConcurrentReaderHashMap();
971 domainMap.put(domain, mbeanMap);
972 }
973 return mbeanMap;
974 }
975 }