001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.management;
006:
007: import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArrayList;
008:
009: import com.tc.exception.TCRuntimeException;
010: import com.tc.logging.TCLogger;
011: import com.tc.logging.TCLogging;
012: import com.tc.properties.TCPropertiesImpl;
013:
014: import java.util.Iterator;
015: import java.util.List;
016: import java.util.Locale;
017: import java.util.MissingResourceException;
018: import java.util.ResourceBundle;
019:
020: import javax.management.ListenerNotFoundException;
021: import javax.management.MBeanFeatureInfo;
022: import javax.management.MBeanNotificationInfo;
023: import javax.management.NotCompliantMBeanException;
024: import javax.management.Notification;
025: import javax.management.NotificationEmitter;
026: import javax.management.NotificationFilter;
027: import javax.management.NotificationListener;
028: import javax.management.StandardMBean;
029:
030: public abstract class AbstractTerracottaMBean extends StandardMBean
031: implements NotificationEmitter, TerracottaMBean {
032:
033: private static final ResourceBundle DEFAULT_BUNDLE = getBundleForMBean(
034: TerracottaMBean.class, TCLogging
035: .getLogger(TerracottaMBean.class));
036:
037: private static final boolean ENABLED = TCPropertiesImpl
038: .getProperties().getBoolean("tc.management.mbeans.enabled");
039:
040: private final TCLogger logger;
041: private final ResourceBundle beanBundle;
042: private final boolean isNotificationBroadcaster;
043:
044: // NOTE: The use of NotificationBroadcasterSupport has been removed and re-implemented internally
045: // to avoid issues with JDK logging (DEV-421)
046: private final List notificationListeners = new CopyOnWriteArrayList();
047: private boolean isActive;
048:
049: protected AbstractTerracottaMBean(final Class mBeanInterface,
050: final boolean isNotificationBroadcaster)
051: throws NotCompliantMBeanException {
052: this (mBeanInterface, isNotificationBroadcaster, ENABLED);
053: }
054:
055: protected AbstractTerracottaMBean(final Class mBeanInterface,
056: final boolean isNotificationBroadcaster,
057: final boolean isActive) throws NotCompliantMBeanException {
058: super (mBeanInterface);
059: this .logger = TCLogging.getLogger(mBeanInterface);
060: this .beanBundle = getBundleForMBean(mBeanInterface, logger);
061: this .isNotificationBroadcaster = isNotificationBroadcaster;
062: this .isActive = isActive;
063: }
064:
065: public final String getInterfaceClassName() {
066: return getMBeanInterface().getName();
067: }
068:
069: public final void addNotificationListener(
070: final NotificationListener listener,
071: final NotificationFilter filter, final Object obj) {
072: notificationListeners.add(new Listener(listener, filter, obj));
073: }
074:
075: public MBeanNotificationInfo[] getNotificationInfo() {
076: if (isNotificationBroadcaster()) {
077: final RuntimeException re = new TCRuntimeException(
078: "MBean error: this MBean["
079: + getClass().getName()
080: + "] must override getNotificationInfo() since"
081: + " it broadcasts notifications");
082: throw re;
083: }
084: return new MBeanNotificationInfo[0];
085: }
086:
087: public final void removeNotificationListener(
088: final NotificationListener listener,
089: final NotificationFilter filter, final Object obj)
090: throws ListenerNotFoundException {
091: boolean removed = false;
092:
093: for (Iterator i = notificationListeners.iterator(); i.hasNext();) {
094: Listener lsnr = (Listener) i.next();
095: if (lsnr.listener == listener && lsnr.filter == filter
096: && lsnr.handback == obj) {
097: removed = true;
098: notificationListeners.remove(lsnr);
099: }
100: }
101:
102: if (!removed) {
103: throw new ListenerNotFoundException();
104: }
105: }
106:
107: public final void removeNotificationListener(
108: final NotificationListener listener)
109: throws ListenerNotFoundException {
110: boolean removed = false;
111:
112: for (Iterator i = notificationListeners.iterator(); i.hasNext();) {
113: Listener lsnr = (Listener) i.next();
114: if (lsnr.listener == listener) {
115: removed = true;
116: notificationListeners.remove(lsnr);
117: }
118: }
119:
120: if (!removed) {
121: throw new ListenerNotFoundException();
122: }
123: }
124:
125: public final void sendNotification(final Notification notification) {
126: if (isEnabled()) {
127: for (Iterator i = notificationListeners.iterator(); i
128: .hasNext();) {
129: Listener lsnr = (Listener) i.next();
130:
131: if (lsnr.filter == null
132: || lsnr.filter
133: .isNotificationEnabled(notification)) {
134: lsnr.listener.handleNotification(notification,
135: lsnr.handback);
136: }
137: }
138: }
139: }
140:
141: public final boolean isNotificationBroadcaster() {
142: return isNotificationBroadcaster;
143: }
144:
145: public final void enable() {
146: setState(true);
147: }
148:
149: public final void disable() {
150: setState(false);
151: }
152:
153: private synchronized void setState(final boolean isActive) {
154: if (this .isActive && !isActive) {
155: reset();
156: }
157: this .isActive = isActive;
158: }
159:
160: public final synchronized boolean isEnabled() {
161: return isActive;
162: }
163:
164: /**
165: * As far as I can tell (at least with the Sun implementation), most if not all of the {@link StandardMBean}
166: * customization hooks for descriptions come through this one method. Since we are using a {@link ResourceBundle} we
167: * don't really need to worry about the exact type of the feature (only the name), so we should be able to get away
168: * with overriding only this particular method to supply descriptions.
169: */
170: protected String getDescription(final MBeanFeatureInfo featureInfo) {
171: final String name = featureInfo.getName();
172: String bundleDescription = null;
173: if (beanBundle != null) {
174: try {
175: bundleDescription = beanBundle.getString(name);
176: } catch (MissingResourceException mre) {
177: if (DEFAULT_BUNDLE != null) {
178: try {
179: bundleDescription = DEFAULT_BUNDLE
180: .getString(name);
181: } catch (MissingResourceException defaultMre) {
182: // We tried :)
183: }
184: }
185: } catch (Throwable t) {
186: // Not important enough to do anything about, but the log might reveal an operational problem
187: logger.warn(
188: "Unexpected error while trying to retrieve feature description["
189: + name + "]", t);
190: } finally {
191: if (bundleDescription == null) {
192: bundleDescription = super
193: .getDescription(featureInfo);
194: }
195: }
196: }
197: return bundleDescription;
198: }
199:
200: private static ResourceBundle getBundleForMBean(
201: final Class mBeanInterface, final TCLogger logger) {
202: ResourceBundle bundle = null;
203: try {
204: bundle = ResourceBundle.getBundle(mBeanInterface.getName(),
205: Locale.getDefault(), AbstractTerracottaMBean.class
206: .getClassLoader());
207: } catch (MissingResourceException mre) {
208: /* Caller must deal with null return value when missing */
209: } catch (Throwable t) {
210: logger.warn(
211: "Unexpected error loading resource bundle for MBean "
212: + mBeanInterface.getName(), t);
213: }
214: return bundle;
215: }
216:
217: private static class Listener {
218: private final NotificationListener listener;
219: private final NotificationFilter filter;
220: private final Object handback;
221:
222: Listener(NotificationListener listener,
223: NotificationFilter filter, Object obj) {
224: this.listener = listener;
225: this.filter = filter;
226: this.handback = obj;
227: }
228:
229: }
230:
231: }
|