/* * 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; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import javax.ejb.EJB; import javax.ejb.Stateless; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; 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.AlertDampeningEvent; import org.rhq.core.domain.alert.AlertDefinition; import org.rhq.core.domain.alert.BooleanExpression; import org.rhq.core.domain.alert.notification.AlertNotification; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.authz.Permission; import org.rhq.core.domain.common.composite.IntegerOptionItem; import org.rhq.core.domain.criteria.AlertDefinitionCriteria; import org.rhq.core.domain.measurement.MeasurementDefinition; import org.rhq.core.domain.measurement.NumericType; import org.rhq.core.domain.resource.Resource; 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.domain.util.PageOrdering; import org.rhq.core.util.collection.ArrayUtils; import org.rhq.enterprise.server.RHQConstants; import org.rhq.enterprise.server.alert.engine.AlertDefinitionEvent; 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.cloud.StatusManagerLocal; import org.rhq.enterprise.server.plugin.pc.alert.AlertSender; import org.rhq.enterprise.server.plugin.pc.alert.AlertSenderPluginManager; import org.rhq.enterprise.server.util.CriteriaQueryGenerator; import org.rhq.enterprise.server.util.CriteriaQueryGenerator.AuthorizationTokenType; import org.rhq.enterprise.server.util.CriteriaQueryRunner; /** * @author Joseph Marques */ @Stateless public class AlertDefinitionManagerBean implements AlertDefinitionManagerLocal, AlertDefinitionManagerRemote { private static final Log LOG = LogFactory.getLog(AlertDefinitionManagerBean.class); @PersistenceContext(unitName = RHQConstants.PERSISTENCE_UNIT_NAME) private EntityManager entityManager; @EJB private AuthorizationManagerLocal authorizationManager; @EJB private AlertDefinitionManagerLocal alertDefinitionManager; @EJB private AlertTemplateManagerLocal alertTemplateManager; @EJB private GroupAlertDefinitionManagerLocal groupAlertDefintionManager; @EJB private AlertManagerLocal alertManager; @EJB private StatusManagerLocal agentStatusManager; @EJB private AlertNotificationManagerLocal alertNotificationManager; @EJB private SubjectManagerLocal subjectManager; private boolean checkViewPermission(Subject subject, AlertDefinition alertDefinition) { if (alertDefinition.getResourceType() != null) { // an alert template return true; // anyone can view templates } else if (alertDefinition.getGroup() != null) { // a groupAlertDefinition return authorizationManager.canViewGroup(subject, alertDefinition.getGroup().getId()); } else { // an alert definition return authorizationManager.canViewResource(subject, alertDefinition.getResource().getId()); } } private boolean checkPermission(Subject subject, AlertDefinition alertDefinition) { /* * system side-effects are primarily call-outs from the template manager to the definition manager, when the * template operation is being cascaded */ if (authorizationManager.isOverlord(subject)) { return true; } if (alertDefinition.getResourceType() != null) { // an alert template return authorizationManager.hasGlobalPermission(subject, Permission.MANAGE_SETTINGS); } else if (alertDefinition.getGroup() != null) { // a groupAlertDefinition return authorizationManager.hasGroupPermission(subject, Permission.MANAGE_ALERTS, alertDefinition .getGroup().getId()); } else { // an alert definition return authorizationManager.hasResourcePermission(subject, Permission.MANAGE_ALERTS, alertDefinition .getResource().getId()); } } @Override @SuppressWarnings("unchecked") public PageList<AlertDefinition> findAlertDefinitions(Subject subject, int resourceId, PageControl pageControl) { pageControl.initDefaultOrderingField("ctime", PageOrdering.DESC); Query queryCount = PersistenceUtility.createCountQuery(entityManager, AlertDefinition.QUERY_FIND_BY_RESOURCE); Query query = PersistenceUtility.createQueryWithOrderBy(entityManager, AlertDefinition.QUERY_FIND_BY_RESOURCE, pageControl); queryCount.setParameter("id", resourceId); query.setParameter("id", resourceId); long totalCount = (Long) queryCount.getSingleResult(); List<AlertDefinition> list = query.getResultList(); return new PageList<AlertDefinition>(list, (int) totalCount, pageControl); } @Override public AlertDefinition getAlertDefinitionById(Subject subject, int alertDefinitionId) { AlertDefinition alertDefinition = entityManager.find(AlertDefinition.class, alertDefinitionId); if (alertDefinition == null) { return null; // fail-fast to avoid downstream NPEs } if (checkViewPermission(subject, alertDefinition) == false) { throw new PermissionException("User[" + subject.getName() + "] does not have permission to view alertDefinition[id=" + alertDefinitionId + "] for resource[id=" + alertDefinition.getResource().getId() + "]"); } alertDefinition.getConditions().size(); // this is now lazy for (AlertCondition cond : alertDefinition.getConditions()) { if (cond.getMeasurementDefinition() != null) { cond.getMeasurementDefinition().getId(); } } // DO NOT LOAD ALL ALERTS FOR A DEFINITION... This would be all alerts that have been fired //alertDefinition.getAlertsForResource().size(); for (AlertNotification notification : alertDefinition.getAlertNotifications()) { notification.getConfiguration().getProperties().size(); // eager load configuration and properties too if (notification.getExtraConfiguration() != null) { notification.getExtraConfiguration().getProperties().size(); } } return alertDefinition; } @Override @SuppressWarnings("unchecked") public List<IntegerOptionItem> findAlertDefinitionOptionItemsForResource(Subject subject, int resourceId) { PageControl pageControl = PageControl.getUnlimitedInstance(); pageControl.initDefaultOrderingField("ad.name", PageOrdering.ASC); Query query = PersistenceUtility.createQueryWithOrderBy(entityManager, AlertDefinition.QUERY_FIND_OPTION_ITEMS_BY_RESOURCE, pageControl); query.setParameter("resourceId", resourceId); List<IntegerOptionItem> results = query.getResultList(); return results; } @Override @SuppressWarnings("unchecked") public List<IntegerOptionItem> findAlertDefinitionOptionItemsForGroup(Subject subject, int groupId) { PageControl pageControl = PageControl.getUnlimitedInstance(); pageControl.initDefaultOrderingField("ad.name", PageOrdering.ASC); Query query = PersistenceUtility.createQueryWithOrderBy(entityManager, AlertDefinition.QUERY_FIND_OPTION_ITEMS_BY_GROUP, pageControl); query.setParameter("groupId", groupId); List<IntegerOptionItem> results = query.getResultList(); return results; } @Override @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public int createDependentAlertDefinition(Subject subject, AlertDefinition alertDefinition, int resourceId) throws InvalidAlertDefinitionException { AlertDefinition newAlertDefinition = createAlertDefinitionInternal(subject, alertDefinition, resourceId, false, false); return newAlertDefinition.getId(); } @Override @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public AlertDefinition createAlertDefinitionInNewTransaction(Subject subject, AlertDefinition alertDefinition, Integer resourceId, boolean validateNotificationConfiguration) throws InvalidAlertDefinitionException { AlertDefinition newAlertDefinition = createAlertDefinitionInternal(subject, alertDefinition, resourceId, true, validateNotificationConfiguration); return newAlertDefinition; } private AlertDefinition createAlertDefinitionInternal(Subject subject, AlertDefinition alertDefinition, Integer resourceId, boolean checkPerms, boolean validateNotificationConfiguration) throws InvalidAlertDefinitionException { checkAlertDefinition(subject, null, alertDefinition, resourceId, validateNotificationConfiguration); // if this is an resource alert definition, set up the link to a resource if (resourceId != null) { // don't attach an alertTemplate or groupAlertDefinition to any particular resource // they should have already been attached to the resourceType or resourceGroup by the caller // use proxy trick to subvert having to load the entire resource into memory // Resource resource = LookupUtil.getResourceManager().getResourceById(user, resourceId); alertDefinition.setResource(new Resource(resourceId)); } // after the resource is set up (in the case of non-templates), we can use the checkPermission on it if (checkPerms && checkPermission(subject, alertDefinition) == false) { if (alertDefinition.getResourceType() != null) { throw new PermissionException("User [" + subject.getName() + "] does not have permission to create alert templates for type [" + alertDefinition.getResourceType() + "]"); } else if (alertDefinition.getGroup() != null) { throw new PermissionException("User [" + subject.getName() + "] does not have permission to create alert definitions for group [" + alertDefinition.getGroup() + "]"); } else { throw new PermissionException("User [" + subject.getName() + "] does not have permission to create alert definitions for resource [" + alertDefinition.getResource() + "]"); } } /* * performance optimization for the common case of single-condition alerts; it's easier for the * out-of-band process to check whether or not ANY conditions are true rather than ALL of them */ if (alertDefinition.getConditions().size() == 1) { alertDefinition.setConditionExpression(BooleanExpression.ANY); } fixRecoveryId(alertDefinition); entityManager.persist(alertDefinition); boolean addToCache = false; // don't notify on an alert template, only for those that get attached to a resource // Only add to the cache if the alert definition was created as active if ((resourceId != null) && alertDefinition.getEnabled()) { // if this is a recovery alert if (alertDefinition.getRecoveryId() != 0) { // only add to the cache if the to-be-recovered definition is disabled, and thus needs recovering // use entityManager direct to bypass security checks, we already know this user is authorized AlertDefinition toBeRecoveredDefinition = entityManager.find(AlertDefinition.class, alertDefinition.getRecoveryId()); if (toBeRecoveredDefinition.getEnabled() == false) { addToCache = true; } } else { addToCache = true; } } if (addToCache) { notifyAlertConditionCacheManager(subject, "createAlertDefinition", alertDefinition, AlertDefinitionEvent.CREATED); } return alertDefinition; } private void fixRecoveryId(AlertDefinition definition) { try { if (definition.getParentId() != 0 && definition.getRecoveryId() != 0) { // so we need to set the resource-level recovery id properly String findCorrectRecoveryId = "" // + " SELECT toBeRecovered.id " // + " FROM AlertDefinition toBeRecovered " // + " WHERE toBeRecovered.resource.id = :resourceId " // + " AND toBeRecovered.parentId = :parentId "; Query fixRecoveryIdQuery = entityManager.createQuery(findCorrectRecoveryId); fixRecoveryIdQuery.setParameter("resourceId", definition.getResource().getId()); // definition.recoveryId current points at the toBeRecovered template, we want the definition fixRecoveryIdQuery.setParameter("parentId", definition.getRecoveryId()); // wrong one to be replaced Integer correctRecoveryId = (Integer) fixRecoveryIdQuery.getSingleResult(); definition.setRecoveryId(correctRecoveryId); } else if (definition.getGroupAlertDefinition() != null && definition.getRecoveryId() != 0) { // so we need to set the resource-level recovery id properly String findCorrectRecoveryId = "" // + " SELECT toBeRecovered.id " // + " FROM AlertDefinition toBeRecovered " // + " WHERE toBeRecovered.resource.id = :resourceId " // + " AND toBeRecovered.groupAlertDefinition.id = :groupAlertDefinitionId "; Query fixRecoveryIdQuery = entityManager.createQuery(findCorrectRecoveryId); fixRecoveryIdQuery.setParameter("resourceId", definition.getResource().getId()); // definition.recoveryId current points at the toBeRecovered template, we want the definition fixRecoveryIdQuery.setParameter("groupAlertDefinitionId", definition.getRecoveryId()); // wrong one to be replaced Integer correctRecoveryId = (Integer) fixRecoveryIdQuery.getSingleResult(); definition.setRecoveryId(correctRecoveryId); } } catch (NoResultException nre) { // expected when the recovery ids have already been fixed } } @Override public int removeAlertDefinitions(Subject subject, int[] alertDefinitionIds) { if (null == alertDefinitionIds || alertDefinitionIds.length == 0) { return 0; } AlertDefinitionCriteria criteria = new AlertDefinitionCriteria(); criteria.addFilterIds(ArrayUtils.wrapInArray(alertDefinitionIds)); criteria.addFilterDeleted(false); criteria.clearPaging(); List<AlertDefinition> defs = alertDefinitionManager.findAlertDefinitionsByCriteria(subject, criteria); if (defs.isEmpty()) { return 0; } int modifiedCount = 0; List<Integer> resourceDefIds = new ArrayList(defs.size()); Boolean hasManageSettings = authorizationManager.hasGlobalPermission(subject, Permission.MANAGE_SETTINGS); for (AlertDefinition ad : defs) { if (null != ad.getResourceType()) { if (hasManageSettings) { // these can be big requests, do 1 per transaction alertTemplateManager.removeAlertTemplates(subject, new Integer[] { ad.getId() }); ++modifiedCount; } } else if (null != ad.getGroup()) { // these can be big requests, do 1 per transaction groupAlertDefintionManager.removeGroupAlertDefinitions(subject, new Integer[] { ad.getId() }); ++modifiedCount; } else { resourceDefIds.add(ad.getId()); } } if (!resourceDefIds.isEmpty()) { alertDefinitionManager.removeResourceAlertDefinitions(subject, ArrayUtils.unwrapCollection(resourceDefIds)); modifiedCount += resourceDefIds.size(); } return modifiedCount; } @Override @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public int enableAlertDefinitions(Subject subject, int[] alertDefinitionIds) { if (null == alertDefinitionIds || alertDefinitionIds.length == 0) { return 0; } AlertDefinitionCriteria criteria = new AlertDefinitionCriteria(); criteria.addFilterIds(ArrayUtils.wrapInArray(alertDefinitionIds)); criteria.addFilterEnabled(false); criteria.addFilterDeleted(false); criteria.clearPaging(); List<AlertDefinition> defs = alertDefinitionManager.findAlertDefinitionsByCriteria(subject, criteria); if (defs.isEmpty()) { return 0; } int modifiedCount = 0; List<Integer> resourceDefIds = new ArrayList(defs.size()); Boolean hasManageSettings = authorizationManager.hasGlobalPermission(subject, Permission.MANAGE_SETTINGS); for (AlertDefinition ad : defs) { if (null != ad.getResourceType()) { if (hasManageSettings) { // these can be big requests, do 1 per transaction alertTemplateManager.enableAlertTemplates(subject, new Integer[] { ad.getId() }); ++modifiedCount; } } else if (null != ad.getGroup()) { // these can be big requests, do 1 per transaction groupAlertDefintionManager.enableGroupAlertDefinitions(subject, new Integer[] { ad.getId() }); ++modifiedCount; } else { resourceDefIds.add(ad.getId()); } } if (!resourceDefIds.isEmpty()) { alertDefinitionManager.enableResourceAlertDefinitions(subject, ArrayUtils.unwrapCollection(resourceDefIds)); modifiedCount += resourceDefIds.size(); } return modifiedCount; } @Override @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public int disableAlertDefinitions(Subject subject, int[] alertDefinitionIds) { if (null == alertDefinitionIds || alertDefinitionIds.length == 0) { return 0; } AlertDefinitionCriteria criteria = new AlertDefinitionCriteria(); criteria.addFilterIds(ArrayUtils.wrapInArray(alertDefinitionIds)); criteria.addFilterEnabled(true); criteria.addFilterDeleted(false); criteria.clearPaging(); List<AlertDefinition> defs = alertDefinitionManager.findAlertDefinitionsByCriteria(subject, criteria); if (defs.isEmpty()) { return 0; } int modifiedCount = 0; List<Integer> resourceDefIds = new ArrayList(defs.size()); Boolean hasManageSettings = authorizationManager.hasGlobalPermission(subject, Permission.MANAGE_SETTINGS); for (AlertDefinition ad : defs) { if (null != ad.getResourceType()) { if (hasManageSettings) { // these can be big requests, do 1 per transaction alertTemplateManager.disableAlertTemplates(subject, new Integer[] { ad.getId() }); ++modifiedCount; } } else if (null != ad.getGroup()) { // these can be big requests, do 1 per transaction groupAlertDefintionManager.disableGroupAlertDefinitions(subject, new Integer[] { ad.getId() }); ++modifiedCount; } else { resourceDefIds.add(ad.getId()); } } if (!resourceDefIds.isEmpty()) { alertDefinitionManager .disableResourceAlertDefinitions(subject, ArrayUtils.unwrapCollection(resourceDefIds)); modifiedCount += resourceDefIds.size(); } return modifiedCount; } @Override public int enableResourceAlertDefinitions(Subject subject, int[] resourceAlertDefinitionIds) { int modifiedCount = 0; for (int alertDefId : resourceAlertDefinitionIds) { AlertDefinition alertDefinition = entityManager.find(AlertDefinition.class, alertDefId); if (null == alertDefinition) { continue; } // only enable an alert if it's not currently enabled if (!alertDefinition.getEnabled()) { alertDefinition.setEnabled(true); modifiedCount++; notifyAlertConditionCacheManager(subject, "enableResourceAlertDefinitions", alertDefinition, AlertDefinitionEvent.ENABLED); } } return modifiedCount; } @Override public int disableResourceAlertDefinitions(Subject subject, int[] resourceAlertDefinitionIds) { int modifiedCount = 0; for (int alertDefId : resourceAlertDefinitionIds) { AlertDefinition alertDefinition = entityManager.find(AlertDefinition.class, alertDefId); if (null == alertDefinition) { continue; } // only disable an alert if it's not currently disabled if (alertDefinition.getEnabled()) { alertDefinition.setEnabled(false); modifiedCount++; notifyAlertConditionCacheManager(subject, "disableResourceAlertDefinitions", alertDefinition, AlertDefinitionEvent.DISABLED); } } return modifiedCount; } @Override public int removeResourceAlertDefinitions(Subject subject, int[] resourceAlertDefinitionIds) { int modifiedCount = 0; for (int alertDefId : resourceAlertDefinitionIds) { AlertDefinition alertDefinition = entityManager.find(AlertDefinition.class, alertDefId); if (null == alertDefinition) { continue; } // only remove an alert if it's not currently deleted if (!alertDefinition.getDeleted()) { alertDefinition.setDeleted(true); modifiedCount++; notifyAlertConditionCacheManager(subject, "removeResourceAlertDefinitions", alertDefinition, AlertDefinitionEvent.DELETED); } } return modifiedCount; } @Override @SuppressWarnings("unchecked") public boolean isEnabled(Integer definitionId) { Query enabledQuery = entityManager.createNamedQuery(AlertDefinition.QUERY_IS_ENABLED); enabledQuery.setParameter("alertDefinitionId", definitionId); List<Integer> resultIds = enabledQuery.getResultList(); return (resultIds.size() == 1); } @Override @SuppressWarnings("unchecked") public boolean isTemplate(Integer definitionId) { Query query = entityManager.createNamedQuery(AlertDefinition.QUERY_IS_TEMPLATE); query.setParameter("alertDefinitionId", definitionId); List<Integer> resultIds = query.getResultList(); return (resultIds.size() == 1); } @Override @SuppressWarnings("unchecked") public boolean isGroupAlertDefinition(Integer definitionId) { Query query = entityManager.createNamedQuery(AlertDefinition.QUERY_IS_GROUP_ALERT_DEFINITION); query.setParameter("alertDefinitionId", definitionId); List<Integer> resultIds = query.getResultList(); return (resultIds.size() == 1); } @Override @SuppressWarnings("unchecked") public boolean isResourceAlertDefinition(Integer definitionId) { Query query = entityManager.createNamedQuery(AlertDefinition.QUERY_IS_RESOURCE_ALERT_DEFINITION); query.setParameter("alertDefinitionId", definitionId); List<Integer> resultIds = query.getResultList(); return (resultIds.size() == 1); } @Override public void copyAlertDefinitions(Subject subject, Integer[] alertDefinitionIds) { for (int alertDefId : alertDefinitionIds) { AlertDefinition alertDefinition = entityManager.find(AlertDefinition.class, alertDefId); // TODO GH: Can be more efficient if (checkPermission(subject, alertDefinition)) { AlertDefinition newAlertDefinition = new AlertDefinition(alertDefinition); newAlertDefinition.setEnabled(false); // this is a "true" copy, so update parentId, resource, and resourceType, group, groupAlertDefinition newAlertDefinition.setParentId(alertDefinition.getParentId()); newAlertDefinition.setResource(alertDefinition.getResource()); newAlertDefinition.setResourceType(alertDefinition.getResourceType()); newAlertDefinition.setGroup(alertDefinition.getGroup()); newAlertDefinition.setGroupAlertDefinition(alertDefinition.getGroupAlertDefinition()); entityManager.persist(newAlertDefinition); notifyAlertConditionCacheManager(subject, "copyAlertDefinitions", alertDefinition, AlertDefinitionEvent.CREATED); } } } @Override @SuppressWarnings("unchecked") public List<AlertDefinition> findAllRecoveryDefinitionsById(Subject subject, Integer alertDefinitionId) { if (authorizationManager.isOverlord(subject) == false) { throw new PermissionException("User [" + subject.getName() + "] does not have permission to call " + "getAllRecoveryDefinitionsById; only the overlord has that right"); } Query query = entityManager.createNamedQuery(AlertDefinition.QUERY_FIND_ALL_BY_RECOVERY_DEFINITION_ID); query.setParameter("recoveryDefinitionId", alertDefinitionId); List<AlertDefinition> list = query.getResultList(); return list; } @Override @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public AlertDefinition updateAlertDefinition(Subject subject, int alertDefinitionId, AlertDefinition alertDefinition, boolean resetMatching) throws InvalidAlertDefinitionException, AlertDefinitionUpdateException { return updateAlertDefinitionInternal(subject, alertDefinitionId, alertDefinition, resetMatching, true, true); } @Override @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public AlertDefinition updateDependentAlertDefinition(Subject subject, int alertDefinitionId, AlertDefinition alertDefinition, boolean resetMatching) throws InvalidAlertDefinitionException, AlertDefinitionUpdateException { return updateAlertDefinitionInternal(subject, alertDefinitionId, alertDefinition, resetMatching, false, false); } @Override public AlertDefinition updateAlertDefinitionInternal(Subject subject, int alertDefinitionId, AlertDefinition alertDefinition, boolean resetMatching, boolean checkPerms, boolean finalizeNotifications) throws InvalidAlertDefinitionException, AlertDefinitionUpdateException { if (resetMatching) { alertDefinitionManager.purgeInternals(alertDefinitionId); } /* * Method for catching ENABLE / DISABLE changes will use switch logic off of the delta instead of calling out to * the enable/disable functions */ AlertDefinition oldAlertDefinition = entityManager.find(AlertDefinition.class, alertDefinitionId); if (checkPerms && checkPermission(subject, oldAlertDefinition) == false) { if (oldAlertDefinition.getResourceType() != null) { throw new PermissionException("User [" + subject.getName() + "] does not have permission to modify alert templates for type [" + oldAlertDefinition.getResourceType() + "]"); } else if (oldAlertDefinition.getGroup() != null) { throw new PermissionException("User [" + subject.getName() + "] does not have permission to modify alert definitions for group [" + oldAlertDefinition.getGroup() + "]"); } else { throw new PermissionException("User [" + subject.getName() + "] does not have permission to modify alert definitions for resource [" + oldAlertDefinition.getResource() + "]"); } } /* * only need to check the validity of the new alert definition if the authz checks pass *and* the old definition * is not currently deleted */ boolean isResourceLevel = (oldAlertDefinition.getResource() != null); checkAlertDefinition(subject, oldAlertDefinition, alertDefinition, isResourceLevel ? oldAlertDefinition .getResource().getId() : null, finalizeNotifications); /* * Should not be able to update an alert definition if the old alert definition is in an invalid state */ if (oldAlertDefinition.getDeleted()) { throw new AlertDefinitionUpdateException("Can not update deleted " + oldAlertDefinition.toSimpleString()); } AlertDefinitionUpdateType updateType = AlertDefinitionUpdateType.get(oldAlertDefinition, alertDefinition); if (isResourceLevel && ((updateType == AlertDefinitionUpdateType.JUST_DISABLED) || (updateType == AlertDefinitionUpdateType.STILL_ENABLED))) { /* * if you were JUST_DISABLED or STILL_ENABLED, you are coming from the ENABLED state, which means you need * to be removed from the cache as the first half of this update */ if (LOG.isDebugEnabled()) { LOG.debug("Updating AlertConditionCacheManager with AlertDefinition[ id=" + oldAlertDefinition.getId() + " ]...DELETING"); for (AlertCondition nextCondition : oldAlertDefinition.getConditions()) { LOG.debug("OldAlertCondition[ id=" + nextCondition.getId() + " ]"); } } notifyAlertConditionCacheManager(subject, "updateAlertDefinition", oldAlertDefinition, AlertDefinitionEvent.DELETED); } /* * performance optimization for the common case of single-condition alerts; it's easier for the * out-of-band process to check whether or not ANY conditions are true rather than ALL of them */ if (alertDefinition.getConditions().size() == 1) { alertDefinition.setConditionExpression(BooleanExpression.ANY); } oldAlertDefinition.update(alertDefinition, resetMatching); if (LOG.isDebugEnabled()) { LOG.debug("Updating: " + oldAlertDefinition); for (AlertCondition nextCondition : oldAlertDefinition.getConditions()) { LOG.debug("Condition: " + nextCondition); } for (AlertNotification nextNotification : oldAlertDefinition.getAlertNotifications()) { LOG.debug("Notification: " + nextNotification); LOG.debug("Notification-Configuration: " + nextNotification.getConfiguration().toString(true)); if (nextNotification.getExtraConfiguration() != null) { LOG.debug("Notification-Extra-Configuration: " + nextNotification.getExtraConfiguration().toString(true)); } } } fixRecoveryId(oldAlertDefinition); oldAlertDefinition.setMtime(System.currentTimeMillis()); AlertDefinition newAlertDefinition = entityManager.merge(oldAlertDefinition); if (isResourceLevel && ((updateType == AlertDefinitionUpdateType.JUST_ENABLED) || (updateType == AlertDefinitionUpdateType.STILL_ENABLED))) { /* * if you were JUST_ENABLED or STILL_ENABLED, you are moving to the ENABLED state, which means you need to * be added to the cache as the last half of this update */ boolean addToCache = false; // if this was a recovery alert, or was recently turned into one if (newAlertDefinition.getRecoveryId() != 0) { // only add to the cache if the to-be-recovered definition is disabled, and thus needs recovering AlertDefinition toBeRecoveredDefinition = getAlertDefinitionById(subject, newAlertDefinition.getRecoveryId()); if (toBeRecoveredDefinition.getEnabled() == false) { addToCache = true; } } else { addToCache = true; } if (addToCache) { if (LOG.isDebugEnabled()) { LOG.debug("Updating AlertConditionCacheManager with AlertDefinition[ id=" + newAlertDefinition.getId() + " ]...CREATING"); for (AlertCondition nextCondition : newAlertDefinition.getConditions()) { LOG.debug("NewAlertCondition[ id=" + nextCondition.getId() + " ]"); } } notifyAlertConditionCacheManager(subject, "updateAlertDefinition", newAlertDefinition, AlertDefinitionEvent.CREATED); } } /* * note, nothing is done to the cache in the STILL_DISABLED case because nothing should've been in the cache to * begin with, and nothing needs to be added to the cache as a result */ //we've been touching both conditions and notifications of the updated alert definition, so we should //return an object with the same... let's force lazy load before we leave the persistence context new ArrayList<AlertCondition>(newAlertDefinition.getConditions()); new ArrayList<AlertNotification>(newAlertDefinition.getAlertNotifications()); return newAlertDefinition; } /* * A helper enum to make for cleaner logic in updateAlertDefinition( Subject, AlertDefinition, boolean ) */ enum AlertDefinitionUpdateType { JUST_ENABLED, JUST_DISABLED, STILL_ENABLED, STILL_DISABLED; public static AlertDefinitionUpdateType get(AlertDefinition oldDefinition, AlertDefinition newDefinition) { if ((oldDefinition.getEnabled() == false) && (newDefinition.getEnabled() == true)) { return AlertDefinitionUpdateType.JUST_ENABLED; } else if ((oldDefinition.getEnabled() == true) && (newDefinition.getEnabled() == false)) { return AlertDefinitionUpdateType.JUST_DISABLED; } else if ((oldDefinition.getEnabled() == true) && (newDefinition.getEnabled() == true)) { return AlertDefinitionUpdateType.STILL_ENABLED; } else { return AlertDefinitionUpdateType.STILL_DISABLED; } } } private void checkAlertDefinition(Subject subject, AlertDefinition persistedAlertDefinition, AlertDefinition alertDefinition, Integer resourceId, boolean finalizeNotifications) throws InvalidAlertDefinitionException { // if someone enters a really long description, we need to truncate it - the column is only 250 chars if (alertDefinition.getDescription() != null && alertDefinition.getDescription().length() > 250) { alertDefinition.setDescription(alertDefinition.getDescription().substring(0, 250)); } for (AlertCondition alertCondition : alertDefinition.getConditions()) { AlertConditionCategory alertConditionCategory = alertCondition.getCategory(); if (alertConditionCategory == AlertConditionCategory.ALERT) { throw new InvalidAlertDefinitionException( "AlertDefinitionManager does not yet support condition category: " + alertConditionCategory); } if (alertConditionCategory == AlertConditionCategory.BASELINE) { MeasurementDefinition def = alertCondition.getMeasurementDefinition(); def = entityManager.find(MeasurementDefinition.class, def.getId()); NumericType numType = def.getNumericType(); if (numType != NumericType.DYNAMIC) { throw new InvalidAlertDefinitionException("Invalid Condition: '" + def.getDisplayName() + "' is a trending metric, and thus will never have baselines calculated for it."); } } } if (finalizeNotifications) { List<AlertNotification> notifications = new ArrayList<AlertNotification>( alertDefinition.getAlertNotifications()); //now remove the notifications that have not changed if (persistedAlertDefinition != null) { List<AlertNotification> persistedNotifications = persistedAlertDefinition.getAlertNotifications() == null ? Collections .<AlertNotification> emptyList() : persistedAlertDefinition.getAlertNotifications(); if (persistedNotifications.size() > 0) { Iterator<AlertNotification> it = notifications.iterator(); while (it.hasNext()) { AlertNotification newNotification = it.next(); if (newNotification.getId() == 0) { //this is a fresh, not persisted notif. These guys have to be always finalized. continue; } for (AlertNotification persistedNotification : persistedNotifications) { //ignore the ids on the notifications as they may vary if we are comparing parent alert def with its children //it's enough for us they they are semantically the same. if (newNotification.getSenderName().equals(persistedNotification.getSenderName()) && newNotification.equalsData(persistedNotification)) { it.remove(); break; } } } } } if (!alertNotificationManager.finalizeNotifications(subject, notifications)) { throw new InvalidAlertDefinitionException("Some of the notifications failed to validate."); } } } private void notifyAlertConditionCacheManager(Subject subject, String methodName, AlertDefinition alertDefinition, AlertDefinitionEvent alertDefinitionEvent) { if (LOG.isDebugEnabled()) { LOG.debug("Invoking... " + methodName + " with AlertDefinitionEvent[" + alertDefinitionEvent + "]"); } if (alertDefinitionEvent == AlertDefinitionEvent.CREATED) { if (alertDefinition.getResource() != null) { int resourceId = alertDefinition.getResource().getId(); if (LOG.isDebugEnabled()) { LOG.debug("Invoking... agentStatusManager.updateByResource(" + resourceId + ")"); } agentStatusManager.updateByResource(subject, resourceId); } else { if (LOG.isDebugEnabled()) { LOG.debug("notifyAlertConditionCacheManager skipping alert template or group alert definition"); } } } else { if (LOG.isDebugEnabled()) { LOG.debug("Invoking... agentStatusManager.updateByAlertDefinition(" + alertDefinition.getId() + ")"); } agentStatusManager.updateByAlertDefinition(subject, alertDefinition.getId()); } } @Override @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void purgeInternals(int alertDefinitionId) { try { Query alertDampeningEventPurgeQuery = entityManager .createNamedQuery(AlertDampeningEvent.QUERY_DELETE_BY_ALERT_DEFINITION_ID); Query unmatchedAlertConditionLogPurgeQuery = entityManager .createNamedQuery(AlertConditionLog.QUERY_DELETE_UNMATCHED_BY_ALERT_DEFINITION_ID); alertDampeningEventPurgeQuery.setParameter("alertDefinitionId", alertDefinitionId); unmatchedAlertConditionLogPurgeQuery.setParameter("alertDefinitionId", alertDefinitionId); int alertDampeningEventPurgeCount = alertDampeningEventPurgeQuery.executeUpdate(); int unmatchedAlertConditionLogPurgeCount = unmatchedAlertConditionLogPurgeQuery.executeUpdate(); if (LOG.isDebugEnabled()) { LOG.debug("Update to AlertDefinition[id=" + alertDefinitionId + " caused a purge of internal, dampening constructs."); if (alertDampeningEventPurgeCount > 0) { LOG.debug("Removed " + alertDampeningEventPurgeCount + " AlertDampeningEvent" + (alertDampeningEventPurgeCount == 1 ? "" : "s")); } if (unmatchedAlertConditionLogPurgeCount > 0) { LOG.debug("Removed " + unmatchedAlertConditionLogPurgeCount + " unmatched AlertConditionLog" + (unmatchedAlertConditionLogPurgeCount == 1 ? "" : "s")); } } } catch (Throwable t) { LOG.debug("Could not purge internal alerting constructs for: " + alertDefinitionId, t); } } @Override @SuppressWarnings("unchecked") public int purgeUnusedAlertDefinitions() { Query purgeQuery = entityManager.createNamedQuery(AlertDefinition.QUERY_FIND_UNUSED_DEFINITION_IDS); List<Integer> resultIds = purgeQuery.getResultList(); int removed = 0; for (int unusedDefinitionId : resultIds) { AlertDefinition unusedDefinition = entityManager.find(AlertDefinition.class, unusedDefinitionId); if (unusedDefinition != null) { entityManager.remove(unusedDefinition); removed++; } else { LOG.warn("Could not find alertDefinition[id=" + unusedDefinitionId + "] for purge"); } } return removed; } @Override public AlertDefinition getAlertDefinition(Subject subject, int alertDefinitionId) { return getAlertDefinitionById(subject, alertDefinitionId); } @Override @SuppressWarnings("unchecked") public PageList<AlertDefinition> findAlertDefinitionsByCriteria(Subject subject, AlertDefinitionCriteria criteria) { CriteriaQueryGenerator generator = new CriteriaQueryGenerator(subject, criteria); // Inv managers can do anything and anyone can inspect templates if (!authorizationManager.isInventoryManager(subject) && !criteria.isTemplateCriteria()) { // otherwise, for group alert defs ensure group view authz and for everything else, assume resource view authz AuthorizationTokenType tokenType = criteria.isGroupCriteria() ? AuthorizationTokenType.GROUP : AuthorizationTokenType.RESOURCE; generator.setAuthorizationResourceFragment(tokenType, subject.getId()); } CriteriaQueryRunner<AlertDefinition> queryRunner = new CriteriaQueryRunner<AlertDefinition>(criteria, generator, entityManager); return queryRunner.execute(); } @Override public String[] getAlertNotificationConfigurationPreview(Subject sessionSubject, AlertNotification[] notifications) { if (notifications == null || notifications.length == 0) { return new String[0]; } AlertSenderPluginManager alertPluginManager = alertManager.getAlertPluginManager(); String[] previews = new String[notifications.length]; int i = 0; for (AlertNotification notif : notifications) { AlertSender<?> sender = alertPluginManager.getAlertSenderForNotification(notif); if (sender != null) { String preview = null; try { preview = sender.previewConfiguration(); } catch (Exception e) { preview = "Error! There is a problem with this notification: " + e.getMessage(); } previews[i++] = preview; } else { previews[i++] = "n/a (unknown sender)"; } } return previews; } }