/*
* 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 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;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.Stack;
import java.util.TreeSet;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.authz.Permission;
import org.rhq.core.domain.criteria.ResourceTypeCriteria;
import org.rhq.core.domain.operation.OperationDefinition;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.MissingPolicy;
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.ResourceTypeTemplateCountComposite;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.core.domain.util.PageList;
import org.rhq.core.util.collection.ArrayUtils;
import org.rhq.enterprise.server.RHQConstants;
import org.rhq.enterprise.server.authz.AuthorizationManagerLocal;
import org.rhq.enterprise.server.authz.RequiredPermission;
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.ResourceType}s.
*
* @author Ian Springer
* @author Joseph Marques
*/
@Stateless
public class ResourceTypeManagerBean implements ResourceTypeManagerLocal, ResourceTypeManagerRemote {
private final Log log = LogFactory.getLog(ResourceTypeManagerBean.class);
// TODO: Add a getResourceTypeByResourceId method.
@PersistenceContext(unitName = RHQConstants.PERSISTENCE_UNIT_NAME)
EntityManager entityManager;
@EJB
private AuthorizationManagerLocal authorizationManager;
@EJB
private ResourceManagerLocal resourceManager;
@EJB
private ResourceTypeManagerLocal resourceTypeManager; // self-reference
@Override
@RequiredPermission(Permission.MANAGE_INVENTORY)
@TransactionAttribute(TransactionAttributeType.NEVER)
public void setResourceTypeIgnoreFlagAndUninventoryResources(Subject subject, int resourceTypeId, boolean ignoreFlag) {
// if we are to ignore the type, we first must successfully uninventory all resources of that type
if (ignoreFlag == true) {
resourceManager.uninventoryResourcesOfResourceType(subject, resourceTypeId);
}
// now we simply flip the ignore setting on the type
resourceTypeManager.setResourceTypeIgnoreFlag(subject, resourceTypeId, ignoreFlag);
return;
}
@Override
@RequiredPermission(Permission.MANAGE_INVENTORY)
public void setResourceTypeIgnoreFlag(Subject subject, int resourceTypeId, boolean ignoreFlag) {
ResourceType resourceType = getResourceTypeById(subject, resourceTypeId);
// don't bother to do anything if the type's ignore flag is already set to the value the caller wants
if (resourceType.isIgnored() == ignoreFlag) {
return;
}
log.info("Changing ignore flag to [" + ignoreFlag + "] for resource type [" + resourceType.getName()
+ "] with id=[" + resourceTypeId + "]");
resourceType.setIgnored(ignoreFlag);
return;
}
@Override
@RequiredPermission(Permission.MANAGE_INVENTORY)
public void setResourceTypeMissingPolicy(Subject subject, int resourceTypeId, MissingPolicy policy) {
ResourceType resourceType = getResourceTypeById(subject, resourceTypeId);
// don't bother to do anything if the type's flag is already set to the value the caller wants
if (resourceType.getMissingPolicy().equals(policy)) {
return;
}
log.info("Changing Missing policy to [" + policy.name() + "] for resource type [" + resourceType.getName()
+ "] with id=[" + resourceTypeId + "]");
resourceType.setMissingPolicy(policy);
return;
}
@Override
public ResourceType getResourceTypeById(Subject subject, int id) throws ResourceTypeNotFoundException {
// this operation does not need to be secured; types are data side-effects of authorized resources
ResourceType resourceType = entityManager.find(ResourceType.class, id);
if (resourceType == null) {
throw new ResourceTypeNotFoundException("Resource type with id [" + id + "] does not exist.");
}
return resourceType;
}
// remote
@Override
public ResourceType getResourceTypeByNameAndPlugin(Subject subject, String name, String plugin) {
return getResourceTypeByNameAndPlugin(name, plugin);
}
// local
@Override
public ResourceType getResourceTypeByNameAndPlugin(String name, String plugin) {
try {
Query query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_BY_NAME_AND_PLUGIN);
query.setParameter("name", name);
query.setParameter("plugin", plugin);
return (ResourceType) query.getSingleResult();
} catch (NoResultException nre) {
return null;
}
}
@Override
@SuppressWarnings("unchecked")
public List<ResourceType> getChildResourceTypes(Subject subject, ResourceType parent) {
Query query = null;
query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_CHILDREN);
query.setParameter("resourceTypeId", parent.getId());
List<ResourceType> results = query.getResultList();
return results;
}
@Override
@SuppressWarnings("unchecked")
public List<ResourceType> getChildResourceTypesByCategory(Subject subject, Resource parentResource,
ResourceCategory category) {
Query query;
if (authorizationManager.isInventoryManager(subject)) {
query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_CHILDREN_BY_CATEGORY_admin);
} else {
query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_CHILDREN_BY_CATEGORY);
// TODO: Uncomment the below line once the query supports authz.
//query.setParameter(5, subject.getId());
}
query.setParameter(1, parentResource.getId());
query.setParameter(2, category.name());
query.setParameter(3, parentResource.getId());
query.setParameter(4, category.name());
List<ResourceType> results = query.getResultList();
return results;
}
@Override
@SuppressWarnings("unchecked")
public List<ResourceType> getUtilizedChildResourceTypesByCategory(Subject subject, Resource parentResource,
ResourceCategory category) {
Query query;
if (authorizationManager.isInventoryManager(subject)) {
query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_UTILIZED_CHILDREN_BY_CATEGORY_admin);
} else {
query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_UTILIZED_CHILDREN_BY_CATEGORY);
query.setParameter("subject", subject);
}
query.setParameter("parentResource", parentResource);
query.setParameter("category", category);
query.setParameter("inventoryStatus", InventoryStatus.COMMITTED);
List<ResourceType> results = query.getResultList();
return results;
}
/**
* Obtain ResourceTypes that match a given category or all if category is null.
*
* @param subject subject of the caller
* @param category the category to check for. If this is null, entries from all categories will be returned.
*
* @return a List of ResourceTypes
*
* @see ResourceCategory
*/
@Override
@SuppressWarnings("unchecked")
public List<ResourceType> getAllResourceTypesByCategory(Subject subject, ResourceCategory category) {
Query query;
if (category != null) {
query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_BY_CATEGORY);
query.setParameter("category", category);
} else {
query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_ALL);
}
List<ResourceType> res = query.getResultList();
return res;
}
@Override
public List<String> getUtilizedResourceTypeNamesByCategory(Subject subject, ResourceCategory category,
String nameFilter, String pluginName) {
List<ResourceType> types = getUtilizeTypes_helper(subject, category, nameFilter, pluginName);
List<String> typeNames = new ArrayList<String>();
for (ResourceType type : types) {
if (typeNames.contains(type.getName()) == false) {
typeNames.add(type.getName());
}
}
return typeNames;
}
@Override
public List<ResourceType> getUtilizedResourceTypesByCategory(Subject subject, ResourceCategory category,
String nameFilter) {
List<ResourceType> types = getUtilizeTypes_helper(subject, category, nameFilter, null);
return types;
}
@SuppressWarnings("unchecked")
private List<ResourceType> getUtilizeTypes_helper(Subject subject, ResourceCategory category, String nameFilter,
String pluginName) {
Query query = null;
if (authorizationManager.isInventoryManager(subject)) {
query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_UTILIZED_BY_CATEGORY_admin);
} else {
query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_UTILIZED_BY_CATEGORY);
query.setParameter("subject", subject);
}
query.setParameter("category", category);
query.setParameter("nameFilter", QueryUtility.formatSearchParameter(nameFilter));
query.setParameter("escapeChar", QueryUtility.getEscapeCharacter());
query.setParameter("pluginName", pluginName);
query.setParameter("inventoryStatus", InventoryStatus.COMMITTED);
List<ResourceType> results = query.getResultList();
return results;
}
/**
* Return all ResourceTypes that are children of the passed ones
*
* @param types List of ResourceTypes
*
* @return List of ResourceTypes. If nothing is found, then this list is empty
*/
@Override
@SuppressWarnings("unchecked")
public Map<Integer, SortedSet<ResourceType>> getChildResourceTypesForResourceTypes(List<ResourceType> types) {
// nothing to do
if ((types == null) || (types.size() == 0)) {
return new HashMap<Integer, SortedSet<ResourceType>>();
}
// save array with inputs as we need this later
List<Integer> ids = new ArrayList<Integer>(types.size());
for (ResourceType type : types) {
ids.add(type.getId());
}
Query q = entityManager.createNamedQuery(ResourceType.FIND_CHILDREN_BY_PARENT);
q.setParameter("resourceType", types);
List<ResourceType> childResourceTypes = q.getResultList();
/*
* We have the original children of the input. Now get their children as well until we are done. We need to pay
* attention a) not to add duplicates. This is done by using a Set later. b) prevent running in cycles. This
* should not happen if the ResourceTypes are correctly added the system, but one never knows
*/
List<ResourceType> newChildResourceTypes = new ArrayList<ResourceType>();
newChildResourceTypes.addAll(childResourceTypes);
int numberOfChildrenToFetch = childResourceTypes.size();
while (numberOfChildrenToFetch > 0) {
if (log.isTraceEnabled()) {
log.trace("*** getting children for " + newChildResourceTypes);
}
q.setParameter("resourceType", newChildResourceTypes);
List<ResourceType> newChildren = q.getResultList();
newChildResourceTypes = new ArrayList<ResourceType>();
// check for infinite recursion and only add children we did not yet see
for (ResourceType rt : newChildren) {
if (!childResourceTypes.contains(rt)) {
childResourceTypes.add(rt);
newChildResourceTypes.add(rt);
}
}
numberOfChildrenToFetch = newChildResourceTypes.size();
}
/*
* now sort the result list in the result map Idea is to have a map <key of parent, List of children>.
*
* As we have an arbitrary depth of the tree, parents are not only the originally wanted parents, but also
* children that have grand children
*/
Map<Integer, SortedSet<ResourceType>> result = new HashMap<Integer, SortedSet<ResourceType>>();
for (ResourceType childType : childResourceTypes) {
for (ResourceType parent : childType.getParentResourceTypes()) {
Integer id = parent.getId();
// add a new map entry if not yet there
if (!result.containsKey(id)) {
result.put(id, new TreeSet<ResourceType>());
}
SortedSet<ResourceType> rtl = result.get(id);
rtl.add(childType);
}
}
if (log.isTraceEnabled()) {
log.trace("============ input: ");
log.trace(ids);
Set<Integer> keys = result.keySet();
for (Integer key : keys) {
log.trace("Key: " + key);
SortedSet<ResourceType> rts = result.get(key);
for (ResourceType rt : rts) {
log.trace(" \\-> " + rt);
}
}
}
return result;
}
@Override
@SuppressWarnings("unchecked")
public List<ResourceType> getResourceTypesForCompatibleGroups(Subject subject, String pluginName) {
Query query = null;
if (authorizationManager.isInventoryManager(subject)) {
query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_BY_RESOURCE_GROUP_admin);
} else {
query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_BY_RESOURCE_GROUP);
query.setParameter("subject", subject);
}
query.setParameter("pluginName", pluginName);
List<ResourceType> results = query.getResultList();
return results;
}
@Override
@SuppressWarnings("unchecked")
public Map<String, Integer> getResourceTypeCountsByGroup(Subject subject, ResourceGroup group, boolean recursive) {
final String queryName = recursive ? ResourceType.QUERY_GET_IMPLICIT_RESOURCE_TYPE_COUNTS_BY_GROUP
: ResourceType.QUERY_GET_EXPLICIT_RESOURCE_TYPE_COUNTS_BY_GROUP;
Query query = entityManager.createNamedQuery(queryName);
query.setParameter("groupId", group.getId());
List results = query.getResultList();
Map<String, Integer> map = new HashMap<String, Integer>();
for (int i = 0, sz = results.size(); i < sz; i++) {
Object[] elements = (Object[]) results.get(i);
long count = (Long) elements[2];
map.put((String) elements[1], (int) count);
}
return map;
}
@Override
public boolean ensureResourceType(Subject subject, Integer resourceTypeId, Integer[] resourceIds)
throws ResourceTypeNotFoundException {
Set<Integer> uniqueIds = new HashSet<Integer>();
uniqueIds.addAll(Arrays.asList(resourceIds));
int[] ids = ArrayUtils.unwrapCollection(uniqueIds);
ResourceType type = this.getResourceTypeById(subject, resourceTypeId);
int count = resourceManager.getResourceCountByTypeAndIds(subject, type, ids);
return (count == ids.length);
}
/**
* Return which facets are available for the passed return type. This is e.g. used to determine which tabs (Monitor,
* Inventory, ...) can be displayed for a resource of a certain type
*/
@Override
@SuppressWarnings("unchecked")
public ResourceFacets getResourceFacets(int resourceTypeId) {
ResourceFacets cachedFacet = ResourceFacetsCache.getSingleton().getResourceFacets(resourceTypeId);
if (cachedFacet != null) {
return cachedFacet;
}
// be paranoid and fallback to getting the results directly from the database
Query query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_RESOURCE_FACETS);
query.setParameter("resourceTypeId", resourceTypeId);
List<ResourceFacets> facets = query.getResultList();
if (facets.size() != 1) {
return new ResourceFacets(resourceTypeId, false, false, false, false, false, false, false, false, false,
false);
}
return facets.get(0);
}
@Override
@SuppressWarnings("unchecked")
public void reloadResourceFacetsCache() {
Query query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_RESOURCE_FACETS);
query.setParameter("resourceTypeId", null);
List<ResourceFacets> facets = query.getResultList();
ResourceFacetsCache.getSingleton().reload(facets);
}
@Override
public Map<Integer, ResourceTypeTemplateCountComposite> getTemplateCountCompositeMap() {
Query templateCountQuery = entityManager.createNamedQuery(ResourceType.FIND_ALL_TEMPLATE_COUNT_COMPOSITES);
@SuppressWarnings("unchecked")
List<ResourceTypeTemplateCountComposite> results = templateCountQuery.getResultList();
for (ResourceTypeTemplateCountComposite result : results) {
ResourceType type = result.getType();
// TODO (ips, 01/24/11): I tried to fetch the parents via the
// ResourceType.FIND_ALL_TEMPLATE_COUNT_COMPOSITES named query,
// but was unable to get it working. This will suffice for the time being.
type.getParentResourceTypes().size();
}
// we don't need to send all the data in resource types over the wire, so we'll be scrubbing the types;
// to avoid the scrubbed types getting persisted back to the db, let's clear the persistence context
entityManager.clear();
Map<Integer, ResourceTypeTemplateCountComposite> compositeMap = new HashMap<Integer, ResourceTypeTemplateCountComposite>();
for (ResourceTypeTemplateCountComposite result : results) {
ResourceType type = result.getType();
// scrub it to avoid sending data over the wire we don't need;
// some of these are eagerly loaded, but my paranoia says assume everything is eagerly loaded to purge it all
scrubType(type);
compositeMap.put(type.getId(), result);
}
return compositeMap;
}
private void scrubType(ResourceType type) {
type.setResourceTypeBundleConfiguration(null);
type.setBundleType(null);
type.setChildResourceTypes(null);
type.setChildSubCategories(null);
type.setClassLoaderType(null);
type.setEventDefinitions(null);
type.setMetricDefinitions(null);
type.setOperationDefinitions(null);
type.setPackageTypes(null);
type.setPluginConfigurationDefinition(null);
type.setProcessScans(null);
type.setProductVersions(null);
type.setResourceConfigurationDefinition(null);
type.setResourceGroups(null);
type.setResources(null);
type.setSubCategory(null);
}
@Override
@SuppressWarnings("unchecked")
public List<ResourceType> getResourceTypesByPlugin(String pluginName) {
final String queryName = ResourceType.QUERY_FIND_BY_PLUGIN;
Query query = entityManager.createNamedQuery(queryName);
query.setParameter("plugin", pluginName);
List<ResourceType> results = query.getResultList();
return results;
}
@Override
@SuppressWarnings("unchecked")
public List<Integer> getResourceTypeIdsByPlugin(String plugin) {
return entityManager.createNamedQuery(ResourceType.QUERY_FIND_IDS_BY_PLUGIN).setParameter("plugin", plugin)
.getResultList();
}
@Override
public Integer getResourceTypeCountByPlugin(String plugin) {
return (Integer) entityManager.createNamedQuery(ResourceType.QUERY_FIND_COUNT_BY_PLUGIN)
.setParameter("plugin", plugin).getSingleResult();
}
@Override
@SuppressWarnings("unchecked")
public PageList<ResourceType> findResourceTypesByCriteria(Subject subject, ResourceTypeCriteria criteria) {
CriteriaQueryGenerator generator = new CriteriaQueryGenerator(subject, criteria);
CriteriaQueryRunner<ResourceType> queryRunner = new CriteriaQueryRunner(criteria, generator, entityManager);
return queryRunner.execute();
}
@Override
@SuppressWarnings("unchecked")
public List<String> getDuplicateTypeNames() {
Query query = entityManager.createNamedQuery(ResourceType.QUERY_FIND_DUPLICATE_TYPE_NAMES);
List<String> results = query.getResultList();
return results;
}
@Override
public LinkedHashMap<Integer, String> getResourceTypeDescendantsWithOperations(Subject subject, int resourceTypeId) {
List<ResourceType> types = getAllResourceTypeDescendants(subject, resourceTypeId);
excludeThoseWithoutOperations(types);
LinkedHashMap<Integer, String> results = new LinkedHashMap<Integer, String>(types.size());
for (ResourceType type : types) {
results.put(type.getId(), type.getPlugin() + " -> " + type.getName());
}
return results;
}
private void excludeThoseWithoutOperations(List<ResourceType> types) {
Iterator<ResourceType> iterator = types.iterator();
while (iterator.hasNext()) {
ResourceType next = iterator.next();
Set<OperationDefinition> operations = next.getOperationDefinitions();
if (operations == null || operations.isEmpty()) {
iterator.remove();
}
}
}
@Override
public List<ResourceType> getAllResourceTypeAncestors(Subject subject, int resourceTypeId) {
Set<ResourceType> uniqueTypes = new HashSet<ResourceType>();
Stack<ResourceType> toProcess = new Stack<ResourceType>();
toProcess.add(entityManager.find(ResourceType.class, resourceTypeId));
boolean sawTopLevelServer = false;
while (toProcess.size() > 0) {
ResourceType next = toProcess.pop();
Set<ResourceType> parentTypes = next.getParentResourceTypes();
if (parentTypes != null && parentTypes.size() != 0) {
toProcess.addAll(parentTypes);
} else {
if (next.getCategory() == ResourceCategory.SERVER) {
sawTopLevelServer = true;
}
}
uniqueTypes.add(next);
}
List<ResourceType> results = new ArrayList<ResourceType>(uniqueTypes);
if (sawTopLevelServer) {
// get all platform types, whether or not they are ignored
ResourceTypeCriteria criteria = new ResourceTypeCriteria();
criteria.addFilterIgnored(null);
criteria.addFilterCategory(ResourceCategory.PLATFORM);
criteria.clearPaging();//disable paging as the code assumes all the results will be returned.
List<ResourceType> platforms = findResourceTypesByCriteria(subject, criteria);
results.addAll(platforms);
}
Collections.sort(results);
return results;
}
@Override
@SuppressWarnings("unchecked")
public List<ResourceType> getAllResourceTypeDescendants(Subject subject, int resourceTypeId) {
ResourceType first = entityManager.find(ResourceType.class, resourceTypeId);
if (first.getCategory() == ResourceCategory.PLATFORM) {
// get all platform types, whether or not they are ignored
ResourceTypeCriteria criteria = new ResourceTypeCriteria();
criteria.addFilterIgnored(null);
criteria.clearPaging();//disable paging as the code assumes all the results will be returned.
List<ResourceType> allResourceTypes = findResourceTypesByCriteria(subject, criteria);
List<ResourceType> results = new ArrayList<ResourceType>();
for (ResourceType nextType : allResourceTypes) {
if (nextType.getCategory() != ResourceCategory.PLATFORM) {
results.add(nextType);
}
}
Collections.sort(results);
return results;
}
Set<ResourceType> uniqueTypes = new HashSet<ResourceType>();
Stack<ResourceType> toProcess = new Stack<ResourceType>();
toProcess.add(first);
Query findChildrenQuery = entityManager.createNamedQuery(ResourceType.FIND_CHILDREN_BY_PARENT);
while (toProcess.size() > 0) {
ResourceType next = toProcess.pop();
findChildrenQuery.setParameter("resourceType", Arrays.asList(next));
List<ResourceType> childTypes = findChildrenQuery.getResultList();
if (childTypes != null) {
toProcess.addAll(childTypes);
}
uniqueTypes.add(next);
}
List<ResourceType> results = new ArrayList<ResourceType>(uniqueTypes);
Collections.sort(results);
return results;
}
}