/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.enterprise.server.alert.engine.jms; import javax.ejb.ActivationConfigProperty; import javax.ejb.EJB; import javax.ejb.MessageDriven; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.ObjectMessage; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.rhq.core.domain.alert.Alert; import org.rhq.core.domain.resource.InventoryStatus; import org.rhq.enterprise.server.alert.AlertConditionManagerLocal; import org.rhq.enterprise.server.alert.AlertManagerLocal; import org.rhq.enterprise.server.alert.CachedConditionManagerLocal; import org.rhq.enterprise.server.alert.engine.jms.model.AbstractAlertConditionMessage; import org.rhq.enterprise.server.cloud.instance.CacheConsistencyManagerLocal; import org.rhq.enterprise.server.util.concurrent.AlertSerializer; /** * Use the default message provider * * @author Joseph Marques */ @MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), @ActivationConfigProperty(propertyName = "destination", propertyValue = "java:/queue/AlertConditionQueue"), @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"), @ActivationConfigProperty(propertyName = "subscriptionDurability", propertyValue = "NonDurable") }) public class AlertConditionConsumerBean implements MessageListener { private final Log log = LogFactory.getLog(AlertConditionConsumerBean.class); @EJB private AlertManagerLocal alertManager; @EJB private AlertConditionManagerLocal alertConditionManager; @EJB private CachedConditionManagerLocal cachedConditionManager; @EJB private CacheConsistencyManagerLocal cacheConsistencyManager; @Override public void onMessage(Message message) { AbstractAlertConditionMessage conditionMessage = null; try { ObjectMessage objectMessage = (ObjectMessage) message; conditionMessage = (AbstractAlertConditionMessage) objectMessage.getObject(); } catch (Throwable t) { log.error("Error getting content of jms message", t); return; } Integer definitionId = null; try { if (log.isDebugEnabled()) { log.debug("Received message: " + conditionMessage); } int alertConditionId = conditionMessage.getAlertConditionId(); InventoryStatus status = alertConditionManager.getResourceStatusByConditionIdNewTx(alertConditionId); if (status != InventoryStatus.COMMITTED) { if (log.isDebugEnabled()) { log.debug("Resource for AlertCondition[id=" + alertConditionId + "] is no longer COMMITTED, status was '" + status + "'; this message will be discarded"); } return; } definitionId = alertConditionManager.getAlertDefinitionByConditionIdNewTx(alertConditionId); if (definitionId == null) { log.info("AlertCondition[id=" + alertConditionId + "] has been removed after it was triggered; this message will be discarded"); return; } AlertSerializer.getSingleton().lock(definitionId); /* * must be executed in a new, nested transaction so that by it completes and unlocks, the next thread * will see all of its results. */ Alert newAlert = cachedConditionManager.processCachedConditionMessageNewTx(conditionMessage, definitionId); /* * In general it's not required to reload the caches directly. Changes made via the AlertDefinitionManager * will update the cache indirectly via the status fields on the server (for the global cache) and * owning agent (for the agent cache) and the periodic job that checks it. But, for recovery alert * handling (see BZ 1003132) the delay of up to 30s is unacceptably long and can cause recovery to be * missed. There may be non-recovery issues like this as well. So, when any alert is fired, for the server * in question, perform an immediate cache reload check. This ensures the recovery semantics are quickly * put in place, minimizing the window of vulnerability. Note that other HA nodes will be updated via the * scheduled check, which should be fine, as that is mainly to handle an agent failover use case. * * Note that we must do this *after* the alert firing transaction completes. * * As of 4.10 we've moved the alert notification handling out of the alert firing transaction and * after the cache reload. This ensures that the cache, and in particular, recovery alert defs are * updated before executing notifications that could initiate recovery processing (like an automated * restart of a down resource). It also makes the alert firing transaction more lean. */ if (null != newAlert) { log.debug("Checking for cache reload due to alert firing"); cacheConsistencyManager.reloadServerCacheIfNeededNSTx(); // the alert is already persisted, now process notifications alertManager.sendAlertNotificationsNSTx(newAlert); } } catch (Throwable t) { log.error("Error handling " + conditionMessage + " - " + t.toString()); } finally { try { if (definitionId != null) { AlertSerializer.getSingleton().unlock(definitionId); } } catch (Throwable t) { } } } }