/*
* 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.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import org.rhq.core.domain.measurement.Availability;
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;
/**
* Will create a tree of resources based off of a short-hand, string representation of the tree Pre-condition: resource
* names are globally unique (because the parser is too simple to handle uniqueness wrt your parent resource) Notes: all
* whitespace is ignored For example: A=1,2; 1=a,b; a=i,ii; b=iii,iv; B=3 | | \|/ v +-- A +-- 1 | +-- a | | +-- i | |
* +-- ii | | | +-- b | +-- iii | +-- iv +-- 2 +-- B +-- 3 For now, all resources have the exact same resourceType, but
* it would be fairly easy to create rules where different ranges of characters (e.g., [A-Z], [a-z], etc) could be used
* to represent different resource types or, at the very least, different categories. A more flexible solution could be
* to enable a separate specification mechanism for resource types and categories. For example, we might use a compound,
* parenthetical annotation for category-type specification. So if we assume that, p = platform s = server v = service
* Then our annotated resource would look like A(p1) This would set A's resource type to the newly created resource type
* with the name of "fake platform 1", and set A's resource category to ResourceCategory.PLATFORM Thus, the new
* short-hand would look like: A(p1)=1,2; 1(s1)=a,b; a(v1)=i,ii; b(v2)=iii,iv; B(p2)=3; 2(s2); 3(s3) However, as you can
* see, this requires a slightly more verbose specification because the type information is parenthetically attached to
* the LHS of the relationship, requiring leaf nodes in the resource hierarchy to be specified solely to attach this
* information to it.
*/
public class ResourceTreeHelper {
private static int fakeTypeId = 1;
private static final String FAKE_TYPE_NAME = "fakeType";
private static final String FAKE_PLUGIN_NAME = "fakePlugin";
/*
* This will create the resource tree, but won't persist it
*/
public static List<Resource> createTree(String flatStructure) {
return createTree(null, flatStructure);
}
/*
* This will create the resource tree, and related resource types
*/
public static synchronized List<Resource> createTree(EntityManager entityManager, String flatStructure) {
// remove any and all whitespace from the string
flatStructure = flatStructure.replaceAll("\\s", "");
// if necessary, create the fakeType which ALL resources will be attached too
ResourceType fakeType;
if (null != entityManager) {
Query namedQuery = entityManager.createNamedQuery(ResourceType.QUERY_FIND_BY_NAME_AND_PLUGIN);
namedQuery.setParameter("name", FAKE_TYPE_NAME);
namedQuery.setParameter("plugin", FAKE_PLUGIN_NAME);
try {
fakeType = (ResourceType) namedQuery.getSingleResult();
} catch (NoResultException e) {
fakeType = null;
}
if (fakeType == null) {
fakeType = new ResourceType(FAKE_TYPE_NAME, FAKE_PLUGIN_NAME, ResourceCategory.PLATFORM, null);
persist(entityManager, fakeType);
fakeTypeId = fakeType.getId();
}
} else {
fakeType = new ResourceType(FAKE_TYPE_NAME, FAKE_PLUGIN_NAME, ResourceCategory.PLATFORM, null);
}
// create the map that will hold all of the resources - root, leaf, or inner node
Map<String, Resource> resources = new HashMap<String, Resource>();
// relationships in the form of X=y,z are semi-colon delimited
String[] relationships = flatStructure.split(";");
for (int i = 0; i < relationships.length; i++) {
String relationship = relationships[i];
int splitIndex = relationship.indexOf("=");
String parent = relationship.substring(0, splitIndex);
String[] children = relationship.substring(splitIndex + 1).split(",");
Resource parentResource = null;
// only add the parent if it doesn't already exist
if (resources.containsKey(parent)) {
parentResource = resources.get(parent);
} else {
parentResource = new Resource(parent, parent, fakeType);
parentResource.setUuid("" + new Random().nextInt());
resources.put(parent, parentResource);
}
for (String child : children) {
Resource childResource = null;
// only add the child if it doesn't already exist
// this happens when children were specified before their parents
if (resources.containsKey(child)) {
childResource = resources.get(child);
} else {
childResource = new Resource(child, child, fakeType);
childResource.setUuid("" + new Random().nextInt());
resources.put(child, childResource);
}
childResource.setParentResource(parentResource);
parentResource.addChildResource(childResource);
}
}
// this object returns only root resources, which can be traversed to find the children
List<Resource> roots = new ArrayList<Resource>();
for (Resource potentialRoot : resources.values()) {
potentialRoot.setInventoryStatus(InventoryStatus.COMMITTED);
persist(entityManager, potentialRoot);
// roots are those with no parent resource
if (potentialRoot.getParentResource() == null) {
roots.add(potentialRoot);
}
}
// only flush when we are in persisting mode
if (entityManager != null)
entityManager.flush();
return roots;
}
public static void deleteForest(EntityManager entityManager, List<Resource> roots) {
for (Resource root : roots) {
Resource doomedRoot = entityManager.find(Resource.class, root.getId());
if (null != doomedRoot) {
ResourceType doomedResourceType = doomedRoot.getResourceType();
deleteTree(entityManager, doomedRoot);
if (null != doomedResourceType) {
doomedResourceType = entityManager.find(ResourceType.class, doomedResourceType.getId());
if (doomedResourceType.getResources().isEmpty()) {
entityManager.remove(doomedResourceType);
}
}
}
}
}
private static void deleteTree(EntityManager entityManager, Resource root) {
for (Resource child : root.getChildResources()) {
deleteTree(entityManager, child);
}
deleteResource(entityManager, entityManager.find(Resource.class, root.getId()));
}
public static void deleteResource(EntityManager entityManager, Resource doomedResource) {
if (null == doomedResource) {
return;
}
for (Availability avail : doomedResource.getAvailability()) {
entityManager.remove(avail);
}
entityManager.remove(doomedResource);
}
public static Resource findNode(List<Resource> roots, String name) {
Resource node = null;
for (Resource root : roots) {
node = findNode(root, name);
if (node != null) {
break;
}
}
return node;
}
// public static Resource findNode(Resource rootResource, String name) {
// return findNodeHelper(rootResource, name);
// }
public static Resource findNode(Resource resource, String name) {
if (name.equals(resource.getName())) {
return resource;
}
Resource result = null;
for (Resource child : resource.getChildResources()) {
result = findNode(child, name);
if (result != null) {
break;
}
}
return result;
}
public static List<Resource> getSubtree(Resource resource) {
List<Resource> subtree = new ArrayList<Resource>();
List<Resource> toBeSearched = new LinkedList<Resource>();
toBeSearched.add(resource);
while (toBeSearched.size() > 0) {
Resource next = toBeSearched.remove(0);
subtree.add(next);
toBeSearched.addAll(next.getChildResources());
}
return subtree;
}
public static List<Resource> findSubtree(Resource resource, String name) {
return getSubtree(findNode(resource, name));
}
private static void persist(EntityManager entityManager, Object object) {
if (entityManager != null) {
try {
entityManager.persist(object);
} catch (EntityExistsException e) {
// ignore
}
}
}
public static void printForest(List<Resource> resources) {
for (Resource root : resources) {
printTree(root);
}
}
public static void printTree(Resource resource) {
printTreeHelper(resource, 0, "");
}
private static void printTreeHelper(Resource resource, int level, String namePrefix) {
for (int i = 0; i < level; i++) {
System.out.print(" ");
}
System.out.println("<resource " + //
"id=\"" + resource.getId() + "\" " + //
"name=\"" + resource.getName() + "\" " + //
"path=\"" + namePrefix + resource.getName() + "\" />");
for (Resource child : resource.getChildResources()) {
printTreeHelper(child, level + 1, namePrefix + resource.getName() + ".");
}
}
public static void main(String[] args) {
List<Resource> resourceRoots = createTree("A=1,2; 1=a,b; a=i,ii; b=iii,iv; B=3");
for (Resource root : resourceRoots) {
printTree(root);
}
}
}