/* * RHQ Management Platform * Copyright (C) 2005-2015 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 as published by * the Free Software Foundation version 2 of the License. * * 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.enterprise.server.resource.group; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.PostConstruct; import javax.ejb.EJB; import javax.ejb.Stateless; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.persistence.EntityManager; import javax.persistence.EntityNotFoundException; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceException; import javax.persistence.Query; import javax.sql.DataSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.rhq.core.db.DatabaseType; import org.rhq.core.db.DatabaseTypeFactory; import org.rhq.core.db.H2DatabaseType; import org.rhq.core.db.OracleDatabaseType; import org.rhq.core.db.PostgresqlDatabaseType; import org.rhq.core.db.SQLServerDatabaseType; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.authz.Permission; import org.rhq.core.domain.authz.Role; import org.rhq.core.domain.configuration.PluginConfigurationUpdate; import org.rhq.core.domain.configuration.ResourceConfigurationUpdate; import org.rhq.core.domain.criteria.GroupOperationHistoryCriteria; import org.rhq.core.domain.criteria.ResourceGroupCriteria; import org.rhq.core.domain.measurement.DataType; import org.rhq.core.domain.measurement.DisplayType; import org.rhq.core.domain.operation.GroupOperationHistory; import org.rhq.core.domain.operation.bean.GroupOperationSchedule; import org.rhq.core.domain.resource.InventoryStatus; import org.rhq.core.domain.resource.Resource; import org.rhq.core.domain.resource.ResourceCategory; import org.rhq.core.domain.resource.ResourceType; import org.rhq.core.domain.resource.composite.ResourceFacets; import org.rhq.core.domain.resource.composite.ResourcePermission; import org.rhq.core.domain.resource.group.GroupCategory; import org.rhq.core.domain.resource.group.ResourceGroup; import org.rhq.core.domain.resource.group.composite.ResourceGroupComposite; import org.rhq.core.domain.server.PersistenceUtility; import org.rhq.core.domain.util.OrderingField; import org.rhq.core.domain.util.PageControl; import org.rhq.core.domain.util.PageList; import org.rhq.core.util.collection.ArrayUtils; import org.rhq.core.util.jdbc.JDBCUtil; import org.rhq.enterprise.server.RHQConstants; import org.rhq.enterprise.server.alert.GroupAlertDefinitionManagerLocal; import org.rhq.enterprise.server.auth.SubjectManagerLocal; import org.rhq.enterprise.server.authz.AuthorizationManagerLocal; import org.rhq.enterprise.server.authz.PermissionException; import org.rhq.enterprise.server.authz.RequiredPermission; import org.rhq.enterprise.server.operation.OperationManagerLocal; import org.rhq.enterprise.server.resource.ResourceManagerLocal; import org.rhq.enterprise.server.resource.ResourceTypeManagerLocal; import org.rhq.enterprise.server.util.CriteriaQueryGenerator; import org.rhq.enterprise.server.util.CriteriaQueryRunner; import org.rhq.enterprise.server.util.QueryUtility; /** * A manager that provides methods for creating, updating, deleting, and querying * {@link org.rhq.core.domain.resource.group.ResourceGroup}s. * * @author Ian Springer * @author Joseph Marques */ @Stateless public class ResourceGroupManagerBean implements ResourceGroupManagerLocal, ResourceGroupManagerRemote { private final Log log = LogFactory.getLog(ResourceGroupManagerBean.class); @PersistenceContext(unitName = RHQConstants.PERSISTENCE_UNIT_NAME) private EntityManager entityManager; @EJB //@IgnoreDependency private OperationManagerLocal operationManager; @EJB private SubjectManagerLocal subjectManager; @EJB private AuthorizationManagerLocal authorizationManager; @EJB //@IgnoreDependency private ResourceTypeManagerLocal resourceTypeManager; @EJB //@IgnoreDependency private ResourceManagerLocal resourceManager; @EJB private ResourceGroupManagerLocal resourceGroupManager; @EJB private GroupAlertDefinitionManagerLocal groupAlertDefinitionManager; @javax.annotation.Resource(name = "RHQ_DS", mappedName = RHQConstants.DATASOURCE_JNDI_NAME) private DataSource rhqDs; private DatabaseType dbType; @PostConstruct public void init() { dbType = DatabaseTypeFactory.getDefaultDatabaseType(); } public ResourceGroup createPrivateResourceGroup(Subject subject, ResourceGroup group) { group.setSubject(subject); group.setRecursive(false); return resourceGroupManager.createResourceGroup(subjectManager.getOverlord(), group); } @RequiredPermission(Permission.MANAGE_INVENTORY) public ResourceGroup createResourceGroup(Subject user, ResourceGroup group) { // We are now allowing Groups where names collide if the group is not visible as for autogroups and clusters Query query = entityManager.createNamedQuery(ResourceGroup.QUERY_FIND_BY_NAME_VISIBLE_GROUP); query.setParameter("name", group.getName()); @SuppressWarnings("unchecked") List<ResourceGroup> groups = query.getResultList(); if (groups.size() != 0) { throw new ResourceGroupAlreadyExistsException("ResourceGroup with name " + group.getName() + " already exists"); } long time = System.currentTimeMillis(); group.setCtime(time); group.setMtime(time); group.setModifiedBy(user.getName()); entityManager.persist(group); return group; } @RequiredPermission(Permission.MANAGE_INVENTORY) public ResourceGroup updateResourceGroup(Subject subject, ResourceGroup group) throws ResourceGroupUpdateException { return updateResourceGroup(subject, group, null, true); } @RequiredPermission(Permission.MANAGE_INVENTORY) public ResourceGroup updateResourceGroup(Subject subject, ResourceGroup group, RecursivityChangeType changeType) throws ResourceGroupUpdateException { return updateResourceGroup(subject, group, changeType, true); } @RequiredPermission(Permission.MANAGE_INVENTORY) public ResourceGroup updateResourceGroup(Subject user, ResourceGroup group, RecursivityChangeType changeType, boolean updateMembership) throws ResourceGroupUpdateException { int groupId = group.getId(); ResourceGroup attachedGroup = entityManager.find(ResourceGroup.class, groupId); if (attachedGroup == null) { throw new ResourceGroupNotFoundException(groupId); } if (!authorizationManager.hasGroupPermission(user, Permission.MODIFY_RESOURCE, groupId)) { throw new PermissionException("User [" + user + "] does not have permission to modify Resource group with id [" + groupId + "]."); } //roles are not to be updated by this call but the group entity //owns the relationship. Let's make sure we don't change the assigned roles here. group.getRoles().clear(); group.getRoles().addAll(attachedGroup.getRoles()); if (changeType == null) { changeType = RecursivityChangeType.None; if (attachedGroup.isRecursive() == true && group.isRecursive() == false) { // making a recursive group into a "normal" group changeType = RecursivityChangeType.RemovedRecursion; } else if (attachedGroup.isRecursive() == false && group.isRecursive() == true) { // making a "normal" group into a recursive group changeType = RecursivityChangeType.AddedRecursion; } else { // recursive bit didn't change } } if (!updateMembership) { group.setExplicitResources(attachedGroup.getExplicitResources()); group.setImplicitResources(attachedGroup.getImplicitResources()); } group.setMtime(System.currentTimeMillis()); group.setModifiedBy(user.getName()); ResourceGroup newlyAttachedGroup = entityManager.merge(group); if (changeType == RecursivityChangeType.AddedRecursion) { newlyAttachedGroup.setRecursive(true); enableRecursivityForGroup(user, groupId); } else if (changeType == RecursivityChangeType.RemovedRecursion) { newlyAttachedGroup.setRecursive(false); clearImplicitResources(groupId); makeImplicitMirrorExplicit(groupId); } if (updateMembership) { try { setResourceTypeInNewTx(groupId); } catch (ResourceGroupDeleteException e) { throw new ResourceGroupNotFoundException(e.getMessage()); } } return newlyAttachedGroup; } private void clearImplicitResources(int resourceGroupId) throws ResourceGroupUpdateException { Connection conn = null; PreparedStatement removeImplicitStatement = null; try { conn = rhqDs.getConnection(); removeImplicitStatement = conn.prepareStatement(ResourceGroup.QUERY_UPDATE_REMOVE_IMPLICIT); removeImplicitStatement.setInt(1, resourceGroupId); removeImplicitStatement.executeUpdate(); } catch (SQLException sqle) { log.error("Error removing implicit resources from group[id=" + resourceGroupId + "]: ", sqle); throw new ResourceGroupUpdateException("Error removing implicit resources from group[id=" + resourceGroupId + "]: " + sqle.getMessage()); } finally { JDBCUtil.safeClose(removeImplicitStatement); JDBCUtil.safeClose(conn); } } private void makeImplicitMirrorExplicit(int resourceGroupId) throws ResourceGroupUpdateException { Connection conn = null; PreparedStatement updateImplicitMirrorExplicitStatement = null; try { conn = rhqDs.getConnection(); updateImplicitMirrorExplicitStatement = conn .prepareStatement(ResourceGroup.QUERY_UPDATE_IMPLICIT_MIRROR_EXPLICIT); updateImplicitMirrorExplicitStatement.setInt(1, resourceGroupId); updateImplicitMirrorExplicitStatement.executeUpdate(); } catch (SQLException sqle) { log.error("Error making implicit resources mirror explicit resources for group[id=" + resourceGroupId + "]: ", sqle); throw new ResourceGroupUpdateException( "Error making implicit resources mirror explicit resources for group[id=" + resourceGroupId + "]: " + sqle.getMessage()); } finally { JDBCUtil.safeClose(updateImplicitMirrorExplicitStatement); JDBCUtil.safeClose(conn); } } @RequiredPermission(Permission.MANAGE_INVENTORY) public void deleteResourceGroup(Subject subject, int groupId) throws ResourceGroupNotFoundException, ResourceGroupDeleteException { ResourceGroup group = getResourceGroupById(subject, groupId, null); // create a copy of the collection in order to avoid ConcurrentModificationException // (as a consequence of iterating and modifying it at once). Set<Role> roles = new HashSet<Role>(group.getRoles()); for (Role doomedRoleRelationship : roles) { group.removeRole(doomedRoleRelationship); entityManager.merge(doomedRoleRelationship); } // remove all resources in the group resourceGroupManager.removeAllResourcesFromGroup(subject, groupId); if (group.getGroupCategory() == GroupCategory.COMPATIBLE) { removeCompatibleGroupConstructs(subject, group); } // break resource and plugin configuration update links in order to preserve individual change history Query q = null; q = entityManager.createNamedQuery(ResourceConfigurationUpdate.QUERY_DELETE_GROUP_UPDATES_FOR_GROUP); q.setParameter("groupId", group.getId()); q.executeUpdate(); q = entityManager.createNamedQuery(PluginConfigurationUpdate.QUERY_DELETE_GROUP_UPDATES_FOR_GROUP); q.setParameter("groupId", group.getId()); q.executeUpdate(); entityManager.remove(group); } @RequiredPermission(Permission.MANAGE_INVENTORY) public void deleteResourceGroups(Subject subject, int[] groupIds) throws ResourceGroupNotFoundException, ResourceGroupDeleteException { for (int nextGroupId : groupIds) { deleteResourceGroup(subject, nextGroupId); } } /* * TODO: Deletion of all associated group data (except implicit/explicit resource members) should be moved here. * in other words, we don't want Hibernate cascade annotations to remove that history upon deletion of an * entity anymore because there are now two cases where group constructs need to be destroyed: * * 1) compatible group deletion - a group is deleted, all history removed, entity is gone from the system * 2) dynagroup recomputation - a group definition is recalculated, a compatible group turns into a mixed * group, compatible constructs need to be removed, but the entity survives * * For now, this implementation should suffice for -- https://bugzilla.redhat.com/show_bug.cgi?id=535671 */ private void removeCompatibleGroupConstructs(Subject subject, ResourceGroup group) throws ResourceGroupDeleteException { // for compatible groups, first recursively remove any referring backing groups for auto-clusters List<ResourceGroup> clusterBackingGroups = group.getClusterBackingGroups(); if (null != clusterBackingGroups) { for (ResourceGroup referringGroup : clusterBackingGroups) { deleteResourceGroup(subject, referringGroup.getId()); } } Subject overlord = subjectManager.getOverlord(); try { List<GroupOperationSchedule> ops = operationManager.findScheduledGroupOperations(overlord, group.getId()); for (GroupOperationSchedule schedule : ops) { operationManager.unscheduleGroupOperation(overlord, schedule.getJobId().toString(), group.getId()); } } catch (Exception e) { throw new ResourceGroupDeleteException("Failed to get jobs for a group being deleted [" + group + "]; will not attempt to unschedule anything", e); } GroupOperationHistoryCriteria criteria = new GroupOperationHistoryCriteria(); criteria.addFilterResourceGroupIds(Arrays.asList(group.getId())); for (GroupOperationHistory history : operationManager.findGroupOperationHistoriesByCriteria(subject, criteria)) { operationManager.deleteOperationHistory(history.getId(), true); } groupAlertDefinitionManager.purgeAllGroupAlertDefinitions(subject, group.getId()); } public ResourceGroup getResourceGroupById(Subject user, int id, GroupCategory category) throws ResourceGroupNotFoundException { ResourceGroup group = entityManager.find(ResourceGroup.class, id); if (group == null) { throw new ResourceGroupNotFoundException(id); } if (!authorizationManager.canViewGroup(user, group.getId())) { throw new PermissionException("You do not have permission to view this resource group"); } // null category means calling context doesn't care about category if ((category != null) && (category != group.getGroupCategory())) { throw new ResourceGroupNotFoundException("Expected group to belong to '" + category + "' category, " + "it belongs to '" + group.getGroupCategory() + "' category instead"); } initLazyFields(group); return group; } private void initLazyFields(ResourceGroup group) { group.getAlertDefinitions().size(); } @SuppressWarnings("unchecked") public int[] getResourceGroupCountSummary(Subject user) { Query query; if (authorizationManager.isInventoryManager(user)) { query = entityManager.createNamedQuery(ResourceGroup.QUERY_FIND_RESOURCE_GROUP_SUMMARY_admin); } else { query = entityManager.createNamedQuery(ResourceGroup.QUERY_FIND_RESOURCE_GROUP_SUMMARY); query.setParameter("subject", user); } int[] counts = new int[2]; List<Object[]> resultList = query.getResultList(); for (Object[] row : resultList) { switch ((GroupCategory) row[0]) { case MIXED: counts[0] = ((Long) row[1]).intValue(); break; case COMPATIBLE: counts[1] = ((Long) row[1]).intValue(); break; } } return counts; } @RequiredPermission(Permission.MANAGE_INVENTORY) @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void enableRecursivityForGroup(Subject subject, int groupId) throws ResourceGroupNotFoundException, ResourceGroupUpdateException { // step 1: clear the implicit and preparation for adding a different set of resources to it clearImplicitResources(groupId); // step 2: prepare the list of resources to be used to pass to the method that does the recursive logic List<Integer> explicitResourceIdList = resourceManager.findExplicitResourceIdsByResourceGroup(groupId); // step 3: add the explicit resources back, this time with the recursive bit flipped on addResourcesToGroupImplicit(subject, groupId, explicitResourceIdList, false, true); } @RequiredPermission(Permission.MANAGE_INVENTORY) public void addResourcesToGroup(Subject subject, int groupId, int[] resourceIds) { addResourcesToGroup(subject, groupId, resourceIds, true); } private void addResourcesToGroup(Subject subject, int groupId, int[] resourceIds, boolean setType) { Integer[] ids = ArrayUtils.wrapInArray(resourceIds); if (ids == null || ids.length == 0) { return; } boolean isRecursive = isRecursive(groupId); // will perform check for group existence // batch the removes to prevent the ORA error about IN clauses containing more than 1000 items for (int batchIndex = 0; batchIndex < ids.length; batchIndex += 1000) { Integer[] batchIdArray = ArrayUtils.copyOfRange(ids, batchIndex, batchIndex + 1000); List<Integer> batchIds = Arrays.asList(batchIdArray); addResourcesToGroupImplicit(subject, groupId, batchIds, true, isRecursive); addResourcesToGroupExplicit(subject, groupId, batchIds, isRecursive); } if (setType) { try { setResourceTypeInNewTx(groupId); } catch (ResourceGroupDeleteException e) { throw new ResourceGroupNotFoundException(e.getMessage()); } } } private void addResourcesToGroupExplicit(Subject subject, Integer groupId, List<Integer> resourceIds, boolean isRecursive) throws ResourceGroupUpdateException { // nothing to add if (resourceIds == null || resourceIds.size() == 0) { return; } List<Integer> nonMemberResources = getNonMemberExplicitResources(groupId, resourceIds); if (nonMemberResources.size() == 0) { // everybody was already a member return; } int[] resourceIdsToAdd = ArrayUtils.unwrapCollection(nonMemberResources); groupAlertDefinitionManager.addGroupMemberAlertDefinitions(subject, groupId, resourceIdsToAdd); Connection conn = null; PreparedStatement insertExplicitStatement = null; try { conn = rhqDs.getConnection(); // insert explicit resources String insertExplicitQueryString = JDBCUtil .transformQueryForMultipleInParameters(ResourceGroup.QUERY_NATIVE_ADD_RESOURCES_TO_GROUP_EXPLICIT, "@@RESOURCE_IDS@@", resourceIdsToAdd.length); insertExplicitStatement = conn.prepareStatement(insertExplicitQueryString); insertExplicitStatement.setInt(1, groupId); JDBCUtil.bindNTimes(insertExplicitStatement, resourceIdsToAdd, 2); insertExplicitStatement.executeUpdate(); } catch (SQLException sqle) { log.error("Error adding resources to group[id=" + groupId + "]: ", sqle); throw new ResourceGroupUpdateException("Error adding resources from group[id=" + groupId + "]: " + sqle.getMessage()); } finally { JDBCUtil.safeClose(insertExplicitStatement); JDBCUtil.safeClose(conn); } return; } private void addResourcesToGroupImplicit(Subject subject, Integer groupId, List<Integer> resourceIds, boolean filterByExplicitMembership, boolean isRecursive) throws ResourceGroupUpdateException { if (resourceIds == null || resourceIds.size() == 0) { // nothing to add return; } int[] resourceIdsToAdd; if (filterByExplicitMembership) { List<Integer> nonMemberResources = getNonMemberExplicitResources(groupId, resourceIds); if (nonMemberResources.size() == 0) { // everybody was already a member return; } resourceIdsToAdd = ArrayUtils.unwrapCollection(nonMemberResources); } else { resourceIdsToAdd = ArrayUtils.unwrapCollection(resourceIds); } Connection conn = null; PreparedStatement insertExplicitStatement = null; PreparedStatement insertImplicitStatement = null; try { conn = rhqDs.getConnection(); // insert implicit resources if (isRecursive) { insertImplicitStatement = conn .prepareStatement(ResourceGroup.QUERY_NATIVE_ADD_RESOURCES_TO_GROUP_IMPLICIT_RECURSIVE); insertImplicitStatement.setInt(1, groupId); insertImplicitStatement.setInt(9, groupId); for (int resourceId : resourceIdsToAdd) { insertImplicitStatement.setInt(2, resourceId); insertImplicitStatement.setInt(3, resourceId); insertImplicitStatement.setInt(4, resourceId); insertImplicitStatement.setInt(5, resourceId); insertImplicitStatement.setInt(6, resourceId); insertImplicitStatement.setInt(7, resourceId); insertImplicitStatement.setInt(8, resourceId); insertImplicitStatement.executeUpdate(); } } else { String insertImplicitQueryString = JDBCUtil.transformQueryForMultipleInParameters( ResourceGroup.QUERY_NATIVE_ADD_RESOURCES_TO_GROUP_IMPLICIT, "@@RESOURCE_IDS@@", resourceIdsToAdd.length); insertImplicitStatement = conn.prepareStatement(insertImplicitQueryString); insertImplicitStatement.setInt(1, groupId); JDBCUtil.bindNTimes(insertImplicitStatement, resourceIdsToAdd, 2); insertImplicitStatement.executeUpdate(); } } catch (SQLException sqle) { log.error("Error adding resources to group[id=" + groupId + "]: ", sqle); throw new ResourceGroupUpdateException("Error adding resources from group[id=" + groupId + "]: " + sqle.getMessage()); } finally { JDBCUtil.safeClose(insertExplicitStatement); JDBCUtil.safeClose(insertImplicitStatement); JDBCUtil.safeClose(conn); } return; } private boolean isRecursive(int groupId) { Subject overlord = subjectManager.getOverlord(); ResourceGroup attachedGroup = getResourceGroupById(overlord, groupId, null); return attachedGroup.isRecursive(); } @SuppressWarnings("unchecked") private List<Integer> getNonMemberExplicitResources(int groupId, List<Integer> resourceIds) { if (resourceIds == null || resourceIds.size() == 0) { return Collections.emptyList(); } Query query = entityManager.createNamedQuery(ResourceGroup.QUERY_FIND_RESOURCE_IDS_NOT_IN_GROUP_EXPLICIT); query.setParameter("groupId", groupId); query.setParameter("resourceIds", resourceIds); List<Integer> nonMemberResources = query.getResultList(); return nonMemberResources; } @RequiredPermission(Permission.MANAGE_INVENTORY) public void removeResourcesFromGroup(Subject subject, int groupId, int[] resourceIds) { removeResourcesFromGroup(subject, groupId, resourceIds, true); } private void removeResourcesFromGroup(Subject subject, int groupId, int[] resourceIds, boolean setType) { Integer[] ids = ArrayUtils.wrapInArray(resourceIds); if (ids == null || ids.length == 0) { return; } boolean isRecursive = isRecursive(groupId); // will perform check for group existence // batch the removes to prevent the ORA error about IN clauses containing more than 1000 items for (int batchIndex = 0; batchIndex < ids.length; batchIndex += 1000) { Integer[] batchIdArray = ArrayUtils.copyOfRange(ids, batchIndex, batchIndex + 1000); removeResourcesFromGroup_helper(subject, groupId, batchIdArray, isRecursive); } if (setType) { try { setResourceTypeInNewTx(groupId); } catch (ResourceGroupDeleteException e) { throw new ResourceGroupNotFoundException(e.getMessage()); } } } private void removeResourcesFromGroup_helper(Subject subject, Integer groupId, Integer[] resourceIds, boolean isRecursive) throws ResourceGroupNotFoundException, ResourceGroupUpdateException { List<Integer> nonMembersToBeRemoved = getNonMemberExplicitResources(groupId, Arrays.asList(resourceIds)); if (nonMembersToBeRemoved.size() != 0) { throw new ResourceGroupUpdateException("Can not remove resources[" + nonMembersToBeRemoved + "] which are not part of the group[id=" + groupId + "]"); } groupAlertDefinitionManager.removeGroupMemberAlertDefinitions(subject, groupId, resourceIds); Connection conn = null; PreparedStatement deleteExplicitStatement = null; PreparedStatement deleteImplicitStatement = null; try { conn = rhqDs.getConnection(); int[] resourceIdsArray = ArrayUtils.unwrapArray(resourceIds); // insert implicit resources, must occur before deleting explicit if (isRecursive) { deleteImplicitStatement = conn .prepareStatement(ResourceGroup.QUERY_NATIVE_REMOVE_RESOURCES_FROM_GROUP_IMPLICIT_RECURSIVE); deleteImplicitStatement.setInt(1, groupId); deleteImplicitStatement.setInt(9, groupId); for (Integer resourceId : resourceIds) { // no-op if this resource's ancestor is also in the explicit list List<Integer> lineage = resourceManager.getResourceIdLineage(resourceId); List<Integer> nonMembers = getNonMemberExplicitResources(groupId, lineage); if (lineage.size() != nonMembers.size()) { // one or more of my parents were in the explicit list, no-op to remove me continue; } deleteImplicitStatement.setInt(2, resourceId); deleteImplicitStatement.setInt(3, resourceId); deleteImplicitStatement.setInt(4, resourceId); deleteImplicitStatement.setInt(5, resourceId); deleteImplicitStatement.setInt(6, resourceId); deleteImplicitStatement.setInt(7, resourceId); deleteImplicitStatement.setInt(8, resourceId); deleteImplicitStatement.setInt(10, resourceId); deleteImplicitStatement.executeUpdate(); } } else { String deleteImplicitQueryString = JDBCUtil.transformQueryForMultipleInParameters( ResourceGroup.QUERY_NATIVE_REMOVE_RESOURCES_FROM_GROUP_IMPLICIT, "@@RESOURCE_IDS@@", resourceIds.length); deleteImplicitStatement = conn.prepareStatement(deleteImplicitQueryString); deleteImplicitStatement.setInt(1, groupId); JDBCUtil.bindNTimes(deleteImplicitStatement, resourceIdsArray, 2); deleteImplicitStatement.executeUpdate(); } // delete explicit resources String deleteExplicitQueryString = JDBCUtil .transformQueryForMultipleInParameters(ResourceGroup.QUERY_NATIVE_REMOVE_RESOURCES_FROM_GROUP_EXPLICIT, "@@RESOURCE_IDS@@", resourceIds.length); deleteExplicitStatement = conn.prepareStatement(deleteExplicitQueryString); deleteExplicitStatement.setInt(1, groupId); JDBCUtil.bindNTimes(deleteExplicitStatement, resourceIdsArray, 2); deleteExplicitStatement.executeUpdate(); } catch (SQLException sqle) { log.error("Error removing resources from group[id=" + groupId + "]: ", sqle); throw new ResourceGroupUpdateException("Error removing resources from group[id=" + groupId + "]: " + sqle.getMessage()); } finally { JDBCUtil.safeClose(deleteExplicitStatement); JDBCUtil.safeClose(deleteImplicitStatement); JDBCUtil.safeClose(conn); } return; } @RequiredPermission(Permission.MANAGE_INVENTORY) @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void removeAllResourcesFromGroup(Subject subject, int groupId) throws ResourceGroupDeleteException { Connection conn = null; PreparedStatement explicitStatement = null; PreparedStatement implicitStatement = null; try { conn = rhqDs.getConnection(); explicitStatement = conn .prepareStatement("delete from rhq_resource_group_res_exp_map where resource_group_id = ?"); implicitStatement = conn .prepareStatement("delete from rhq_resource_group_res_imp_map where resource_group_id = ?"); explicitStatement.setInt(1, groupId); implicitStatement.setInt(1, groupId); explicitStatement.executeUpdate(); implicitStatement.executeUpdate(); } catch (SQLException sqle) { log.error("Error removing group resources", sqle); throw new ResourceGroupDeleteException("Error removing group resources: " + sqle.getMessage()); } finally { JDBCUtil.safeClose(explicitStatement); JDBCUtil.safeClose(implicitStatement); JDBCUtil.safeClose(conn); } } @RequiredPermission(Permission.MANAGE_SECURITY) @SuppressWarnings("unchecked") public PageList<ResourceGroup> findAvailableResourceGroupsForRole(Subject subject, int roleId, int[] excludeIds, PageControl pageControl) { pageControl.initDefaultOrderingField("rg.name"); List<Integer> excludeList = ArrayUtils.wrapInList(excludeIds); final String queryName; if ((excludeList != null) && (excludeList.size() != 0)) { queryName = ResourceGroup.QUERY_GET_AVAILABLE_RESOURCE_GROUPS_FOR_ROLE_WITH_EXCLUDES; } else { queryName = ResourceGroup.QUERY_GET_AVAILABLE_RESOURCE_GROUPS_FOR_ROLE; } Query queryCount = PersistenceUtility.createCountQuery(entityManager, queryName); Query query = PersistenceUtility.createQueryWithOrderBy(entityManager, queryName, pageControl); if ((excludeList != null) && (excludeList.size() != 0)) { queryCount.setParameter("excludeIds", excludeList); query.setParameter("excludeIds", excludeList); } queryCount.setParameter("roleId", roleId); query.setParameter("roleId", roleId); long count = (Long) queryCount.getSingleResult(); List<ResourceGroup> groups = query.getResultList(); return new PageList<ResourceGroup>(groups, (int) count, pageControl); } @SuppressWarnings("unchecked") public PageList<ResourceGroup> findResourceGroupByIds(Subject subject, int[] resourceGroupIds, PageControl pageControl) { pageControl.initDefaultOrderingField("rg.name"); List<Integer> groupIdList = ArrayUtils.wrapInList(resourceGroupIds); if ((groupIdList == null) || (groupIdList.size() == 0)) { return new PageList<ResourceGroup>(pageControl); } Query queryCount = null; Query query = null; if (authorizationManager.isInventoryManager(subject)) { final String queryName = ResourceGroup.QUERY_FIND_BY_IDS_admin; queryCount = PersistenceUtility.createCountQuery(entityManager, queryName); query = PersistenceUtility.createQueryWithOrderBy(entityManager, queryName, pageControl); } else { final String queryName = ResourceGroup.QUERY_FIND_BY_IDS; queryCount = PersistenceUtility.createCountQuery(entityManager, queryName); query = PersistenceUtility.createQueryWithOrderBy(entityManager, queryName, pageControl); queryCount.setParameter("subject", subject); query.setParameter("subject", subject); } queryCount.setParameter("ids", groupIdList); query.setParameter("ids", groupIdList); long count = (Long) queryCount.getSingleResult(); List<ResourceGroup> groups = query.getResultList(); return new PageList<ResourceGroup>(groups, (int) count, pageControl); } @RequiredPermission(Permission.MANAGE_INVENTORY) @SuppressWarnings("unchecked") public void updateImplicitGroupMembership(Subject subject, Resource resource) { /* * Get all the groups the parent of this resource is implicitly in. This will tell us which we need to update * (because we added new descendants). */ Query query = entityManager .createNamedQuery(ResourceGroup.QUERY_FIND_IMPLICIT_RECURSIVE_GROUP_IDS_BY_RESOURCE_ID); query.setParameter("id", resource.getParentResource().getId()); List<Integer> implicitRecursiveGroupIds = query.getResultList(); // the resource isn't currently in a group -> no work to do if (implicitRecursiveGroupIds.size() == 0) { return; } /* * BFS-construct the resource tree */ List<Integer> resourceIdsToAdd = new ArrayList<Integer>(); List<Resource> toBeSearched = new LinkedList<Resource>(); toBeSearched.add(resource); while (toBeSearched.size() > 0) { Resource next = toBeSearched.remove(0); resourceIdsToAdd.add(next.getId()); toBeSearched.addAll(next.getChildResources()); } /* * now add this resource and all of its descendants to whatever recursive groups it's parent is already in */ Connection conn = null; PreparedStatement insertImplicitStatement = null; try { conn = rhqDs.getConnection(); for (Integer implicitRecursiveGroupId : implicitRecursiveGroupIds) { /* * do have to worry about whether these resources are already in the explicit resource list because * they are being newly committed to inventory and thus shouldn't be in any group except the work * being done right now. * * also, since we've already computed the toAddResourceIds by recursing down the chain resource passed * to this method, we can just do simple RHQ_RESOURCE_GROUP_RES_IMP_MAP table insertions */ String insertImplicitQueryString = JDBCUtil.transformQueryForMultipleInParameters( ResourceGroup.QUERY_NATIVE_ADD_RESOURCES_TO_GROUP_IMPLICIT, "@@RESOURCE_IDS@@", resourceIdsToAdd.size()); insertImplicitStatement = conn.prepareStatement(insertImplicitQueryString); insertImplicitStatement.setInt(1, implicitRecursiveGroupId); JDBCUtil.bindNTimes(insertImplicitStatement, ArrayUtils.unwrapCollection(resourceIdsToAdd), 2); insertImplicitStatement.executeUpdate(); /* * when automatically updating recursive groups during inventory sync we need to make sure that we also * update the resourceGroup; this will realistically only happen when a recursive group definition is * created, but which initially creates one or more resource groups of size 1; if this happens, the * group will be created as a compatible group, if resources are later added via an inventory sync, and * if this compatible group's membership changes, we need to recalculate the group category * * NOTE: this is no longer true. the group category used to be based off of the explicit membership, * but that assumption was changed for 1.2.0 release so we could support a compatible left-nav * tree with recursive access to descendant for easy authorization. this method only modifies * the implicit resource membership, not the explicit, so setResourceType would be a no-op. */ //setResourceType(implicitRecursiveGroupId); } } catch (Exception e) { throw new ResourceGroupUpdateException("Could not add resource[id=" + resource.getId() + "] to necessary implicit groups", e); } finally { JDBCUtil.safeClose(insertImplicitStatement); JDBCUtil.safeClose(conn); } } /* (non-Javadoc) * @see * org.rhq.enterprise.server.resource.group.ResourceGroupManagerLocal#findResourcesForAutoGroup(org.jboss.on.domain.auth.Subject, * int, int) */ @SuppressWarnings("unchecked") public List<Resource> findResourcesForAutoGroup(Subject subject, int autoGroupParentResourceId, int autoGroupChildResourceTypeId) { List<Resource> resources; try { Query q = entityManager.createNamedQuery(Resource.QUERY_FIND_FOR_AUTOGROUP); q.setParameter("type", autoGroupChildResourceTypeId); q.setParameter("parent", autoGroupParentResourceId); q.setParameter("inventoryStatus", InventoryStatus.COMMITTED); // q.setParameter("subject", subject); resources = q.getResultList(); } catch (PersistenceException pe) { return new ArrayList<Resource>(); } return resources; } /* (non-Javadoc) * @see * org.rhq.enterprise.server.resource.group.ResourceGroupManagerLocal#findResourcesForResourceGroup(org.jboss.on.domain.auth.Subject, * int) */ @SuppressWarnings("unchecked") public List<Resource> findResourcesForResourceGroup(Subject subject, int groupId, GroupCategory category) { ResourceGroup group = getResourceGroupById(subject, groupId, category); Set<Resource> res = group.getExplicitResources(); if (res != null && res.size() > 0) { List<Resource> resources = PersistenceUtility.getHibernateSession(entityManager) .createFilter(res, "where this.inventoryStatus = :inventoryStatus") .setParameter("inventoryStatus", InventoryStatus.COMMITTED).list(); return resources; } else { List<Resource> ret = new ArrayList<Resource>(res.size()); ret.addAll(res); return ret; } } /* (non-Javadoc) * @see * org.rhq.enterprise.server.resource.group.ResourceGroupManagerLocal#findDefinitionsForAutoGroup(org.jboss.on.domain.auth.Subject, * int, int) */ public int[] findDefinitionsForAutoGroup(Subject subject, int autoGroupParentResourceId, int autoGroupChildResourceTypeId, boolean displayTypeSummaryOnly) { int[] ret; try { ResourceType rt = entityManager.find(ResourceType.class, autoGroupChildResourceTypeId); ret = getMeasurementDefinitionIdsForResourceType(rt, displayTypeSummaryOnly); } catch (EntityNotFoundException enfe) { ret = new int[0]; } return ret; } /* (non-Javadoc) * @see * org.rhq.enterprise.server.resource.group.ResourceGroupManagerLocal#findDefinitionsForCompatibleGroup(org.jboss.on.domain.auth.Subject, * int) TODO rework */ public int[] findDefinitionsForCompatibleGroup(Subject subject, int groupId, boolean displayTypeSummaryOnly) { int[] ret = new int[0]; try { ResourceGroup group = getResourceGroupById(subject, groupId, GroupCategory.COMPATIBLE); Set<Resource> resources = group.getExplicitResources(); if ((resources != null) && (resources.size() > 0)) { Resource resource = resources.iterator().next(); ResourceType type = resource.getResourceType(); ret = getMeasurementDefinitionIdsForResourceType(type, displayTypeSummaryOnly); } } catch (ResourceGroupNotFoundException e) { log.debug("Resources for groupID: " + groupId + " not found " + e); } return ret; } @SuppressWarnings("unchecked") private int[] getMeasurementDefinitionIdsForResourceType(ResourceType type, boolean summariesOnly) { String queryString = "" // + "SELECT id " // + " FROM MeasurementDefinition md " // + " WHERE md.resourceType.id = :resourceTypeId "; queryString += " AND md.dataType = :dataType"; if (summariesOnly) { queryString += " AND md.displayType = :dispType"; } // should respect the ordering queryString += " ORDER BY md.displayOrder, md.displayName"; Query q = entityManager.createQuery(queryString); q.setParameter("resourceTypeId", type.getId()); q.setParameter("dataType", DataType.MEASUREMENT); if (summariesOnly) { q.setParameter("dispType", DisplayType.SUMMARY); } List<Integer> res = q.getResultList(); int[] ret = new int[res.size()]; int i = 0; for (Integer r : res) { ret[i++] = r; } return ret; } @SuppressWarnings("unchecked") public ResourceGroup getByGroupDefinitionAndGroupByClause(int groupDefinitionId, String groupByClause) { Query query = entityManager.createNamedQuery(ResourceGroup.QUERY_FIND_BY_GROUP_DEFINITION_AND_EXPRESSION); /* * since oracle interprets empty strings as null, let's cleanse the * groupByClause so that the processing is identical on postgres */ if (groupByClause.equals("")) { groupByClause = null; } query.setParameter("groupDefinitionId", groupDefinitionId); query.setParameter("groupByClause", groupByClause); List<ResourceGroup> groups = query.getResultList(); if (groups.size() == 1) { // fyi, database constraints prevent dups on these two attributes ResourceGroup group = groups.get(0); return group; } else // if ( groups.size() == 0 ) { return null; } } @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void setResourceTypeInNewTx(int resourceGroupId) throws ResourceGroupDeleteException { Query query = entityManager.createNamedQuery(ResourceType.QUERY_GET_EXPLICIT_RESOURCE_TYPE_COUNTS_BY_GROUP); query.setParameter("groupId", resourceGroupId); Subject overlord = subjectManager.getOverlord(); ResourceGroup resourceGroup = getResourceGroupById(overlord, resourceGroupId, null); @SuppressWarnings("unchecked") List<Object> results = query.getResultList(); if (results.size() == 1) { Object[] info = (Object[]) results.get(0); int resourceTypeId = (Integer) info[0]; ResourceType flyWeightType = new ResourceType(); flyWeightType.setId(resourceTypeId); resourceGroup.setResourceType(flyWeightType); } else { if (resourceGroup.getResourceType() != null) { // converting compatible group to mixed group, remove all corresponding compatible constructs removeCompatibleGroupConstructs(overlord, resourceGroup); } resourceGroup.setResourceType(null); } } public int getExplicitGroupMemberCount(int resourceGroupId) { Query countQuery = entityManager .createNamedQuery(Resource.QUERY_FIND_EXPLICIT_RESOURCES_FOR_RESOURCE_GROUP_COUNT_ADMIN); countQuery.setParameter("groupId", resourceGroupId); long count = (Long) countQuery.getSingleResult(); return (int) count; } public int getImplicitGroupMemberCount(int resourceGroupId) { Query countQuery = entityManager .createNamedQuery(Resource.QUERY_FIND_IMPLICIT_RESOURCES_FOR_RESOURCE_GROUP_COUNT_ADMIN); countQuery.setParameter("groupId", resourceGroupId); long count = (Long) countQuery.getSingleResult(); return (int) count; } // note, resourceId and groupId can both be NULL, and so must use the numeric wrapper classes public PageList<ResourceGroupComposite> findResourceGroupComposites(Subject subject, GroupCategory groupCategory, ResourceCategory resourceCategory, String resourceTypeName, String pluginName, String nameFilter, Integer resourceId, Integer groupId, PageControl pc) { if ((resourceId == null) && (groupId == null)) { return new PageList<ResourceGroupComposite>(0, pc); } String query = ResourceGroup.QUERY_NATIVE_FIND_FILTERED_MEMBER; if (authorizationManager.isInventoryManager(subject)) { query = query.replace("%SECURITY_FRAGMENT_JOIN%", ""); query = query.replace("%SECURITY_FRAGMENT_WHERE%", ""); } else { // add the security fragments when not the inventory manager query = query.replace("%SECURITY_FRAGMENT_JOIN%", ResourceGroup.QUERY_NATIVE_FIND_FILTERED_MEMBER_SECURITY_FRAGMENT_JOIN); query = query.replace("%SECURITY_FRAGMENT_WHERE%", ResourceGroup.QUERY_NATIVE_FIND_FILTERED_MEMBER_SECURITY_FRAGMENT_WHERE); } if (resourceId != null) { query = query.replace("%RESOURCE_FRAGMENT_WHERE%", ResourceGroup.QUERY_NATIVE_FIND_FILTERED_MEMBER_RESOURCE_FRAGMENT_WHERE); } else { query = query.replace("%RESOURCE_FRAGMENT_WHERE%", ""); } pc.initDefaultOrderingField("groupName"); pc.truncateOrderingFields(1); // remove all but the primary sort OrderingField primary = pc.getOrderingFields().get(0); String field = primary.getField(); if (field.endsWith("Avail")) { String prefix = field.substring(0, field.length() - 5); String secondaryField = prefix + "Count"; pc.addDefaultOrderingField(secondaryField, primary.getOrdering()); } if (field.equals("groupName") == false) { pc.addDefaultOrderingField("groupName"); } nameFilter = QueryUtility.formatSearchParameter(nameFilter); Connection conn = null; PreparedStatement stmt = null; List<Object[]> rawResults = new ArrayList<Object[]>(); try { conn = rhqDs.getConnection(); if (groupId == null) { // only filter by visibility if the user isn't selecting a group directly if (this.dbType instanceof PostgresqlDatabaseType || this.dbType instanceof H2DatabaseType) { query = query.replace("%GROUP_AND_VISIBILITY_FRAGMENT_WHERE%", "rg.visible = TRUE"); } else if (this.dbType instanceof OracleDatabaseType || this.dbType instanceof SQLServerDatabaseType) { query = query.replace("%GROUP_AND_VISIBILITY_FRAGMENT_WHERE%", "rg.visible = 1"); } else { throw new RuntimeException("Unknown database type: " + this.dbType); } } else { // otherwise filter by the passed groupId query = query.replace("%GROUP_AND_VISIBILITY_FRAGMENT_WHERE%", "rg.id = ?"); } if (this.dbType instanceof PostgresqlDatabaseType) { query = PersistenceUtility.addPostgresNativePagingSortingToQuery(query, pc); } else if (this.dbType instanceof OracleDatabaseType) { query = PersistenceUtility.addOracleNativePagingSortingToQuery(query, pc); } else if (this.dbType instanceof H2DatabaseType) { query = PersistenceUtility.addH2NativePagingSortingToQuery(query, pc); } else if (this.dbType instanceof SQLServerDatabaseType) { query = PersistenceUtility.addSQLServerNativePagingSortingToQuery(query, pc); } else { throw new RuntimeException("Unknown database type: " + this.dbType); } stmt = conn.prepareStatement(query); String search = nameFilter; String resourceCategoryName = resourceCategory == null ? null : resourceCategory.name(); String groupCategoryName = groupCategory == null ? null : groupCategory.name(); int i = 0; if (resourceId != null) { stmt.setInt(++i, resourceId); } if (groupId != null) { stmt.setInt(++i, groupId); } if (search == null) { stmt.setNull(++i, Types.VARCHAR); stmt.setNull(++i, Types.VARCHAR); stmt.setString(++i, QueryUtility.getEscapeCharacter()); stmt.setNull(++i, Types.VARCHAR); stmt.setString(++i, QueryUtility.getEscapeCharacter()); } else { stmt.setString(++i, search); stmt.setString(++i, search); stmt.setString(++i, QueryUtility.getEscapeCharacter()); stmt.setString(++i, search); stmt.setString(++i, QueryUtility.getEscapeCharacter()); } if (resourceTypeName == null) { stmt.setNull(++i, Types.VARCHAR); stmt.setNull(++i, Types.VARCHAR); } else { stmt.setString(++i, resourceTypeName); stmt.setString(++i, resourceTypeName); } if (pluginName == null) { stmt.setNull(++i, Types.VARCHAR); stmt.setNull(++i, Types.VARCHAR); } else { stmt.setString(++i, pluginName); stmt.setString(++i, pluginName); } if (resourceCategoryName == null) { stmt.setNull(++i, Types.VARCHAR); stmt.setNull(++i, Types.VARCHAR); } else { stmt.setString(++i, resourceCategoryName); stmt.setString(++i, resourceCategoryName); } if (groupCategoryName == null) { stmt.setNull(++i, Types.VARCHAR); stmt.setNull(++i, Types.VARCHAR); } else { stmt.setString(++i, groupCategoryName); stmt.setString(++i, groupCategoryName); } if (authorizationManager.isInventoryManager(subject) == false) { stmt.setInt(++i, subject.getId()); } ResultSet rs = stmt.executeQuery(); try { while (rs.next()) { long explicitCount = rs.getLong(1); long explicitDown = rs.getLong(2); long explicitUnknown = rs.getLong(3); long explicitDisabled = rs.getLong(4); long implicitCount = rs.getLong(5); long implicitDown = rs.getLong(6); long implicitUnknown = rs.getLong(7); long implicitDisabled = rs.getLong(8); int groupKey = rs.getInt(9); Object[] next = new Object[] { explicitCount, explicitDown, explicitUnknown, explicitDisabled, implicitCount, implicitDown, implicitUnknown, implicitDisabled, groupKey }; rawResults.add(next); } } finally { rs.close(); } } catch (Throwable t) { log.error("Could not execute groups query [ " + query + " ]: ", t); return new PageList<ResourceGroupComposite>(); } finally { JDBCUtil.safeClose(conn, stmt, null); } Query queryCount; if (authorizationManager.isInventoryManager(subject)) { queryCount = entityManager.createNamedQuery(ResourceGroup.QUERY_FIND_ALL_FILTERED_COUNT_ADMIN); } else { queryCount = entityManager.createNamedQuery(ResourceGroup.QUERY_FIND_ALL_FILTERED_COUNT); queryCount.setParameter("subject", subject); } queryCount.setParameter("groupCategory", groupCategory); queryCount.setParameter("category", resourceCategory); queryCount.setParameter("resourceTypeName", resourceTypeName); queryCount.setParameter("pluginName", pluginName); queryCount.setParameter("search", nameFilter); queryCount.setParameter("resourceId", resourceId); queryCount.setParameter("groupId", groupId); long count = (Long) queryCount.getSingleResult(); List<Integer> groupIds = new ArrayList<Integer>(); for (Object[] row : rawResults) { groupIds.add(((Number) row[8]).intValue()); } Map<Integer, ResourceGroup> groupMap = getIdGroupMap(groupIds); List<ResourceGroupComposite> results = new ArrayList<ResourceGroupComposite>(rawResults.size()); int i = 0; for (Object[] row : rawResults) { long explicitCount = (Long) row[0]; long explicitDown = (Long) row[1]; long explicitUnknown = (Long) row[2]; long explicitDisabled = (Long) row[3]; long implicitCount = (Long) row[4]; long implicitDown = (Long) row[5]; long implicitUnknown = (Long) row[6]; long implicitDisabled = (Long) row[7]; ResourceGroup group = groupMap.get(groupIds.get(i++)); ResourceType type = group.getResourceType(); ResourceFacets facets; if (type == null) { // mixed group facets = ResourceFacets.NONE; } else { // compatible group facets = resourceTypeManager.getResourceFacets(type.getId()); } ResourceGroupComposite composite = new ResourceGroupComposite(explicitCount, explicitDown, explicitUnknown, explicitDisabled, implicitCount, implicitDown, implicitUnknown, implicitDisabled, group, facets); Set<Permission> perms = authorizationManager.getImplicitGroupPermissions(subject, group.getId()); composite.setResourcePermission(new ResourcePermission(perms)); results.add(composite); } return new PageList<ResourceGroupComposite>(results, (int) count, pc); } @SuppressWarnings("unchecked") private Map<Integer, ResourceGroup> getIdGroupMap(List<Integer> groupIds) { if (groupIds == null || groupIds.size() == 0) { return new HashMap<Integer, ResourceGroup>(); } Query query = entityManager.createNamedQuery(ResourceGroup.QUERY_FIND_BY_IDS_admin); query.setParameter("ids", groupIds); List<ResourceGroup> groups = query.getResultList(); Map<Integer, ResourceGroup> results = new HashMap<Integer, ResourceGroup>(); for (ResourceGroup group : groups) { results.put(group.getId(), group); } return results; } @SuppressWarnings("unchecked") // returns a subset of the passed groupIds which no longer exist public List<Integer> findDeletedResourceGroupIds(int[] possibleGroupIds) { List<Integer> groupIds = ArrayUtils.wrapInList(possibleGroupIds); if (groupIds == null || groupIds.size() == 0) { return Collections.emptyList(); } String queryString = "" // + "SELECT rg.id " // + " FROM ResourceGroup rg " // + " WHERE rg.id IN ( :groupIds ) "; Query query = entityManager.createQuery(queryString); query.setParameter("groupIds", groupIds); List<Integer> validIds = query.getResultList(); groupIds.removeAll(validIds); return groupIds; } public void setAssignedResources(Subject subject, int groupId, int[] resourceIds, boolean setType) throws ResourceGroupDeleteException { ResourceGroup group = entityManager.find(ResourceGroup.class, groupId); if (group.isPrivateGroup()) { List<Integer> ids = new ArrayList<Integer>(resourceIds.length); for (int id : resourceIds) { ids.add(id); } if (!authorizationManager.canViewResources(subject, ids)) { throw new PermissionException("Subject [" + subject.getName() + "] does not have VIEW permission for all specified resources."); } } else { if (!authorizationManager.isInventoryManager(subject)) { throw new PermissionException("Subject [" + subject.getName() + "] is not authorized for [ MANAGE_INVENTORY ]. Required for changing group membership "); } } List<Integer> currentMembers = resourceManager.findExplicitResourceIdsByResourceGroup(groupId); List<Integer> newMembers = ArrayUtils.wrapInList(resourceIds); // members needing addition newMembers.removeAll(currentMembers); if (newMembers.size() > 0) { addResourcesToGroup(subjectManager.getOverlord(), groupId, ArrayUtils.unwrapCollection(newMembers), false); } List<Integer> extraMembers = new ArrayList<Integer>(currentMembers); // members needing removal extraMembers.removeAll(ArrayUtils.wrapInList(resourceIds)); if (extraMembers.size() > 0) { removeResourcesFromGroup(subjectManager.getOverlord(), groupId, ArrayUtils.unwrapCollection(extraMembers), false); } // As a result of the membership change ensure that the group type is set correctly. if (setType) { setResourceTypeInNewTx(groupId); } } public void setAssignedResourceGroupsForResource(Subject subject, int resourceId, int[] resourceGroupIds, boolean setType) throws ResourceGroupDeleteException { Resource resource = entityManager.find(Resource.class, resourceId); Set<ResourceGroup> currentGroups = resource.getExplicitGroups(); List<Integer> currentGroupIds = new ArrayList<Integer>(currentGroups.size()); for (ResourceGroup currentGroup : currentGroups) { currentGroupIds.add(currentGroup.getId()); } int[] resourceIdArr = new int[] { resourceId }; List<Integer> addedGroupIds = ArrayUtils.wrapInList(resourceGroupIds); addedGroupIds.removeAll(currentGroupIds); for (Integer addedGroupId : addedGroupIds) { addResourcesToGroup(subject, addedGroupId, resourceIdArr); // As a result of the membership change ensure that the group type is set correctly. if (setType) { setResourceTypeInNewTx(addedGroupId); } } List<Integer> removedGroupIds = new ArrayList<Integer>(currentGroupIds); // groups needing removal removedGroupIds.removeAll(ArrayUtils.wrapInList(resourceGroupIds)); for (Integer removedGroupId : removedGroupIds) { removeResourcesFromGroup(subject, removedGroupId, resourceIdArr); // As a result of the membership change ensure that the group type is set correctly. if (setType) { setResourceTypeInNewTx(removedGroupId); } } } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Remote interface impl // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! public ResourceGroup getResourceGroup( // Subject subject, // int groupId) { return getResourceGroupById(subject, groupId, null); } @SuppressWarnings("unchecked") public ResourceGroupComposite getResourceGroupComposite(Subject subject, int groupId) { // Auto cluster backing groups have a special security allowance that let's a non-inventory-manager // view them even if they aren't directly in one of their roles, by nature of the fact that the user has // the parent cluster group in one of its roles. if (!authorizationManager.canViewGroup(subject, groupId)) { throw new PermissionException("You do not have permission to view this resource group"); } // Could do this with two GROUP BY queries but we'll go with one RT to the db and hope that's best, despite // all the subselects. String queryString = "SELECT \n" // + " (SELECT count(er) " // Total explicit + " FROM ResourceGroup g JOIN g.explicitResources er where er.inventoryStatus = 'COMMITTED' and g.id = :groupId),\n" + " (SELECT count(er) " // DOWN explicit + " FROM ResourceGroup g JOIN g.explicitResources er where er.inventoryStatus = 'COMMITTED' and g.id = :groupId" + " AND er.currentAvailability.availabilityType = 0 ),\n" + " (SELECT count(er) " // UNKNOWN explicit + " FROM ResourceGroup g JOIN g.explicitResources er where er.inventoryStatus = 'COMMITTED' and g.id = :groupId" + " AND er.currentAvailability.availabilityType = 2 ),\n" + " (SELECT count(er) " // DISABLED explicit + " FROM ResourceGroup g JOIN g.explicitResources er where er.inventoryStatus = 'COMMITTED' and g.id = :groupId" + " AND er.currentAvailability.availabilityType = 3 ),\n" + " (SELECT count(ir) " // Total implicit + " FROM ResourceGroup g JOIN g.implicitResources ir where ir.inventoryStatus = 'COMMITTED' and g.id = :groupId),\n" + " (SELECT count(ir) " // DOWN implicit + " FROM ResourceGroup g JOIN g.implicitResources ir where ir.inventoryStatus = 'COMMITTED' and g.id = :groupId" + " AND ir.currentAvailability.availabilityType = 0 ),\n" + " (SELECT count(ir) " // UNKNOWN implicit + " FROM ResourceGroup g JOIN g.implicitResources ir where ir.inventoryStatus = 'COMMITTED' and g.id = :groupId" + " AND ir.currentAvailability.availabilityType = 2 ),\n" + " (SELECT count(ir) " // DISABLED implicit + " FROM ResourceGroup g JOIN g.implicitResources ir where ir.inventoryStatus = 'COMMITTED' and g.id = :groupId" + " AND ir.currentAvailability.availabilityType = 3 )\n," + " g " + "FROM ResourceGroup g where g.id = :groupId"; Query query = entityManager.createQuery(queryString); query.setParameter("groupId", groupId); List<Object[]> results = query.getResultList(); if (results.size() == 0) { throw new ResourceGroupNotFoundException(groupId); } Object[] data = results.get(0); ResourceGroup group = (ResourceGroup) data[8]; ResourceType type = group.getResourceType(); ResourceFacets facets; if (type == null) { // mixed group facets = ResourceFacets.NONE; } else { // compatible group facets = resourceTypeManager.getResourceFacets(group.getResourceType().getId()); } ResourceGroupComposite composite = null; if (((Number) data[4]).longValue() > 0) { long explicitCount = ((Number) data[0]).longValue(); long explicitDown = ((Number) data[1]).longValue(); long explicitUnknown = ((Number) data[2]).longValue(); long explicitDisabled = ((Number) data[3]).longValue(); long implicitCount = ((Number) data[4]).longValue(); long implicitDown = ((Number) data[5]).longValue(); long implicitUnknown = ((Number) data[6]).longValue(); long implicitDisabled = ((Number) data[7]).longValue(); // In the past we had only DOWN/0 and UP/1 avails/ordinal and and the avails were just averages. // Now we have DISABLED and UNKNOWN. So group avail is done differently, instead of a ratio of // of UP vs DOWN it is now handled with counts. This is handled in the composite. composite = new ResourceGroupComposite(explicitCount, explicitDown, explicitUnknown, explicitDisabled, implicitCount, implicitDown, implicitUnknown, implicitDisabled, group, facets); } else { composite = new ResourceGroupComposite(0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, group, facets); } return composite; } @SuppressWarnings("unchecked") // if a user doesn't have MANAGE_SETTINGS, they can only see groups under their own roles public PageList<ResourceGroup> findResourceGroupsForRole(Subject subject, int roleId, PageControl pc) { pc.initDefaultOrderingField("rg.name"); String queryName = null; if (authorizationManager.hasGlobalPermission(subject, Permission.MANAGE_SETTINGS)) { queryName = ResourceGroup.QUERY_GET_RESOURCE_GROUPS_ASSIGNED_TO_ROLE_admin; } else { queryName = ResourceGroup.QUERY_GET_RESOURCE_GROUPS_ASSIGNED_TO_ROLE; } Query queryCount = PersistenceUtility.createCountQuery(entityManager, queryName); Query query = PersistenceUtility.createQueryWithOrderBy(entityManager, queryName, pc); if (authorizationManager.hasGlobalPermission(subject, Permission.MANAGE_SETTINGS) == false) { queryCount.setParameter("subjectId", subject.getId()); query.setParameter("subjectId", subject.getId()); } queryCount.setParameter("id", roleId); query.setParameter("id", roleId); long count = (Long) queryCount.getSingleResult(); List<ResourceGroup> groups = query.getResultList(); return new PageList<ResourceGroup>(groups, (int) count, pc); } @RequiredPermission(Permission.MANAGE_INVENTORY) public void setRecursive( // Subject subject, // int groupId, // boolean isRecursive) { ResourceGroup group = entityManager.find(ResourceGroup.class, groupId); if (group == null) { throw new ResourceGroupNotFoundException(groupId); } updateResourceGroup(subject, group, isRecursive ? RecursivityChangeType.AddedRecursion : RecursivityChangeType.RemovedRecursion); } public PageList<ResourceGroup> findResourceGroupsByCriteria(Subject subject, ResourceGroupCriteria criteria) { CriteriaAuthzType authzType = getCriteriaAuthzType(subject, criteria); CriteriaQueryGenerator generator = getCriteriaQueryGenerator(subject, criteria, authzType); CriteriaQueryRunner<ResourceGroup> queryRunner = new CriteriaQueryRunner<ResourceGroup>(criteria, generator, entityManager); PageList<ResourceGroup> result = queryRunner.execute(); return result; } /** * This method adheres to all of the regular semantics of {@link Criteria}-based queries. In other words, * all of the methods on the {@link Criteria} object - including paging, sorting, filtering, fetching - will * work with this method. The only thing that differs is the ResultSet which, instead of being a collection * of {@link ResourceGroup} objects is a collection of {@link ResourceGroupComposite} objects. * * The extended data in the composite object, however, is treated differently: * * 1) It is always fetched * 2) It can not be a candidate for filtering * 3) It must be sorted by using the zero-based positional ordinal within the projection * * This method offers 2 new aggregates that you can sort on. The * * explicitCount (ordinal 0) - the count of the number of children in the group * implicitCount (ordinal 2) - the count of the number of descendents in the group */ @SuppressWarnings("unchecked") public PageList<ResourceGroupComposite> findResourceGroupCompositesByCriteria(Subject subject, ResourceGroupCriteria criteria) { CriteriaAuthzType authzType = getCriteriaAuthzType(subject, criteria); // first select groups only, then we'll select composites PageList<ResourceGroup> resourceGroups = findResourceGroupsByCriteria(subject, criteria); if (resourceGroups.isEmpty()) { return new PageList<ResourceGroupComposite>(); } // put group IDs to list, so we can query composites for those qroups only List<Integer> groupIds = new ArrayList<Integer>(resourceGroups.size()); // create a map of groupId, it's index - in the end we'll need to re-order composites and respect ordering from criteria final Map<Integer, Integer> groupIndexes = new HashMap<Integer, Integer>(resourceGroups.size()); int index = 0; for (ResourceGroup rg : resourceGroups) { groupIds.add(rg.getId()); groupIndexes.put(rg.getId(), index); index++; } // JPA queries does not allow to nest SELECT (ie Select x from (Select y) ..) // we'd need to run 2 (or 3) queries // first JOIN ON implicitResources // then JOIN ON explicitResources // then (AUTO_CLUSTER) join on roles/permissions // we're reusing ResourceGroupComposite for selected data String compositeProjection = "" + " new org.rhq.core.domain.resource.group.composite.ResourceGroupComposite( " + " SUM(CASE WHEN res.inventoryStatus = 'COMMITTED' THEN 1 ELSE 0 END), " + " SUM(CASE WHEN res.inventoryStatus = 'COMMITTED' AND avail.availabilityType = 0 THEN 1 ELSE 0 END), " + " SUM(CASE WHEN res.inventoryStatus = 'COMMITTED' AND avail.availabilityType = 2 THEN 1 ELSE 0 END), " + " SUM(CASE WHEN res.inventoryStatus = 'COMMITTED' AND avail.availabilityType = 3 THEN 1 ELSE 0 END), " + " 0L,0L, 0L, 0L, %alias%) "; String compositeFromCause = criteria.getPersistentClass().getSimpleName() + " %alias% " + " LEFT JOIN %alias%.%membership%Resources res " + " LEFT JOIN res.currentAvailability avail "; String permissionsProjection = null; String permissionsFromCause = null; switch (authzType) { case NONE: case SUBJECT_OWNED: break; case ROLE_OWNED: case AUTO_CLUSTER: permissionsProjection = "" + " new org.rhq.core.domain.resource.group.composite.ResourceGroupComposite( " + " 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, %alias%, " + " SUM(CASE WHEN s.id = %subjectId% and p = 8 THEN 1 ELSE 0 END), " // MANAGE_MEASUREMENTS + " SUM(CASE WHEN s.id = %subjectId% and p = 4 THEN 1 ELSE 0 END), " // MODIFY_RESOURCE + " SUM(CASE WHEN s.id = %subjectId% and p = 10 THEN 1 ELSE 0 END), " // CONTROL + " SUM(CASE WHEN s.id = %subjectId% and p = 7 THEN 1 ELSE 0 END), " // MANAGE_ALERTS + " SUM(CASE WHEN s.id = %subjectId% and p = 14 THEN 1 ELSE 0 END), " // MANAGE_EVENTS + " SUM(CASE WHEN s.id = %subjectId% and p = 13 THEN 1 ELSE 0 END), " // CONFIGURE_READ + " SUM(CASE WHEN s.id = %subjectId% and p = 11 THEN 1 ELSE 0 END), " // CONFIGURE_WRITE + " SUM(CASE WHEN s.id = %subjectId% and p = 9 THEN 1 ELSE 0 END), " // MANAGE_CONTENT + " SUM(CASE WHEN s.id = %subjectId% and p = 6 THEN 1 ELSE 0 END), " // CREATE_CHILD_RESOURCES + " SUM(CASE WHEN s.id = %subjectId% and p = 5 THEN 1 ELSE 0 END), " // DELETE_RESOURCES + " SUM(CASE WHEN s.id = %subjectId% and p = 16 THEN 1 ELSE 0 END) " // MANAGE_DRIFT + ") "; permissionsProjection = permissionsProjection.replace("%subjectId%", String.valueOf(subject.getId())); permissionsFromCause = criteria.getPersistentClass().getSimpleName() + " %alias% " + " LEFT JOIN %permAlias%.roles r" + " LEFT JOIN r.subjects s" + " LEFT JOIN r.permissions p"; break; default: throw new IllegalStateException("Unexpected CriteriaAuthzType: " + authzType); } String alias = criteria.getAlias(); String permAlias = alias + ((authzType == CriteriaAuthzType.AUTO_CLUSTER) ? ".clusterResourceGroup" : ""); String from = compositeFromCause .replace("%membership%", "implicit") .replace("%alias%", alias) .replace("%permAlias%", permAlias); String projection = compositeProjection.replace("%alias%", alias); String compositeQuery = "SELECT " + projection + " FROM " + from + " WHERE " + alias + ".id IN ( :ids ) GROUP BY " + alias + ".id"; // query implicit group members List<ResourceGroupComposite> implicitResults = entityManager.createQuery(compositeQuery) .setParameter("ids", groupIds).getResultList(); from = compositeFromCause.replace("%membership%", "explicit").replace("%alias%", alias) .replace("%permAlias%", permAlias); compositeQuery = "SELECT " + projection + " FROM " + from + " WHERE " + alias + ".id IN ( :ids ) GROUP BY " + alias + ".id"; // query explicit members List<ResourceGroupComposite> explicitResults = entityManager.createQuery(compositeQuery) .setParameter("ids", groupIds).getResultList(); List<ResourceGroupComposite> permissionResults = null; if (permissionsProjection != null) { from = permissionsFromCause.replace("%alias%", alias).replace("%permAlias%", permAlias); projection = permissionsProjection.replaceFirst("%alias%", alias); compositeQuery = "SELECT " + projection + " FROM " + from + " WHERE " + alias + ".id IN ( :ids ) GROUP BY " + alias + ".id"; // query permissions permissionResults = entityManager.createQuery(compositeQuery).setParameter("ids", groupIds).getResultList(); } // now "join" results together List<ResourceGroupComposite> results = new ArrayList<ResourceGroupComposite>(explicitResults.size()); // some groups could be being added/removed in the meantime // we must join it using resourceGroup ID // map ResourceGroupID and [implicitComposite, explicitComposite, permComposite] Map<Integer, List<ResourceGroupComposite>> joinMap = new HashMap<Integer, List<ResourceGroupComposite>>(); for (ResourceGroupComposite c : implicitResults) { if (!joinMap.containsKey(c.getResourceGroup().getId())) { joinMap.put(c.getResourceGroup().getId(), new ArrayList<ResourceGroupComposite>(3)); } joinMap.get(c.getResourceGroup().getId()).add(c); } for (ResourceGroupComposite c : explicitResults) { // Discard them early if (joinMap.containsKey(c.getResourceGroup().getId())) { joinMap.get(c.getResourceGroup().getId()).add(c); } } if (permissionResults != null) { for (ResourceGroupComposite c : permissionResults) { if (joinMap.containsKey(c.getResourceGroup().getId())) { joinMap.get(c.getResourceGroup().getId()).add(c); } } // produce results (include permissionResults) for (List<ResourceGroupComposite> list : joinMap.values()) { if (list.size() < 3) { continue; // we did not fully select this composite with all 3 queries, ignore it } ResourceGroupComposite composite = createComposite(list.get(0), list.get(1), list.get(2)); results.add(composite); } } else { // prouduce results, but exclude permissionResults for (List<ResourceGroupComposite> list : joinMap.values()) { if (list.size() < 2) { continue; // we did not fully select this composite with all 2 queries, ignore it } ResourceGroupComposite composite = createComposite(list.get(0), list.get(1), null); results.add(composite); } } // finally sort results by the same order we got in resoureGroups PageList Collections.sort(results, new Comparator<ResourceGroupComposite>() { @Override public int compare(ResourceGroupComposite arg0, ResourceGroupComposite arg1) { Integer index0 = groupIndexes.get(arg0.getResourceGroup().getId()); Integer index1 = groupIndexes.get(arg1.getResourceGroup().getId()); // in theory we may not find index in map, in case we've selected more composites than groups if (index0 != null && index1 != null) { return index0.compareTo(index1); } return 0; } }); // and transform it to PageList and assign original ResourceGroups PageList<ResourceGroupComposite> resultPageList = new PageList<ResourceGroupComposite>(resourceGroups.getTotalSize(), resourceGroups.getPageControl()); index = 0; for (ResourceGroupComposite composite : results) { ResourceGroup fetched = resourceGroups.get(index); resultPageList.add(createComposite(composite, fetched)); index++; } resultPageList = getAuthorizedGroupComposites(subject, authzType, resultPageList); for (ResourceGroupComposite composite : resultPageList) { ResourceGroup group = composite.getResourceGroup(); ResourceType type = group.getResourceType(); ResourceFacets facets = (type != null) ? resourceTypeManager.getResourceFacets(type.getId()) : ResourceFacets.NONE; composite.setResourceFacets(facets); } return resultPageList; } private ResourceGroupComposite createComposite(ResourceGroupComposite original, ResourceGroup group) { return new ResourceGroupComposite( original.getExplicitCount(), original.getExplicitDown(), original.getExplicitUnknown(), original.getExplicitDisabled(), original.getImplicitCount(), original.getImplicitDown(), original.getImplicitUnknown(), original.getImplicitDisabled(), group, null, original.getResourcePermission() ); } private ResourceGroupComposite createComposite(ResourceGroupComposite implicit, ResourceGroupComposite explicit, ResourceGroupComposite permission) { return new ResourceGroupComposite( explicit.getExplicitCount(), explicit.getExplicitDown(), explicit.getExplicitUnknown(), explicit.getExplicitDisabled(), implicit.getExplicitCount(), implicit.getExplicitDown(), implicit.getExplicitUnknown(), implicit.getExplicitDisabled(), implicit.getResourceGroup(), null, permission != null ? permission.getResourcePermission() : new ResourcePermission() ); } private enum CriteriaAuthzType { // inv manager / no auth required NONE, // standard role-subject-group auth ROLE_OWNED, // private group auth SUBJECT_OWNED, // auto cluster backing group AUTO_CLUSTER } private CriteriaAuthzType getCriteriaAuthzType(Subject subject, ResourceGroupCriteria criteria) { Set<Permission> globalUserPerms = authorizationManager.getExplicitGlobalPermissions(subject); if (criteria.isSecurityManagerRequired() && !globalUserPerms.contains(Permission.MANAGE_SECURITY)) { throw new PermissionException("Subject [" + subject.getName() + "] requires SecurityManager permission for requested query criteria."); } boolean isInventoryManager = globalUserPerms.contains(Permission.MANAGE_INVENTORY); if (criteria.isInventoryManagerRequired() && !isInventoryManager) { throw new PermissionException("Subject [" + subject.getName() + "] requires InventoryManager permission for requested query criteria."); } CriteriaAuthzType result = CriteriaAuthzType.ROLE_OWNED; if (isInventoryManager) { result = CriteriaAuthzType.NONE; } else if (criteria.isFilterPrivate()) { result = CriteriaAuthzType.SUBJECT_OWNED; } else if (!criteria.isFilterVisible()) { result = CriteriaAuthzType.AUTO_CLUSTER; } return result; } private CriteriaQueryGenerator getCriteriaQueryGenerator(Subject subject, ResourceGroupCriteria criteria, CriteriaAuthzType authzType) { // if we're searching for private groups set the subject filter to the current user's subjectId. // setting it here prevents the caller from spoofing a different user. This is why the subject and // private filters are different. The subject filter can specify any user and therefore requires // inventory manager. if (criteria.isFilterPrivate()) { criteria.addFilterPrivate(null); criteria.addFilterSubjectId(subject.getId()); } CriteriaQueryGenerator generator = new CriteriaQueryGenerator(subject, criteria); if (authzType != CriteriaAuthzType.NONE) { generator.setAuthorizationResourceFragment(CriteriaQueryGenerator.AuthorizationTokenType.GROUP, null, subject.getId()); } return generator; } private PageList<ResourceGroupComposite> getAuthorizedGroupComposites(Subject subject, CriteriaAuthzType authzType, PageList<ResourceGroupComposite> groupComposites) { switch (authzType) { case NONE: // leave resourcePermissions unset on the assumption that it will not be checked for inv managers break; case ROLE_OWNED: // the permissions are already set by the query projection break; case AUTO_CLUSTER: // the permissions are already set by the query projection break; case SUBJECT_OWNED: Iterator<ResourceGroupComposite> iterator = groupComposites.iterator(); while (iterator.hasNext()) { ResourceGroupComposite groupComposite = iterator.next(); ResourceGroup group = groupComposite.getResourceGroup(); Subject groupOwner = group.getSubject(); if (null != groupOwner) { // private group, we need to set the group resource permissions since we couldn't do it in // the projection. groupComposite.setResourcePermission(new ResourcePermission(authorizationManager .getExplicitGroupPermissions(groupOwner, group.getId()))); } else { throw new IllegalStateException("Unexpected group, not subject owned: " + groupComposite); } } break; default: throw new IllegalStateException("Unexpected CriteriaAuthzType: " + authzType); } return groupComposites; } @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) @RequiredPermission(Permission.MANAGE_INVENTORY) public void uninventoryMembers(Subject subject, int groupId) { List<Integer> resourceMemberIds = resourceManager.findExplicitResourceIdsByResourceGroup(groupId); for (int doomedResourceId : resourceMemberIds) { resourceManager.uninventoryResource(subject, doomedResourceId); } } public void updateResourceGroupName(Subject subject, int groupId, String name) { if (name == null) { throw new IllegalArgumentException("Group name cannot be null."); } ResourceGroup group = getResourceGroupToBeModified(subject, groupId); group.setName(name); group.setMtime(System.currentTimeMillis()); } public void updateResourceGroupDescription(Subject subject, int groupId, String description) { ResourceGroup group = getResourceGroupToBeModified(subject, groupId); group.setDescription(description); group.setMtime(System.currentTimeMillis()); } public void updateResourceGroupLocation(Subject subject, int groupId, String location) { ResourceGroup group = getResourceGroupToBeModified(subject, groupId); group.setDescription(location); group.setMtime(System.currentTimeMillis()); } private ResourceGroup getResourceGroupToBeModified(Subject subject, int groupId) { ResourceGroup group = entityManager.find(ResourceGroup.class, groupId); if (group == null) { throw new ResourceGroupNotFoundException(groupId); } if (!authorizationManager.hasGroupPermission(subject, Permission.MODIFY_RESOURCE, groupId)) { throw new PermissionException("User [" + subject + "] does not have permission to modify Resource group with id [" + groupId + "]."); } return group; } }