/* * 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(); } }