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.monitor.alarm;
023:
024: import java.util.Collection;
025: import java.util.HashMap;
026: import java.util.LinkedHashMap;
027: import java.util.Map;
028:
029: import javax.management.Notification;
030: import javax.management.ObjectName;
031:
032: import org.jboss.system.ServiceMBeanSupport;
033:
034: import EDU.oswego.cs.dl.util.concurrent.SynchronizedLong;
035:
036: /**
037: * AlarmTable
038: *
039: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
040: * @version $Revision: 57210 $
041: */
042: public class AlarmTable {
043: // Private/Protected Data ----------------------------------------
044:
045: /** Mediates the related MBean */
046: protected MBeanImplAccess mbeanImpl;
047:
048: /** The serverId to use when producing AlarmTableNotification alarmIds */
049: private String serverId;
050:
051: /** Counter the help produce unique ids: serverId-alarmIdCount */
052: private SynchronizedLong alarmIdCount;
053:
054: /** The active alarm table, maps AlarmId(String) -> AlarmTableNotification */
055: private Map alarmMap;
056:
057: /** Maps AlarmKey -> AlarmId(String), for stateful alarms */
058: private Map statefulMap;
059:
060: /** Maximum number of entries to keep */
061: private int maxSize = -1;
062:
063: // Constructors --------------------------------------------------
064:
065: /**
066: * CTOR
067: */
068: public AlarmTable(MBeanImplAccess mbeanImpl) {
069: this .mbeanImpl = mbeanImpl;
070: this .alarmIdCount = new SynchronizedLong(0);
071: this .alarmMap = new LinkedHashMap();
072: this .statefulMap = new HashMap();
073: }
074:
075: /**
076: * CTOR
077: *
078: * @param service hosting the AlarmManager
079: */
080: public AlarmTable(final ServiceMBeanSupport service) {
081: this (new MBeanImplAccess() {
082: public ObjectName getMBeanName() {
083: return service.getServiceName();
084: }
085:
086: public long getSequenceNumber() {
087: return service.nextNotificationSequenceNumber();
088: }
089:
090: public void emitNotification(Notification n) {
091: service.sendNotification(n);
092: }
093: });
094: }
095:
096: // AlarmTable Implementation -------------------------------------
097:
098: /**
099: * Sets the serverId
100: */
101: public void setServerId(String serverId) {
102: this .serverId = serverId;
103: }
104:
105: /**
106: * Gets the serverId
107: */
108: public String getServerId() {
109: return serverId;
110: }
111:
112: /**
113: * Sets the maximum number of entries to keep
114: * -1 equals to no limit.
115: */
116: public void setMaxSize(int maxSize) {
117: this .maxSize = maxSize;
118: }
119:
120: /**
121: * Gets the maximum number of entries to keep
122: */
123: public int getMaxSize() {
124: return maxSize;
125: }
126:
127: /**
128: * Update the AlarmTable based on the incoming Notification
129: */
130: public void update(Notification n) {
131: if (n instanceof AlarmTableNotification) {
132: // ignore - those notification are
133: // meant to be produced only by me
134: } else if (n instanceof AlarmNotification) {
135: AlarmNotification an = (AlarmNotification) n;
136:
137: if (an.getAlarmState() == Alarm.STATE_NONE) {
138: updateNotificationStateless(n, an.getSeverity());
139: } else {
140: updateNotificationStatefull(an);
141: }
142: } else {
143: updateNotificationStateless(n, Alarm.SEVERITY_UNKNOWN);
144: }
145: }
146:
147: /**
148: * Acknowledge an Alarm
149: *
150: * @return true if ack was succesful, false otherwise
151: * (not in table or acked already)
152: */
153: public boolean acknowledge(String alarmId, String user,
154: String system) {
155: AlarmTableNotification atn;
156:
157: synchronized (this ) {
158: AlarmTableNotification entry = (AlarmTableNotification) alarmMap
159: .get(alarmId);
160:
161: if (entry == null || entry.getAckState() == true) {
162: return false; // ack failed
163: }
164: // ack the alarm
165: entry.setAckParams(true, System.currentTimeMillis(), user,
166: system);
167:
168: // prepare the AlarmTableNotification to send
169: atn = new AlarmTableNotification(entry);
170:
171: // this is a new notification
172: atn.setSequenceNumber(mbeanImpl.getSequenceNumber());
173: atn.setTimeStamp(System.currentTimeMillis());
174:
175: // if alarm Stateless or Statefull but Cleared, remove from table
176: int alarmState = entry.getAlarmState();
177: if (alarmState == Alarm.STATE_NONE
178: || alarmState == Alarm.STATE_CLEARED) {
179: alarmMap.remove(alarmId);
180: }
181: }
182: // send the AlarmTableNotification
183: mbeanImpl.emitNotification(atn);
184:
185: return true; // ok
186: }
187:
188: /**
189: * Unacknowledge an Alarm
190: *
191: * @return true if unack was succesful, false otherwise
192: * (not in table or unacked already)
193: */
194: public boolean unacknowledge(String alarmId, String user,
195: String system) {
196: AlarmTableNotification atn;
197:
198: synchronized (this ) {
199: AlarmTableNotification entry = (AlarmTableNotification) alarmMap
200: .get(alarmId);
201:
202: if (entry == null || entry.getAckState() == false) {
203: return false; // unack failed
204: }
205: // unack the alarm
206: entry.setAckParams(false, System.currentTimeMillis(), user,
207: system);
208:
209: // prepare the AlarmTableNotification to send
210: atn = new AlarmTableNotification(entry);
211:
212: // this is a new notification
213: atn.setSequenceNumber(mbeanImpl.getSequenceNumber());
214: atn.setTimeStamp(System.currentTimeMillis());
215: }
216: // send the AlarmTableNotification
217: mbeanImpl.emitNotification(atn);
218:
219: return true; // ok
220: }
221:
222: /**
223: * Gets a copy of the AlarmTable
224: */
225: public AlarmTableNotification[] getAlarmTable() {
226: // this syncronized deep copy is quite expensive
227: synchronized (this ) {
228: Collection alarms = alarmMap.values();
229: AlarmTableNotification[] array = new AlarmTableNotification[alarms
230: .size()];
231: return (AlarmTableNotification[]) alarms.toArray(array);
232: }
233: }
234:
235: /**
236: * Gets the number of entries in the table
237: */
238: public int getAlarmSize() {
239: synchronized (this ) {
240: return alarmMap.size();
241: }
242: }
243:
244: // Private Methods -----------------------------------------------
245:
246: /**
247: * Since this is stateful, first check if there is already
248: * an entry in the stateful alarm map, then update both maps.
249: */
250: private void updateNotificationStatefull(AlarmNotification an) {
251: int alarmState = an.getAlarmState();
252: int severity = an.getSeverity();
253:
254: // Create a key based on source+type
255: Object alarmKey = AlarmNotification.createKey(an);
256:
257: AlarmTableNotification atn;
258:
259: // Check if this stateful alarm is already stored
260: synchronized (this ) {
261: String alarmId = (String) statefulMap.get(alarmKey);
262: if (alarmId == null) {
263: // the stateful alarm is not known
264: if (isMaxSizeReached()) {
265: // return if table is full
266: return;
267: } else {
268: // generate a new Id, if not found
269: alarmId = generateAlarmId();
270: }
271: }
272: // create an AlarmTableNotification
273: atn = new AlarmTableNotification(alarmId,
274: AlarmTableNotification.ALARM_TABLE_UPDATE,
275: this .mbeanImpl.getMBeanName(), null, severity,
276: alarmState, this .mbeanImpl.getSequenceNumber(),
277: System.currentTimeMillis(), null);
278:
279: // store a reference to the original notification
280: atn.setUserData(an);
281:
282: // need to check if acked already, in which case
283: // we must copy the ack data to the new AlarmTableNotification
284: // and remove the entry from the table
285: if (alarmState == Alarm.STATE_CLEARED) {
286: AlarmTableNotification entry = (AlarmTableNotification) alarmMap
287: .get(alarmId);
288:
289: if (entry != null && entry.getAckState() == true) {
290: statefulMap.remove(alarmKey);
291: alarmMap.remove(alarmId);
292:
293: atn.setAckParams(true, entry.getAckTime(), entry
294: .getAckUser(), entry.getAckSystem());
295: } else {
296: // just add it
297: statefulMap.put(alarmKey, alarmId);
298: alarmMap.put(alarmId, atn);
299: }
300: } else {
301: // just add it
302: statefulMap.put(alarmKey, alarmId);
303: alarmMap.put(alarmId, atn);
304: }
305: }
306: // the only case to be acked is when it is not stored in table
307: // in which case send the new AlarmTableNotification itself
308: if (atn.getAckState() == true) {
309: mbeanImpl.emitNotification(atn);
310: } else // send a copy away
311: {
312: mbeanImpl.emitNotification(new AlarmTableNotification(atn));
313: }
314: }
315:
316: /**
317: * Store the notification in the active alarm map.
318: */
319: private void updateNotificationStateless(Notification n,
320: int severity) {
321: synchronized (this ) {
322: if (isMaxSizeReached()) {
323: // can't hold no more alarms
324: return;
325: }
326: }
327: // create an AlarmTableNotification
328: AlarmTableNotification atn = new AlarmTableNotification(
329: generateAlarmId(),
330: AlarmTableNotification.ALARM_TABLE_UPDATE,
331: this .mbeanImpl.getMBeanName(), null, severity,
332: Alarm.STATE_NONE, this .mbeanImpl.getSequenceNumber(),
333: System.currentTimeMillis(), null);
334: // store a reference to the original notification
335: atn.setUserData(n);
336:
337: // store the AlarmTableNotification - this is always a new entry
338: synchronized (this ) {
339: alarmMap.put(atn.getAlarmId(), atn);
340: }
341:
342: // send a copy away
343: mbeanImpl.emitNotification(new AlarmTableNotification(atn));
344: }
345:
346: /**
347: * Generate a (hopefully) unique alarmId
348: */
349: private String generateAlarmId() {
350: return serverId + '-' + alarmIdCount.increment();
351: }
352:
353: /**
354: * Check if table is full
355: */
356: private boolean isMaxSizeReached() {
357: if (maxSize != -1) {
358: return (alarmMap.size() >= maxSize) ? true : false;
359: } else {
360: return false;
361: }
362: }
363: }
|