/* * 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.List; 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.rhq.core.domain.alert.AlertDefinition; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.authz.Permission; import org.rhq.core.domain.resource.InventoryStatus; import org.rhq.core.domain.resource.ResourceType; 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.auth.SubjectManagerLocal; import org.rhq.enterprise.server.authz.AuthorizationManagerLocal; import org.rhq.enterprise.server.authz.RequiredPermission; import org.rhq.enterprise.server.resource.ResourceTypeManagerLocal; import org.rhq.enterprise.server.resource.ResourceTypeNotFoundException; import org.rhq.enterprise.server.safeinvoker.HibernateDetachUtility; import org.rhq.enterprise.server.safeinvoker.HibernateDetachUtility.SerializationType; /** * @author Joseph Marques */ @Stateless public class AlertTemplateManagerBean implements AlertTemplateManagerLocal { private static final Log LOG = LogFactory.getLog(AlertTemplateManagerBean.class); @PersistenceContext(unitName = RHQConstants.PERSISTENCE_UNIT_NAME) private EntityManager entityManager; @EJB private AuthorizationManagerLocal authorizationManager; @EJB private AlertDefinitionManagerLocal alertDefinitionManager; @EJB private ResourceTypeManagerLocal resourceTypeManager; @EJB private SubjectManagerLocal subjectManager; @SuppressWarnings("unchecked") public PageList<AlertDefinition> getAlertTemplates(Subject user, int resourceTypeId, PageControl pageControl) { pageControl.initDefaultOrderingField("ctime", PageOrdering.DESC); Query queryCount = PersistenceUtility.createCountQuery(entityManager, AlertDefinition.QUERY_FIND_BY_RESOURCE_TYPE); Query query = PersistenceUtility.createQueryWithOrderBy(entityManager, AlertDefinition.QUERY_FIND_BY_RESOURCE_TYPE, pageControl); queryCount.setParameter("typeId", resourceTypeId); query.setParameter("typeId", resourceTypeId); long totalCount = (Long) queryCount.getSingleResult(); List<AlertDefinition> list = query.getResultList(); return new PageList<AlertDefinition>(list, (int) totalCount, pageControl); } @SuppressWarnings("unchecked") private List<Integer> getChildrenAlertDefinitionIds(Subject user, int alertTemplateId) { Query query = entityManager.createNamedQuery(AlertDefinition.QUERY_FIND_BY_ALERT_TEMPLATE_ID); query.setParameter("alertTemplateId", alertTemplateId); List<Integer> list = query.getResultList(); return list; } @SuppressWarnings("unchecked") private List<Integer> getCommittedResourceIdsNeedingTemplateApplication(Subject user, int alertTemplateId, int resourceTypeId) { Query query = entityManager .createNamedQuery(AlertDefinition.QUERY_FIND_RESOURCE_IDS_NEEDING_TEMPLATE_APPLICATION); query.setParameter("alertTemplateId", alertTemplateId); query.setParameter("resourceTypeId", resourceTypeId); query.setParameter("inventoryStatus", InventoryStatus.COMMITTED); List<Integer> list = query.getResultList(); return list; } @RequiredPermission(Permission.MANAGE_SETTINGS) @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public int createAlertTemplate(Subject user, AlertDefinition alertTemplate, Integer resourceTypeId) throws InvalidAlertDefinitionException, ResourceTypeNotFoundException, AlertDefinitionCreationException { if (LOG.isDebugEnabled()) { LOG.debug("createAlertTemplate: " + alertTemplate); } ResourceType type = resourceTypeManager.getResourceTypeById(user, resourceTypeId); alertTemplate.setResourceType(type); // mark this as an alert "template" definition AlertDefinition persistedAlertTemplate = null; try { persistedAlertTemplate = alertDefinitionManager.createAlertDefinitionInNewTransaction(user, alertTemplate, null, true); } catch (Throwable t) { throw new AlertDefinitionCreationException("Could not create alertTemplate for " + type + " with data " + alertTemplate.toSimpleString(), t); } Throwable firstThrowable = null; List<Integer> resourceIdsForType = getCommittedResourceIdsNeedingTemplateApplication(user, persistedAlertTemplate.getId(), resourceTypeId); List<Integer> resourceIdsInError = new ArrayList<Integer>(); for (Integer resourceId : resourceIdsForType) { try { // construct the child AlertDefinition childAlertDefinition = new AlertDefinition(persistedAlertTemplate); childAlertDefinition.setParentId(alertTemplate.getId()); // persist the child as a dependent alert definition alertDefinitionManager.createDependentAlertDefinition(user, childAlertDefinition, resourceId); } catch (Throwable t) { // continue on error, create as many as possible if (firstThrowable == null) { firstThrowable = t; } resourceIdsInError.add(resourceId); } } if (firstThrowable != null) { throw new AlertDefinitionCreationException("Could not create child alert definition for Resources " + resourceIdsInError + " with template" + alertTemplate.toSimpleString(), firstThrowable); } return persistedAlertTemplate.getId(); } @SuppressWarnings("unchecked") public void updateAlertDefinitionsForResource(Subject user, Integer resourceId) throws AlertDefinitionCreationException, InvalidAlertDefinitionException { if (authorizationManager.isOverlord(user) == false) { throw new AlertDefinitionCreationException( "Updating the alert definitions for a resource is an implicit system operation; " + "It can only be performed by the overlord"); } // get list of AlertTemplates that should be, but haven't already been, applied to this resource Query query = entityManager.createQuery("" // + " SELECT template " // + " FROM AlertDefinition template, Resource res " // + " WHERE template.resourceType.id = res.resourceType.id " // + " AND res.id = :resourceId " // + " AND template.deleted = false " // + " AND template.id NOT IN ( SELECT ad.id " // + " FROM AlertDefinition ad " // + " WHERE ad.resource.id = :resourceId " // + " AND ad.deleted = false ) " // + " ORDER BY template.recoveryId ASC "); // process recovered defs first, so recovery defs can reference them query.setParameter("resourceId", resourceId); List<AlertDefinition> unappliedTemplates = query.getResultList(); for (AlertDefinition template : unappliedTemplates) { // construct the child, note that the proxy will pull in lazy data during the copy AlertDefinition childAlertDefinition = new AlertDefinition(template); childAlertDefinition.setParentId(template.getId()); // persist the child, user is known to be overlord at this point for this system side-effect try { // convert the childAlertDef to a pojo, remove all proxies left over from the copy. We can't // pass entities across a Tx boundary and the call to create the alert def is executed in a new trans. HibernateDetachUtility .nullOutUninitializedFields(childAlertDefinition, SerializationType.SERIALIZATION); alertDefinitionManager.createAlertDefinitionInNewTransaction(user, childAlertDefinition, resourceId, false); } catch (Throwable t) { throw new AlertDefinitionCreationException("Failed to create child AlertDefinition for Resource[id=" + resourceId + "] with template " + template.toSimpleString(), t); } } } @RequiredPermission(Permission.MANAGE_SETTINGS) public void removeAlertTemplates(Subject user, Integer[] alertTemplateIds) { if (null == alertTemplateIds || alertTemplateIds.length == 0) { return; } for (Integer alertTemplateId : alertTemplateIds) { AlertDefinition template = entityManager.find(AlertDefinition.class, alertTemplateId); if (null == template || template.getDeleted()) { continue; } // remove the template template.setDeleted(true); // remove the child resource-level defs Subject overlord = subjectManager.getOverlord(); List<Integer> childDefinitionIds = getChildrenAlertDefinitionIds(user, alertTemplateId); if (childDefinitionIds.isEmpty()) { continue; } alertDefinitionManager.removeResourceAlertDefinitions(overlord, ArrayUtils.unwrapCollection(childDefinitionIds)); // finally, detach protected alert defs Query query = entityManager .createNamedQuery(AlertDefinition.QUERY_UPDATE_DETACH_PROTECTED_BY_ALERT_TEMPLATE_ID); query.setParameter("alertTemplateId", alertTemplateId); int numDetached = query.executeUpdate(); } } @RequiredPermission(Permission.MANAGE_SETTINGS) public void enableAlertTemplates(Subject subject, Integer[] alertTemplateIds) { if (null == alertTemplateIds || alertTemplateIds.length == 0) { return; } for (Integer alertTemplateId : alertTemplateIds) { AlertDefinition template = entityManager.find(AlertDefinition.class, alertTemplateId); if (null == template || template.getEnabled() || template.getDeleted()) { continue; } // enable the template template.setEnabled(true); // enable the child resource-level defs Subject overlord = subjectManager.getOverlord(); List<Integer> childDefinitionIds = getChildrenAlertDefinitionIds(overlord, alertTemplateId); if (childDefinitionIds.isEmpty()) { continue; } alertDefinitionManager.enableResourceAlertDefinitions(overlord, ArrayUtils.unwrapCollection(childDefinitionIds)); } } @RequiredPermission(Permission.MANAGE_SETTINGS) public void disableAlertTemplates(Subject subject, Integer[] alertTemplateIds) { if (null == alertTemplateIds || alertTemplateIds.length == 0) { return; } for (Integer alertTemplateId : alertTemplateIds) { AlertDefinition template = entityManager.find(AlertDefinition.class, alertTemplateId); if (null == template || !template.getEnabled() || template.getDeleted()) { continue; } // disable the template template.setEnabled(false); // enable the child resource-level defs Subject overlord = subjectManager.getOverlord(); List<Integer> childDefinitionIds = getChildrenAlertDefinitionIds(overlord, alertTemplateId); if (childDefinitionIds.isEmpty()) { continue; } alertDefinitionManager.disableAlertDefinitions(overlord, ArrayUtils.unwrapCollection(childDefinitionIds)); } } @RequiredPermission(Permission.MANAGE_SETTINGS) @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public AlertDefinition updateAlertTemplate(Subject user, AlertDefinition alertTemplate, boolean resetMatching) throws InvalidAlertDefinitionException, AlertDefinitionUpdateException, AlertNotificationValidationException { if (LOG.isDebugEnabled()) { LOG.debug("updateAlertTemplate: " + alertTemplate); } // first update the actual alert template AlertDefinition updated = null; try { updated = alertDefinitionManager.updateAlertDefinition(user, alertTemplate.getId(), alertTemplate, resetMatching); // do not allow direct undeletes of an alert definition } catch (Throwable t) { throw new AlertDefinitionUpdateException("Failed to update an AlertTemplate " + alertTemplate.toSimpleString(), t); } // overlord will be used for all system-side effects as a result of updating this alert template Subject overlord = subjectManager.getOverlord(); Throwable firstThrowable = null; // update all of the definitions that were spawned from alert templates List<Integer> alertDefinitions = getChildrenAlertDefinitionIds(overlord, alertTemplate.getId()); if (LOG.isDebugEnabled()) { LOG.debug("Need to update the following children alert definition ids: " + alertDefinitions); } List<Integer> alertDefinitionIdsInError = new ArrayList<Integer>(); for (Integer alertDefinitionId : alertDefinitions) { try { alertDefinitionManager.updateDependentAlertDefinition(user, alertDefinitionId, updated, resetMatching); } catch (Throwable t) { // continue on error, update as many as possible if (firstThrowable == null) { firstThrowable = t; } alertDefinitionIdsInError.add(alertDefinitionId); } } // if the user deleted the alert definition spawned from a template, a cascade update will recreate it List<Integer> resourceIds = getCommittedResourceIdsNeedingTemplateApplication(overlord, alertTemplate.getId(), getResourceTypeIdForAlertTemplateId(alertTemplate.getId())); if (LOG.isDebugEnabled()) { LOG.debug("Need to re-create alert definitions for the following resource ids: " + resourceIds); } List<Integer> resourceIdsInError = new ArrayList<Integer>(); for (Integer resourceId : resourceIds) { try { // construct the child AlertDefinition childAlertDefinition = new AlertDefinition(updated); childAlertDefinition.setParentId(alertTemplate.getId()); // persist the child alertDefinitionManager.createAlertDefinitionInNewTransaction(overlord, childAlertDefinition, resourceId, false); } catch (Throwable t) { // continue on error, update as many as possible if (firstThrowable == null) { firstThrowable = t; } resourceIdsInError.add(resourceId); } } if (firstThrowable != null) { StringBuilder error = new StringBuilder(); if (alertDefinitionIdsInError.size() != 0) { error.append("Failed to update child AlertDefinitions " + alertDefinitionIdsInError + "; "); } if (resourceIdsInError.size() != 0) { error.append("Failed to re-create child AlertDefinition for Resources " + resourceIdsInError + "; "); } throw new AlertDefinitionUpdateException(error.toString(), firstThrowable); } return updated; } private int getResourceTypeIdForAlertTemplateId(int alertTemplateId) { Query query = entityManager.createQuery("" // + "SELECT template.resourceType.id " // + " FROM AlertDefinition template " // + " WHERE template.id = :alertTemplateId"); query.setParameter("alertTemplateId", alertTemplateId); int typeId = ((Number) query.getSingleResult()).intValue(); return typeId; } }