/* * 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, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser 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.core.domain.alert; import java.io.Serializable; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OrderBy; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import org.rhq.core.domain.alert.notification.AlertNotificationLog; /** * @author Joseph Marques */ @Entity @NamedQueries({ @NamedQuery(name = Alert.QUERY_FIND_BY_MEASUREMENT_DEFINITION_ID, query = "SELECT a " + " FROM Alert AS a " + " JOIN a.alertDefinition definition " + " JOIN definition.conditions condition " + " WHERE condition.measurementDefinition.id = :measurementDefinitionId " + " AND a.ctime BETWEEN :begin AND :end"), @NamedQuery(name = Alert.QUERY_FIND_BY_MEAS_DEF_ID_AND_RESOURCES, query = "SELECT a " + " FROM Alert AS a " + " JOIN a.alertDefinition definition " + " JOIN definition.conditions condition " + " WHERE condition.measurementDefinition.id = :measurementDefinitionId " + " AND definition.resource.id IN (:resourceIds) " // + " AND (a.ctime BETWEEN :startDate AND :endDate)"), @NamedQuery(name = Alert.QUERY_FIND_BY_MEAS_DEF_ID_AND_RESOURCEGROUP, query = "" // + "SELECT a " // + " FROM Alert AS a " // + " JOIN a.alertDefinition definition " // + " JOIN definition.conditions condition " // + " WHERE condition.measurementDefinition.id = :measurementDefinitionId " // + " AND definition.resource IN ( SELECT res " // + " FROM ResourceGroup rg " // + " JOIN rg.explicitResources res " // + " WHERE rg.id = :groupId ) " // + " AND ( a.ctime BETWEEN :startDate AND :endDate )"), @NamedQuery(name = Alert.QUERY_FIND_BY_MEAS_DEF_ID_AND_AUTOGROUP, query = "" // + "SELECT a " // + " FROM Alert AS a " // + " JOIN a.alertDefinition definition " // + " JOIN definition.conditions condition " // + " WHERE condition.measurementDefinition.id = :measurementDefinitionId " // + " AND definition.resource IN ( SELECT res " // + " FROM Resource res " // + " WHERE res.parentResource.id = :parentId " // + " AND res.resourceType.id = :typeId ) " // + " AND ( a.ctime BETWEEN :startDate AND :endDate )"), @NamedQuery(name = Alert.QUERY_FIND_BY_MEAS_DEF_ID_AND_RESOURCE, query = "" // + "SELECT a " // + " FROM Alert AS a " // + " JOIN a.alertDefinition definition " // + " JOIN definition.conditions condition " // + " WHERE condition.measurementDefinition.id = :measurementDefinitionId " // + " AND definition.resource.id = ( :resourceId ) " // + " AND ( a.ctime BETWEEN :startDate AND :endDate )"), @NamedQuery(name = Alert.QUERY_GET_ALERT_COUNT_FOR_SCHEDULES, query = "SELECT sched.id, count(*) " + " FROM Alert AS a JOIN a.alertDefinition aDef JOIN aDef.conditions condition " + " JOIN aDef.resource res JOIN condition.measurementDefinition mDef JOIN mDef.schedules sched" + " WHERE sched.definition = mDef.id AND sched.resource = res AND sched.id IN (:schedIds) " + " AND (a.ctime BETWEEN :startDate AND :endDate)" + "GROUP BY sched.id"), @NamedQuery(name = Alert.QUERY_DELETE_BY_CTIME, query = "" // + "DELETE FROM Alert AS a " // + " WHERE a.ctime BETWEEN :begin AND :end"),// @NamedQuery(name = Alert.QUERY_RETURN_EXISTING_IDS, query = "" // + " SELECT a.id " // + " FROM Alert a " // + " WHERE a.id IN ( :alertIds ) "), // @NamedQuery(name = Alert.QUERY_CHECK_PERMISSION_BY_IDS, query = "" // + " SELECT COUNT(a) " // + " FROM Alert a " // + " JOIN a.alertDefinition ad " // + " JOIN ad.resource res " // + " WHERE a.id IN ( :alertIds ) " // + " AND res.id IN ( SELECT rr.id FROM Resource rr " // + " JOIN rr.implicitGroups g JOIN g.roles r JOIN r.permissions p JOIN r.subjects s " // + " WHERE s.id = :subjectId " // + " AND p = :permission ) "), // @NamedQuery(name = Alert.QUERY_DELETE_ALL, query = "" // + "DELETE FROM Alert a "), // @NamedQuery(name = Alert.QUERY_DELETE_BY_IDS, query = "" // + "DELETE Alert AS alert " // + " WHERE alert.id IN ( :alertIds )"), // @NamedQuery(name = Alert.QUERY_DELETE_BY_RESOURCES, query = "" // + "DELETE FROM Alert alert " // + " WHERE alert.id IN ( SELECT innerA.id " // + " FROM AlertDefinition ad " // + " JOIN ad.alerts innerA " // + " WHERE ad.resource.id IN ( :resourceIds ) )"), @NamedQuery(name = Alert.QUERY_DELETE_BY_RESOURCE_TEMPLATE, query = "DELETE FROM Alert alert " + "WHERE alert.id IN (SELECT innerAlerts.id " + " FROM AlertDefinition alertDef " + " JOIN alertDef.alerts innerAlerts " + " WHERE alertDef.resourceType.id = :resourceTypeId)"), @NamedQuery(name = Alert.QUERY_DELETE_BY_RESOURCE_GROUPS, query = "" // + "DELETE FROM Alert alert " // + " WHERE alert.id IN ( SELECT innerA.id " // + " FROM AlertDefinition ad " // + " JOIN ad.alerts innerA " // + " JOIN ad.resource.implicitGroups rg " // + " WHERE rg.id IN ( :groupIds ) )"), @NamedQuery(name = Alert.QUERY_ACKNOWLEDGE_ALL, query = "" // + "UPDATE Alert AS alert " // + " SET alert.acknowledgingSubject = :subjectName, " // + " alert.acknowledgeTime = :ackTime " // + " WHERE alert.acknowledgingSubject IS NULL "), // @NamedQuery(name = Alert.QUERY_ACKNOWLEDGE_BY_IDS, query = "" // + "UPDATE Alert AS alert " // + " SET alert.acknowledgingSubject = :subjectName, " // + " alert.acknowledgeTime = :ackTime " // + " WHERE alert.id IN ( :alertIds ) " // + " AND alert.acknowledgingSubject IS NULL "), // only ack what hasn't already been ack'ed @NamedQuery(name = Alert.QUERY_ACKNOWLEDGE_BY_RESOURCES, query = "" // + "UPDATE Alert AS alert " // + " SET alert.acknowledgingSubject = :subjectName, " // + " alert.acknowledgeTime = :ackTime " // + " WHERE alert.id IN ( SELECT innerA.id " // + " FROM AlertDefinition ad " // + " JOIN ad.alerts innerA " // + " WHERE ad.resource.id IN ( :resourceIds ) )" // + " AND alert.acknowledgingSubject IS NULL "), @NamedQuery(name = Alert.QUERY_ACKNOWLEDGE_BY_RESOURCE_GROUPS, query = "" // + "UPDATE Alert AS alert " // + " SET alert.acknowledgingSubject = :subjectName, " // + " alert.acknowledgeTime = :ackTime " // + " WHERE alert.id IN ( SELECT innerA.id " // + " FROM AlertDefinition ad " // + " JOIN ad.alerts innerA " // + " JOIN ad.resource.implicitGroups rg " // + " WHERE rg.id IN ( :groupIds ) )" // + " AND alert.acknowledgingSubject IS NULL "), @NamedQuery(name = Alert.QUERY_FIND_ALL_COMPOSITES_ADMIN, query = "" // + " SELECT new org.rhq.core.domain.alert.composite.AlertHistoryComposite" // + " ( a, parent.id, parent.name ) " // + " FROM Alert a " // + " JOIN a.alertDefinition ad " // + " JOIN ad.resource res " // + "LEFT JOIN res.parentResource parent " // /* * as much as i want to (for efficiency of the query [namely roundtrips to the db]) i can't use fetching here * because, when added, the query parser chokes with "query specified join fetching, but the owner of the * fetched association was not present in the select list"...even though it clearly is ;/ */ //+ " JOIN FETCH a.conditionLogs acl " // + " WHERE (UPPER(res.name) LIKE :resourceFilter ESCAPE :escapeChar OR :resourceFilter IS NULL) " // + " AND (UPPER(parent.name) LIKE :parentFilter ESCAPE :escapeChar OR :parentFilter IS NULL) " // + " AND (a.ctime > :startTime OR :startTime IS NULL) " // + " AND (a.ctime < :endTime OR :endTime IS NULL) " // + " AND (a.id IN ( SELECT aa.id FROM Alert aa " // + " JOIN aa.conditionLogs aacl " // + " JOIN aacl.condition ac " // + " WHERE ac.category = :category ) " // + " OR :category IS NULL) "), // @NamedQuery(name = Alert.QUERY_FIND_ALL_COMPOSITES, query = "" // + " SELECT new org.rhq.core.domain.alert.composite.AlertHistoryComposite" // + " ( a, parent.id, parent.name ) " // + " FROM Alert a " // + " JOIN a.alertDefinition ad " // + " JOIN ad.resource res " // + "LEFT JOIN res.parentResource parent " // /* * as much as i want to (for efficiency of the query [namely roundtrips to the db]) i can't use fetching here * because, when added, the query parser chokes with "query specified join fetching, but the owner of the * fetched association was not present in the select list"...even though it clearly is ;/ */ //+ " JOIN FETCH a.conditionLogs acl " // + " WHERE res.id IN ( SELECT rr.id FROM Resource rr " // + " JOIN rr.implicitGroups g JOIN g.roles r JOIN r.subjects s " // + " WHERE s.id = :subjectId ) " // + " AND (UPPER(res.name) LIKE :resourceFilter ESCAPE :escapeChar OR :resourceFilter IS NULL) " // + " AND (UPPER(parent.name) LIKE :parentFilter ESCAPE :escapeChar OR :parentFilter IS NULL) " // + " AND (a.ctime > :startTime OR :startTime IS NULL) " // + " AND (a.ctime < :endTime OR :endTime IS NULL) " // + " AND (a.id IN ( SELECT aa.id FROM Alert aa " // + " JOIN aa.conditionLogs aacl " // + " JOIN aacl.condition ac " // + " WHERE ac.category = :category ) " // + " OR :category IS NULL) "), @NamedQuery(name = Alert.QUERY_MARK_RECOVERED_BY_DEFINITION_ID, query = "" // + "UPDATE Alert AS alert " // + " SET alert.recoveryTime = :recoveryTime " // + "WHERE alert.id IN ( SELECT innerA.id " // + " FROM AlertDefinition AS ad " // + " JOIN ad.alerts AS innerA " // + " WHERE ad.id = :alertDefinitionId )" + " AND alert.ctime < :recoveryTime " // + " AND alert.willRecover = true " // + " AND alert.recoveryTime < 0" // don't process again )}) @SequenceGenerator(allocationSize = org.rhq.core.domain.util.Constants.ALLOCATION_SIZE, name = "RHQ_ALERT_ID_SEQ", sequenceName = "RHQ_ALERT_ID_SEQ") @Table(name = "RHQ_ALERT") public class Alert implements Serializable { public static final String QUERY_FIND_BY_MEASUREMENT_DEFINITION_ID = "Alert.findByMeasurementDefinitionId"; /** * @deprecated as of RHQ 4.13, no longer used */ @Deprecated public static final String QUERY_DELETE_BY_CTIME = "Alert.deleteByCTime"; public static final String QUERY_RETURN_EXISTING_IDS = "Alert.returnExistingIds"; public static final String QUERY_CHECK_PERMISSION_BY_IDS = "Alert.checkPermissionByIds"; public static final String QUERY_DELETE_ALL = "Alert.deleteByAll"; public static final String QUERY_DELETE_BY_IDS = "Alert.deleteByIds"; public static final String QUERY_DELETE_BY_RESOURCES = "Alert.deleteByResources"; public static final String QUERY_DELETE_BY_RESOURCE_TEMPLATE = "Alert.deleteByResourceType"; public static final String QUERY_DELETE_BY_RESOURCE_GROUPS = "Alert.deleteByResourceGroups"; public static final String QUERY_ACKNOWLEDGE_ALL = "Alert.acknowledgeByAll"; public static final String QUERY_ACKNOWLEDGE_BY_IDS = "Alert.acknowledgeByIds"; public static final String QUERY_ACKNOWLEDGE_BY_RESOURCES = "Alert.acknowledgeByResources"; public static final String QUERY_ACKNOWLEDGE_BY_RESOURCE_GROUPS = "Alert.acknowledgeByResourceGroups"; public static final String QUERY_FIND_BY_MEAS_DEF_ID_AND_RESOURCES = "Alert.findByMeasDefIdAndResources"; public static final String QUERY_FIND_BY_MEAS_DEF_ID_AND_RESOURCEGROUP = "Alert.findByMeasDefIdAndResourceGroup"; public static final String QUERY_FIND_BY_MEAS_DEF_ID_AND_AUTOGROUP = "Alert.findByMeasDefIdAndAutoGroup"; public static final String QUERY_FIND_BY_MEAS_DEF_ID_AND_RESOURCE = "Alert.findByMeasDefIdAndResource"; public static final String QUERY_GET_ALERT_COUNT_FOR_SCHEDULES = "Alert.QUERY_GET_ALERT_COUNT_FOR_SCHEDULES"; public static final String QUERY_MARK_RECOVERED_BY_DEFINITION_ID = "Alert.markRecoveredByDefinitionId"; /** * @deprecated as of RHQ 4.13, no longer used */ @Deprecated public static final String QUERY_NATIVE_TRUNCATE_SQL = "TRUNCATE TABLE RHQ_ALERT"; // for subsystem view public static final String QUERY_FIND_ALL_COMPOSITES = "Alert.findAllComposites"; public static final String QUERY_FIND_ALL_COMPOSITES_ADMIN = "Alert.findAllComposites_admin"; private static final long serialVersionUID = 1L; @Column(name = "ID", nullable = false) @GeneratedValue(strategy = GenerationType.AUTO, generator = "RHQ_ALERT_ID_SEQ") @Id private int id; @Column(name = "CTIME", nullable = false) private long ctime; @JoinColumn(name = "ALERT_DEFINITION_ID", referencedColumnName = "ID") @ManyToOne private AlertDefinition alertDefinition; @OneToMany(mappedBy = "alert", cascade = CascadeType.ALL, orphanRemoval = true) @OrderBy // primary key private Set<AlertConditionLog> conditionLogs = new LinkedHashSet<AlertConditionLog>(); @OneToMany(mappedBy = "alert", cascade = CascadeType.ALL) private List<AlertNotificationLog> alertNotificationLogs = new ArrayList<AlertNotificationLog>(); /* * recoveryId and willRecover==true are mutually exclusive * * you are either a recovery alert, or an alert to be recovered */ @Column(name = "RECOVERY_ID") private Integer recoveryId; @JoinColumn(name = "RECOVERY_ID", referencedColumnName = "ID", insertable = false, updatable = false, nullable = false) @ManyToOne(fetch = FetchType.LAZY, optional = false) private AlertDefinition recoveryAlertDefinition; @Column(name = "WILL_RECOVER", nullable = false) private boolean willRecover; @Column(name = "RECOVERY_TIME") private Long recoveryTime = -1L; @Column(name = "ACK_TIME") private Long acknowledgeTime = -1L; @Column(name = "ACK_SUBJECT") private String acknowledgingSubject; /** * Creates a new alert. (required by EJB3 spec, but not used) */ public Alert() { } /** * Creates a new alert with the specified definition and creation time. * * @param alertDefinition the definition * @param ctime the creation time */ public Alert(AlertDefinition alertDefinition, long ctime) { this.alertDefinition = alertDefinition; this.recoveryId = alertDefinition.getRecoveryId(); this.willRecover = alertDefinition.getWillRecover(); // Do not load the collection side from a one-to-many, This is very slow to load all existing alerts // and unnecessary for creating the link // alertDefinition.addAlert(this); this.ctime = ctime; } public int getId() { return this.id; } public AlertDefinition getAlertDefinition() { return this.alertDefinition; } public void setAlertDefinition(AlertDefinition alertDefinition) { this.alertDefinition = alertDefinition; } public long getCtime() { return this.ctime; } public Set<AlertConditionLog> getConditionLogs() { return this.conditionLogs; } public void addConditionLog(AlertConditionLog conditionLog) { this.conditionLogs.add(conditionLog); conditionLog.setAlert(this); } public List<AlertNotificationLog> getAlertNotificationLogs() { return alertNotificationLogs; } public void setAlertNotificationLogs(List<AlertNotificationLog> alertNotificationLogs) { this.alertNotificationLogs = alertNotificationLogs; } public void addAlertNotificatinLog(AlertNotificationLog log) { this.alertNotificationLogs.add(log); } public Long getAcknowledgeTime() { return acknowledgeTime; } public void setAcknowledgeTime(Long acknowledgeTime) { this.acknowledgeTime = acknowledgeTime; } public String getAcknowledgingSubject() { return acknowledgingSubject; } public void setAcknowledgingSubject(String acknowledgingSubject) { this.acknowledgingSubject = acknowledgingSubject; } public boolean getWillRecover() { return this.willRecover; } public void setWillRecover(boolean willRecover) { if (willRecover && getRecoveryId() != 0) { throw new IllegalStateException( "An alert definition can either be a recovery definition or a definition to-be-recovered, but not both."); } this.willRecover = willRecover; } public Integer getRecoveryId() { return this.recoveryId; } public void setRecoveryId(Integer actOnTriggerId) { if (getWillRecover() && actOnTriggerId != 0) { throw new IllegalStateException( "An alert definition can either be a recovery definition or a definition to-be-recovered, but not both."); } this.recoveryId = actOnTriggerId; } public Long getRecoveryTime() { return recoveryTime; } public void setRecoveryTime(Long recoveryTime) { this.recoveryTime = recoveryTime; } public AlertDefinition getRecoveryAlertDefinition() { return this.recoveryAlertDefinition; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if ((obj == null) || !(obj instanceof Alert)) { return false; } Alert alert = (Alert) obj; return (id == alert.id) && (ctime == alert.ctime); } @Override public int hashCode() { int result = id; result = (31 * result) + (int) (ctime ^ (ctime >>> 32)); return result; } public String toSimpleString() { return "Alert[id=" + this.id + "]"; } @Override public String toString() { return "org.rhq.core.domain.alert.Alert[id=" + this.id + ", alertDefinition=" + this.alertDefinition + ", ctime=" + this.ctime + "]"; } }