/* * RHQ Management Platform * Copyright (C) 2005-2014 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package org.rhq.enterprise.server.alert; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.ejb.EJB; import javax.ejb.Stateless; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.ejb.QueryImpl; import org.rhq.core.db.DatabaseType; import org.rhq.core.db.DatabaseTypeFactory; import org.rhq.core.domain.alert.Alert; import org.rhq.core.domain.alert.AlertCondition; import org.rhq.core.domain.alert.AlertConditionCategory; import org.rhq.core.domain.alert.AlertConditionLog; import org.rhq.core.domain.alert.AlertConditionOperator; import org.rhq.core.domain.alert.AlertDefinition; import org.rhq.core.domain.alert.notification.AlertNotification; import org.rhq.core.domain.alert.notification.AlertNotificationLog; import org.rhq.core.domain.alert.notification.ResultState; import org.rhq.core.domain.alert.notification.SenderResult; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.authz.Permission; import org.rhq.core.domain.common.EntityContext; import org.rhq.core.domain.common.composite.SystemSetting; import org.rhq.core.domain.criteria.AlertCriteria; import org.rhq.core.domain.measurement.MeasurementUnits; import org.rhq.core.domain.operation.OperationDefinition; import org.rhq.core.domain.resource.Resource; import org.rhq.core.domain.resource.ResourceAncestryFormat; import org.rhq.core.domain.server.PersistenceUtility; import org.rhq.core.domain.util.PageControl; import org.rhq.core.domain.util.PageList; import org.rhq.core.server.MeasurementConverter; import org.rhq.core.util.collection.ArrayUtils; import org.rhq.enterprise.server.RHQConstants; import org.rhq.enterprise.server.alert.i18n.AlertI18NFactory; import org.rhq.enterprise.server.alert.i18n.AlertI18NResourceKeys; import org.rhq.enterprise.server.auth.SubjectManagerLocal; import org.rhq.enterprise.server.authz.AuthorizationManagerLocal; import org.rhq.enterprise.server.authz.PermissionException; import org.rhq.enterprise.server.authz.RequiredPermission; import org.rhq.enterprise.server.common.ApplicationException; import org.rhq.enterprise.server.core.EmailManagerLocal; import org.rhq.enterprise.server.operation.OperationManagerLocal; import org.rhq.enterprise.server.plugin.pc.MasterServerPluginContainer; import org.rhq.enterprise.server.plugin.pc.alert.AlertSender; import org.rhq.enterprise.server.plugin.pc.alert.AlertSenderPluginManager; import org.rhq.enterprise.server.plugin.pc.alert.AlertServerPluginContainer; import org.rhq.enterprise.server.resource.ResourceManagerLocal; import org.rhq.enterprise.server.system.SystemManagerLocal; import org.rhq.enterprise.server.util.BatchIterator; import org.rhq.enterprise.server.util.CriteriaQueryGenerator; import org.rhq.enterprise.server.util.CriteriaQueryRunner; import org.rhq.enterprise.server.util.LookupUtil; /** * @author Joseph Marques * @author Ian Springer */ @Stateless public class AlertManagerBean implements AlertManagerLocal, AlertManagerRemote { @PersistenceContext(unitName = RHQConstants.PERSISTENCE_UNIT_NAME) private EntityManager entityManager; private final Log log = LogFactory.getLog(AlertManagerBean.class); @EJB //@IgnoreDependency private AlertConditionLogManagerLocal alertConditionLogManager; @EJB private AlertDefinitionManagerLocal alertDefinitionManager; @EJB private AlertManagerLocal alertManager; @EJB private AuthorizationManagerLocal authorizationManager; @EJB //@IgnoreDependency private ResourceManagerLocal resourceManager; @EJB private SubjectManagerLocal subjectManager; @EJB private SystemManagerLocal systemManager; @EJB //@IgnoreDependency private OperationManagerLocal operationManager; @EJB private EmailManagerLocal emailManager; @Override public int deleteAlerts(Subject user, int[] alertIds) { if (alertIds == null || alertIds.length == 0) { return 0; } List<Integer> alertIdList = ArrayUtils.wrapInList(alertIds); checkAlertsPermission(user, alertIdList); Query deleteConditionLogsQuery = entityManager.createNamedQuery(AlertConditionLog.QUERY_DELETE_BY_ALERT_IDS); Query deleteNotifLogsQuery = entityManager.createNamedQuery(AlertNotificationLog.QUERY_DELETE_BY_ALERT_IDS); Query deleteAlertsQuery = entityManager.createNamedQuery(Alert.QUERY_DELETE_BY_IDS); int updated = 0; BatchIterator<Integer> batchIter = new BatchIterator<Integer>(alertIdList); for (List<Integer> nextBatch : batchIter) { // need to delete related objects before deleting alerts deleteConditionLogsQuery.setParameter("alertIds", nextBatch); deleteConditionLogsQuery.executeUpdate(); deleteNotifLogsQuery.setParameter("alertIds", nextBatch); deleteNotifLogsQuery.executeUpdate(); // now we can delete alerts deleteAlertsQuery.setParameter("alertIds", nextBatch); updated += deleteAlertsQuery.executeUpdate(); } return updated; } /** * Acknowledge alert(s) so that administrators know who is working on remedying the underlying * condition(s) that caused the alert(s) in the first place. * * @param subject calling user * @param alertIds PKs of the alerts to acknowledge * @return number of alerts acknowledged */ @Override public int acknowledgeAlerts(Subject subject, int[] alertIds) { if (alertIds == null || alertIds.length == 0) { return 0; } List<Integer> alertIdList = ArrayUtils.wrapInList(alertIds); checkAlertsPermission(subject, alertIdList); Query ackAlertsQuery = entityManager.createNamedQuery(Alert.QUERY_ACKNOWLEDGE_BY_IDS); ackAlertsQuery.setParameter("subjectName", subject.getName()); ackAlertsQuery.setParameter("ackTime", System.currentTimeMillis()); int modified = 0; BatchIterator<Integer> batchIter = new BatchIterator<Integer>(alertIdList); for (List<Integer> nextBatch : batchIter) { ackAlertsQuery.setParameter("alertIds", nextBatch); modified += ackAlertsQuery.executeUpdate(); } return modified; } @Override @RequiredPermission(Permission.MANAGE_SETTINGS) public int deleteAlertsByContext(Subject subject, EntityContext context) { Query deleteConditionLogsQuery = null; Query deleteNotificationLogsQuery = null; Query deleteAlertsQuery = null; if (context.type == EntityContext.Type.Resource) { if (!authorizationManager.hasResourcePermission(subject, Permission.MANAGE_ALERTS, context.resourceId)) { throw new PermissionException("Can not delete alerts - " + subject + " lacks " + Permission.MANAGE_ALERTS + " for resource[id=" + context.resourceId + "]"); } deleteConditionLogsQuery = entityManager.createNamedQuery(AlertConditionLog.QUERY_DELETE_BY_RESOURCES); deleteConditionLogsQuery.setParameter("resourceIds", Arrays.asList(context.resourceId)); deleteNotificationLogsQuery = entityManager .createNamedQuery(AlertNotificationLog.QUERY_DELETE_BY_RESOURCES); deleteNotificationLogsQuery.setParameter("resourceIds", Arrays.asList(context.resourceId)); deleteAlertsQuery = entityManager.createNamedQuery(Alert.QUERY_DELETE_BY_RESOURCES); deleteAlertsQuery.setParameter("resourceIds", Arrays.asList(context.resourceId)); } else if (context.type == EntityContext.Type.ResourceGroup) { if (!authorizationManager.hasGroupPermission(subject, Permission.MANAGE_ALERTS, context.groupId)) { throw new PermissionException("Can not delete alerts - " + subject + " lacks " + Permission.MANAGE_ALERTS + " for group[id=" + context.groupId + "]"); } deleteConditionLogsQuery = entityManager .createNamedQuery(AlertConditionLog.QUERY_DELETE_BY_RESOURCE_GROUPS); deleteConditionLogsQuery.setParameter("groupIds", Arrays.asList(context.groupId)); deleteNotificationLogsQuery = entityManager .createNamedQuery(AlertNotificationLog.QUERY_DELETE_BY_RESOURCE_GROUPS); deleteNotificationLogsQuery.setParameter("groupIds", Arrays.asList(context.groupId)); deleteAlertsQuery = entityManager.createNamedQuery(Alert.QUERY_DELETE_BY_RESOURCE_GROUPS); deleteAlertsQuery.setParameter("groupIds", Arrays.asList(context.groupId)); } else if (context.type == EntityContext.Type.SubsystemView) { if (!authorizationManager.isInventoryManager(subject)) { throw new PermissionException("Can not delete alerts - " + subject + " lacks " + Permission.MANAGE_INVENTORY + " for global alerts history"); } deleteConditionLogsQuery = entityManager.createNamedQuery(AlertConditionLog.QUERY_DELETE_ALL); deleteNotificationLogsQuery = entityManager.createNamedQuery(AlertNotificationLog.QUERY_DELETE_ALL); deleteAlertsQuery = entityManager.createNamedQuery(Alert.QUERY_DELETE_ALL); } else if (context.type == EntityContext.Type.ResourceTemplate) { // TODO Need to determine what security check(s) need to be performed here deleteAlertsQuery = entityManager.createNamedQuery(Alert.QUERY_DELETE_BY_RESOURCE_TEMPLATE); deleteAlertsQuery.setParameter("resourceTypeId", context.resourceTypeId); deleteConditionLogsQuery = entityManager .createNamedQuery(AlertConditionLog.QUERY_DELETE_BY_RESOURCE_TEMPLATE); deleteConditionLogsQuery.setParameter("resourceTypeId", context.resourceTypeId); deleteNotificationLogsQuery = entityManager .createNamedQuery(AlertNotificationLog.QUERY_DELETE_BY_RESOURCE_TEMPLATE); deleteNotificationLogsQuery.setParameter("resourceTypeId", context.resourceTypeId); } else { throw new IllegalArgumentException("No support for deleting alerts for " + context); } deleteConditionLogsQuery.executeUpdate(); deleteNotificationLogsQuery.executeUpdate(); int affectedRows = deleteAlertsQuery.executeUpdate(); return affectedRows; } @Override public int acknowledgeAlertsByContext(Subject subject, EntityContext context) { Query query = null; if (context.type == EntityContext.Type.Resource) { if (!authorizationManager.hasResourcePermission(subject, Permission.MANAGE_ALERTS, context.resourceId)) { throw new PermissionException("Can not acknowledge alerts - " + subject + " lacks " + Permission.MANAGE_ALERTS + " for resource[id=" + context.resourceId + "]"); } query = entityManager.createNamedQuery(Alert.QUERY_ACKNOWLEDGE_BY_RESOURCES); query.setParameter("resourceIds", Arrays.asList(context.resourceId)); } else if (context.type == EntityContext.Type.ResourceGroup) { if (!authorizationManager.hasGroupPermission(subject, Permission.MANAGE_ALERTS, context.groupId)) { throw new PermissionException("Can not acknowledge alerts - " + subject + " lacks " + Permission.MANAGE_ALERTS + " for group[id=" + context.groupId + "]"); } query = entityManager.createNamedQuery(Alert.QUERY_ACKNOWLEDGE_BY_RESOURCE_GROUPS); query.setParameter("groupIds", Arrays.asList(context.groupId)); } else if (context.type == EntityContext.Type.SubsystemView) { if (!authorizationManager.isInventoryManager(subject)) { throw new PermissionException("Can not acknowledge alerts - " + subject + " lacks " + Permission.MANAGE_INVENTORY + " for global alerts history"); } query = entityManager.createNamedQuery(Alert.QUERY_ACKNOWLEDGE_ALL); } else { throw new IllegalArgumentException("No support for acknowledging alerts for " + context); } query.setParameter("subjectName", subject.getName()); query.setParameter("ackTime", System.currentTimeMillis()); int affectedRows = query.executeUpdate(); return affectedRows; } // TODO: if user passes an alertId that doesn't exist, it will generate a permission exception // because the query will think the user does not have access to the corresponding resource. // we need another check that ensures all alertIds exist first, or perhaps code that removes // and/or gracefully ignores the ones that don't exist // // TODO: need to break up this query and iterate in blocks of 1000 ids at a time, to avoid oracle // in-clause issues private void checkAlertsPermission(Subject subject, List<Integer> alertIds) { if (authorizationManager.isInventoryManager(subject)) { return; // inventory manager } long canModifyCount = checkAuthz(subject, alertIds); long canNotModifyCount = alertIds.size() - canModifyCount; if (canNotModifyCount != 0) { /* * implies one of two things: * 1) user does not have permission to modify alerts for some of the corresponding resources * 2) some of the passed alertIds do not exist * * to remedy this, let's remove alertIds that no longer exist. if the new list is smaller than the * original list, we know that the list DID contain non-existent entries and we should perform the authz * check again. however, if all of the elements in the original list existed, then we know that the * original authz check was valid, and we should throw the necessary PermissionException */ List<Integer> validAlertIds = removeNonExistent(alertIds); if (validAlertIds.size() == alertIds.size()) { throw new PermissionException(subject + " does not have permission to delete " + canNotModifyCount + " of the " + alertIds.size() + " passsed alertIds"); } else { canModifyCount = checkAuthz(subject, alertIds); canNotModifyCount = alertIds.size() - canModifyCount; if (canNotModifyCount != 0) { throw new PermissionException(subject + " does not have permission to delete " + canNotModifyCount + " of the " + alertIds.size() + " passsed alertIds"); } } } } private long checkAuthz(Subject subject, List<Integer> alertIds) { /* * get the count of the number of these alerts for which user * has MANAGE_ALERTS permission on the corresponding resource */ Query authzQuery = entityManager.createNamedQuery(Alert.QUERY_CHECK_PERMISSION_BY_IDS); authzQuery.setParameter("subjectId", subject.getId()); authzQuery.setParameter("permission", Permission.MANAGE_ALERTS); long canModifyCount = 0; BatchIterator<Integer> batchIter = new BatchIterator<Integer>(alertIds); for (List<Integer> nextBatch : batchIter) { authzQuery.setParameter("alertIds", nextBatch); canModifyCount += (Long) authzQuery.getSingleResult(); } return canModifyCount; } @SuppressWarnings("unchecked") private List<Integer> removeNonExistent(List<Integer> alertIds) { /* * get the count of the number of these alerts for which user * has MANAGE_ALERTS permission on the corresponding resource */ Query authzQuery = entityManager.createNamedQuery(Alert.QUERY_RETURN_EXISTING_IDS); List<Integer> existingAlertIds = new ArrayList<Integer>(); BatchIterator<Integer> batchIter = new BatchIterator<Integer>(alertIds); for (List<Integer> nextBatch : batchIter) { authzQuery.setParameter("alertIds", nextBatch); existingAlertIds.addAll(authzQuery.getResultList()); } return existingAlertIds; } @Override public int getAlertCountByMeasurementDefinitionId(Integer measurementDefinitionId, long begin, long end) { Query query = PersistenceUtility.createCountQuery(entityManager, Alert.QUERY_FIND_BY_MEASUREMENT_DEFINITION_ID); query.setParameter("measurementDefinitionId", measurementDefinitionId); query.setParameter("begin", begin); query.setParameter("end", end); long count = (Long) query.getSingleResult(); return (int) count; } @Override public int getAlertCountByMeasurementDefinitionAndResources(int measurementDefinitionId, int[] resourceIds, long beginDate, long endDate) { Query query = PersistenceUtility.createCountQuery(entityManager, Alert.QUERY_FIND_BY_MEAS_DEF_ID_AND_RESOURCES); query.setParameter("measurementDefinitionId", measurementDefinitionId); query.setParameter("startDate", beginDate); query.setParameter("endDate", endDate); query.setParameter("resourceIds", ArrayUtils.wrapInList(resourceIds)); long count = (Long) query.getSingleResult(); return (int) count; } @Override public int getAlertCountByMeasurementDefinitionAndResourceGroup(int measurementDefinitionId, int groupId, long beginDate, long endDate) { Query query = PersistenceUtility.createCountQuery(entityManager, Alert.QUERY_FIND_BY_MEAS_DEF_ID_AND_RESOURCEGROUP); query.setParameter("measurementDefinitionId", measurementDefinitionId); query.setParameter("startDate", beginDate); query.setParameter("endDate", endDate); query.setParameter("groupId", groupId); long count = (Long) query.getSingleResult(); return (int) count; } @Override public int getAlertCountByMeasurementDefinitionAndAutoGroup(int measurementDefinitionId, int resourceParentId, int resourceTypeId, long beginDate, long endDate) { Query query = PersistenceUtility.createCountQuery(entityManager, Alert.QUERY_FIND_BY_MEAS_DEF_ID_AND_AUTOGROUP); query.setParameter("measurementDefinitionId", measurementDefinitionId); query.setParameter("startDate", beginDate); query.setParameter("endDate", endDate); query.setParameter("parentId", resourceParentId); query.setParameter("typeId", resourceTypeId); long count = (Long) query.getSingleResult(); return (int) count; } @Override public int getAlertCountByMeasurementDefinitionAndResource(int measurementDefinitionId, int resourceId, long beginDate, long endDate) { Query query = PersistenceUtility.createCountQuery(entityManager, Alert.QUERY_FIND_BY_MEAS_DEF_ID_AND_RESOURCE); query.setParameter("measurementDefinitionId", measurementDefinitionId); query.setParameter("startDate", beginDate); query.setParameter("endDate", endDate); query.setParameter("resourceId", resourceId); long count = (Long) query.getSingleResult(); return (int) count; } @Override @SuppressWarnings("unchecked") public Map<Integer, Integer> getAlertCountForSchedules(long begin, long end, List<Integer> scheduleIds) { if ((scheduleIds == null) || (scheduleIds.size() == 0) || (end < begin)) { return new HashMap<Integer, Integer>(); } final int BATCH_SIZE = 1000; int numSched = scheduleIds.size(); int rounds = (numSched / BATCH_SIZE) + 1; Map<Integer, Integer> resMap = new HashMap<Integer, Integer>(); // iterate over the passed schedules ids when we have more than 1000 of them, as some // databases bail out with more than 1000 resources in IN () clauses. for (int round = 0; round < rounds; round++) { int fromIndex = round * BATCH_SIZE; int toIndex = fromIndex + BATCH_SIZE; if (toIndex > numSched) // don't run over the end of the list toIndex = numSched; List<Integer> scheds = scheduleIds.subList(fromIndex, toIndex); if (fromIndex == toIndex) continue; Query q = entityManager.createNamedQuery(Alert.QUERY_GET_ALERT_COUNT_FOR_SCHEDULES); q.setParameter("startDate", begin); q.setParameter("endDate", end); q.setParameter("schedIds", scheds); List<Object[]> ret = q.getResultList(); if (ret.size() > 0) { for (Object[] obj : ret) { Integer scheduleId = (Integer) obj[0]; Long tmp = (Long) obj[1]; int alertCount = tmp.intValue(); resMap.put(scheduleId, alertCount); } } } // Now fill in those schedules without return value to have an alertCount of 0 for (int scheduleId : scheduleIds) { if (!resMap.containsKey(scheduleId)) { resMap.put(scheduleId, 0); } } return resMap; } private void fetchCollectionFields(Alert alert) { alert.getConditionLogs().size(); for (AlertConditionLog log : alert.getConditionLogs()) { // this is now lazy if (log.getCondition() != null && log.getCondition().getMeasurementDefinition() != null) { log.getCondition().getMeasurementDefinition().getId(); } } alert.getAlertNotificationLogs().size(); } private void fetchCollectionFields(List<Alert> alerts) { for (Alert alert : alerts) { fetchCollectionFields(alert); } } @Override public Alert fireAlert(int alertDefinitionId) { if (log.isDebugEnabled()) { log.debug("Firing an alert for alertDefinition with id=" + alertDefinitionId + "..."); } Subject overlord = subjectManager.getOverlord(); AlertDefinition alertDefinition = alertDefinitionManager.getAlertDefinitionById(overlord, alertDefinitionId); /* * creating an alert via an alertDefinition automatically creates the needed auditing data structures such as * alertConditionLogs and alertNotificationLogs */ Alert newAlert = new Alert(alertDefinition, System.currentTimeMillis()); /* * the AlertConditionLog children objects are already in the database, we need to persist the alert first * to prevent: * * "TransientObjectException: object references an unsaved transient instance - save the transient instance before * flushing org.jboss.on.domain.event.alert.AlertConditionLog.alert -> org.jboss.on.domain.event.alert.Alert" */ entityManager.persist(newAlert); if (log.isDebugEnabled()) { log.debug("New alert identifier=" + newAlert.getId()); } // AlertNotificationLog alertNotifLog = new AlertNotificationLog(newAlert); TODO - is that all? // entityManager.persist(alertNotifLog); List<AlertConditionLog> unmatchedConditionLogs = alertConditionLogManager .getUnmatchedLogsByAlertDefinitionId(alertDefinitionId); for (AlertConditionLog unmatchedLog : unmatchedConditionLogs) { if (log.isDebugEnabled()) { log.debug("Matched alert condition log for alertId=" + newAlert.getId() + ": " + unmatchedLog); } newAlert.addConditionLog(unmatchedLog); // adds both relationships } // process recovery actions processRecovery(alertDefinition); return newAlert; } /** * This is the core of the alert sending process. For each AlertNotification that is hanging * on the alerts definition, the sender is instantiated and its send() method called. If a sender * returns a list of email addresses, those will be collected and sent at the end. * @param alert the fired alert */ @Override @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public void sendAlertNotificationsNSTx(Alert alert) { /* * make this method public in case we show the notification failure to the user in the UI in the future and want * to give them some way to explicitly try to re-send the notification for some client-side auditing purposes */ try { if (log.isDebugEnabled()) { log.debug("Sending alert notifications for " + alert.toSimpleString() + "..."); } List<AlertNotification> alertNotifications = alert.getAlertDefinition().getAlertNotifications(); if (alertNotifications != null && alertNotifications.size() > 0) { AlertSenderPluginManager alertSenderPluginManager = getAlertPluginManager(); for (AlertNotification alertNotification : alertNotifications) { try { AlertNotificationLog notificationLog = null; String senderName = alertNotification.getSenderName(); if (alertSenderPluginManager == null) { notificationLog = new AlertNotificationLog(alert, senderName, ResultState.FAILURE, "Notification was not sent as alert sender plugins are not yet initialized "); } else if (senderName == null) { notificationLog = new AlertNotificationLog(alert, senderName, ResultState.FAILURE, "Sender '" + senderName + "' is not defined"); } else { AlertSender<?> notificationSender = alertSenderPluginManager .getAlertSenderForNotification(alertNotification); if (notificationSender == null) { notificationLog = new AlertNotificationLog(alert, senderName, ResultState.FAILURE, "Failed to obtain a sender with given name"); } else { try { SenderResult result = notificationSender.send(alert); if (log.isDebugEnabled()) { log.debug(result); } if (result == null) { notificationLog = new AlertNotificationLog(alert, senderName, ResultState.UNKNOWN, "Sender did not return any result"); } else { notificationLog = new AlertNotificationLog(alert, senderName, result); } } catch (Throwable t) { log.error("Notification processing terminated abruptly" + t.getMessage()); notificationLog = new AlertNotificationLog(alert, senderName, ResultState.FAILURE, "Notification processing terminated abruptly, cause: " + t.getMessage()); } } } alertManager.addNotificationLog(alert.getId(), notificationLog); } catch (Throwable t) { log.error( "Failed to send notification [" + alertNotification + "] for [" + alert.toSimpleString() + "].", t); } } } } catch (Throwable t) { log.error("Failed to send all notifications for [" + alert.toSimpleString() + "].", t); } } public void addNotificationLog(int alertId, AlertNotificationLog notificationLog) { Alert alert = entityManager.find(Alert.class, alertId); if (null == alert) { throw new ApplicationException("Alert not found with id [" + alertId + "]."); } // make sure we don't exceed the max message length for the db vendor DatabaseType dbType = DatabaseTypeFactory.getDefaultDatabaseType(); String message = dbType.getString(notificationLog.getMessage(), AlertNotificationLog.MESSAGE_MAX_LENGTH); if (null != message && !message.equals(notificationLog.getMessage())) { notificationLog = new AlertNotificationLog(notificationLog.getAlert(), notificationLog.getSender(), notificationLog.getResultState(), message); } entityManager.persist(notificationLog); alert.addAlertNotificatinLog(notificationLog); } /** * Return the plugin manager that is managing alert sender plugins * @return The alert sender plugin manager */ @Override public AlertSenderPluginManager getAlertPluginManager() { MasterServerPluginContainer container = LookupUtil.getServerPluginService().getMasterPluginContainer(); if (container == null) { log.warn(MasterServerPluginContainer.class.getSimpleName() + " is not started yet"); return null; } AlertServerPluginContainer pc = container.getPluginContainerByClass(AlertServerPluginContainer.class); if (pc == null) { log.warn(AlertServerPluginContainer.class.getSimpleName() + " has not been loaded by the " + MasterServerPluginContainer.class.getSimpleName() + " yet"); return null; } AlertSenderPluginManager manager = (AlertSenderPluginManager) pc.getPluginManager(); return manager; } @Override public Collection<String> sendAlertNotificationEmails(Alert alert, Collection<String> emailAddresses) { if (log.isDebugEnabled()) { log.debug("Sending alert notifications for " + alert.toSimpleString() + "..."); } if (emailAddresses.size() == 0) { return new ArrayList<String>(0); // No email to send -> no bad addresses } AlertDefinition alertDefinition = alert.getAlertDefinition(); Resource resource = alertDefinition.getResource(); Map<Integer, String> ancestry = resourceManager.getResourcesAncestry(subjectManager.getOverlord(), new Integer[] { resource.getId() }, ResourceAncestryFormat.VERBOSE); Map<String, String> alertMessage = emailManager.getAlertEmailMessage(ancestry.get(resource.getId()), // resource.getName(), // alertDefinition.getName(), // alertDefinition.getPriority().toString(), // new Date(alert.getCtime()).toString(), // prettyPrintAlertConditions(alert.getConditionLogs(), false), // prettyPrintAlertURL(alert)); String messageSubject = alertMessage.keySet().iterator().next(); String messageBody = alertMessage.values().iterator().next(); Set<String> uniqueAddresses = new HashSet<String>(emailAddresses); Collection<String> badAddresses = emailManager.sendEmail(uniqueAddresses, messageSubject, messageBody); if (log.isDebugEnabled()) { if (badAddresses.isEmpty()) { log.debug("All notifications for " + alert.toSimpleString() + " succeeded"); } else { log.debug("Sending email notifications for " + badAddresses + " failed"); } } return badAddresses; } private static String NEW_LINE = System.getProperty("line.separator"); /** * Create a human readable description of the conditions that led to this alert. * @param alert Alert to create human readable condition description * @param shortVersion if true the messages printed are abbreviated to save space * @return human readable condition log */ @Override public String prettyPrintAlertConditions(Alert alert, boolean shortVersion) { return prettyPrintAlertConditions(alert.getConditionLogs(), shortVersion); } private String prettyPrintAlertConditions(Set<AlertConditionLog> conditionLogs, boolean shortVersion) { StringBuilder builder = new StringBuilder(); int conditionCounter = 1; for (AlertConditionLog aLog : conditionLogs) { String formattedValue = null; try { formattedValue = MeasurementConverter.format(Double.valueOf(aLog.getValue()), aLog.getCondition() .getMeasurementDefinition().getUnits(), true); } catch (Exception e) { // If the value does not parse just report the value as is. formattedValue = aLog.getValue(); } builder.append(NEW_LINE); String format; if (shortVersion) format = AlertI18NResourceKeys.ALERT_EMAIL_CONDITION_LOG_FORMAT_SHORT; else format = AlertI18NResourceKeys.ALERT_EMAIL_CONDITION_LOG_FORMAT; SimpleDateFormat dateFormat; if (shortVersion) dateFormat = new SimpleDateFormat("yy/MM/dd HH:mm:ss z"); else dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss z"); builder.append(AlertI18NFactory.getMessage(format, conditionCounter, prettyPrintAlertCondition(aLog.getCondition(), shortVersion), dateFormat.format(new Date(aLog.getCtime())), formattedValue)); conditionCounter++; } return builder.toString(); } private String prettyPrintAlertCondition(AlertCondition condition, boolean isShort) { StringBuilder str = new StringBuilder(); AlertConditionCategory category = condition.getCategory(); switch (category) { case AVAILABILITY: { AlertConditionOperator operator = AlertConditionOperator.valueOf(condition.getName().toUpperCase()); String msg; switch (operator) { case AVAIL_GOES_DISABLED: msg = isShort ? AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_DISABLED_SHORT : AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_DISABLED; break; case AVAIL_GOES_DOWN: msg = isShort ? AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_DOWN_SHORT : AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_DOWN; break; case AVAIL_GOES_UNKNOWN: msg = isShort ? AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_UNKNOWN_SHORT : AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_UNKNOWN; break; case AVAIL_GOES_UP: msg = isShort ? AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_UP_SHORT : AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_UP; break; case AVAIL_GOES_NOT_UP: default: msg = isShort ? AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_NOT_UP_SHORT : AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_NOT_UP; break; } str.append(AlertI18NFactory.getMessage(msg)); break; } case AVAIL_DURATION: { AlertConditionOperator operator = AlertConditionOperator.valueOf(condition.getName().toUpperCase()); String msg; switch (operator) { case AVAIL_DURATION_DOWN: msg = isShort ? AlertI18NResourceKeys.ALERT_AVAILABILITY_DURATION_DOWN_SHORT : AlertI18NResourceKeys.ALERT_AVAILABILITY_DURATION_DOWN; break; case AVAIL_DURATION_NOT_UP: default: msg = isShort ? AlertI18NResourceKeys.ALERT_AVAILABILITY_DURATION_NOT_UP_SHORT : AlertI18NResourceKeys.ALERT_AVAILABILITY_DURATION_NOT_UP; break; } str.append(AlertI18NFactory.getMessage(msg)); str.append(" ["); // stored in seconds but present in minutes String value = String.valueOf(Integer.valueOf(condition.getOption()) / 60); String formatted = MeasurementConverter.format(value, MeasurementUnits.MINUTES); str.append(formatted); str.append("]"); break; } case THRESHOLD: { double value = condition.getThreshold(); MeasurementUnits units = condition.getMeasurementDefinition().getUnits(); String formatted = MeasurementConverter.format(value, units, true); if (condition.getOption() == null) { String metricName = condition.getName(); String comparator = condition.getComparator(); str.append(metricName).append(' ').append(comparator).append(' ').append(formatted); } else { // this is a calltime threshold condition String metricName = ""; if (condition.getMeasurementDefinition() != null) { metricName = condition.getMeasurementDefinition().getDisplayName(); } String limit = condition.getOption(); // MIN, MAX, AVG (never null) String comparator = condition.getComparator(); // <, >, = if (condition.getName() != null && condition.getName().length() > 0) { String regex = condition.getName(); if (isShort) { str.append(AlertI18NFactory.getMessage( AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_THRESHOLD_WITH_EXPR_SHORT, metricName, limit, comparator, formatted, regex)); } else { str.append(AlertI18NFactory.getMessage( AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_THRESHOLD_WITH_EXPR, metricName, limit, comparator, formatted, regex)); } } else { if (isShort) { str.append(AlertI18NFactory.getMessage( AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_THRESHOLD_SHORT, metricName, limit, comparator, formatted)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_THRESHOLD, metricName, limit, comparator, formatted)); } } } break; } case BASELINE: { String metricName = condition.getName(); String comparator = condition.getComparator(); double value = condition.getThreshold(); MeasurementUnits units = MeasurementUnits.PERCENTAGE; String percentage = MeasurementConverter.format(value, units, true); String baselineThreshold = condition.getOption(); // mean, min, max if (isShort) { if (baselineThreshold.equalsIgnoreCase("min")) { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_BASELINE_MIN_SHORT, metricName, comparator, percentage)); } else if (baselineThreshold.equalsIgnoreCase("max")) { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_BASELINE_MAX_SHORT, metricName, comparator, percentage)); } else { // mean str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_BASELINE_MEAN_SHORT, metricName, comparator, percentage)); } } else { if (baselineThreshold.equalsIgnoreCase("min")) { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_BASELINE_MIN, metricName, comparator, percentage)); } else if (baselineThreshold.equalsIgnoreCase("max")) { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_BASELINE_MAX, metricName, comparator, percentage)); } else { // mean str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_BASELINE_MEAN, metricName, comparator, percentage)); } } break; } case CHANGE: { if (condition.getOption() == null) { String metricName = condition.getName(); if (isShort) { str.append(AlertI18NFactory .getMessage(AlertI18NResourceKeys.ALERT_METRIC_CHANGED_SHORT, metricName)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CHANGED, metricName)); } } else { // this is a calltime change condition double value = condition.getThreshold(); MeasurementUnits units = MeasurementUnits.PERCENTAGE; String formatted = MeasurementConverter.format(value, units, true); String comparator; if ("HI".equalsIgnoreCase(condition.getComparator())) { comparator = AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_GROWS); } else if ("LO".equalsIgnoreCase(condition.getComparator())) { comparator = AlertI18NFactory .getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_SHRINKS); } else { // CH comparator = AlertI18NFactory .getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_CHANGES); } String metricName = ""; if (condition.getMeasurementDefinition() != null) { metricName = condition.getMeasurementDefinition().getDisplayName(); } String limit = condition.getOption(); // MIN, MAX, AVG (never null) if (condition.getName() != null && condition.getName().length() > 0) { String regex = condition.getName(); if (isShort) { str.append(AlertI18NFactory.getMessage( AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_WITH_EXPR_SHORT, metricName, limit, comparator, formatted, regex)); } else { str.append(AlertI18NFactory.getMessage( AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_WITH_EXPR, metricName, limit, comparator, formatted, regex)); } } else { if (isShort) { str.append(AlertI18NFactory.getMessage( AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_SHORT, metricName, limit, comparator, formatted)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE, metricName, limit, comparator, formatted)); } } } break; } case TRAIT: { String metricName = condition.getName(); String expression = condition.getOption(); if (expression != null && !expression.isEmpty()) { if (isShort) { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CHANGED_WITH_EXPR_SHORT, metricName, expression)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CHANGED_WITH_EXPR, metricName, expression)); } } else { if (isShort) { str.append(AlertI18NFactory .getMessage(AlertI18NResourceKeys.ALERT_METRIC_CHANGED_SHORT, metricName)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CHANGED, metricName)); } } break; } case CONTROL: { String opName; try { Integer resourceTypeId = condition.getAlertDefinition().getResource().getResourceType().getId(); String operationName = condition.getName(); OperationDefinition definition = operationManager.getOperationDefinitionByResourceTypeAndName( resourceTypeId, operationName, false); opName = definition.getDisplayName(); } catch (Exception e) { opName = condition.getName(); // can't look up the op display name (are we in a test?), just use the op name } String status = condition.getOption(); if (isShort) { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_OPERATION_SHORT, opName, status)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_OPERATION, opName, status)); } break; } case RESOURCE_CONFIG: { if (isShort) { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_RESOURCECONFIGCHANGE_SHORT)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_RESOURCECONFIGCHANGE)); } break; } case EVENT: { String severity = condition.getName(); if (condition.getOption() != null && condition.getOption().length() > 0) { String expression = condition.getOption(); String regexEventDetails = "", regexSourceLocation = ""; if (expression.contains(AlertCondition.ADHOC_SEPARATOR)) { String[] regexes = expression.split(AlertCondition.ADHOC_SEPARATOR); if (regexes.length > 0) { regexEventDetails = regexes[0]; if (regexes.length > 1) { regexSourceLocation = regexes[1]; } } } else { regexEventDetails = expression; // old approach -> probably working with db before rhq 4.13 } if (isShort) { if (!regexEventDetails.isEmpty()) { if (!regexSourceLocation.isEmpty()) { str.append(AlertI18NFactory.getMessage( AlertI18NResourceKeys.ALERT_EVENT_WITH_EXPR_WITH_SOURCE_SHORT, severity, regexEventDetails, regexSourceLocation)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_EVENT_WITH_EXPR_SHORT, severity, regexEventDetails)); } } else if (!regexSourceLocation.isEmpty()) { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_EVENT_WITH_SOURCE_SHORT, severity, regexSourceLocation)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_EVENT_SHORT, severity)); } } else { if (!regexEventDetails.isEmpty()) { if (!regexSourceLocation.isEmpty()) { str.append(AlertI18NFactory.getMessage( AlertI18NResourceKeys.ALERT_EVENT_WITH_EXPR_WITH_SOURCE, severity, regexEventDetails, regexSourceLocation)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_EVENT_WITH_EXPR, severity, regexEventDetails)); } } else if (!regexSourceLocation.isEmpty()) { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_EVENT_WITH_SOURCE, severity, regexSourceLocation)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_EVENT, severity)); } } } else { if (isShort) { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_EVENT_SHORT, severity)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_EVENT, severity)); } } break; } case DRIFT: { String configNameRegex = condition.getName(); String pathNameRegex = condition.getOption(); if (isShort) { if (configNameRegex == null || configNameRegex.length() == 0) { if (pathNameRegex == null || pathNameRegex.length() == 0) { // neither a config name regex nor path regex was specified str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_DRIFT_SHORT)); } else { // a path name regex was specified, but not a config name regex str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_DRIFT_ONLYPATHS_SHORT, pathNameRegex)); } } else { if (pathNameRegex == null || pathNameRegex.length() == 0) { // a config name regex was specified, but not a path name regex str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_DRIFT_ONLYCONFIG_SHORT, configNameRegex)); } else { // both a config name regex and a path regex was specified str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_DRIFT_CONFIGPATHS_SHORT, pathNameRegex, configNameRegex)); } } } else { if (configNameRegex == null || configNameRegex.length() == 0) { if (pathNameRegex == null || pathNameRegex.length() == 0) { // neither a config name regex nor path regex was specified str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_DRIFT)); } else { // a path name regex was specified, but not a config name regex str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_DRIFT_ONLYPATHS, pathNameRegex)); } } else { if (pathNameRegex == null || pathNameRegex.length() == 0) { // a config name regex was specified, but not a path name regex str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_DRIFT_ONLYCONFIG, configNameRegex)); } else { // both a config name regex and a path regex was specified str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_DRIFT_CONFIGPATHS, pathNameRegex, configNameRegex)); } } } break; } case RANGE: { String metricName = condition.getName(); Double loValue = condition.getThreshold(); String hiValueStr = condition.getOption(); String loValueFormatted; String hiValueFormatted; MeasurementUnits units = condition.getMeasurementDefinition().getUnits(); if (units == null) { loValueFormatted = "" + loValue; hiValueFormatted = hiValueStr; } else { loValueFormatted = MeasurementConverter.format(loValue, units, true); try { hiValueFormatted = MeasurementConverter.format(Double.parseDouble(hiValueStr), units, true); } catch (Exception e) { hiValueFormatted = "?[" + hiValueStr + "]?"; } } // < means "inside the range", > means "outside the range" - exclusive // <= means "inside the range", >= means "outside the range" - inclusive if ("<".equals(condition.getComparator())) { if (isShort) { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_RANGE_INSIDE_EXCL_SHORT, metricName, loValueFormatted, hiValueFormatted)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_RANGE_INSIDE_EXCL, metricName, loValueFormatted, hiValueFormatted)); } } else if (">".equals(condition.getComparator())) { if (isShort) { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_RANGE_OUTSIDE_EXCL_SHORT, metricName, loValueFormatted, hiValueFormatted)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_RANGE_OUTSIDE_EXCL, metricName, loValueFormatted, hiValueFormatted)); } } else if ("<=".equals(condition.getComparator())) { if (isShort) { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_RANGE_INSIDE_INCL_SHORT, metricName, loValueFormatted, hiValueFormatted)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_RANGE_INSIDE_INCL, metricName, loValueFormatted, hiValueFormatted)); } } else if (">=".equals(condition.getComparator())) { if (isShort) { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_RANGE_OUTSIDE_INCL_SHORT, metricName, loValueFormatted, hiValueFormatted)); } else { str.append(AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_RANGE_OUTSIDE_INCL, metricName, loValueFormatted, hiValueFormatted)); } } else { str.append("invalid range comparator [" + condition.getComparator() + "] (" + loValueFormatted + "," + hiValueFormatted + ")"); } break; } default: { str.append("unknown category [" + category.name() + "]"); break; } } return str.toString(); } @Override public String prettyPrintAlertURL(Alert alert) { StringBuilder builder = new StringBuilder(); String baseUrl = systemManager.getUnmaskedSystemSettings(true).get(SystemSetting.BASE_URL); builder.append(baseUrl); if (!baseUrl.endsWith("/")) { builder.append("/"); } builder.append("coregui/CoreGUI.html#Resource/"); builder.append(alert.getAlertDefinition().getResource().getId()); builder.append("/Alerts/History/"); builder.append(alert.getId()); return builder.toString(); } private int markAlertsRecovered(Integer alertDefinitionId) { if (log.isDebugEnabled()) { log.debug("Trying to mark alertDefinitionId " + alertDefinitionId + " recovered"); } Query markRecovered = entityManager.createNamedQuery(Alert.QUERY_MARK_RECOVERED_BY_DEFINITION_ID); markRecovered.setParameter("recoveryTime", System.currentTimeMillis()); markRecovered.setParameter("alertDefinitionId", alertDefinitionId); int recoveredAlerts = markRecovered.executeUpdate(); if (log.isDebugEnabled()) { log.debug("Recovered " + recoveredAlerts + " with alertDefinitionId " + alertDefinitionId); } return recoveredAlerts; } private void processRecovery(AlertDefinition firedDefinition) { Subject overlord = subjectManager.getOverlord(); Integer recoveryDefinitionId = firedDefinition.getRecoveryId(); if (recoveryDefinitionId != 0) { if (log.isDebugEnabled()) { log.debug("Processing recovery rules... Found recoveryDefinitionId " + recoveryDefinitionId); } AlertDefinition toBeRecoveredDefinition = alertDefinitionManager.getAlertDefinitionById(overlord, recoveryDefinitionId); boolean wasEnabled = toBeRecoveredDefinition.getEnabled(); if (log.isDebugEnabled()) { log.debug(firedDefinition + (wasEnabled ? "does not need to recover " : "needs to recover ") + toBeRecoveredDefinition + (wasEnabled ? ", it was already enabled " : ", it was currently disabled ")); } if (!wasEnabled) { /* * - recover the other alert def, go through the manager layer so as to update the alert cache * - we know this is a resource alert def so make that call directly. this is more efficient * and also avoids the issue in BZ 1126853. */ alertDefinitionManager.enableResourceAlertDefinitions(overlord, new int[] { recoveryDefinitionId }); markAlertsRecovered(recoveryDefinitionId); } /* * there's no reason to update the cache directly anymore. even though this direct type of update is safe * (because we know the AlertManager will only be executing on the same server instance that is processing * these recovery alerts now) it's unnecessary because changes made via the AlertDefinitionManager will * update the cache indirectly via the status field on the owning agent and the periodic job that checks it. */ } else if (firedDefinition.getWillRecover()) { if (log.isDebugEnabled()) { log.debug("Disabling " + firedDefinition + " until recovered manually or by recovery definition"); } /* * - disable alert def until recovered manually or by recovery definition. * - go through the manager layer so as to update the alert cache. * - we know this is a resource alert def so make that call directly. this is more efficient * and also avoids the issue in BZ 1126853. */ alertDefinitionManager.disableResourceAlertDefinitions(overlord, new int[] { firedDefinition.getId() }); /* * there's no reason to update the cache directly anymore. even though this direct type of update is safe * (because we know the AlertManager will only be executing on the same server instance that is processing * these recovery alerts now) it's unnecessary because changes made via the AlertDefinitionManager will * update the cache indirectly via the status field on the owning agent and the periodic job that checks it. */ } } /** * Tells us if the definition of the passed alert will be disabled after this alert was fired * @param alert alert to check * @return true if the definition got disabled */ @Override public boolean willDefinitionBeDisabled(Alert alert) { entityManager.refresh(alert); AlertDefinition firedDefinition = alert.getAlertDefinition(); Subject overlord = subjectManager.getOverlord(); Integer recoveryDefinitionId = firedDefinition.getRecoveryId(); if (recoveryDefinitionId != 0) { AlertDefinition toBeRecoveredDefinition = alertDefinitionManager.getAlertDefinitionById(overlord, recoveryDefinitionId); boolean wasEnabled = toBeRecoveredDefinition.getEnabled(); if (!wasEnabled) return false; } else if (firedDefinition.getWillRecover()) { return true; } return false; // Default is not to disable the definition } @Override public PageList<Alert> findAlertsByCriteria(Subject subject, AlertCriteria criteria) { CriteriaQueryGenerator generator = new CriteriaQueryGenerator(subject, criteria); if (!authorizationManager.isInventoryManager(subject)) { generator.setAuthorizationResourceFragment(CriteriaQueryGenerator.AuthorizationTokenType.RESOURCE, "alertDefinition.resource", subject.getId()); } Query query = generator.getQuery(entityManager); if (log.isDebugEnabled()) { QueryImpl queryImpl = (QueryImpl) query; PageControl pageControl = CriteriaQueryGenerator.getPageControl(criteria); log.debug("*Executing JPA query: " + queryImpl.getHibernateQuery().getQueryString() + ", selection=[" + pageControl.getStartRow() + ".." + (pageControl.getStartRow() + pageControl.getPageSize() - 1) + "]..."); } CriteriaQueryRunner<Alert> queryRunner = new CriteriaQueryRunner<Alert>(criteria, generator, entityManager); PageList<Alert> alerts = queryRunner.execute(); fetchCollectionFields(alerts); return alerts; } }