/* * NOTE: This copyright doesnot cover user programs that use HQ program services * by normal system calls through the application program interfaces provided as * part of the Hyperic Plug-in Development Kit or the Hyperic Client Development * Kit - this is merely considered normal use of the program, and doesnot fall * under the heading of "derived work". Copyright (C) [2004, 2005, 2006], * Hyperic, Inc. This file is part of HQ. HQ is free software; you can * redistribute it and/or modify it under the terms version 2 of the GNU General * Public License as published by the Free Software Foundation. This program 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 General Public License for more details. You * should have received a copy of the GNU General Public License along with this * program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA. */ /* * RegisteredTriggers.java Created on October 1, 2002, 1:50 PM */ package org.hyperic.hq.events.ext; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperic.hq.bizapp.server.trigger.conditional.ValueChangeTrigger; import org.hyperic.hq.events.AbstractEvent; import org.hyperic.hq.events.server.session.AlertRegulator; import org.hyperic.hq.measurement.ext.MeasurementEvent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; /** * Repository of in memory triggers for event processing * @author jhickey * */ @Repository public class RegisteredTriggers implements RegisterableTriggerRepository { public static final Integer KEY_ALL = new Integer(0); private final Object triggerUpdateLock = new Object(); private Map<TriggerEventKey, Map<Integer, RegisterableTriggerInterface>> triggers = new ConcurrentHashMap<TriggerEventKey, Map<Integer, RegisterableTriggerInterface>>(); private final AlertRegulator alertRegulator; private final Log log = LogFactory.getLog(RegisteredTriggers.class); @Autowired public RegisteredTriggers(AlertRegulator alertRegulator) { this.alertRegulator = alertRegulator; } Map<TriggerEventKey, Map<Integer, RegisterableTriggerInterface>> getTriggers() { return this.triggers; } public void init() { this.triggers = new ConcurrentHashMap<TriggerEventKey, Map<Integer, RegisterableTriggerInterface>>(); } public Collection<RegisterableTriggerInterface> getInterestedTriggers(AbstractEvent event, Integer instanceId) { HashSet<RegisterableTriggerInterface> trigs = new HashSet<RegisterableTriggerInterface>(); // All alerts are disabled, so no triggers should be processing events if (!alertRegulator.alertsAllowed()) { return trigs; } TriggerEventKey key = new TriggerEventKey(event.getClass(), instanceId.intValue()); Map<Integer, RegisterableTriggerInterface> triggersById = triggers .get(key); if (triggersById != null) { trigs.addAll(triggersById.values()); } // Remove disabled triggers from new set so don't have to synchronize // retrieval around concurrent triggers map for (Iterator<RegisterableTriggerInterface> iterator = trigs.iterator(); iterator.hasNext();) { RegisterableTriggerInterface trigger = iterator.next(); if (!trigger.isEnabled()) { if (trigger instanceof ValueChangeTrigger) { if (event instanceof MeasurementEvent) { ((ValueChangeTrigger)trigger).setLast(((MeasurementEvent)event)); } } iterator.remove(); } } return trigs; } public void addTrigger(RegisterableTriggerInterface trigger) { Class<?>[] types = trigger.getInterestedEventTypes(); for (Class<?> type : types) { // Now get the instances Integer[] instances = trigger.getInterestedInstanceIDs(type); if (instances == null) { continue; } for (Integer instance : instances) { TriggerEventKey key = new TriggerEventKey(type, instance.intValue()); // Despite using ConcurrentHashMaps - need to synchronize // updates due to iteration required by unregisterTrigger synchronized (triggerUpdateLock) { Map<Integer, RegisterableTriggerInterface> triggersById = triggers .get(key); if (triggersById == null) { triggersById = new ConcurrentHashMap<Integer, RegisterableTriggerInterface>(); } triggersById.put(trigger.getId(), trigger); triggers.put(key, triggersById); } } } } void setTriggers(Map<TriggerEventKey, Map<Integer, RegisterableTriggerInterface>> triggers) { this.triggers = triggers; } public void removeTrigger(Integer triggerId) { synchronized (triggerUpdateLock) { for (Iterator<Map<Integer, RegisterableTriggerInterface>> triggerMaps = triggers .values().iterator(); triggerMaps.hasNext();) { Map<Integer, RegisterableTriggerInterface> triggerIdsToTriggers = triggerMaps .next(); triggerIdsToTriggers.remove(triggerId); if (triggerIdsToTriggers.isEmpty()) { triggerMaps.remove(); } } } } public RegisterableTriggerInterface getTriggerById(Integer triggerId) { synchronized (triggerUpdateLock) { for (Map<Integer, RegisterableTriggerInterface> triggerIdsToTriggers : triggers .values()) { RegisterableTriggerInterface trigger = triggerIdsToTriggers .get(triggerId); if (trigger != null) { return trigger; } } return null; } } public void setTriggersEnabled(Collection<Integer> triggerIds, boolean enabled) { Set<RegisterableTriggerInterface> triggersToProcess = new HashSet<RegisterableTriggerInterface>(); for (Integer triggerId : triggerIds) { RegisterableTriggerInterface trigger = getTriggerById(triggerId); if (trigger != null) { triggersToProcess.add(trigger); } } // not concerned with thread-safety of "enabled" boolean - willing to // deal with some being read with old value during update for (RegisterableTriggerInterface trigger : triggersToProcess) { trigger.setEnabled(enabled); } } public Collection<RegisterableTriggerInterface> getInterestedTriggers(AbstractEvent event) { HashSet<RegisterableTriggerInterface> trigs = new HashSet<RegisterableTriggerInterface>(); // Can't very well look up a null object if (event.getInstanceId() != null) { // Get the triggers that are interested in this instance trigs.addAll(getInterestedTriggers(event, event.getInstanceId())); } // Get the triggers that are interested in all instances trigs.addAll(getInterestedTriggers(event, KEY_ALL)); return trigs; } public boolean isTriggerInterested(AbstractEvent event) { // If the event happened more than a day ago, does anyone care? final long ONE_DAY = 86400000; long current = System.currentTimeMillis(); if (event.getTimestamp() < (current - ONE_DAY)) { return false; } // Can't very well look up a null object if (event.getInstanceId() != null) { // Get the triggers that are interested in this instance Collection<RegisterableTriggerInterface> trigs = getInterestedTriggers( event, event.getInstanceId()); if (trigs.size() > 0) { return true; } } // Check the triggers that are interested in all instances Collection<RegisterableTriggerInterface> trigs = getInterestedTriggers(event, KEY_ALL); return (trigs.size() > 0); } }