001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.jmx;
031:
032: import com.caucho.loader.*;
033: import com.caucho.util.L10N;
034:
035: import javax.management.*;
036: import javax.management.loading.ClassLoaderRepository;
037: import java.lang.ref.WeakReference;
038: import java.util.ArrayList;
039: import java.util.Hashtable;
040: import java.util.LinkedHashMap;
041: import java.util.Map;
042: import java.util.logging.Level;
043: import java.util.logging.Logger;
044:
045: /**
046: * The context containing mbeans registered at a particular level.
047: */
048: public class MBeanContext {
049: private static final Logger log = Logger
050: .getLogger(MBeanContext.class.getName());
051: private static final L10N L = new L10N(MBeanContext.class);
052:
053: // The owning MBeanServer
054: private AbstractMBeanServer _mbeanServer;
055:
056: private MBeanContext _globalContext;
057:
058: private MBeanServerDelegate _delegate;
059: private long _seq;
060:
061: // class loader for this server
062: private ClassLoader _loader;
063:
064: private String _domain = "resin";
065: private LinkedHashMap<String, String> _properties = new LinkedHashMap<String, String>();
066:
067: private ClassLoaderRepositoryImpl _classLoaderRepository = new ClassLoaderRepositoryImpl();
068:
069: // map of all mbeans
070: private Hashtable<ObjectName, MBeanWrapper> _mbeans = new Hashtable<ObjectName, MBeanWrapper>();
071:
072: private ArrayList<Listener> _listeners = new ArrayList<Listener>();
073:
074: // The local view associated with the context
075: private MBeanView _view;
076: // The global view associated with the context
077: private MBeanView _globalView;
078:
079: MBeanContext(AbstractMBeanServer mbeanServer, ClassLoader loader,
080: MBeanServerDelegate delegate, MBeanContext globalContext) {
081: for (; loader != null
082: && loader != ClassLoader.getSystemClassLoader()
083: && !(loader instanceof EnvironmentClassLoader); loader = loader
084: .getParent()) {
085: }
086:
087: if (loader == null)
088: loader = ClassLoader.getSystemClassLoader();
089:
090: _mbeanServer = mbeanServer;
091: _loader = loader;
092: _delegate = delegate;
093: _globalContext = globalContext;
094:
095: Environment.addClassLoaderListener(new CloseListener(this ),
096: _loader);
097: //Environment.addClassLoaderListener(new WeakCloseListener(this), _loader);
098:
099: _classLoaderRepository.addClassLoader(_loader);
100:
101: _view = new MBeanView(mbeanServer, _loader, "resin");
102: _globalView = new MBeanView(mbeanServer, _loader, "resin");
103: }
104:
105: /**
106: * Returns the parent view.
107: */
108: protected MBeanView getParentView() {
109: return _mbeanServer.getParentView();
110: }
111:
112: /**
113: * Returns the ClassLoaderRepository.
114: */
115: public ClassLoaderRepository getClassLoaderRepository() {
116: return _classLoaderRepository;
117: }
118:
119: /**
120: * Returns the class loader.
121: */
122: public ClassLoader getClassLoader() {
123: return _loader;
124: }
125:
126: /**
127: * Returns the view for this context.
128: */
129: MBeanView getView() {
130: return _view;
131: }
132:
133: /**
134: * Returns the view for this context.
135: */
136: MBeanView getGlobalView() {
137: return _globalView;
138: }
139:
140: /**
141: * Sets the properties.
142: */
143: public void setProperties(Map<String, String> props) {
144: _properties.clear();
145: _properties.putAll(props);
146: }
147:
148: /**
149: * Sets the properties.
150: */
151: public LinkedHashMap<String, String> copyProperties() {
152: return new LinkedHashMap<String, String>(_properties);
153: }
154:
155: /**
156: * Returns the object name.
157: */
158: public ObjectName getObjectName(String name)
159: throws MalformedObjectNameException {
160: int len = name.length();
161:
162: for (int i = 0; i < len; i++) {
163: char ch = name.charAt(i);
164:
165: if (ch == ':')
166: return new ObjectName(name);
167: else if ('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z'
168: || '0' <= ch && ch <= '9' || ch == '-' || ch == '_'
169: || ch == '.') {
170: continue;
171: } else
172: break;
173: }
174:
175: LinkedHashMap<String, String> properties;
176: properties = new LinkedHashMap<String, String>();
177:
178: properties.putAll(_properties);
179: Jmx.parseProperties(properties, name);
180:
181: return Jmx.getObjectName(_domain, properties);
182: }
183:
184: /**
185: * Finds an admin object.
186: */
187: MBeanWrapper getMBean(ObjectName name) {
188: if (name != null)
189: return _mbeans.get(name);
190: else
191: return null;
192: }
193:
194: /**
195: * Registers an MBean with the server.
196: *
197: * @param mbean the object to be registered as an MBean
198: * @param name the name of the mbean.
199: *
200: * @return the instantiated object.
201: */
202: ObjectInstance registerMBean(MBeanWrapper mbean, ObjectName name)
203: throws InstanceAlreadyExistsException,
204: MBeanRegistrationException, NotCompliantMBeanException {
205: if (mbean == null)
206: throw new NotCompliantMBeanException(L.l(
207: "{0} is a null mbean", name));
208: if (_mbeans.get(name) != null)
209: throw new InstanceAlreadyExistsException(String
210: .valueOf(name));
211:
212: Object object = mbean.getObject();
213:
214: MBeanRegistration registration = null;
215:
216: if (object instanceof MBeanRegistration)
217: registration = (MBeanRegistration) object;
218:
219: try {
220: if (registration != null)
221: name = registration.preRegister(_mbeanServer, name);
222: } catch (Exception e) {
223: throw new MBeanRegistrationException(e);
224: }
225:
226: if (log.isLoggable(Level.FINER)
227: && !name.equals(_mbeanServer.SERVER_DELEGATE_NAME)) {
228: log.finer(getDebugName(name, mbean) + " registered in "
229: + this );
230: }
231:
232: addMBean(name, mbean);
233:
234: try {
235: if (registration != null)
236: registration.postRegister(new Boolean(true));
237: } catch (Exception e) {
238: throw new MBeanRegistrationException(e);
239: }
240:
241: if (_globalContext != null)
242: _globalContext.addMBean(name, mbean);
243:
244: return mbean.getObjectInstance();
245: }
246:
247: /**
248: * Unregisters an MBean from the server.
249: *
250: * @param name the name of the mbean.
251: */
252: public void unregisterMBean(ObjectName name)
253: throws InstanceNotFoundException,
254: MBeanRegistrationException {
255: if (_mbeans == null) {
256: removeMBean(name);
257: return;
258: }
259:
260: if (name.getDomain().equals("JMImplementation"))
261: return;
262:
263: MBeanWrapper mbean = _mbeans.get(name);
264:
265: if (mbean == null)
266: throw new InstanceNotFoundException(String.valueOf(name));
267:
268: Object obj = mbean.getObject();
269: MBeanRegistration registration = null;
270:
271: if (obj instanceof MBeanRegistration)
272: registration = (MBeanRegistration) obj;
273:
274: try {
275: if (registration != null) {
276: try {
277: registration.preDeregister();
278: } catch (Throwable e) {
279: log.log(Level.WARNING, e.toString());
280: }
281: }
282:
283: removeMBean(name);
284:
285: if (registration != null) {
286: try {
287: registration.postDeregister();
288: } catch (Throwable e) {
289: log.log(Level.WARNING, e.toString());
290: }
291: }
292: } catch (Exception e) {
293: throw new MBeanRegistrationException(e);
294: }
295: }
296:
297: /**
298: * Returns the MBean registered with the given name.
299: *
300: * @param name the name of the mbean.
301: *
302: * @return the matching mbean object.
303: */
304: public ObjectInstance getObjectInstance(ObjectName name)
305: throws InstanceNotFoundException {
306: MBeanWrapper mbean = getMBean(name);
307:
308: if (mbean == null)
309: throw new InstanceNotFoundException(String.valueOf(name));
310:
311: return mbean.getObjectInstance();
312: }
313:
314: /**
315: * Returns true if the given object is registered with the server.
316: *
317: * @param name the name of the mbean to test.
318: *
319: * @return true if the object is registered.
320: */
321: public boolean isRegistered(ObjectName name) {
322: return _mbeans.get(name) != null;
323: }
324:
325: /**
326: * Returns the number of MBeans registered.
327: *
328: * @return the number of registered mbeans.
329: */
330: public int getMBeanCount() {
331: return _mbeans.size();
332: }
333:
334: /**
335: * Adds an object.
336: */
337: private void addMBean(ObjectName name, MBeanWrapper mbean) {
338: if (_mbeans == null)
339: throw new IllegalStateException(L
340: .l("Adding MBean when context is closed"));
341: if (mbean == null) {
342: log.warning(L.l("'{0}' is an empty mbean", name));
343: return;
344: }
345:
346: // at finest to avoid double logging for context and global
347: if (log.isLoggable(Level.FINEST))
348: log.finest(getDebugName(name, mbean) + " registered in "
349: + this );
350:
351: //log.fine(L.l("{0} registered in {1}", getDebugName(name, mbean), this));
352:
353: _mbeans.put(name, mbean);
354:
355: _view.add(name, mbean, true);
356: _globalView.add(name, mbean, true);
357:
358: sendRegisterNotification(name);
359:
360: MBeanContext context = this ;
361: while (context._loader != null) {
362: ClassLoader parentLoader = context._loader.getParent();
363:
364: MBeanContext parentContext = _mbeanServer
365: .createContext(parentLoader);
366:
367: if (parentContext == null || parentContext == context)
368: break;
369:
370: if (parentContext._globalView == null) {
371: log.finer("global view is empty");
372: } else if (parentContext._globalView
373: .add(name, mbean, false))
374: parentContext.sendRegisterNotification(name);
375:
376: context = parentContext;
377: }
378: }
379:
380: /**
381: * Removes an object.
382: */
383: private MBeanWrapper removeMBean(ObjectName name) {
384: MBeanWrapper mbean = null;
385:
386: if (_mbeans != null)
387: mbean = _mbeans.remove(name);
388:
389: if (_globalContext != null)
390: _globalContext._mbeans.remove(name);
391:
392: if (_view != null)
393: _view.remove(name);
394:
395: if (_globalView != null && _globalView.remove(name) != null) {
396: try {
397: sendUnregisterNotification(name);
398: } catch (Throwable e) {
399: log.log(Level.WARNING, e.toString(), e);
400: }
401: }
402:
403: ClassLoader loader = _loader.getParent();
404: for (; loader != null; loader = loader.getParent()) {
405: MBeanContext parentContext = _mbeanServer
406: .getContext(loader);
407:
408: if (this == parentContext)
409: return mbean;
410: else if (parentContext != null) {
411: parentContext.removeMBean(name);
412: break;
413: }
414: }
415:
416: return mbean;
417: }
418:
419: /**
420: * Adds a listener to a registered MBean
421: *
422: * @param name the name of the mbean
423: * @param listener the listener object
424: * @param filter filters events the listener is interested in
425: * @param handback context to be returned to the listener
426: */
427: void addNotificationListener(ObjectName name,
428: NotificationListener listener, NotificationFilter filter,
429: Object handback) {
430: synchronized (_listeners) {
431: _listeners.add(new Listener(name, listener, filter,
432: handback));
433: }
434: }
435:
436: /**
437: * Removes a listener to a registered MBean
438: *
439: * @param mbean the name of the mbean
440: * @param listener the listener object
441: */
442: public void removeNotificationListener(ObjectName mbean,
443: NotificationListener listener) {
444: synchronized (_listeners) {
445: for (int i = _listeners.size() - 1; i >= 0; i--) {
446: Listener oldListener = _listeners.get(i);
447:
448: if (oldListener.match(mbean, listener))
449: _listeners.remove(i);
450: }
451: }
452: }
453:
454: /**
455: * Removes a listener to a registered MBean
456: *
457: * @param mbean the name of the mbean
458: * @param listener the listener object
459: * @param filter filters events the listener is interested in
460: * @param handback context to be returned to the listener
461: */
462: public void removeNotificationListener(ObjectName mbean,
463: NotificationListener listener, NotificationFilter filter,
464: Object handback) {
465: synchronized (_listeners) {
466: for (int i = _listeners.size() - 1; i >= 0; i--) {
467: Listener oldListener = _listeners.get(i);
468:
469: if (oldListener
470: .match(mbean, listener, filter, handback))
471: _listeners.remove(i);
472: }
473: }
474: }
475:
476: /**
477: * Sends the register notification.
478: */
479: void sendRegisterNotification(ObjectName name) {
480: serverNotification(name,
481: MBeanServerNotification.REGISTRATION_NOTIFICATION);
482: }
483:
484: /**
485: * Sends the register notification.
486: */
487: void sendUnregisterNotification(ObjectName name) {
488: serverNotification(name,
489: MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
490: }
491:
492: /**
493: * Sends the notification
494: */
495: private void serverNotification(ObjectName name, String type) {
496: MBeanServerNotification notif;
497:
498: ObjectName delegateName = _mbeanServer.SERVER_DELEGATE_NAME;
499:
500: notif = new MBeanServerNotification(type, delegateName, _seq++,
501: name);
502:
503: _delegate.sendNotification(notif);
504: }
505:
506: /**
507: * Closes the context server.
508: */
509: public void destroy() {
510: if (_mbeans == null)
511: return;
512:
513: log.finest(this + " destroy");
514:
515: ArrayList<ObjectName> list = new ArrayList<ObjectName>(_mbeans
516: .keySet());
517:
518: ArrayList<Listener> listeners = new ArrayList<Listener>(
519: _listeners);
520:
521: for (int i = 0; i < listeners.size(); i++) {
522: Listener listener = listeners.get(i);
523:
524: try {
525: MBeanWrapper mbean = _globalView.getMBean(listener
526: .getName());
527:
528: if (mbean != null)
529: mbean.removeNotificationListener(listener
530: .getListener(), listener.getFilter(),
531: listener.getHandback());
532: } catch (Throwable e) {
533: log.log(Level.FINER, e.toString(), e);
534: }
535: }
536:
537: for (int i = 0; i < list.size(); i++) {
538: ObjectName name = list.get(i);
539:
540: try {
541: unregisterMBean(name);
542: } catch (Throwable e) {
543: log.log(Level.FINE, e.toString(), e);
544: }
545: }
546:
547: _mbeanServer.removeContext(this , _loader);
548:
549: _listeners = null;
550: _mbeans = null;
551: _view = null;
552: _globalView = null;
553: }
554:
555: /**
556: * Returns the debug name for a registered mbean.
557: *
558: * @param name the name of the mbean
559: * @param mbean the mbean instance
560: * @return
561: */
562: private String getDebugName(ObjectName name, MBeanWrapper mbean) {
563: String className = mbean.getMBeanInfo().getClassName();
564:
565: int p = className.lastIndexOf('.');
566: if (p > 0)
567: className = className.substring(p + 1);
568:
569: return className + "[" + name + "]";
570: }
571:
572: /**
573: * Display name.
574: */
575: public String toString() {
576: return "MBeanContext[" + _loader + "]";
577: }
578:
579: /**
580: * Finalizer.
581: */
582: /*
583: protected void finalize()
584: throws Throwable
585: {
586: super.finalize();
587:
588: destroy();
589: }
590: */
591:
592: /**
593: * Listener references.
594: */
595: static class Listener {
596: private ObjectName _name;
597: private WeakReference<NotificationListener> _listenerRef;
598: private WeakReference<NotificationFilter> _filterRef;
599: private WeakReference<Object> _handbackRef;
600:
601: Listener(ObjectName name, NotificationListener listener,
602: NotificationFilter filter, Object handback) {
603: _name = name;
604: _listenerRef = new WeakReference<NotificationListener>(
605: listener);
606:
607: if (filter != null)
608: _filterRef = new WeakReference<NotificationFilter>(
609: filter);
610:
611: if (handback != null)
612: _handbackRef = new WeakReference<Object>(handback);
613: }
614:
615: ObjectName getName() {
616: return _name;
617: }
618:
619: NotificationListener getListener() {
620: return _listenerRef.get();
621: }
622:
623: NotificationFilter getFilter() {
624: return _filterRef != null ? _filterRef.get() : null;
625: }
626:
627: Object getHandback() {
628: return _handbackRef != null ? _handbackRef.get() : null;
629: }
630:
631: boolean match(ObjectName name, NotificationListener listener,
632: NotificationFilter filter, Object handback) {
633: if (!_name.equals(name))
634: return false;
635:
636: else if (listener != _listenerRef.get())
637: return false;
638:
639: else if (filter == null && _filterRef != null)
640: return false;
641:
642: else if (_filterRef != null && _filterRef.get() != filter)
643: return false;
644:
645: else if (handback == null && _handbackRef != null)
646: return false;
647:
648: else if (_handbackRef != null
649: && _handbackRef.get() != handback)
650: return false;
651:
652: else
653: return true;
654: }
655:
656: boolean match(ObjectName name, NotificationListener listener) {
657: if (!_name.equals(name))
658: return false;
659:
660: else if (listener != _listenerRef.get())
661: return false;
662:
663: else
664: return true;
665: }
666: }
667: }
|