/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.monitor.alarm; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import javax.management.Notification; import javax.management.ObjectName; import org.jboss.system.ServiceMBeanSupport; import EDU.oswego.cs.dl.util.concurrent.SynchronizedLong; /** * AlarmTable * * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a> * @version $Revision: 81038 $ */ public class AlarmTable { // Private/Protected Data ---------------------------------------- /** Mediates the related MBean */ protected MBeanImplAccess mbeanImpl; /** The serverId to use when producing AlarmTableNotification alarmIds */ private String serverId; /** Counter the help produce unique ids: serverId-alarmIdCount */ private SynchronizedLong alarmIdCount; /** The active alarm table, maps AlarmId(String) -> AlarmTableNotification */ private Map alarmMap; /** Maps AlarmKey -> AlarmId(String), for stateful alarms */ private Map statefulMap; /** Maximum number of entries to keep */ private int maxSize = -1; // Constructors -------------------------------------------------- /** * CTOR */ public AlarmTable(MBeanImplAccess mbeanImpl) { this.mbeanImpl = mbeanImpl; this.alarmIdCount = new SynchronizedLong(0); this.alarmMap = new LinkedHashMap(); this.statefulMap = new HashMap(); } /** * CTOR * * @param service hosting the AlarmManager */ public AlarmTable(final ServiceMBeanSupport service) { this(new MBeanImplAccess() { public ObjectName getMBeanName() { return service.getServiceName(); } public long getSequenceNumber() { return service.nextNotificationSequenceNumber(); } public void emitNotification(Notification n) { service.sendNotification(n); } }); } // AlarmTable Implementation ------------------------------------- /** * Sets the serverId */ public void setServerId(String serverId) { this.serverId = serverId; } /** * Gets the serverId */ public String getServerId() { return serverId; } /** * Sets the maximum number of entries to keep * -1 equals to no limit. */ public void setMaxSize(int maxSize) { this.maxSize = maxSize; } /** * Gets the maximum number of entries to keep */ public int getMaxSize() { return maxSize; } /** * Update the AlarmTable based on the incoming Notification */ public void update(Notification n) { if (n instanceof AlarmTableNotification) { // ignore - those notification are // meant to be produced only by me } else if (n instanceof AlarmNotification) { AlarmNotification an = (AlarmNotification)n; if (an.getAlarmState() == Alarm.STATE_NONE) { updateNotificationStateless(n, an.getSeverity()); } else { updateNotificationStatefull(an); } } else { updateNotificationStateless(n, Alarm.SEVERITY_UNKNOWN); } } /** * Acknowledge an Alarm * * @return true if ack was succesful, false otherwise * (not in table or acked already) */ public boolean acknowledge(String alarmId, String user, String system) { AlarmTableNotification atn; synchronized (this) { AlarmTableNotification entry = (AlarmTableNotification)alarmMap.get(alarmId); if (entry == null || entry.getAckState() == true) { return false; // ack failed } // ack the alarm entry.setAckParams(true, System.currentTimeMillis(), user, system); // prepare the AlarmTableNotification to send atn = new AlarmTableNotification(entry); // this is a new notification atn.setSequenceNumber(mbeanImpl.getSequenceNumber()); atn.setTimeStamp(System.currentTimeMillis()); // if alarm Stateless or Statefull but Cleared, remove from table int alarmState = entry.getAlarmState(); if (alarmState == Alarm.STATE_NONE || alarmState == Alarm.STATE_CLEARED) { alarmMap.remove(alarmId); } } // send the AlarmTableNotification mbeanImpl.emitNotification(atn); return true; // ok } /** * Unacknowledge an Alarm * * @return true if unack was succesful, false otherwise * (not in table or unacked already) */ public boolean unacknowledge(String alarmId, String user, String system) { AlarmTableNotification atn; synchronized (this) { AlarmTableNotification entry = (AlarmTableNotification)alarmMap.get(alarmId); if (entry == null || entry.getAckState() == false) { return false; // unack failed } // unack the alarm entry.setAckParams(false, System.currentTimeMillis(), user, system); // prepare the AlarmTableNotification to send atn = new AlarmTableNotification(entry); // this is a new notification atn.setSequenceNumber(mbeanImpl.getSequenceNumber()); atn.setTimeStamp(System.currentTimeMillis()); } // send the AlarmTableNotification mbeanImpl.emitNotification(atn); return true; // ok } /** * Gets a copy of the AlarmTable */ public AlarmTableNotification[] getAlarmTable() { // this syncronized deep copy is quite expensive synchronized (this) { Collection alarms = alarmMap.values(); AlarmTableNotification[] array = new AlarmTableNotification[alarms.size()]; return (AlarmTableNotification[])alarms.toArray(array); } } /** * Gets the number of entries in the table */ public int getAlarmSize() { synchronized(this) { return alarmMap.size(); } } // Private Methods ----------------------------------------------- /** * Since this is stateful, first check if there is already * an entry in the stateful alarm map, then update both maps. */ private void updateNotificationStatefull(AlarmNotification an) { int alarmState = an.getAlarmState(); int severity = an.getSeverity(); // Create a key based on source+type Object alarmKey = AlarmNotification.createKey(an); AlarmTableNotification atn; // Check if this stateful alarm is already stored synchronized (this) { String alarmId = (String)statefulMap.get(alarmKey); if (alarmId == null) { // the stateful alarm is not known if (isMaxSizeReached()) { // return if table is full return; } else { // generate a new Id, if not found alarmId = generateAlarmId(); } } // create an AlarmTableNotification atn = new AlarmTableNotification( alarmId, AlarmTableNotification.ALARM_TABLE_UPDATE, this.mbeanImpl.getMBeanName(), null, severity, alarmState, this.mbeanImpl.getSequenceNumber(), System.currentTimeMillis(), null ); // store a reference to the original notification atn.setUserData(an); // need to check if acked already, in which case // we must copy the ack data to the new AlarmTableNotification // and remove the entry from the table if (alarmState == Alarm.STATE_CLEARED) { AlarmTableNotification entry = (AlarmTableNotification)alarmMap.get(alarmId); if (entry != null && entry.getAckState() == true) { statefulMap.remove(alarmKey); alarmMap.remove(alarmId); atn.setAckParams(true, entry.getAckTime(), entry.getAckUser(), entry.getAckSystem()); } else { // just add it statefulMap.put(alarmKey, alarmId); alarmMap.put(alarmId, atn); } } else { // just add it statefulMap.put(alarmKey, alarmId); alarmMap.put(alarmId, atn); } } // the only case to be acked is when it is not stored in table // in which case send the new AlarmTableNotification itself if (atn.getAckState() == true) { mbeanImpl.emitNotification(atn); } else // send a copy away { mbeanImpl.emitNotification(new AlarmTableNotification(atn)); } } /** * Store the notification in the active alarm map. */ private void updateNotificationStateless(Notification n, int severity) { synchronized (this) { if (isMaxSizeReached()) { // can't hold no more alarms return; } } // create an AlarmTableNotification AlarmTableNotification atn = new AlarmTableNotification( generateAlarmId(), AlarmTableNotification.ALARM_TABLE_UPDATE, this.mbeanImpl.getMBeanName(), null, severity, Alarm.STATE_NONE, this.mbeanImpl.getSequenceNumber(), System.currentTimeMillis(), null ); // store a reference to the original notification atn.setUserData(n); // store the AlarmTableNotification - this is always a new entry synchronized (this) { alarmMap.put(atn.getAlarmId(), atn); } // send a copy away mbeanImpl.emitNotification(new AlarmTableNotification(atn)); } /** * Generate a (hopefully) unique alarmId */ private String generateAlarmId() { return serverId + '-' + alarmIdCount.increment(); } /** * Check if table is full */ private boolean isMaxSizeReached() { if (maxSize != -1) { return (alarmMap.size() >= maxSize) ? true : false; } else { return false; } } }