content: /*
   * 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.Objects;
  import java.util.Set;
  
  import jakarta.persistence.CascadeType;
  import jakarta.persistence.Column;
  import jakarta.persistence.Entity;
  import jakarta.persistence.FetchType;
  import jakarta.persistence.GeneratedValue;
  import jakarta.persistence.GenerationType;
  import jakarta.persistence.Id;
  import jakarta.persistence.JoinColumn;
  import jakarta.persistence.JoinTable;
  import jakarta.persistence.ManyToMany;
  import jakarta.persistence.NamedQueries;
  import jakarta.persistence.NamedQuery;
  import jakarta.persistence.Table;
  import jakarta.persistence.TableGenerator;
  import jakarta.persistence.UniqueConstraint;
  
  /**
   * The {@link AlertGroupEntity} class manages the logical groupings of
   * {@link AlertDefinitionEntity}s in order to easily define what alerts an
   * {@link AlertTargetEntity} should be notified on.
   */
  @Entity
  @Table(
      name = "alert_group",
      uniqueConstraints = @UniqueConstraint(columnNames = { "cluster_id", "group_name" }))
  @TableGenerator(
      name = "alert_group_id_generator",
      table = "ambari_sequences",
      pkColumnName = "sequence_name",
      valueColumnName = "sequence_value",
      pkColumnValue = "alert_group_id_seq",
      initialValue = 0)
  @NamedQueries({
      @NamedQuery(
          name = "AlertGroupEntity.findAll",
          query = "SELECT alertGroup FROM AlertGroupEntity alertGroup"),
      @NamedQuery(
          name = "AlertGroupEntity.findAllInCluster",
          query = "SELECT alertGroup FROM AlertGroupEntity alertGroup WHERE alertGroup.clusterId = :clusterId"),
      @NamedQuery(
          name = "AlertGroupEntity.findByNameInCluster",
          query = "SELECT alertGroup FROM AlertGroupEntity alertGroup WHERE alertGroup.groupName = :groupName AND alertGroup.clusterId = :clusterId"),
      @NamedQuery(
          name = "AlertGroupEntity.findByAssociatedDefinition",
          query = "SELECT alertGroup FROM AlertGroupEntity alertGroup WHERE :alertDefinition MEMBER OF alertGroup.alertDefinitions"),
      @NamedQuery(
          name = "AlertGroupEntity.findServiceDefaultGroup",
          query = "SELECT alertGroup FROM AlertGroupEntity alertGroup WHERE alertGroup.clusterId = :clusterId AND alertGroup.serviceName = :serviceName AND alertGroup.isDefault = 1"),
      @NamedQuery(
          name = "AlertGroupEntity.findByIds",
          query = "SELECT alertGroup FROM AlertGroupEntity alertGroup WHERE alertGroup.groupId IN :groupIds") })
  public class AlertGroupEntity {
  
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "alert_group_id_generator")
    @Column(name = "group_id", nullable = false, updatable = false)
    private Long groupId;
  
    @Column(name = "cluster_id", nullable = false)
    private Long clusterId;
  
    @Column(name = "group_name", nullable = false, length = 255)
    private String groupName;
  
    @Column(name = "is_default", nullable = false)
    private Integer isDefault = Integer.valueOf(0);
  
    @Column(name = "service_name", nullable = true, length = 255)
    private String serviceName;
  
    /**
     * Bi-directional many-to-many association to {@link AlertDefinitionEntity}
     */
    @ManyToMany(cascade = CascadeType.MERGE)
    @JoinTable(
        name = "alert_grouping",
        joinColumns = { @JoinColumn(name = "group_id", nullable = false) },
        inverseJoinColumns = { @JoinColumn(name = "definition_id", nullable = false) })
    private Set<AlertDefinitionEntity> alertDefinitions;
  
    /**
     * Unidirectional many-to-many association to {@link AlertTargetEntity}
     */
    @ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.MERGE, CascadeType.REFRESH })
    @JoinTable(
        name = "alert_group_target",
        joinColumns = { @JoinColumn(name = "group_id", nullable = false) },
        inverseJoinColumns = { @JoinColumn(name = "target_id", nullable = false) })
    private Set<AlertTargetEntity> alertTargets;
  
    /**
     * Gets the unique ID of this grouping of alerts.
     *
     * @return the ID (never {@code null}).
     */
    public Long getGroupId() {
      return groupId;
    }
  
    /**
     * Sets the unique ID of this grouping of alerts.
     *
     * @param groupId
     *          the ID (not {@code null}).
     */
    public void setGroupId(Long groupId) {
      this.groupId = groupId;
    }
  
    /**
     * Gets the ID of the cluster that this alert group is a part of.
     *
     * @return the ID (never {@code null}).
     */
    public Long getClusterId() {
      return clusterId;
    }
  
    /**
     * Sets the ID of the cluster that this alert group is a part of.
     *
     * @param clusterId
     *          the ID of the cluster (not {@code null}).
     */
    public void setClusterId(Long clusterId) {
      this.clusterId = clusterId;
    }
  
    /**
     * Gets the name of the grouping of alerts. Group names are unique in a given
     * cluster.
     *
     * @return the group name (never {@code null}).
     */
    public String getGroupName() {
      return groupName;
    }
  
    /**
     * Sets the name of this grouping of alerts. Group names are unique in a given
     * cluster.
     *
     * @param groupName
     *          the name of the group (not {@code null}).
     */
    public void setGroupName(String groupName) {
      this.groupName = groupName;
    }
  
    /**
     * Gets whether this is a default group and is mostly read-only. Default
     * groups cannot have their alert definition groupings changed. New alert
     * definitions are automtaically added to the default group that belongs to
     * the service of that definition.
     *
     * @return {@code true} if this is a default group, {@code false} otherwise.
     */
    public boolean isDefault() {
      return isDefault == 0 ? false : true;
    }
  
    /**
     * Sets whether this is a default group and is immutable.
     *
     * @param isDefault
     *          {@code true} to make this group immutable.
     */
    public void setDefault(boolean isDefault) {
      this.isDefault = isDefault == false ? 0 : 1;
    }
  
    /**
     * Gets the name of the service. This is only applicable when
     * {@link #isDefault()} is {@code true}.
     *
     * @return the service that this is the default group for, or {@code null} if
     *         this is not a default group.
     */
    public String getServiceName() {
      return serviceName;
    }
  
    /**
     * Set the service name. This is only applicable when {@link #isDefault()} is
     * {@code true}.
     *
     * @param serviceName
     *          the service that this is the default group for, or {@code null} if
     *          this is not a default group.
     */
    public void setServiceName(String serviceName) {
      this.serviceName = serviceName;
    }
  
    /**
     * Gets all of the alert definitions that are a part of this grouping.
     *
     * @return the alert definitions or an empty set if none (never {@code null).
     */
    public Set<AlertDefinitionEntity> getAlertDefinitions() {
      if (null == alertDefinitions) {
        alertDefinitions = new HashSet<>();
      }
  
      return Collections.unmodifiableSet(alertDefinitions);
    }
  
    /**
     * Set all of the alert definitions that are part of this alert group.
     *
     * @param alertDefinitions
     *          the definitions, or {@code null} for none.
     */
    public void setAlertDefinitions(Set<AlertDefinitionEntity> alertDefinitions) {
      if (null != this.alertDefinitions) {
        for (AlertDefinitionEntity definition : this.alertDefinitions) {
          definition.removeAlertGroup(this);
        }
      }
  
      this.alertDefinitions = alertDefinitions;
  
      if (null != alertDefinitions) {
        for (AlertDefinitionEntity definition : alertDefinitions) {
          definition.addAlertGroup(this);
        }
      }
    }
  
    /**
     * Adds the specified definition to the definitions that this group will
     * dispatch to.
     *
     * @param definition
     *          the definition to add (not {@code null}).
     */
    public void addAlertDefinition(AlertDefinitionEntity definition) {
      if (null == alertDefinitions) {
        alertDefinitions = new HashSet<>();
      }
  
      alertDefinitions.add(definition);
      definition.addAlertGroup(this);
    }
  
    /**
     * Removes the specified definition from the definitions that this group will
     * dispatch to.
     *
     * @param definition
     *          the definition to remove (not {@code null}).
     */
    public void removeAlertDefinition(AlertDefinitionEntity definition) {
      if (null != alertDefinitions) {
        alertDefinitions.remove(definition);
      }
  
      definition.removeAlertGroup(this);
    }
  
    /**
     * Gets an immutable set of the targets that will receive notifications for
     * alert definitions in this group.
     *
     * @return the targets that will be dispatch to for alerts in this group, or
     *         an empty set if there are none (never {@code null}).
     */
    public Set<AlertTargetEntity> getAlertTargets() {
      if( null == alertTargets ) {
        return Collections.emptySet();
      }
  
      return Collections.unmodifiableSet(alertTargets);
    }
  
    /**
     * Adds the specified target to the targets that this group will dispatch to.
     *
     * @param alertTarget
     *          the target to add (not {@code null}).
     */
    public void addAlertTarget(AlertTargetEntity alertTarget) {
      if (null == alertTargets) {
        alertTargets = new HashSet<>();
      }
  
      alertTargets.add(alertTarget);
      alertTarget.addAlertGroup(this);
    }
  
    /**
     * Removes the specified target from the targets that this group will dispatch
     * to.
     *
     * @param alertTarget
     *          the target to remove (not {@code null}).
     */
    public void removeAlertTarget(AlertTargetEntity alertTarget) {
      if (null != alertTargets) {
        alertTargets.remove(alertTarget);
      }
  
      alertTarget.removeAlertGroup(this);
    }
  
    /**
     * Sets all of the targets that will receive notifications for alert
     * definitions in this group.
     *
     * @param alertTargets
     *          the targets, or {@code null} if there are none.
     */
    public void setAlertTargets(Set<AlertTargetEntity> alertTargets) {
      // for any existing associations, remove "this" from those associations
      if (null != this.alertTargets) {
        // make a copy to prevent ConcurrentModificiationExceptions
        Set<AlertTargetEntity> copyOfAssociatedTargets = new HashSet<>(this.alertTargets);
        for (AlertTargetEntity target : copyOfAssociatedTargets) {
          target.removeAlertGroup(this);
        }
      }
  
      // update all new targets to reflect "this" as an associated group
      if (null != alertTargets) {
        for (AlertTargetEntity target : alertTargets) {
          target.addAlertGroup(this);
        }
      }
  
      // update reference
      this.alertTargets = alertTargets;
    }
  
    /**
     *
     */
    @Override
    public boolean equals(Object object) {
      if (this == object) {
        return true;
      }
  
      if (object == null || getClass() != object.getClass()) {
        return false;
      }
  
      AlertGroupEntity that = (AlertGroupEntity) object;
  
      // use the unique ID if it exists
      if( null != groupId ){
        return Objects.equals(groupId, that.groupId);
      }
  
      return Objects.equals(groupId, that.groupId) &&
          Objects.equals(clusterId, that.clusterId) &&
          Objects.equals(groupName, that.groupName) &&
          Objects.equals(serviceName, that.serviceName);
    }
  
    /**
     *
     */
    @Override
    public int hashCode() {
      // use the unique ID if it exists
      if( null != groupId ){
        return groupId.hashCode();
      }
  
      return Objects.hash(groupId, clusterId, groupName, serviceName);
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
      StringBuilder buffer = new StringBuilder();
      buffer.append(getClass().getSimpleName());
      buffer.append("{");
      buffer.append("id=").append(groupId);
      buffer.append(", name=").append(groupName);
      buffer.append(", default=").append(isDefault);
      buffer.append("}");
      return buffer.toString();
    }
  }
  