/* * ALMA - Atacama Large Millimiter Array * (c) European Southern Observatory, 2011 * Copyright by ESO (in the framework of the ALMA collaboration), * All rights reserved * * This library 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 library 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package alma.acs.alarmsystem.source; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import alma.acs.concurrent.ThreadLoopRunner; import alma.acs.concurrent.ThreadLoopRunner.CancelableRunnable; import alma.acs.concurrent.ThreadLoopRunner.ScheduleDelayMode; /** * The map of active and inactive alarms. *
* Alarms that are not updated after {@link #ALARM_ACTIVITY_TIME} are removed * from the map so that they will be sent again to the alarm service. *
* The class is thread safe i.e. the methods can be called without any further locking. *
* Implementation note: * All the methods add new entries or update existing pairs in the {@link #alarms} * map with the exception of the periodic thread {@link AlarmsMapRunnable#run()} * that remove elements that are in the map unchanged since more the {@link #ALARM_ACTIVITY_TIME}. *
* {@link AlarmsMap} does not need any locking because the map contains immutable * {@link AlarmInfo} objects and the map itself is implemented by a {@link ConcurrentHashMap}. * * @author acaproni */ public class AlarmsMap { /** * The data stored for each alarm in the map. *
* Objects of this class are immutable. * * @author acaproni */ public static class AlarmInfo { /** * The time of the last update of this alarm */ public final long time; /** * The state active/terminate of the alarm */ public final boolean active; /** * Constructor * * @param active The initial state active/terminate of the alarm */ public AlarmInfo(boolean active) { this.active=active; this.time=System.currentTimeMillis(); } } /** * The thread to delete the alarms older than the time interval. *
* This thread is scheduled by the ThreadLoopRunner. * Note that this thread is the only one that removes entries from the map: * all other methods update or add entries. * * @author acaproni */ public class AlarmsMapRunnable extends CancelableRunnable { /** * Check the timestamps of the entries in the map and remove those that * have not been changed since more then ALARM_ACTIVITY_TIME. */ public void run() { for (String key: alarms.keySet()) { if (shouldTerminate) { return; } AlarmInfo info = alarms.get(key); // Info can't be null because this is the only // thread removing items from the map // We check nevertheless to improve safety. if (info!=null) { if (System.currentTimeMillis()-ALARM_ACTIVITY_TIME*1000>info.time) { alarms.remove(key); } } } } } /** * The alarms that have no activity after the following time interval * are removed from the map. *
* In practice, it means that an alarm with the same state will be sent again
* if it arrives after ALARM_ACTIVITY_TIME
seconds.
*/
public static final int ALARM_ACTIVITY_TIME=30;
/**
* The map of alarms.
*
* The key is the alarm ID i.e. FF:FM:FC.
* The value is an {@link AlarmInfo} with boolean set to
* This method should not be used because there is no need to clear the content of the
* map during normal operations.
* Especially it must not be invoked when the periodic thread {@link AlarmsMapRunnable#run()}
* is checking the timestamps of the entries because it can generate a NPE
*/
private void clear() {
alarms.clear();
}
/**
*
* @param key The key to look for
* @return The object with the given key
*/
public AlarmInfo get(Object key) {
return alarms.get(key);
}
/**
* This method returns a set of the keys in the map at the moment when the
* method has been called. It is meant to be used for testing purposes only.
* true
* if the alarm has been activated, false
otherwise.
*/
private final Maptrue
if the alarm with the give ID was already present
* in the list and it was active; false
otherwise.
*/
public boolean raise(String alarmID) {
AlarmInfo info=alarms.get(alarmID);
boolean ret=(info!=null) && (info.active);
alarms.put(alarmID, new AlarmInfo(true));
return ret;
}
/**
* An alarm has been cleared and must be added to the map.
*
* @param alarmID The ID of the alarm
* @return true
if the alarm with the give ID was already present
* in the list and it was terminated; false
otherwise.
*/
public boolean clear(String alarmID) {
AlarmInfo info=alarms.get(alarmID);
boolean ret=(info!=null) && (!info.active);
alarms.put(alarmID, new AlarmInfo(false));
return ret;
}
/**
* Start the thread to delete the oldest alarms
*/
public void start() {
loopRunner.setDelayMode(ScheduleDelayMode.FIXED_DELAY);
loopRunner.runLoop();
}
/**
* Shutdown the thread a frees the resources
*/
public void shutdown() {
try {
if (loopRunner.shutdown(1, TimeUnit.SECONDS)) {
logger.finest("Thread shut down");
} else {
logger.warning("Failed to cleanly shut down the AlarmsMap thread");
}
} catch (InterruptedException ie) {
logger.warning("AlarmsMap thread interrupted while shutting down.");
}
clear();
}
/**
* The size of the map
*
* @return The size of the map
*
* @see ConcurrentHashMap#size()
*/
public int size() {
return alarms.size();
}
/**
*
* @param key The key to look for in the map
*
* @return true
if the map contains an item with the given ket
*/
public boolean containsKey(Object key) {
return alarms.containsKey(key);
}
/**
* Empty the map.
*
* Getting elements from the returned set could lead to null
values
* if ssuch a value has been removed by the thread.
* @return A set with the key in the map
*/
public Set