/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ambari.server.orm.entities;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.PreRemove;
import javax.persistence.QueryHint;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.UniqueConstraint;
import org.apache.ambari.server.state.AlertState;
import org.apache.ambari.server.state.alert.Scope;
import org.apache.ambari.server.state.alert.SourceType;
/**
* The {@link AlertDefinitionEntity} class is used to model an alert that needs
* to run in the system. Each received alert from an agent will essentially be
* an instance of this template.
*/
@Entity
@Table(name = "alert_definition", uniqueConstraints = @UniqueConstraint(columnNames = {
"cluster_id", "definition_name"}))
@TableGenerator(name = "alert_definition_id_generator", table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "sequence_value", pkColumnValue = "alert_definition_id_seq", initialValue = 0)
@NamedQueries({
@NamedQuery(name = "AlertDefinitionEntity.findAll", query = "SELECT ad FROM AlertDefinitionEntity ad"),
@NamedQuery(name = "AlertDefinitionEntity.findAllInCluster", query = "SELECT ad FROM AlertDefinitionEntity ad WHERE ad.clusterId = :clusterId"),
@NamedQuery(name = "AlertDefinitionEntity.findAllEnabledInCluster", query = "SELECT ad FROM AlertDefinitionEntity ad WHERE ad.clusterId = :clusterId AND ad.enabled = 1"),
@NamedQuery(name = "AlertDefinitionEntity.findByName", query = "SELECT ad FROM AlertDefinitionEntity ad WHERE ad.definitionName = :definitionName AND ad.clusterId = :clusterId",
hints = {
@QueryHint(name = "eclipselink.query-results-cache", value = "true"),
@QueryHint(name = "eclipselink.query-results-cache.ignore-null", value = "true"),
@QueryHint(name = "eclipselink.query-results-cache.size", value = "5000")
}),
@NamedQuery(name = "AlertDefinitionEntity.findByService", query = "SELECT ad FROM AlertDefinitionEntity ad WHERE ad.serviceName = :serviceName AND ad.clusterId = :clusterId"),
@NamedQuery(name = "AlertDefinitionEntity.findByServiceAndComponent", query = "SELECT ad FROM AlertDefinitionEntity ad WHERE ad.serviceName = :serviceName AND ad.componentName = :componentName AND ad.clusterId = :clusterId"),
@NamedQuery(name = "AlertDefinitionEntity.findByServiceMaster", query = "SELECT ad FROM AlertDefinitionEntity ad WHERE ad.serviceName IN :services AND ad.scope = :scope AND ad.clusterId = :clusterId AND ad.componentName IS NULL"),
@NamedQuery(name = "AlertDefinitionEntity.findByIds", query = "SELECT ad FROM AlertDefinitionEntity ad WHERE ad.definitionId IN :definitionIds")})
public class AlertDefinitionEntity {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "alert_definition_id_generator")
@Column(name = "definition_id", nullable = false, updatable = false)
private Long definitionId;
@Lob
@Basic
@Column(name = "alert_source", nullable = false, length = 32672)
private String source;
@Column(name = "cluster_id", nullable = false)
private Long clusterId;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "cluster_id", referencedColumnName = "cluster_id", insertable = false, updatable = false)
private ClusterEntity clusterEntity;
@Column(name = "component_name", length = 255)
private String componentName;
@Column(name = "definition_name", nullable = false, length = 255)
private String definitionName;
@Column(name = "label", nullable = true, length = 255)
private String label;
@Column(name = "help_url", nullable = true, length = 512)
private String helpURL;
@Lob
@Basic
@Column(name = "description", nullable = true, length = 32672)
private String description;
@Column(name = "scope", length = 255)
@Enumerated(value = EnumType.STRING)
private Scope scope;
@Column(nullable = false)
private Integer enabled = Integer.valueOf(1);
@Column(nullable = false, length = 64)
private String hash;
@Column(name = "schedule_interval", nullable = false)
private Integer scheduleInterval;
@Column(name = "service_name", nullable = false, length = 255)
private String serviceName;
@Column(name = "source_type", nullable = false, length = 255)
@Enumerated(value = EnumType.STRING)
private SourceType sourceType;
@Column(name = "ignore_host", nullable = false)
private Integer ignoreHost = Integer.valueOf(0);
/**
* Indicates how many sequential alerts must be received for a non-OK state
* change to be considered correct. This value is meant to eliminate
* false-positive notifications on alerts which are flaky.
*/
@Column(name = "repeat_tolerance", nullable = false)
private Integer repeatTolerance = Integer.valueOf(1);
/**
* If {@code 1}, then the value of {@link #repeatTolerance} is used to
* override the global alert tolerance value.
*/
@Column(name = "repeat_tolerance_enabled", nullable = false)
private Short repeatToleranceEnabled = Short.valueOf((short) 0);
/**
* Bi-directional many-to-many association to {@link AlertGroupEntity}
*/
@ManyToMany(mappedBy = "alertDefinitions", cascade = {CascadeType.PERSIST,
CascadeType.MERGE, CascadeType.REFRESH})
private Set<AlertGroupEntity> alertGroups;
/**
* Constructor.
*/
public AlertDefinitionEntity() {
}
/**
* Gets the unique identifier for this alert definition.
*
* @return the ID.
*/
public Long getDefinitionId() {
return definitionId;
}
/**
* Sets the unique identifier for this alert definition.
*
* @param definitionId the ID (not {@code null}).
*/
public void setDefinitionId(Long definitionId) {
this.definitionId = definitionId;
}
/**
* Gets the source that defines the type of alert and the alert properties.
* This is typically a JSON structure that can be mapped to a first-class
* object.
*
* @return the alert source (never {@code null}).
*/
public String getSource() {
return source;
}
/**
* Sets the source of the alert, typically in JSON, that defines the type of
* the alert and its properties.
*
* @param alertSource the alert source (not {@code null}).
*/
public void setSource(String alertSource) {
source = alertSource;
}
/**
* Gets the ID of the cluster that this alert definition is created for. Each
* cluster has their own set of alert definitions that are not shared with any
* other cluster.
*
* @return the ID of the cluster (never {@code null}).
*/
public Long getClusterId() {
return clusterId;
}
/**
* Sets the ID of the cluster that this alert definition is created for. Each
* cluster has their own set of alert definitions that are not shared with any
* other cluster.
*
* @param clusterId the ID of the cluster (not {@code null}).
*/
public void setClusterId(Long clusterId) {
this.clusterId = clusterId;
}
/**
* Gets the cluster that this alert definition is a member of.
*
* @return
*/
public ClusterEntity getCluster() {
return clusterEntity;
}
/**
* Sets the cluster that the alert definition is a member of.
*
* @param clusterEntity
*/
public void setCluster(ClusterEntity clusterEntity) {
this.clusterEntity = clusterEntity;
}
/**
* Gets the component name that this alert is associated with, if any. Some
* alerts are scoped at the service level and will not have a component name.
*
* @return the component name or {@code null} if none.
*/
public String getComponentName() {
return componentName;
}
/**
* Sets the component name that this alert is associated with, if any. Some
* alerts are scoped at the service level and will not have a component name.
*
* @param componentName the component name or {@code null} if none.
*/
public void setComponentName(String componentName) {
this.componentName = componentName;
}
/**
* Gets the scope of the alert definition. The scope is defined as either
* being for a {@link Scope#SERVICE} or {@link Scope#HOST}.
*
* @return the scope, or {@code null} if not defined.
*/
public Scope getScope() {
return scope;
}
/**
* Sets the scope of the alert definition. The scope is defined as either
* being for a {@link Scope#SERVICE} or {@link Scope#HOST}.
*
* @param scope the scope to set, or {@code null} for none.
*/
public void setScope(Scope scope) {
this.scope = scope;
}
/**
* Gets the name of this alert definition. Alert definition names are unique
* within a cluster.
*
* @return the name of the alert definition (never {@code null}).
*/
public String getDefinitionName() {
return definitionName;
}
/**
* Sets the name of this alert definition. Alert definition names are unique
* within a cluster.
*
* @param definitionName the name of the alert definition (not {@code null}).
*/
public void setDefinitionName(String definitionName) {
this.definitionName = definitionName;
}
/**
* Gets whether this alert definition is enabled. Disabling an alert
* definition will prevent agents from scheduling the alerts. No alerts will
* be triggered and no alert data will be collected.
*
* @return {@code true} if this alert definition is enabled, {@code false}
* otherwise.
*/
public boolean getEnabled() {
return enabled == Integer.valueOf(0) ? false : true;
}
/**
* Sets whether this alert definition is enabled.
*
* @param enabled {@code true} if this alert definition is enabled, {@code false}
* otherwise.
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled ? Integer.valueOf(1) : Integer.valueOf(0);
}
/**
* Gets whether this alert definition will ignore the hosts reporting the
* alert and combine them all into a single alert entry.
*
* @return {@code true} if this alert definition is to ignore hosts and
* combine all alert instances into a single entry, {@code false}
* otherwise.
*/
public boolean isHostIgnored() {
return ignoreHost == Integer.valueOf(0) ? false : true;
}
/**
* Sets whether this alert definition will ignore the hosts reporting the
* alert and combine them all into a single alert entry.
*
* @param ignoreHost {@code true} if this alert definition is to ignore hosts and
* combine all alert instances into a single entry, {@code false}
* otherwise.
*/
public void setHostIgnored(boolean ignoreHost) {
this.ignoreHost = ignoreHost ? Integer.valueOf(1) : Integer.valueOf(0);
}
/**
* Gets the unique hash for the current state of this definition. If a
* property of this definition changes, a new hash is calculated.
*
* @return the unique hash or {@code null} if there is none.
*/
public String getHash() {
return hash;
}
/**
* Gets the unique hash for the current state of this definition. If a
* property of this definition changes, a new hash is calculated.
*
* @param hash the unique hash to set or {@code null} for none.
*/
public void setHash(String hash) {
this.hash = hash;
}
/**
* Gets the alert trigger interval, in seconds.
*
* @return the interval, in seconds.
*/
public Integer getScheduleInterval() {
return scheduleInterval;
}
/**
* Sets the alert trigger interval, in seconds.
*
* @param scheduleInterval the interval, in seconds.
*/
public void setScheduleInterval(Integer scheduleInterval) {
this.scheduleInterval = scheduleInterval;
}
/**
* Gets the name of the service that this alert definition is associated with.
* Every alert definition is associated with exactly one service.
*
* @return the name of the service (never {@code null}).
*/
public String getServiceName() {
return serviceName;
}
/**
* Gets the name of the service that this alert definition is associated with.
* Every alert definition is associated with exactly one service.
*
* @param serviceName the name of the service (not {@code null}).
*/
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
/**
* @return
*/
public SourceType getSourceType() {
return sourceType;
}
/**
* @param sourceType
*/
public void setSourceType(SourceType sourceType) {
this.sourceType = sourceType;
}
/**
* Gets the alert groups that this alert definition is associated with.
*
* @return the groups, or {@code null} if none.
*/
public Set<AlertGroupEntity> getAlertGroups() {
return Collections.unmodifiableSet(alertGroups);
}
/**
* Sets the alert groups that this alert definition is associated with.
*
* @param alertGroups the groups, or {@code null} for none.
*/
public void setAlertGroups(Set<AlertGroupEntity> alertGroups) {
this.alertGroups = alertGroups;
}
/**
* Sets a human readable label for this alert definition.
*
* @param label the label or {@code null} if none.
*/
public void setLabel(String label) {
this.label = label;
}
/**
* Gets the label for this alert definition.
*
* @return the label or {@code null} if none.
*/
public String getLabel() {
return label;
}
/**
* Gets the help url for this alert.
*
* @return the helpURL or {@code null} if none.
*/
public String getHelpURL() {
return helpURL;
}
/**
* Sets a help url for this alert.
*
* @param helpURL the helpURL or {@code null} if none.
*/
public void setHelpURL(String helpURL) {
this.helpURL = helpURL;
}
/**
* Gets the optional description for this alert definition.
*
* @return the description, or {@code null} if none.
*/
public String getDescription() {
return description;
}
/**
* Gets the optional description for this alert definition.
*
* @param description the description to set or {@code null} for none.
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Gets the number of sequential instances of an non-OK {@link AlertState}
* must be received in order to consider the alert instance as truly being in
* that {@link AlertState}.
* <p/>
* A value of 1 or less indicates that there is no tolerance and a single
* alert will cause dispatching of alert notifications.
*
* @return the repeatTolerance
*/
public int getRepeatTolerance() {
return repeatTolerance;
}
/**
* Sets the number of sequential instances of an non-OK {@link AlertState}
* must be received in order to consider the alert instance as truly being in
* that {@link AlertState}.
* <p/>
* A value of 1 or less indicates that there is no tolerance and a single
* alert will cause dispatching of alert notifications.
*
* @param repeatTolerance
* the tolerance to set
*/
public void setRepeatTolerance(int repeatTolerance) {
this.repeatTolerance = repeatTolerance;
}
/**
* Gets whether this definition overrides the default global tolerance value
* specified in {@code cluster-env/alerts.repeat.tolerance}. If enabled, the
* value from {@link #getRepeatTolerance()} should be used to calculate retry
* tolerance.
*
* @return the repeatToleranceEnabled {@code true} to override the global
* value.
*/
public boolean isRepeatToleranceEnabled() {
return repeatToleranceEnabled == Short.valueOf((short) 0) ? false : true;
}
/**
* Sets whether this definition overrides the default global tolerance value
* specified in {@code cluster-env/alerts.repeat.tolerance}. If enabled, the
* value from {@link #getRepeatTolerance()} should be used to calculate retry
* tolerance.
*
* @param enabled
* {@code true} to override the defautlt value and use the value
* returned from {@link #getRepeatTolerance()}.
*/
public void setRepeatToleranceEnabled(boolean enabled) {
repeatToleranceEnabled = enabled ? Short.valueOf((short) 1) : 0;
}
/**
* Adds the specified alert group to the groups that this definition is
* associated with. This is used to complement the JPA bidirectional
* association.
*
* @param alertGroup
*/
protected void addAlertGroup(AlertGroupEntity alertGroup) {
if (null == alertGroups) {
alertGroups = new HashSet<>();
}
alertGroups.add(alertGroup);
}
/**
* Removes the specified alert group to the groups that this definition is
* associated with. This is used to complement the JPA bidirectional
* association.
*
* @param alertGroup
*/
protected void removeAlertGroup(AlertGroupEntity alertGroup) {
if (null != alertGroups && alertGroups.contains(alertGroup)) {
alertGroups.remove(alertGroup);
}
}
/**
* Called before {@link EntityManager#remove(Object)} for this entity, removes
* the non-owning relationship between definitions and groups.
*/
@PreRemove
public void preRemove() {
if (null == alertGroups || alertGroups.size() == 0) {
return;
}
Iterator<AlertGroupEntity> iterator = alertGroups.iterator();
while (iterator.hasNext()) {
AlertGroupEntity group = iterator.next();
iterator.remove();
group.removeAlertDefinition(this);
}
}
/**
* Gets the equality to another historical alert entry based on the following criteria:
* <ul>
* <li>{@link #definitionId}
* <li>{@link #clusterId}
* <li>{@link #definitionName}
* </ul>
* <p/>
* However, since we're guaranteed that {@link #definitionId} is unique among persisted entities, we
* can return the hashCode based on this value if it is set.
* <p/>
* {@inheritDoc}
*/
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
AlertDefinitionEntity that = (AlertDefinitionEntity) object;
// use the unique ID if it exists
if( null != definitionId ){
return Objects.equals(definitionId, that.definitionId);
}
return Objects.equals(definitionId, that.definitionId) &&
Objects.equals(clusterId, that.clusterId) &&
Objects.equals(definitionName, that.definitionName);
}
/**
* Gets a hash to uniquely identify this alert definition. Since we're
* guaranteed that {@link #definitionId} is unique among persisted entities,
* we can return the hashCode based on this value if it is set.
* <p/>
* {@inheritDoc}
*/
@Override
public int hashCode() {
// use the unique ID if it exists
if( null != definitionId ){
return definitionId.hashCode();
}
return Objects.hash(definitionId, clusterId, definitionName);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
StringBuilder buffer = new StringBuilder();
buffer.append(getClass().getSimpleName());
buffer.append("{");
buffer.append("id=").append(definitionId);
buffer.append(", name=").append(definitionName);
buffer.append(", serviceName=").append(serviceName);
buffer.append(", componentName=").append(componentName);
buffer.append(", enabled=").append(enabled);
buffer.append(", hash=").append(hash);
buffer.append("}");
return buffer.toString();
}
}