/*
* 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, 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.resource.group;
import java.util.ArrayList;
import java.util.HashSet;
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.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.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import org.jetbrains.annotations.NotNull;
import org.rhq.core.domain.alert.AlertDefinition;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.authz.Role;
import org.rhq.core.domain.bundle.BundleDestination;
import org.rhq.core.domain.configuration.group.AbstractGroupConfigurationUpdate;
import org.rhq.core.domain.dashboard.Dashboard;
import org.rhq.core.domain.operation.GroupOperationHistory;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.domain.tagging.Tag;
/**
* A {@link Group} that contains {@link Resource}s. It cannot contain other groups.
*
* @author Greg Hinkle
* @author Joseph Marques
*/
@Entity
@NamedQueries({
@NamedQuery(name = ResourceGroup.QUERY_FIND_ALL_FILTERED_COUNT, query = "SELECT count(DISTINCT g) "
+ "FROM ResourceGroup g JOIN g.roles r JOIN r.subjects s " //
+ "LEFT JOIN g.resourceType type " //
+ "LEFT JOIN g.implicitResources res " // used for inventory>summary "member in groups" section, authz-related
+ "WHERE s = :subject " //
+ " AND g.visible = true " //
+ " AND res.inventoryStatus = 'COMMITTED' " //
+ " AND ( res.id = :resourceId OR :resourceId is null ) " //
+ " AND ( g.id = :groupId OR :groupId is null ) " //
+ " AND ( g.groupCategory = :groupCategory OR :groupCategory is null ) " //
+ " AND (UPPER(g.name) LIKE :search " //
+ " OR UPPER(g.description) LIKE :search " //
+ "OR :search is null) " //
+ "AND ( type is null " //
+ " OR ( (type.name = :resourceTypeName OR :resourceTypeName is null) " //
+ " AND (type.plugin = :pluginName OR :pluginName is null) " //
+ " AND (type.category = :category OR :category is null) ) ) "),
@NamedQuery(name = ResourceGroup.QUERY_FIND_ALL_FILTERED_COUNT_ADMIN, query = "SELECT count(DISTINCT g) FROM ResourceGroup g "
+ "LEFT JOIN g.resourceType type "
+ "LEFT JOIN g.implicitResources res " // used for inventory>summary "member in groups" section, authz-related
+ "WHERE ( g.groupCategory = :groupCategory OR :groupCategory is null ) " //
+ " AND g.visible = true " //
+ " AND ( res.id = :resourceId OR :resourceId is null ) " //
+ " AND res.inventoryStatus = 'COMMITTED' " //
+ " AND ( g.id = :groupId OR :groupId is null ) " //
+ " AND (UPPER(g.name) LIKE :search " //
+ " OR UPPER(g.description) LIKE :search " //
+ " OR :search is null) " //
+ "AND ( type is null " //
+ " OR ( (type.name = :resourceTypeName OR :resourceTypeName is null) " //
+ " AND (type.plugin = :pluginName OR :pluginName is null) " //
+ " AND (type.category = :category OR :category is null) ) ) "),
@NamedQuery(name = ResourceGroup.QUERY_FIND_RESOURCE_GROUP_SUMMARY, query = "" //
+ "SELECT rg.groupCategory, COUNT(DISTINCT rg) " //
+ " FROM ResourceGroup AS rg JOIN rg.roles r JOIN r.subjects s " //
+ " WHERE s = :subject " //
+ " AND rg.visible = true " //
+ " GROUP BY rg.groupCategory "),
@NamedQuery(name = ResourceGroup.QUERY_FIND_RESOURCE_GROUP_SUMMARY_admin, query = "" //
+ "SELECT rg.groupCategory, COUNT(rg) " //
+ " FROM ResourceGroup AS rg " //
+ " WHERE rg.visible = true " //
+ " GROUP BY rg.groupCategory "),
@NamedQuery(name = ResourceGroup.QUERY_FIND_BY_NAME, query = "SELECT rg FROM ResourceGroup AS rg WHERE LOWER(rg.name) = LOWER(:name)"),
@NamedQuery(name = ResourceGroup.QUERY_FIND_BY_NAME_VISIBLE_GROUP, query = "SELECT rg FROM ResourceGroup AS rg "
+ " WHERE LOWER(rg.name) = LOWER(:name)" + " AND rg.visible = true"),
@NamedQuery(name = ResourceGroup.QUERY_FIND_BY_CLUSTER_KEY, query = "SELECT rg FROM ResourceGroup AS rg WHERE rg.clusterKey = :clusterKey"),
@NamedQuery(name = ResourceGroup.QUERY_GET_AVAILABLE_RESOURCE_GROUPS_FOR_ROLE, query = "SELECT DISTINCT rg "
+ "FROM ResourceGroup AS rg LEFT JOIN rg.roles AS r " + "WHERE rg.id NOT IN "
+ " ( SELECT irg.id FROM Role ir JOIN ir.resourceGroups AS irg " + " WHERE ir.id = :roleId )"
+ " AND rg.clusterResourceGroup is NULL"),
@NamedQuery(name = ResourceGroup.QUERY_GET_AVAILABLE_RESOURCE_GROUPS_FOR_ROLE_WITH_EXCLUDES, query = "SELECT DISTINCT rg "
+ "FROM ResourceGroup AS rg LEFT JOIN rg.roles AS r "
+ "WHERE rg.id NOT IN "
+ " ( SELECT irg.id "
+ " FROM Role ir JOIN ir.resourceGroups AS irg "
+ " WHERE ir.id = :roleId ) "
+ " AND rg.id NOT IN ( :excludeIds )" + " AND rg.clusterResourceGroup is NULL"),
@NamedQuery(name = ResourceGroup.QUERY_GET_RESOURCE_GROUPS_ASSIGNED_TO_ROLE_admin, query = "" //
+ "SELECT rg " //
+ " FROM ResourceGroup AS rg " //
+ " JOIN rg.roles AS r " //
+ " WHERE r.id = :id"), //
@NamedQuery(name = ResourceGroup.QUERY_GET_RESOURCE_GROUPS_ASSIGNED_TO_ROLE, query = "" //
+ "SELECT rg " //
+ " FROM ResourceGroup AS rg " //
+ " JOIN rg.roles AS r " //
+ " WHERE r.id = :id " //
+ " AND r.id IN ( SELECT role.id " //
+ " FROM Role role " //
+ " JOIN role.subjects s " //
+ " WHERE s.id = :subjectId ) "),
@NamedQuery(name = ResourceGroup.QUERY_FIND_BY_IDS_admin, query = "" //
+ " SELECT rg " //
+ " FROM ResourceGroup AS rg " //
+ " LEFT JOIN FETCH rg.groupDefinition " //
+ " LEFT JOIN FETCH rg.resourceType " //
+ " WHERE rg.id IN ( :ids )"), //
@NamedQuery(name = ResourceGroup.QUERY_FIND_BY_IDS, query = "" //
+ " SELECT rg " //
+ " FROM ResourceGroup AS rg " //
+ " LEFT JOIN FETCH rg.groupDefinition " //
+ " LEFT JOIN FETCH rg.resourceType " //
+ " JOIN rg.roles roles " //
+ " JOIN roles.subjects s " //
+ " WHERE rg.id IN ( :ids ) " //
+ " AND s = :subject"), //
@NamedQuery(name = ResourceGroup.QUERY_FIND_IMPLICIT_RECURSIVE_GROUP_IDS_BY_RESOURCE_ID, query = "" //
+ "SELECT rg.id " //
+ " FROM Resource AS res " //
+ " JOIN res.implicitGroups rg " //
+ " WHERE res.id = :id " //
+ " AND rg.recursive = true "),
/* the following two are for auto-groups summary */
@NamedQuery(name = ResourceGroup.QUERY_FIND_AUTOGROUP_BY_ID, query = "SELECT new org.rhq.core.domain.resource.group.composite.AutoGroupComposite(AVG(a.availabilityType), res.parentResource, res.resourceType, COUNT(res)) "
+ "FROM Resource res JOIN res.implicitGroups irg JOIN irg.roles r JOIN r.subjects s JOIN res.currentAvailability a "
+ "WHERE s = :subject " + "AND res.id = :resourceId " + "GROUP BY res.resourceType "),
@NamedQuery(name = ResourceGroup.QUERY_FIND_AUTOGROUP_BY_ID_ADMIN, query = "SELECT new org.rhq.core.domain.resource.group.composite.AutoGroupComposite(AVG(a.availabilityType), res.parentResource, res.resourceType, COUNT(res)) "
+ "FROM Resource res JOIN res.currentAvailability a "
+ "WHERE res.id = :resourceId "
+ "GROUP BY res.resourceType "),
@NamedQuery(name = ResourceGroup.QUERY_FIND_RESOURCE_NAMES_BY_GROUP_ID, query = ""
+ "SELECT new org.rhq.core.domain.common.composite.IntegerOptionItem(res.id, res.name) " //
+ " FROM ResourceGroup g " //
+ " JOIN g.explicitResources res " //
+ " WHERE g.id = :groupId AND res.inventoryStatus = 'COMMITTED' "),
@NamedQuery(name = ResourceGroup.QUERY_FIND_BY_GROUP_DEFINITION_AND_EXPRESSION, query = "SELECT g "
+ " FROM ResourceGroup g " + " WHERE (g.groupByClause = :groupByClause OR :groupByClause IS NULL) "
+ " AND g.groupDefinition.id = :groupDefinitionId "),
@NamedQuery(name = ResourceGroup.QUERY_FIND_RESOURCE_IDS_NOT_IN_GROUP_EXPLICIT, query = "" //
+ " SELECT res.id " //
+ " FROM Resource res " //
+ " WHERE res.id IN ( :resourceIds ) " //
+ " AND res.id NOT IN ( SELECT explicitRes.id " //
+ " FROM ResourceGroup rg " //
+ " JOIN rg.explicitResources explicitRes " //
+ " WHERE rg.id = :groupId ) " //
+ " AND res.inventoryStatus = 'COMMITTED' ") })
@SequenceGenerator(allocationSize = org.rhq.core.domain.util.Constants.ALLOCATION_SIZE, name = "RHQ_RESOURCE_GROUP_ID_SEQ", sequenceName = "RHQ_RESOURCE_GROUP_ID_SEQ")
@Table(name = "RHQ_RESOURCE_GROUP")
@XmlAccessorType(XmlAccessType.FIELD)
public class ResourceGroup extends Group {
private static final long serialVersionUID = 1L;
//------- Names of Named Queries -------
public static final String QUERY_FIND_RESOURCE_GROUP_SUMMARY = "ResourceGroup.findResourceGroupSummary";
public static final String QUERY_FIND_RESOURCE_GROUP_SUMMARY_admin = "ResourceGroup.findResourceGroupSummary_admin";
public static final String QUERY_FIND_BY_NAME = "ResourceGroup.findByName";
public static final String QUERY_FIND_BY_NAME_VISIBLE_GROUP = "ResourceGroup.findByNameVisibleGroup";
public static final String QUERY_FIND_BY_CLUSTER_KEY = "ResourceGroup.findByClusterKey";
public static final String QUERY_GET_AVAILABLE_RESOURCE_GROUPS_FOR_ROLE_WITH_EXCLUDES = "ResourceGroup.getAvailableResourceGroupsForRoleWithExcludes";
public static final String QUERY_GET_AVAILABLE_RESOURCE_GROUPS_FOR_ROLE = "ResourceGroup.getAvailableResourceGroupsForRole";
public static final String QUERY_GET_RESOURCE_GROUPS_ASSIGNED_TO_ROLE_admin = "ResourceGroup.getResourceGroupsAssignedToRole_admin";
public static final String QUERY_GET_RESOURCE_GROUPS_ASSIGNED_TO_ROLE = "ResourceGroup.getResourceGroupsAssignedToRole";
public static final String QUERY_FIND_BY_IDS_admin = "ResourceGroup.findByIds_admin";
public static final String QUERY_FIND_BY_IDS = "ResourceGroup.findByIds";
public static final String QUERY_FIND_IMPLICIT_RECURSIVE_GROUP_IDS_BY_RESOURCE_ID = "ResourceGroup.findImplicitRecursiveGroupIdsByResourceId";
public static final String QUERY_FIND_AUTOGROUP_BY_ID = "ResourceGroup.findAutoGroupById";
public static final String QUERY_FIND_AUTOGROUP_BY_ID_ADMIN = "ResourceGroup.findAutoGroupById_admin";
public static final String QUERY_FIND_RESOURCE_NAMES_BY_GROUP_ID = "ResourceGroup.findResourceNamesByGroupId";
public static final String QUERY_FIND_BY_GROUP_DEFINITION_AND_EXPRESSION = "ResourceGroup.findByGroupDefinitionAndExpression";
public static final String QUERY_DELETE_EXPLICIT_BY_RESOURCE_IDS = "DELETE FROM RHQ_RESOURCE_GROUP_RES_EXP_MAP WHERE RESOURCE_ID IN ( :resourceIds )";
public static final String QUERY_DELETE_IMPLICIT_BY_RESOURCE_IDS = "DELETE FROM RHQ_RESOURCE_GROUP_RES_IMP_MAP WHERE RESOURCE_ID IN ( :resourceIds )";
public static final String QUERY_GET_GROUP_IDS_BY_RESOURCE_IDS = "SELECT DISTINCT RESOURCE_GROUP_ID FROM RHQ_RESOURCE_GROUP_RES_EXP_MAP WHERE RESOURCE_ID IN ( :resourceIds )";
public static final String QUERY_FIND_ALL_FILTERED_COUNT = "ResourceGroup.findAllFiltered_Count";
public static final String QUERY_FIND_ALL_FILTERED_COUNT_ADMIN = "ResourceGroup.findAllFiltered_Count_Admin";
//------- Actual Queries -------
public static final String QUERY_UPDATE_REMOVE_IMPLICIT = "" //
+ "DELETE FROM rhq_resource_group_res_imp_map implicitMap " //
+ " WHERE implicitMap.resource_group_id = ?";
public static final String QUERY_UPDATE_IMPLICIT_MIRROR_EXPLICIT = "" //
+ "INSERT INTO rhq_resource_group_res_imp_map (resource_id, resource_group_id) " //
+ " SELECT explicitMap.resource_id, explicitMap.resource_group_id " //
+ " FROM rhq_resource_group_res_exp_map explicitMap " //
+ " WHERE explicitMap.resource_group_id = ?";
public static final String QUERY_NATIVE_FIND_FILTERED_MEMBER = "" //
+ " SELECT "
+ " ( SELECT COUNT(eresAvail.ID) " // total explicit members
+ " FROM rhq_resource_avail eresAvail "
+ " INNER JOIN rhq_resource eres "
+ " ON eresAvail.resource_id = eres.id "
+ " INNER JOIN rhq_resource_group_res_exp_map expMap "
+ " ON eres.id = expMap.resource_id "
+ " WHERE expMap.resource_group_id = rg.id AND eres.inventory_status = 'COMMITTED' "
+ " ) as explicitCount, "
+ "" //
+ " ( SELECT COUNT(eresAvail.ID) " // DOWN explicit members
+ " FROM rhq_resource_avail eresAvail "
+ " INNER JOIN rhq_resource eres "
+ " ON eresAvail.resource_id = eres.id "
+ " INNER JOIN rhq_resource_group_res_exp_map expMap "
+ " ON eres.id = expMap.resource_id "
+ " WHERE expMap.resource_group_id = rg.id AND eres.inventory_status = 'COMMITTED' "
+ " AND eresAvail.availability_type = 0 "
+ " ) as explicitDown, "
+ "" //
+ " ( SELECT COUNT(eresAvail.ID) " // UNKNOWN explicit members
+ " FROM rhq_resource_avail eresAvail "
+ " INNER JOIN rhq_resource eres "
+ " ON eresAvail.resource_id = eres.id "
+ " INNER JOIN rhq_resource_group_res_exp_map expMap "
+ " ON eres.id = expMap.resource_id "
+ " WHERE expMap.resource_group_id = rg.id AND eres.inventory_status = 'COMMITTED' "
+ " AND eresAvail.availability_type = 2 "
+ " ) as explicitUnknown, "
+ "" //
+ " ( SELECT COUNT(eresAvail.ID) " // DISABLED explicit members
+ " FROM rhq_resource_avail eresAvail "
+ " INNER JOIN rhq_resource eres "
+ " ON eresAvail.resource_id = eres.id "
+ " INNER JOIN rhq_resource_group_res_exp_map expMap "
+ " ON eres.id = expMap.resource_id "
+ " WHERE expMap.resource_group_id = rg.id AND eres.inventory_status = 'COMMITTED' "
+ " AND eresAvail.availability_type = 3 "
+ " ) as explicitDisabled, "
+ "" //
+ " ( SELECT COUNT(iresAvail.ID) " // total implicit members
+ " FROM rhq_resource_avail iresAvail "
+ " INNER JOIN rhq_resource ires "
+ " ON iresAvail.resource_id = ires.id "
+ " INNER JOIN rhq_resource_group_res_imp_map impMap "
+ " ON ires.id = impMap.resource_id "
+ " WHERE impMap.resource_group_id = rg.id AND ires.inventory_status = 'COMMITTED' "
+ " ) as implicitCount, "
+ "" //
+ " ( SELECT COUNT(iresAvail.ID) " // DOWN implicit members
+ " FROM rhq_resource_avail iresAvail "
+ " INNER JOIN rhq_resource ires "
+ " ON iresAvail.resource_id = ires.id "
+ " INNER JOIN rhq_resource_group_res_imp_map impMap "
+ " ON ires.id = impMap.resource_id "
+ " WHERE impMap.resource_group_id = rg.id AND ires.inventory_status = 'COMMITTED' "
+ " AND iresAvail.availability_type = 0 "
+ " ) as implicitDown, "
+ "" //
+ " ( SELECT COUNT(iresAvail.ID) " // UNKNOWN implicit members
+ " FROM rhq_resource_avail iresAvail "
+ " INNER JOIN rhq_resource ires "
+ " ON iresAvail.resource_id = ires.id "
+ " INNER JOIN rhq_resource_group_res_imp_map impMap "
+ " ON ires.id = impMap.resource_id "
+ " WHERE impMap.resource_group_id = rg.id AND ires.inventory_status = 'COMMITTED' "
+ " AND iresAvail.availability_type = 2 "
+ " ) as implicitUnknown, "
+ "" //
+ " ( SELECT COUNT(iresAvail.ID) " // DISABLED implicit members
+ " FROM rhq_resource_avail iresAvail "
+ " INNER JOIN rhq_resource ires "
+ " ON iresAvail.resource_id = ires.id "
+ " INNER JOIN rhq_resource_group_res_imp_map impMap "
+ " ON ires.id = impMap.resource_id "
+ " WHERE impMap.resource_group_id = rg.id AND ires.inventory_status = 'COMMITTED' "
+ " AND iresAvail.availability_type = 3 "
+ " ) as implicitDisabled, "
+ "" //
+ " rg.id as groupId, "
+ " rg.name as groupName, "
+ " rg.description as groupDescription, "
+ " resType.name as resourceTypeName "
+ "" //
+ " FROM rhq_resource_group rg "
+ "LEFT OUTER JOIN rhq_resource_type resType "
+ " ON rg.resource_type_id = resType.id "
+ "LEFT OUTER JOIN rhq_resource_group_res_imp_map memberMap "
+ " ON rg.id = memberMap.resource_group_id "
+ "LEFT OUTER JOIN rhq_resource res "
+ " ON memberMap.resource_id = res.id "
+ " %SECURITY_FRAGMENT_JOIN%"
+ "LEFT OUTER JOIN rhq_resource_avail resAvail "
+ " ON res.id = resAvail.resource_id "
+ " WHERE %GROUP_AND_VISIBILITY_FRAGMENT_WHERE% " // postgres uses true/false, oracle uses 1/0
+ " %RESOURCE_FRAGMENT_WHERE% " //
+ " AND ( ? IS NULL " // :search
+ " OR UPPER(rg.name) LIKE ? ESCAPE ?" // :search :escapeChar
+ " OR UPPER(rg.description) LIKE ? ESCAPE ?) " // :search escapeChar
+ " AND ( rg.resource_type_id IS NULL " //
+ " OR ( ( resType.name = ? OR ? IS NULL ) " // :resourceTypeName x2
+ " AND ( resType.plugin = ? OR ? IS NULL ) " // :pluginName x2
+ " AND ( resType.category = ? OR ? IS NULL ) ) ) " // :resourceCategory x2
+ " AND ( rg.category = ? OR ? IS NULL ) " // :groupCategory x2
+ " %SECURITY_FRAGMENT_WHERE%" //
+ " GROUP BY rg.id, rg.category, rg.name, rg.group_by, rg.description, resType.name, resType.plugin ";
public static final String QUERY_NATIVE_FIND_FILTERED_MEMBER_SECURITY_FRAGMENT_JOIN = ""
+ " INNER JOIN rhq_role_resource_group_map roleMap " //
+ " ON roleMap.resource_group_id = rg.id " //
+ " INNER JOIN rhq_subject_role_map subjectMap " //
+ " ON subjectMap.role_id = roleMap.role_id ";
public static final String QUERY_NATIVE_FIND_FILTERED_MEMBER_SECURITY_FRAGMENT_WHERE = ""
+ " AND ( subjectMap.subject_id = ? ) "; // :subjectId
public static final String QUERY_NATIVE_FIND_FILTERED_MEMBER_RESOURCE_FRAGMENT_WHERE = "" //
+ " AND ( res.id = ? ) "; // resourceId
public static final String QUERY_FIND_RESOURCE_IDS_NOT_IN_GROUP_EXPLICIT = "ResourceGroup.findResourceIdsNotInGroupExplicit";
public static final String QUERY_NATIVE_ADD_RESOURCES_TO_GROUP_EXPLICIT = "" //
+ " insert into RHQ_RESOURCE_GROUP_RES_EXP_MAP ( RESOURCE_ID, RESOURCE_GROUP_ID ) " //
+ " select res.ID, ? " // groupId
+ " from RHQ_RESOURCE res " //
+ " where res.ID in ( @@RESOURCE_IDS@@ ) ";
public static final String QUERY_NATIVE_ADD_RESOURCES_TO_GROUP_IMPLICIT = "" //
+ " insert into RHQ_RESOURCE_GROUP_RES_IMP_MAP ( RESOURCE_ID, RESOURCE_GROUP_ID ) " //
+ " select res.ID, ? " // groupId
+ " from RHQ_RESOURCE res " //
+ " where res.ID in ( @@RESOURCE_IDS@@ ) ";
public static final String QUERY_NATIVE_ADD_RESOURCES_TO_GROUP_IMPLICIT_RECURSIVE = "" //
+ " insert into RHQ_RESOURCE_GROUP_RES_IMP_MAP ( RESOURCE_ID, RESOURCE_GROUP_ID ) " //
+ " select res.ID, ? " // groupId
+ " from RHQ_RESOURCE res " //
+ "left outer join RHQ_RESOURCE g1parent on res.PARENT_RESOURCE_ID = g1parent.ID " //
+ "left outer join RHQ_RESOURCE g2parent on g1parent.PARENT_RESOURCE_ID = g2parent.ID " //
+ "left outer join RHQ_RESOURCE g3parent on g2parent.PARENT_RESOURCE_ID = g3parent.ID " //
+ "left outer join RHQ_RESOURCE g4parent on g3parent.PARENT_RESOURCE_ID = g4parent.ID " //
+ "left outer join RHQ_RESOURCE g5parent on g4parent.PARENT_RESOURCE_ID = g5parent.ID " //
+ "left outer join RHQ_RESOURCE g6parent on g5parent.PARENT_RESOURCE_ID = g6parent.ID " //
+ " where ( res.ID = ? or " // resourceId
+ " g1parent.ID = ? or " // resourceId
+ " g2parent.ID = ? or " // resourceId
+ " g3parent.ID = ? or " // resourceId
+ " g4parent.ID = ? or " // resourceId
+ " g5parent.ID = ? or " // resourceId
+ " g6parent.ID = ? ) " // resourceId
+ " and ( res.ID not in ( select impRes.ID " //
+ " from RHQ_RESOURCE_GROUP rg " //
+ " inner join RHQ_RESOURCE_GROUP_RES_IMP_MAP implicitMap on rg.ID = implicitMap.RESOURCE_GROUP_ID " //
+ " inner join RHQ_RESOURCE impRes on implicitMap.RESOURCE_ID = impRes.ID " //
+ " where rg.ID = ? ) ) "; // groupId
public static final String QUERY_NATIVE_REMOVE_RESOURCES_FROM_GROUP_EXPLICIT = "" //
+ " delete from RHQ_RESOURCE_GROUP_RES_EXP_MAP " //
+ " where RESOURCE_GROUP_ID = ? " // groupId
+ " and RESOURCE_ID in ( @@RESOURCE_IDS@@ ) ";
public static final String QUERY_NATIVE_REMOVE_RESOURCES_FROM_GROUP_IMPLICIT = "" //
+ " delete from RHQ_RESOURCE_GROUP_RES_IMP_MAP " //
+ " where RESOURCE_GROUP_ID = ? " // groupId
+ " and RESOURCE_ID in ( @@RESOURCE_IDS@@ ) ";
public static final String QUERY_NATIVE_REMOVE_RESOURCES_FROM_GROUP_IMPLICIT_RECURSIVE = "" //
+ " delete from RHQ_RESOURCE_GROUP_RES_IMP_MAP " // delete mappings
+ " where RESOURCE_GROUP_ID = ? " // groupId
+ " and RESOURCE_ID in " // from any descendant of resourceId, including itself
+ " ( select res.id " //
+ " from RHQ_RESOURCE res " //
+ " left outer join RHQ_RESOURCE g1parent on res.PARENT_RESOURCE_ID = g1parent.ID " //
+ " left outer join RHQ_RESOURCE g2parent on g1parent.PARENT_RESOURCE_ID = g2parent.ID " //
+ " left outer join RHQ_RESOURCE g3parent on g2parent.PARENT_RESOURCE_ID = g3parent.ID " //
+ " left outer join RHQ_RESOURCE g4parent on g3parent.PARENT_RESOURCE_ID = g4parent.ID " //
+ " left outer join RHQ_RESOURCE g5parent on g4parent.PARENT_RESOURCE_ID = g5parent.ID " //
+ " left outer join RHQ_RESOURCE g6parent on g5parent.PARENT_RESOURCE_ID = g6parent.ID " //
+ " where ( res.ID = ? or " // resourceId
+ " g1parent.ID = ? or " // resourceId
+ " g2parent.ID = ? or " // resourceId
+ " g3parent.ID = ? or " // resourceId
+ " g4parent.ID = ? or " // resourceId
+ " g5parent.ID = ? or " // resourceId
+ " g6parent.ID = ? ) ) " // resourceId
+ " and RESOURCE_ID not in " // which aren't already descendants of members in the explicit set
+ " ( select res.id " //
+ " from RHQ_RESOURCE_GROUP_RES_EXP_MAP alreadyMember, RHQ_RESOURCE res " //
+ " left outer join RHQ_RESOURCE g1parent on res.PARENT_RESOURCE_ID = g1parent.ID " //
+ " left outer join RHQ_RESOURCE g2parent on g1parent.PARENT_RESOURCE_ID = g2parent.ID " //
+ " left outer join RHQ_RESOURCE g3parent on g2parent.PARENT_RESOURCE_ID = g3parent.ID " //
+ " left outer join RHQ_RESOURCE g4parent on g3parent.PARENT_RESOURCE_ID = g4parent.ID " //
+ " left outer join RHQ_RESOURCE g5parent on g4parent.PARENT_RESOURCE_ID = g5parent.ID " //
+ " left outer join RHQ_RESOURCE g6parent on g5parent.PARENT_RESOURCE_ID = g6parent.ID " //
+ " where alreadyMember.RESOURCE_GROUP_ID = ? " // groupId
+ " and alreadyMember.RESOURCE_ID <> ? " // resourceId
+ " and ( res.ID = alreadyMember.RESOURCE_ID or " //
+ " g1parent.ID = alreadyMember.RESOURCE_ID or " //
+ " g2parent.ID = alreadyMember.RESOURCE_ID or " //
+ " g3parent.ID = alreadyMember.RESOURCE_ID or " //
+ " g4parent.ID = alreadyMember.RESOURCE_ID or " //
+ " g5parent.ID = alreadyMember.RESOURCE_ID or " //
+ " g6parent.ID = alreadyMember.RESOURCE_ID ) ) ";
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO, generator = "RHQ_RESOURCE_GROUP_ID_SEQ")
@Id
private int id;
@JoinTable(name = "RHQ_RESOURCE_GROUP_RES_EXP_MAP", joinColumns = { @JoinColumn(name = "RESOURCE_GROUP_ID") }, inverseJoinColumns = { @JoinColumn(name = "RESOURCE_ID") })
@ManyToMany
private Set<Resource> explicitResources;
@JoinTable(name = "RHQ_RESOURCE_GROUP_RES_IMP_MAP", joinColumns = { @JoinColumn(name = "RESOURCE_GROUP_ID") }, inverseJoinColumns = { @JoinColumn(name = "RESOURCE_ID") })
@ManyToMany
private Set<Resource> implicitResources;
//TODO ResourceGroup probably isn't the right owning side of this relationship
//this means that any update of a group can update the role membership,
//which is not something one normally does. Maybe the more logical owner of this
//relationship would be the Role.
@JoinTable(name = "RHQ_ROLE_RESOURCE_GROUP_MAP", joinColumns = { @JoinColumn(name = "RESOURCE_GROUP_ID") }, inverseJoinColumns = { @JoinColumn(name = "ROLE_ID") })
@ManyToMany
private Set<Role> roles = new HashSet<Role>();
@OneToMany(mappedBy = "group", cascade = { CascadeType.ALL })
@OrderBy
// by primary key which will also put the operation histories in chronological order
private List<GroupOperationHistory> operationHistories = new ArrayList<GroupOperationHistory>();
@OneToMany(mappedBy = "group", cascade = { CascadeType.ALL })
@OrderBy
// by primary key which will also put the configuration updates in chronological order
private List<AbstractGroupConfigurationUpdate> configurationUpdates = new ArrayList<AbstractGroupConfigurationUpdate>();
@OneToMany(mappedBy = "group", cascade = { CascadeType.REMOVE })
@OrderBy
// by primary key which will also put the bundle destinations in chronological order (not all that useful :)
private List<BundleDestination> bundleDestinations = new ArrayList<BundleDestination>();
@JoinColumn(name = "GROUP_DEFINITION_ID", referencedColumnName = "ID", nullable = true)
@ManyToOne
private GroupDefinition groupDefinition;
@Column(name = "GROUP_BY", nullable = true)
private String groupByClause;
@Column(name = "RECURSIVE")
private boolean recursive;
@Column(name = "CATEGORY", nullable = false)
@Enumerated(EnumType.STRING)
private GroupCategory groupCategory;
@JoinColumn(name = "RESOURCE_TYPE_ID", nullable = true)
@ManyToOne
private ResourceType resourceType; // if non-null, it implies a compatible group
// The group owner. Certain groups (like autogroups) are not associated with Roles. Instead, they
// are bound to specific owners. Subject-Owned groups defer to member-level authorization as needed.
@JoinColumn(name = "SUBJECT_ID", referencedColumnName = "ID", nullable = true)
@ManyToOne
private Subject subject = null;
@Column(name = "CLUSTER_KEY", nullable = true)
private String clusterKey;
// The compatible group for which this is an auto-cluster backing group
@JoinColumn(name = "CLUSTER_RESOURCE_GROUP_ID", referencedColumnName = "ID", nullable = true)
@ManyToOne
private ResourceGroup clusterResourceGroup = null;
// When a compatible group is removed any referring autocluster backing groups should also be removed
@OneToMany(mappedBy = "clusterResourceGroup")
private List<ResourceGroup> clusterBackingGroups = null;
// The parent resource for which this is an auto-group backing group
@JoinColumn(name = "AUTO_GROUP_PARENT_RESOURCE_ID", referencedColumnName = "ID", nullable = true)
@ManyToOne
private Resource autoGroupParentResource = null;
// When false hide this group from the UI. For example, for an autocluster or autogroup backing group.
private boolean visible = true;
// bulk delete @OneToMany(mappedBy = "resource", cascade = { CascadeType.ALL })
@OneToMany(mappedBy = "group", cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH })
private Set<AlertDefinition> alertDefinitions = new LinkedHashSet<AlertDefinition>();
@ManyToMany(mappedBy = "resourceGroups", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
private Set<Tag> tags;
// When a group is removed any owned dashboards are removed automatically
@OneToMany(mappedBy = "group", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
private Set<Dashboard> dashboards = null;
/* no-arg constructor required by EJB spec */
protected ResourceGroup() {
}
public ResourceGroup(@NotNull String name) {
super(name);
setResourceType(null);
}
public ResourceGroup(@NotNull String name, ResourceType type) {
super(name);
setResourceType(type);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void addExplicitResource(@NotNull Resource resource) {
getExplicitResources().add(resource);
resource.addExplicitGroup(this);
}
public void setExplicitResources(Set<Resource> resources) {
this.explicitResources = resources;
}
@NotNull
public Set<Resource> getExplicitResources() {
if (this.explicitResources == null) {
this.explicitResources = new HashSet<Resource>();
}
return this.explicitResources;
}
public boolean removeExplicitResource(@NotNull Resource resource) {
boolean removed = getExplicitResources().remove(resource);
resource.removeExplicitGroup(this);
return removed;
}
public void addImplicitResource(@NotNull Resource resource) {
getImplicitResources().add(resource);
resource.addImplicitGroup(this);
}
public void setImplicitResources(Set<Resource> resources) {
this.implicitResources = resources;
}
public GroupCategory getGroupCategory() {
return groupCategory;
}
protected void setGroupCategory(GroupCategory groupCategory) {
this.groupCategory = groupCategory;
}
@NotNull
public Set<Resource> getImplicitResources() {
if (this.implicitResources == null) {
this.implicitResources = new HashSet<Resource>();
}
return this.implicitResources;
}
public boolean removeImplicitResource(@NotNull Resource resource) {
boolean removed = getImplicitResources().remove(resource);
resource.removeImplicitGroup(this);
return removed;
}
public Set<Role> getRoles() {
//even though the field has an initializer, it can end up
//being null after deserialization. Because there is no
//setter and there are some other public methods depending
//on this not being null, let's make it lazy init.
if (roles == null) {
roles = new HashSet<Role>();
}
return roles;
}
public void addRole(Role role) {
getRoles().add(role);
}
public void removeRole(Role role) {
getRoles().remove(role);
}
@NotNull
public List<GroupOperationHistory> getOperationHistories() {
return operationHistories;
}
public void setOperationHistories(@NotNull List<GroupOperationHistory> operationHistories) {
this.operationHistories = operationHistories;
}
@NotNull
public List<AbstractGroupConfigurationUpdate> getConfigurationUpdates() {
return configurationUpdates;
}
public void setConfigurationUpdates(@NotNull List<AbstractGroupConfigurationUpdate> configurationUpdates) {
this.configurationUpdates = configurationUpdates;
}
public List<BundleDestination> getBundleDestinations() {
return bundleDestinations;
}
public void setBundleDestinations(List<BundleDestination> bundleDestinations) {
this.bundleDestinations = bundleDestinations;
}
public GroupDefinition getGroupDefinition() {
return groupDefinition;
}
public void setGroupDefinition(GroupDefinition groupDefinition) {
this.groupDefinition = groupDefinition;
}
public String getGroupByClause() {
return groupByClause;
}
public void setGroupByClause(String groupByClause) {
this.groupByClause = groupByClause;
}
public boolean isRecursive() {
return recursive;
}
public void setRecursive(boolean recursive) {
this.recursive = recursive;
}
public ResourceType getResourceType() {
return resourceType;
}
public void setResourceType(ResourceType resourceType) {
this.resourceType = resourceType;
if (resourceType == null) {
setGroupCategory(GroupCategory.MIXED);
} else {
setGroupCategory(GroupCategory.COMPATIBLE);
}
}
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
if (this.id > 0 && null != this.subject) {
throw new IllegalStateException("The group owner can not be changed after the group is created.");
}
this.subject = subject;
}
/**
* @return true if this is a subject-owned group, private to the set subject
*/
public boolean isPrivateGroup() {
return (null != this.subject);
}
public String getClusterKey() {
return clusterKey;
}
public void setClusterKey(String clusterKey) {
this.clusterKey = clusterKey;
}
public ResourceGroup getClusterResourceGroup() {
return clusterResourceGroup;
}
public void setClusterResourceGroup(ResourceGroup clusterResourceGroup) {
this.clusterResourceGroup = clusterResourceGroup;
}
public Resource getAutoGroupParentResource() {
return autoGroupParentResource;
}
public void setAutoGroupParentResource(Resource autoGroupParentResource) {
this.autoGroupParentResource = autoGroupParentResource;
}
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
public List<ResourceGroup> getClusterBackingGroups() {
return clusterBackingGroups;
}
public void setClusterBackingGroups(List<ResourceGroup> clusterBackingGroups) {
this.clusterBackingGroups = clusterBackingGroups;
}
public Set<AlertDefinition> getAlertDefinitions() {
if (this.alertDefinitions == null) {
this.alertDefinitions = new LinkedHashSet<AlertDefinition>();
}
return alertDefinitions;
}
public void setAlertDefinitions(Set<AlertDefinition> alertDefinitions) {
this.alertDefinitions = alertDefinitions;
}
public Set<Tag> getTags() {
return tags;
}
public void setTags(Set<Tag> tags) {
this.tags = tags;
}
public void addTag(Tag tag) {
if (this.tags == null) {
tags = new HashSet<Tag>();
}
tags.add(tag);
}
public boolean removeTag(Tag tag) {
if (tags != null) {
return tags.remove(tag);
} else {
return false;
}
}
protected Set<Dashboard> getDashboards() {
return dashboards;
}
protected void setDashboards(Set<Dashboard> dashboards) {
this.dashboards = dashboards;
}
@PrePersist
@PreUpdate
void onPersist() {
// always normalize empty string descriptions to NULL, which will give consistent sorting
// between databases that treat empty string and null as distinct entities (e.g. postgres)
// and those that interpret empty string and null as being equivalent (oracle)
String description = getDescription();
if (description != null && description.trim().equals("")) {
setDescription(null);
}
}
@Override
public String toString() {
StringBuilder buffer = new StringBuilder();
buffer.append("ResourceGroup").append("[");
buffer.append("id=").append(this.id);
buffer.append(", name=").append(this.getName());
buffer.append(", category=").append(groupCategory.name());
String typeName = (this.resourceType != null) ? this.resourceType.getName() : "<mixed>";
buffer.append(", type=").append(typeName);
boolean isDynaGroup = (this.groupDefinition != null);
buffer.append(", isDynaGroup=").append(isDynaGroup);
boolean isClusterGroup = (this.clusterKey != null);
buffer.append(", isClusterGroup=").append(isClusterGroup);
buffer.append("]");
return buffer.toString();
}
}