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