/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004-2008], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. This program is distributed
* in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.appdef.server.session;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import org.hyperic.hq.appdef.shared.AppdefEntityConstants;
import org.hyperic.hq.appdef.shared.AppdefEntityID;
import org.hyperic.hq.appdef.shared.AppdefEntityNotFoundException;
import org.hyperic.hq.appdef.shared.AppdefEntityValue;
import org.hyperic.hq.appdef.shared.ApplicationManager;
import org.hyperic.hq.appdef.shared.ApplicationNotFoundException;
import org.hyperic.hq.appdef.shared.ApplicationValue;
import org.hyperic.hq.appdef.shared.PlatformNotFoundException;
import org.hyperic.hq.appdef.shared.ServerManager;
import org.hyperic.hq.appdef.shared.ServiceManager;
import org.hyperic.hq.appdef.shared.resourceTree.ApplicationNode;
import org.hyperic.hq.appdef.shared.resourceTree.PlatformNode;
import org.hyperic.hq.appdef.shared.resourceTree.ResourceTree;
import org.hyperic.hq.appdef.shared.resourceTree.ServerNode;
import org.hyperic.hq.appdef.shared.resourceTree.ServiceNode;
import org.hyperic.hq.authz.server.session.AuthzSubject;
import org.hyperic.hq.authz.shared.PermissionException;
import org.hyperic.hq.common.SystemException;
import org.hyperic.util.pager.PageControl;
import org.springframework.beans.factory.annotation.Autowired;
/**
* An object which generates ResourceTree objects. The
* generator is called with a single appdef entity, and
* a traversal method. If the traversal method is TRAVERSE_NORMAL,
* the object, and all objects which it depends upon are
* added to the tree. If the traversal is TRAVERSE_UP,
* the tree will contain all resources which it depends upon
* as well as resources which depend upon it.
*/
public class ResourceTreeGenerator {
public static final int TRAVERSE_NORMAL
= AppdefEntityConstants.RESTREE_TRAVERSE_NORMAL;
public static final int TRAVERSE_UP
= AppdefEntityConstants.RESTREE_TRAVERSE_UP;
// Internal information about how traversal should function
private static final int GO_UP = 1 << 5;
private static final int GO_DOWN = 1 << 6;
private final ServerManager serverManager;
private final ServiceManager serviceManager;
private ApplicationManager applicationManager;
private AuthzSubject _subject;
// Caches so we don't do much work twice
private HashSet upPlats = new HashSet(); // Platform IDs which we've traversed up from
private HashSet upServers = new HashSet(); // Server IDs which we've traversed up from
private HashSet upServices = new HashSet(); // Service IDs which we've traversed up from
private HashMap dnPlats = new HashMap(); // Platform IDs which we've traversed down from
private HashMap dnServers = new HashMap(); // Server IDs which we've traversed down from
private HashMap dnServices = new HashMap(); // Service IDs which we've traversed down from
private HashSet dnApps = new HashSet(); // App IDs which we've traversed down from
@Autowired
public ResourceTreeGenerator(ServerManager serverMan, ServiceManager serviceMan,
ApplicationManager applicationManager) {
this.serverManager = serverMan;
this.serviceManager = serviceMan;
this.applicationManager = applicationManager;
}
public void setSubject(AuthzSubject subject) {
_subject = subject;
}
/**
* Generate a tree which includes the passed IDs, as well as
* their dependents (or dependencies) as per the traversal method.
*
* @param ids An array of IDs to generate the tree from
* @param traversal One of TRAVERSE_*
*
* @return a new ResourceTree
*/
ResourceTree generate(AppdefEntityID[] ids, int traversal)
throws AppdefEntityNotFoundException, PermissionException
{
ResourceTree res;
int direction;
if(traversal == TRAVERSE_NORMAL){
direction = GO_DOWN;
} else if(traversal == TRAVERSE_UP){
direction = GO_UP | GO_DOWN;
} else {
throw new IllegalArgumentException("Unknown traversal method");
}
upPlats.clear();
upServers.clear();
upServices.clear();
dnPlats.clear();
dnServers.clear();
dnServices.clear();
dnApps.clear();
res = new ResourceTree();
for (int i = 0; i < ids.length; i++) {
Integer iID = ids[i].getId();
switch(ids[i].getType()){
case AppdefEntityConstants.APPDEF_TYPE_PLATFORM:
addFromPlatform(iID, direction, res);
break;
case AppdefEntityConstants.APPDEF_TYPE_SERVER:
addFromServer(iID, direction, res);
break;
case AppdefEntityConstants.APPDEF_TYPE_SERVICE:
addFromService(iID, direction, res);
break;
case AppdefEntityConstants.APPDEF_TYPE_APPLICATION:
addFromApp(iID, direction, res);
break;
default:
throw new SystemException("Unable to generate tree from " +
ids[i]);
}
}
return res;
}
private void addFromPlatform(Integer id, int direction, ResourceTree tree)
throws PermissionException, AppdefEntityNotFoundException,
PlatformNotFoundException
{
try {
AppdefEntityValue aeval = new AppdefEntityValue(
AppdefEntityID.newPlatformID(id), _subject);
traversePlatform((Platform) aeval.getResourcePOJO(), direction,
tree);
} catch(PermissionException exc){
throw new PermissionException("Failed to find platform " + id +
": permission denied");
}
}
private void addFromServer(Integer id, int direction, ResourceTree tree)
throws PermissionException, AppdefEntityNotFoundException
{
try {
AppdefEntityValue aeval = new AppdefEntityValue(
AppdefEntityID.newServerID(id), _subject);
traverseServer((Server) aeval.getResourcePOJO(), direction, tree);
} catch(PermissionException exc){
throw new PermissionException("Failed to find server " + id +
": permission denied");
}
}
private void addFromService(Integer id, int direction, ResourceTree tree)
throws PermissionException, AppdefEntityNotFoundException
{
try {
AppdefEntityValue aeval = new AppdefEntityValue(
AppdefEntityID.newServiceID(id), _subject);
traverseService((Service) aeval.getResourcePOJO(), direction, tree);
} catch(PermissionException exc){
throw new PermissionException("Failed to find service " + id +
": permission denied");
}
}
private void addFromApp(Integer id, int direction, ResourceTree tree)
throws PermissionException, AppdefEntityNotFoundException
{
AppdefEntityValue aeval = new AppdefEntityValue(
AppdefEntityID.newAppID(id), _subject);
traverseApp((ApplicationValue) aeval.getResourceValue(), direction,
tree);
}
private PlatformNode traversePlatform(Platform platform, int direction,
ResourceTree tree)
throws PermissionException {
PlatformNode res;
Integer platformID = platform.getId();
if ((res = (PlatformNode) dnPlats.get(platformID)) == null) {
res = tree.addPlatform(platform);
dnPlats.put(platformID, res);
}
if ((direction & GO_UP) != 0 && !upPlats.contains(platformID))
{
upPlats.add(platformID);
Collection servers = serverManager.getViewableServers(_subject,
platform);
for (Iterator i = servers.iterator(); i.hasNext();) {
traverseServer((Server) i.next(), GO_UP, tree);
}
}
return res;
}
private ServerNode traverseServer(Server server, int direction,
ResourceTree tree)
throws PermissionException {
Integer serverID = server.getId();
ServerNode res;
if ((res = (ServerNode) dnServers.get(serverID)) == null) {
PlatformNode platformNode;
Platform platform = server.getPlatform();
// Traverse down to the platform
platformNode = traversePlatform(platform, GO_DOWN, tree);
// Add server, now that the platform exists
res = platformNode.addServer(server);
dnServers.put(serverID, res);
}
if ((direction & GO_UP) != 0 && !upServers.contains(serverID)) {
Collection services;
upServers.add(serverID);
try {
services = serviceManager.getServicesByServer(_subject, server);
} catch(AppdefEntityNotFoundException exc){
throw new SystemException("Internal inconsistancy: could not " +
"find services for server '" +
serverID + "'");
} catch(PermissionException exc){
throw new PermissionException("Failed to get services for " +
"server " + serverID +
": permission denied");
}
for (Iterator i = services.iterator(); i.hasNext();) {
traverseService((Service) i.next(), GO_UP, tree);
}
}
return res;
}
private ServiceNode traverseService(Service service, int direction,
ResourceTree tree)
throws PermissionException {
ServiceNode res;
Integer serviceID;
serviceID = service.getId();
if((res = (ServiceNode)dnServices.get(serviceID)) == null){
ServerNode serverNode;
try {
serverNode = traverseServer(service.getServer(), GO_DOWN, tree);
} catch(PermissionException exc){
throw new PermissionException("Failed to get server " +
service.getServer().getId() +
" on which service " + serviceID
+ " resides: Permission denied");
}
res = serverNode.addService(service);
dnServices.put(serviceID, res);
}
if ((direction & GO_UP) != 0 && !upServices.contains(service.getId())) {
Collection apps;
upServices.add(service.getId());
try {
AppdefEntityID id =
AppdefEntityID.newServiceID(service.getId());
apps = applicationManager
.getApplicationsByResource(_subject, id,
PageControl.PAGE_ALL);
} catch(ApplicationNotFoundException exc){
throw new SystemException("Internal inconsistancy: could not " +
"find apps for service '" +
service.getId() + "'");
}
for(Iterator i=apps.iterator(); i.hasNext(); ){
traverseApp((ApplicationValue)i.next(), GO_UP, tree);
}
}
return res;
}
private void traverseApp(ApplicationValue app, int direction,
ResourceTree tree)
throws PermissionException {
if(dnApps.contains(app.getId()))
return;
dnApps.add(app.getId());
ApplicationNode appNode = tree.addApplication(app);
Collection services;
if ((direction & GO_DOWN) != 0) {
try {
services =
serviceManager.getServicesByApplication(_subject, app.getId());
} catch(AppdefEntityNotFoundException exc){
throw new SystemException("Internal inconsistancy: could " +
"not get services on which " +
"application " + app.getId() +
" depends on");
}
for(Iterator i=services.iterator(); i.hasNext(); ){
ServiceNode servNode =
traverseService((Service) i.next(), GO_DOWN, tree);
appNode.linkToService(servNode);
}
}
}
}