/* * NOTE: This copyright does *not* 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 does *not* 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. */ package org.hyperic.hq.events.server.session; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperic.hq.appdef.shared.AppdefUtil; import org.hyperic.hq.authz.server.session.Resource; import org.hyperic.hq.authz.server.shared.ResourceDeletedException; import org.hyperic.hq.common.util.MessagePublisher; import org.hyperic.hq.escalation.server.session.Escalatable; import org.hyperic.hq.escalation.server.session.EscalatableCreator; import org.hyperic.hq.events.ActionExecutionInfo; import org.hyperic.hq.events.AlertDefinitionInterface; import org.hyperic.hq.events.AlertFiredEvent; import org.hyperic.hq.events.EventConstants; import org.hyperic.hq.events.TriggerFiredEvent; import org.hyperic.hq.events.shared.AlertConditionLogValue; import org.hyperic.hq.events.shared.AlertManager; import org.hyperic.hq.measurement.server.session.AlertConditionsSatisfiedZEvent; import org.hyperic.hq.measurement.server.session.AlertConditionsSatisfiedZEventPayload; import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; /** * This class has the knowledge to create an {@link Escalatable} object based on * a {@link TriggerFiredEvent} if the escalation subsytem deems it necessary. */ public class ClassicEscalatableCreator implements EscalatableCreator { private static final Log _log = LogFactory.getLog(ClassicEscalatableCreator.class); private final AlertDefinition _def; private final AlertConditionsSatisfiedZEvent _event; private final MessagePublisher messagePublisher; private final AlertManager alertMan; /** * Creates an instance. * * @param def The alert definition. * @param event The event that triggered the escalation. * @param messagePublisher The messenger to use for publishing * AlertFiredEvents * @param alertMan The alert manager to use */ public ClassicEscalatableCreator(AlertDefinition def, AlertConditionsSatisfiedZEvent event, MessagePublisher messagePublisher, AlertManager alertMan) { _def = def; _event = event; this.messagePublisher = messagePublisher; this.alertMan = alertMan; } /** * In the classic escalatable architecture, we still need to support the * execution of the actions defined for the regular alert defintion (in * addition to executing the actions specified by the escalation). * * Here, we generate the alert and also execute the old-skool actions. May * or may not be the right place to do that. */ public Escalatable createEscalatable() throws ResourceDeletedException { Escalatable escalatable = createEscalatableNoNotify(); registerAlertFiredEvent(escalatable.getAlertInfo().getId(), (AlertConditionsSatisfiedZEventPayload) _event.getPayload()); return escalatable; } Escalatable createEscalatableNoNotify() throws ResourceDeletedException { final Alert alert = createAlert(); createConditionLogs(alert, (AlertConditionsSatisfiedZEventPayload) _event.getPayload()); String shortReason = alertMan.getShortReason(alert); String longReason = alertMan.getLongReason(alert); executeActions(alert, shortReason, longReason); return createEscalatable(alert, shortReason, longReason); } private Alert createAlert() throws ResourceDeletedException { Resource r = _def.getResource(); if (r == null || r.isInAsyncDeleteState()) { throw ResourceDeletedException.newInstance(r); } return alertMan.createAlert(_def, ((AlertConditionsSatisfiedZEventPayload) _event .getPayload()).getTimestamp()); } private void createConditionLogs(final Alert alert, final AlertConditionsSatisfiedZEventPayload payload) { // Create a alert condition logs for every condition that triggered the // alert // Create the trigger event map Map trigMap = new HashMap(); TriggerFiredEvent[] events = payload.getTriggerFiredEvents(); for (int i = 0; i < events.length; i++) { trigMap.put(events[i].getInstanceId(), events[i]); } Collection conds = _def.getConditions(); for (Iterator i = conds.iterator(); i.hasNext();) { AlertCondition cond = (AlertCondition) i.next(); if (shouldCreateConditionLogFor(cond, trigMap)) { AlertConditionLogValue clog = new AlertConditionLogValue(); clog.setCondition(cond.getAlertConditionValue()); Integer trigId = cond.getTrigger().getId(); clog.setValue(trigMap.get(trigId).toString()); alert.createConditionLog(clog.getValue(), cond); } } } private void executeActions(Alert alert, String shortReason, String longReason) { Collection actions = _def.getActions(); // Iterate through the actions for (Iterator i = actions.iterator(); i.hasNext();) { Action act = (Action) i.next(); try { ActionExecutionInfo execInfo = new ActionExecutionInfo(shortReason, longReason, Collections.EMPTY_LIST); String detail = act.executeAction(alert, execInfo); alertMan.logActionDetail(alert, act, detail, null); }catch(OptimisticLockingFailureException e) { throw e; } catch (Exception e) { // For any exception, just log it. We can't afford not // letting the other actions go un-processed. _log.warn("Error executing action [" + act + "]", e); } } } private void registerAlertFiredEvent(final Integer alertId, final AlertConditionsSatisfiedZEventPayload payload) { try { TransactionSynchronizationManager .registerSynchronization(new TransactionSynchronization() { public void suspend() { } public void resume() { } public void flush() { } public void beforeCompletion() { } public void beforeCommit(boolean readOnly) { } public void afterCompletion(int status) { } public void afterCommit() { // TODO HE-565 Possibly have MessagePublisher always // wait until successful tx commit before publishing // messages messagePublisher.publishMessage(EventConstants.EVENTS_TOPIC, new AlertFiredEvent(alertId, _def.getId(), AppdefUtil .newAppdefEntityId(_def.getResource()), _def.getName(), payload .getTimestamp(), payload.getMessage())); } }); }catch(OptimisticLockingFailureException e) { throw e; } catch (Throwable t) { _log .error( "Error registering to send an AlertFiredEvent on transaction commit. The alert will be fired, but the event will not be sent. This could cause a future recovery alert not to fire.", t); } } public AlertDefinitionInterface getAlertDefinition() { return _def; } public static Escalatable createEscalatable(Alert alert, String shortReason, String longReason) { return new ClassicEscalatable(alert, shortReason, longReason); } private boolean shouldCreateConditionLogFor(AlertCondition cond, Map triggerMap) { if (cond.getType() == EventConstants.TYPE_ALERT) { // Don't create a log for recovery alerts, so that we don't // get the multi-condition effect in the logs return false; } Integer trigId = cond.getTrigger().getId(); return triggerMap.containsKey(trigId); } }