org.hyperic.hq.appdef.server.session.ServiceManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperic.hq.appdef.server.session.ServiceManagerImpl.java

Source

/*
 * 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-2009], 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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.ObjectNotFoundException;
import org.hyperic.hq.appdef.AppService;
import org.hyperic.hq.appdef.ConfigResponseDB;
import org.hyperic.hq.appdef.ServiceCluster;
import org.hyperic.hq.appdef.shared.AppdefDuplicateNameException;
import org.hyperic.hq.appdef.shared.AppdefEntityID;
import org.hyperic.hq.appdef.shared.AppdefEntityNotFoundException;
import org.hyperic.hq.appdef.shared.ApplicationNotFoundException;
import org.hyperic.hq.appdef.shared.CPropManager;
import org.hyperic.hq.appdef.shared.InvalidAppdefTypeException;
import org.hyperic.hq.appdef.shared.PlatformNotFoundException;
import org.hyperic.hq.appdef.shared.ServerNotFoundException;
import org.hyperic.hq.appdef.shared.ServiceManager;
import org.hyperic.hq.appdef.shared.ServiceNotFoundException;
import org.hyperic.hq.appdef.shared.ServiceTypeValue;
import org.hyperic.hq.appdef.shared.ServiceValue;
import org.hyperic.hq.appdef.shared.UpdateException;
import org.hyperic.hq.appdef.shared.ValidationException;
import org.hyperic.hq.authz.server.session.AuthzSubject;
import org.hyperic.hq.authz.server.session.Operation;
import org.hyperic.hq.authz.server.session.Resource;
import org.hyperic.hq.authz.server.session.ResourceGroup;
import org.hyperic.hq.authz.server.session.ResourceType;
import org.hyperic.hq.authz.shared.AuthzConstants;
import org.hyperic.hq.authz.shared.AuthzSubjectManager;
import org.hyperic.hq.authz.shared.PermissionException;
import org.hyperic.hq.authz.shared.PermissionManager;
import org.hyperic.hq.authz.shared.ResourceGroupManager;
import org.hyperic.hq.authz.shared.ResourceManager;
import org.hyperic.hq.common.NotFoundException;
import org.hyperic.hq.common.SystemException;
import org.hyperic.hq.common.VetoException;
import org.hyperic.hq.grouping.server.session.GroupUtil;
import org.hyperic.hq.grouping.shared.GroupNotCompatibleException;
import org.hyperic.hq.measurement.shared.MeasurementManager;
import org.hyperic.hq.product.ServiceTypeInfo;
import org.hyperic.hq.zevents.ZeventEnqueuer;
import org.hyperic.util.MapUtil;
import org.hyperic.util.pager.PageControl;
import org.hyperic.util.pager.PageList;
import org.hyperic.util.pager.Pager;
import org.hyperic.util.pager.SortAttribute;
import org.hyperic.util.timer.StopWatch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

/**
 * This class is responsible for managing Server objects in appdef and their
 * relationships
 */
@org.springframework.stereotype.Service
@Transactional
public class ServiceManagerImpl implements ServiceManager {

    private final Log log = LogFactory.getLog(ServiceManagerImpl.class);

    private static final String VALUE_PROCESSOR = "org.hyperic.hq.appdef.server.session.PagerProcessor_service";
    private Pager valuePager;
    private static final Integer APPDEF_RES_TYPE_UNDEFINED = new Integer(-1);
    private AppServiceDAO appServiceDAO;
    private PermissionManager permissionManager;
    private ServiceDAO serviceDAO;
    private ApplicationDAO applicationDAO;
    private ConfigResponseDAO configResponseDAO;
    private ResourceManager resourceManager;
    private ServerDAO serverDAO;
    private ServerTypeDAO serverTypeDAO;
    private ServiceTypeDAO serviceTypeDAO;
    private ResourceGroupManager resourceGroupManager;
    private CPropManager cpropManager;
    private MeasurementManager measurementManager;
    private AuthzSubjectManager authzSubjectManager;
    private ZeventEnqueuer zeventManager;

    @Autowired
    public ServiceManagerImpl(AppServiceDAO appServiceDAO, PermissionManager permissionManager,
            ServiceDAO serviceDAO, ApplicationDAO applicationDAO, ConfigResponseDAO configResponseDAO,
            ResourceManager resourceManager, ServerDAO serverDAO, ServerTypeDAO serverTypeDAO,
            ServiceTypeDAO serviceTypeDAO, ResourceGroupManager resourceGroupManager, CPropManager cpropManager,
            MeasurementManager measurementManager, AuthzSubjectManager authzSubjectManager,
            ZeventEnqueuer zeventManager) {
        this.appServiceDAO = appServiceDAO;
        this.permissionManager = permissionManager;
        this.serviceDAO = serviceDAO;
        this.applicationDAO = applicationDAO;
        this.configResponseDAO = configResponseDAO;
        this.resourceManager = resourceManager;
        this.serverDAO = serverDAO;
        this.serverTypeDAO = serverTypeDAO;
        this.serviceTypeDAO = serviceTypeDAO;
        this.resourceGroupManager = resourceGroupManager;
        this.cpropManager = cpropManager;
        this.measurementManager = measurementManager;
        this.authzSubjectManager = authzSubjectManager;
        this.zeventManager = zeventManager;
    }

    public Service createService(AuthzSubject subject, Server server, ServiceType type, String name, String desc,
            String location, Service parent) throws PermissionException {
        name = name.trim();
        desc = desc == null ? "" : desc.trim();
        location = location == null ? "" : location.trim();

        Service service = serviceDAO.create(type, server, name, desc, subject.getName(), location,
                subject.getName(), parent);
        // Create the authz resource type. This also does permission checking
        createAuthzService(subject, service);

        // Add Service to parent collection
        server.getServices().add(service);

        ResourceCreatedZevent zevent = new ResourceCreatedZevent(subject, service.getEntityId());
        NewResourceEvent event = new NewResourceEvent(server.getResource().getId(), service.getResource());
        zeventManager.enqueueEventAfterCommit(event, 2);
        zeventManager.enqueueEventAfterCommit(zevent);
        return service;
    }

    /**
     * Move a Service from one Platform to another.
     * 
     * @param subject The user initiating the move.
     * @param target The target Service to move.
     * @param destination The destination Platform to move this Service to.
     * 
     * @throws org.hyperic.hq.authz.shared.PermissionException If the passed
     *         user does not have permission to move the Service.
     * @throws org.hyperic.hq.common.VetoException If the operation canot be
     *         performed due to incompatible types.
     */

    public void moveService(AuthzSubject subject, Service target, Platform destination)
            throws VetoException, PermissionException {
        ServerType targetType = target.getServer().getServerType();

        Server destinationServer = null;
        for (Server s : destination.getServers()) {

            if (s.getServerType().equals(targetType)) {
                destinationServer = s;
                break;
            }
        }

        if (destinationServer == null) {
            throw new VetoException("Unable find applicable server on platform " + destination.getName()
                    + " as destination for " + target.getName());
        }

        moveService(subject, target, destinationServer);
    }

    /**
     * Move a Service from one Server to another.
     * 
     * @param subject The user initiating the move.
     * @param target The target Service to move.
     * @param destination The destination Server to move this Service to.
     * 
     * @throws org.hyperic.hq.authz.shared.PermissionException If the passed
     *         user does not have permission to move the Service.
     * @throws org.hyperic.hq.common.VetoException If the operation canot be
     *         performed due to incompatible types.
     */

    public void moveService(AuthzSubject subject, Service target, Server destination)
            throws VetoException, PermissionException {
        try {
            // Permission checking on destination
            if (destination.getServerType().isVirtual()) {
                permissionManager.checkPermission(subject,
                        resourceManager.findResourceTypeByName(AuthzConstants.platformResType),
                        destination.getPlatform().getId(), AuthzConstants.platformOpAddServer);
            } else {
                permissionManager.checkPermission(subject,
                        resourceManager.findResourceTypeByName(AuthzConstants.serverResType), destination.getId(),
                        AuthzConstants.serverOpAddService);
            }

            // Permission check on target
            permissionManager.checkPermission(subject,
                    resourceManager.findResourceTypeByName(AuthzConstants.serviceResType), target.getId(),
                    AuthzConstants.serviceOpRemoveService);
        } catch (NotFoundException e) {
            throw new VetoException("Caught NotFoundException checking permission: " + e.getMessage()); // notgonnahappen
        }

        // Check arguments
        if (!target.getServiceType().getServerType().equals(destination.getServerType())) {
            throw new VetoException("Incompatible resources passed to move(), " + "cannot move service of type "
                    + target.getServiceType().getName() + " to " + destination.getServerType().getName());

        }

        // Unschedule measurements
        measurementManager.disableMeasurements(subject, target.getResource());

        // Reset Service parent id
        target.setServer(destination);

        // Add/Remove Server from Server collections
        target.getServer().getServices().remove(target);
        destination.addService(target);

        // Move Authz resource.
        resourceManager.moveResource(subject, target.getResource(), destination.getResource());

        // Flush to ensure the reschedule of metrics occurs
        serviceDAO.getSession().flush();

        // Reschedule metrics
        ResourceUpdatedZevent zevent = new ResourceUpdatedZevent(subject, target.getEntityId());
        zeventManager.enqueueEventAfterCommit(zevent);
    }

    /**
     * Create a Service which runs on a given server
     * @return The service id.
     */

    public Service createService(AuthzSubject subject, Integer serverId, Integer serviceTypeId, String name,
            String desc, String location)
            throws ValidationException, PermissionException, ServerNotFoundException, AppdefDuplicateNameException {
        Server server = serverDAO.findById(serverId);
        ServiceType serviceType = serviceTypeDAO.findById(serviceTypeId);
        return createService(subject, server, serviceType, name, desc, location, null);
    }

    /**
     * Create the Authz service resource
     */
    private void createAuthzService(AuthzSubject subject, Service service) throws PermissionException {
        log.debug("Begin Authz CreateService");
        try {
            // check to see that the user has permission to addServices
            Server server = service.getServer();
            if (server.getServerType().isVirtual()) {
                // to the server in question
                permissionManager.checkPermission(subject,
                        resourceManager.findResourceTypeByName(AuthzConstants.platformResType),
                        server.getPlatform().getId(), AuthzConstants.platformOpAddServer);
            } else {
                // to the platform in question
                permissionManager.checkPermission(subject,
                        resourceManager.findResourceTypeByName(AuthzConstants.serverResType), server.getId(),
                        AuthzConstants.serverOpAddService);
            }

            ResourceType serviceProto = resourceManager
                    .findResourceTypeByName(AuthzConstants.servicePrototypeTypeName);
            Resource prototype = resourceManager.findResourceByInstanceId(serviceProto,
                    service.getServiceType().getId());

            Resource parent = resourceManager.findResource(server.getEntityId());
            if (parent == null) {
                throw new SystemException("Unable to find parent server [id=" + server.getEntityId() + "]");
            }
            Resource resource = resourceManager.createResource(subject,
                    resourceManager.findResourceTypeByName(AuthzConstants.serviceResType), prototype,
                    service.getId(), service.getName(), false, parent);
            service.setResource(resource);
        } catch (NotFoundException e) {
            throw new SystemException("Unable to find authz resource type", e);
        }
    }

    /**
     * Get service IDs by service type.
     * 
     * @param subject The subject trying to list service.
     * @param servTypeId service type id.
     * @return An array of service IDs.
     */
    @Transactional(readOnly = true)
    public Integer[] getServiceIds(AuthzSubject subject, Integer servTypeId) throws PermissionException {

        try {

            Collection<Service> services = serviceDAO.findByType(servTypeId, true);
            if (services.size() == 0) {
                return new Integer[0];
            }
            List<Integer> serviceIds = new ArrayList<Integer>(services.size());

            // now get the list of PKs
            Set<Integer> viewable = new HashSet<Integer>(getViewableServices(subject));
            // and iterate over the List to remove any item not in the
            // viewable list
            int i = 0;
            for (Iterator<Service> it = services.iterator(); it.hasNext(); i++) {
                Service service = it.next();
                if (viewable.contains(service.getId())) {
                    // add the item, user can see it
                    serviceIds.add(service.getId());
                }
            }

            return (Integer[]) serviceIds.toArray(new Integer[0]);
        } catch (NotFoundException e) {
            // There are no viewable servers
            return new Integer[0];
        }
    }

    /**
     * @return List of ServiceValue objects
     */
    @Transactional(readOnly = true)
    public List<ServiceValue> findServicesById(AuthzSubject subject, Integer[] serviceIds)
            throws ServiceNotFoundException, PermissionException {
        List<ServiceValue> serviceList = new ArrayList<ServiceValue>(serviceIds.length);
        for (int i = 0; i < serviceIds.length; i++) {
            Service s = findServiceById(serviceIds[i]);
            permissionManager.checkViewPermission(subject, s.getEntityId());
            serviceList.add(s.getServiceValue());
        }
        return serviceList;
    }

    /**
     * Find Service by Id.
     */
    @Transactional(readOnly = true)
    public Service findServiceById(Integer id) throws ServiceNotFoundException {
        Service service = getServiceById(id);

        if (service == null) {
            throw new ServiceNotFoundException(id);
        }

        return service;
    }

    /**
     * Get Service by Id.
     * 
     * @return The Service identified by this id, or null if it does not exist.
     */
    @Transactional(readOnly = true)
    public Service getServiceById(Integer id) {
        return serviceDAO.get(id);
    }

    /**
     * Get Service by Id and perform permission check.
     * 
     * @return The Service identified by this id.
     */
    @Transactional(readOnly = true)
    public Service getServiceById(AuthzSubject subject, Integer id)
            throws ServiceNotFoundException, PermissionException {

        Service service = findServiceById(id);
        permissionManager.checkViewPermission(subject, service.getEntityId());
        return service;
    }

    /**
     * @param server {@link Server}
     * @param aiid service autoinventory identifier
     * @return {@link List} of {@link Service}
     */
    @Transactional(readOnly = true)
    public List<Service> getServicesByAIID(Server server, String aiid) {
        return serviceDAO.getByAIID(server, aiid);
    }

    /**
     * @param server {@link Server}
     * @param name corresponds to the EAM_RESOURCE.sort_name column
     */
    @Transactional(readOnly = true)
    public Service getServiceByName(Server server, String name) {
        return serviceDAO.findByName(server, name);
    }

    @Transactional(readOnly = true)
    public Service getServiceByName(Platform platform, String name) {
        return serviceDAO.findByName(platform, name);
    }

    /**
     * Find a ServiceType by id
     */
    @Transactional(readOnly = true)
    public ServiceType findServiceType(Integer id) throws ObjectNotFoundException {
        return serviceTypeDAO.findById(id);
    }

    /**
     * Find service type by name
     */
    @Transactional(readOnly = true)
    public ServiceType findServiceTypeByName(String name) {
        return serviceTypeDAO.findByName(name);
    }

    @Transactional(readOnly = true)
    public Collection<Service> findDeletedServices() {
        return serviceDAO.findDeletedServices();
    }

    /**
     * @return PageList of ServiceTypeValues
     */
    @Transactional(readOnly = true)
    public PageList<ServiceTypeValue> getAllServiceTypes(AuthzSubject subject, PageControl pc) {
        Collection<ServiceType> serviceTypes = serviceTypeDAO.findAll();
        // valuePager converts local/remote interfaces to value objects
        // as it pages through them.
        return valuePager.seek(serviceTypes, pc);
    }

    /**
     * @return List of ServiceTypeValues
     */
    @Transactional(readOnly = true)
    public PageList<ServiceTypeValue> getViewableServiceTypes(AuthzSubject subject, PageControl pc)
            throws PermissionException, NotFoundException {
        // build the server types from the visible list of servers
        final List<Integer> authzPks = getViewableServices(subject);
        final Collection<ServiceType> serviceTypes = serviceDAO.getServiceTypes(authzPks, true);

        // valuePager converts local/remote interfaces to value objects
        // as it pages through them.
        return valuePager.seek(serviceTypes, pc);
    }

    @Transactional(readOnly = true)
    public PageList<ServiceTypeValue> getServiceTypesByServerType(AuthzSubject subject, int serverTypeId) {
        Collection<ServiceType> serviceTypes = serviceTypeDAO.findByServerType_orderName(serverTypeId, true);
        if (serviceTypes.size() == 0) {
            return new PageList<ServiceTypeValue>();
        }
        return valuePager.seek(serviceTypes, PageControl.PAGE_ALL);
    }

    @Transactional(readOnly = true)
    public PageList<ServiceTypeValue> findVirtualServiceTypesByPlatform(AuthzSubject subject, Integer platformId) {
        Collection<ServiceType> serviceTypes = serviceTypeDAO
                .findVirtualServiceTypesByPlatform(platformId.intValue());
        if (serviceTypes.size() == 0) {
            return new PageList<ServiceTypeValue>();
        }
        return valuePager.seek(serviceTypes, PageControl.PAGE_ALL);
    }

    /**
     * @return A List of ServiceValue objects representing all of the services
     *         that the given subject is allowed to view.
     */
    @Transactional(readOnly = true)
    public PageList<ServiceValue> getAllServices(AuthzSubject subject, PageControl pc)
            throws PermissionException, NotFoundException {
        // valuePager converts local/remote interfaces to value objects
        // as it pages through them.
        return valuePager.seek(getViewableServices(subject, pc), pc);
    }

    /**
     * Get the scope of viewable services for a given user
     * @return List of ServiceLocals for which subject has
     *         AuthzConstants.serviceOpViewService
     */
    private Collection<Service> getViewableServices(AuthzSubject subject, PageControl pc)
            throws PermissionException, NotFoundException {
        // get list of pks user can view
        final Collection<Integer> authzPks = getViewableServices(subject);
        List<Service> services;
        pc = PageControl.initDefaults(pc, SortAttribute.RESOURCE_NAME);
        switch (pc.getSortattribute()) {
        case SortAttribute.RESOURCE_NAME:
            services = getServices(authzPks, pc);
            Collections.sort(services, new AppdefNameComparator(pc.isAscending()));
            break;
        case SortAttribute.SERVICE_NAME:
            services = getServices(authzPks, pc);
            Collections.sort(services, new AppdefNameComparator(pc.isAscending()));
            break;
        case SortAttribute.CTIME:
            services = getServices(authzPks, pc);
            Collections.sort(services, new ServiceCtimeComparator(pc.isAscending()));
            break;
        default:
            services = getServices(authzPks, pc);
            break;
        }
        return services;
    }

    /**
     * Note: This method pulls all services from the EHCache one by one. This
     * should be faster than pulling everything from the DB since they are in
     * memory.
     */
    private List<Service> getServices(Collection<Integer> authzPks, PageControl pc) {
        final List<Integer> aeids = new ArrayList<Integer>(authzPks);
        final List<Service> rtn = new ArrayList<Service>(authzPks.size());
        final int start = pc.getPageEntityIndex();
        final int end = (pc.getPagesize() == PageControl.SIZE_UNLIMITED) ? authzPks.size()
                : pc.getPagesize() + start;
        for (int i = start; i < end; i++) {
            final Integer aeid = aeids.get(i);
            try {
                final Service s = findServiceById(aeid);
                final Resource r = s.getResource();
                if (r == null || r.isInAsyncDeleteState()) {
                    continue;
                }
                rtn.add(s);
            } catch (ServiceNotFoundException e) {
                log.debug(e.getMessage(), e);
            }
        }
        return rtn;
    }

    private class ServiceCtimeComparator implements Comparator<AppdefResource> {
        final boolean _asc;

        ServiceCtimeComparator(boolean ascending) {
            _asc = ascending;
        }

        public int compare(AppdefResource arg0, AppdefResource arg1) {

            final Long c0 = new Long(arg0.getCreationTime());
            final Long c1 = new Long(arg1.getCreationTime());
            return (_asc) ? c0.compareTo(c1) : c1.compareTo(c0);
        }
    }

    /**
     * Fetch all services that haven't been assigned to a cluster and that
     * haven't been assigned to any applications.
     * @return A List of ServiceValue objects representing all of the unassigned
     *         services that the given subject is allowed to view.
     */
    @Transactional(readOnly = true)
    public PageList<ServiceValue> getAllClusterAppUnassignedServices(AuthzSubject subject, PageControl pc)
            throws PermissionException, NotFoundException {
        // get list of pks user can view
        Set<Integer> authzPks = new HashSet<Integer>(getViewableServices(subject));
        Collection<Service> services = null;
        Collection<Service> toBePaged = new ArrayList<Service>();
        pc = PageControl.initDefaults(pc, SortAttribute.RESOURCE_NAME);

        switch (pc.getSortattribute()) {
        case SortAttribute.RESOURCE_NAME:
            if (pc != null) {
                services = serviceDAO.findAllClusterAppUnassigned_orderName(pc.isAscending());
            }
            break;
        case SortAttribute.SERVICE_NAME:
            if (pc != null) {
                services = serviceDAO.findAllClusterAppUnassigned_orderName(pc.isAscending());
            }
            break;
        default:
            services = serviceDAO.findAllClusterAppUnassigned_orderName(true);
            break;
        }
        for (Service aService : services) {

            // remove service if its not viewable
            if (authzPks.contains(aService.getId())) {
                toBePaged.add(aService);
            }
        }
        // valuePager converts local/remote interfaces to value objects
        // as it pages through them.
        return valuePager.seek(toBePaged, pc);
    }

    private PageList filterAndPage(Collection svcCol, AuthzSubject subject, Integer svcTypeId, PageControl pc)
            throws ServiceNotFoundException, PermissionException {
        List services = new ArrayList();
        // iterate over the services and only include those whose pk is
        // present in the viewablePKs list
        if (svcTypeId != null && svcTypeId != APPDEF_RES_TYPE_UNDEFINED) {
            for (Iterator it = svcCol.iterator(); it.hasNext();) {
                Object o = it.next();
                Integer thisSvcTypeId;

                if (o instanceof Service) {
                    thisSvcTypeId = ((Service) o).getServiceType().getId();
                } else {
                    ResourceGroup cluster = (ResourceGroup) o;
                    thisSvcTypeId = cluster.getResourcePrototype().getInstanceId();
                }
                // first, if they specified a server type, then filter on it
                if (!(thisSvcTypeId.equals(svcTypeId))) {
                    continue;
                }

                services.add(o instanceof Service ? o : getServiceCluster((ResourceGroup) o));
            }
        } else {
            services.addAll(svcCol);
        }

        List toBePaged = filterUnviewable(subject, services);
        return valuePager.seek(toBePaged, pc);
    }

    /**
     * @return {@link List} of {@link AppdefEntityID}s that represent the total
     *         set of service inventory that the subject is authorized to see.
     *         This includes all services as well as all clusters
     */
    protected List<AppdefEntityID> getViewableServiceInventory(AuthzSubject whoami)
            throws PermissionException, NotFoundException {
        List<Integer> idList = getViewableServices(whoami);
        List<AppdefEntityID> ids = new ArrayList<AppdefEntityID>();
        for (int i = 0; i < idList.size(); i++) {
            Integer pk = (Integer) idList.get(i);
            ids.add(AppdefEntityID.newServiceID(pk));
        }

        Collection<Integer> viewableGroups = permissionManager.findOperationScopeBySubject(whoami,
                AuthzConstants.groupOpViewResourceGroup, AuthzConstants.groupResourceTypeName);
        List<AppdefEntityID> groupIds = new ArrayList<AppdefEntityID>();
        for (Integer gid : viewableGroups) {
            groupIds.add(AppdefEntityID.newGroupID(gid));
        }
        ids.addAll(groupIds);
        return ids;
    }

    /**
     * Get the scope of viewable services for a given user
     * @param whoami - the user
     * @return List of ServicePK's for which subject has
     *         AuthzConstants.serviceOpViewService
     */
    protected List<Integer> getViewableServices(AuthzSubject whoami) throws PermissionException, NotFoundException {
        Operation op = getOperationByName(resourceManager.findResourceTypeByName(AuthzConstants.serviceResType),
                AuthzConstants.serviceOpViewService);
        Collection<Integer> idList = permissionManager.findOperationScopeBySubject(whoami, op.getId());
        return new ArrayList<Integer>(idList);
    }

    private List filterUnviewable(AuthzSubject subject, Collection services)
            throws PermissionException, ServiceNotFoundException {
        List<AppdefEntityID> viewableEntityIds;
        try {
            viewableEntityIds = getViewableServiceInventory(subject);
        } catch (NotFoundException e) {
            throw new ServiceNotFoundException("no viewable services for " + subject);
        }

        List retVal = new ArrayList();
        // if a cluster has some members that aren't viewable then
        // the user can't get at them but we don't worry about it here
        // when the cluster members are accessed, the group subsystem
        // will filter them
        // so here's the case for the ServiceLocal amongst the
        // List of services
        // *****************
        // Note: yes, that's the case with regard to group members,
        // but not groups themselves. Clusters still need to be weeded
        // out here. - desmond
        for (Iterator iter = services.iterator(); iter.hasNext();) {
            Object o = iter.next();
            if (o instanceof Service) {
                Service aService = (Service) o;
                if (viewableEntityIds != null && viewableEntityIds.contains(aService.getEntityId())) {
                    retVal.add(o);
                }
            } else if (o instanceof ResourceGroup) {
                ResourceGroup aCluster = (ResourceGroup) o;
                AppdefEntityID clusterId = AppdefEntityID.newGroupID(aCluster.getId());
                if (viewableEntityIds != null && viewableEntityIds.contains(clusterId)) {
                    retVal.add(getServiceCluster(aCluster));
                }
            }
        }
        return retVal;
    }

    /**
     * Get services by server and type.
     */
    @Transactional(readOnly = true)
    public PageList getServicesByServer(AuthzSubject subject, Integer serverId, PageControl pc)
            throws ServiceNotFoundException, ServerNotFoundException, PermissionException {
        return getServicesByServer(subject, serverId, null, pc);
    }

    @Transactional(readOnly = true)
    public PageList<ServiceValue> getServicesByServer(AuthzSubject subject, Integer serverId, Integer svcTypeId,
            PageControl pc) throws ServiceNotFoundException, PermissionException {
        List toBePaged = getServicesByServerImpl(subject, serverId, svcTypeId, pc);
        return valuePager.seek(toBePaged, pc);
    }

    private List getServicesByServerImpl(AuthzSubject subject, Integer serverId, Integer svcTypeId, PageControl pc)
            throws PermissionException, ServiceNotFoundException {
        if (svcTypeId == null) {
            svcTypeId = APPDEF_RES_TYPE_UNDEFINED;
        }

        List<Service> services;

        switch (pc.getSortattribute()) {
        case SortAttribute.SERVICE_TYPE:
            services = serviceDAO.findByServer_orderType(serverId);
            break;
        case SortAttribute.SERVICE_NAME:
        default:
            if (svcTypeId != APPDEF_RES_TYPE_UNDEFINED) {
                services = serviceDAO.findByServerAndType_orderName(serverId, svcTypeId);
            } else {
                services = serviceDAO.findByServer_orderName(serverId);
            }
            break;
        }
        // Reverse the list if descending
        if (pc != null && pc.isDescending()) {
            Collections.reverse(services);
        }

        return filterUnviewable(subject, services);
    }

    /**
     * Get service POJOs by server and type.
     */
    @Transactional(readOnly = true)
    public List getServicesByServer(AuthzSubject subject, Server server)
            throws PermissionException, ServiceNotFoundException {
        return filterUnviewable(subject, server.getServices());
    }

    @Transactional(readOnly = true)
    public Integer[] getServiceIdsByServer(AuthzSubject subject, Integer serverId, Integer svcTypeId)
            throws ServiceNotFoundException, PermissionException {
        if (svcTypeId == null)
            svcTypeId = APPDEF_RES_TYPE_UNDEFINED;

        List<Service> services;

        if (svcTypeId == APPDEF_RES_TYPE_UNDEFINED) {
            services = serviceDAO.findByServer_orderType(serverId);
        } else {
            services = serviceDAO.findByServerAndType_orderName(serverId, svcTypeId);
        }

        // Filter the unviewables
        List<Service> viewables = filterUnviewable(subject, services);

        Integer[] ids = new Integer[viewables.size()];
        Iterator<Service> it = viewables.iterator();
        for (int i = 0; it.hasNext(); i++) {
            Service local = it.next();
            ids[i] = local.getId();
        }

        return ids;
    }

    @Transactional(readOnly = true)
    public List<ServiceValue> getServicesByType(AuthzSubject subject, String svcName, boolean asc)
            throws PermissionException, InvalidAppdefTypeException {
        ServiceType st = serviceTypeDAO.findByName(svcName);
        if (st == null) {
            return new PageList<ServiceValue>();
        }

        try {
            Collection<Service> services = serviceDAO.findByType(st.getId(), asc);
            if (services.size() == 0) {
                return new PageList<ServiceValue>();
            }
            List<ServiceValue> toBePaged = filterUnviewable(subject, services);
            return valuePager.seek(toBePaged, PageControl.PAGE_ALL);
        } catch (ServiceNotFoundException e) {
            return new PageList<ServiceValue>();
        }
    }

    @Transactional(readOnly = true)
    public PageList getServicesByService(AuthzSubject subject, Integer serviceId, PageControl pc)
            throws ServiceNotFoundException, PermissionException {
        return getServicesByService(subject, serviceId, APPDEF_RES_TYPE_UNDEFINED, pc);
    }

    /**
     * Get services by server.
     */
    @Transactional(readOnly = true)
    public PageList<ServiceValue> getServicesByService(AuthzSubject subject, Integer serviceId, Integer svcTypeId,
            PageControl pc) throws ServiceNotFoundException, PermissionException {
        // find any children
        Collection<Service> childSvcs = serviceDAO.findByParentAndType(serviceId, svcTypeId);
        return filterAndPage(childSvcs, subject, svcTypeId, pc);
    }

    /**
     * Get service IDs by service.
     */
    @Transactional(readOnly = true)
    public Integer[] getServiceIdsByService(AuthzSubject subject, Integer serviceId, Integer svcTypeId)
            throws ServiceNotFoundException, PermissionException {
        // find any children
        Collection<Service> childSvcs = serviceDAO.findByParentAndType(serviceId, svcTypeId);

        List<Service> viewables = filterUnviewable(subject, childSvcs);

        Integer[] ids = new Integer[viewables.size()];
        Iterator<Service> it = viewables.iterator();
        for (int i = 0; it.hasNext(); i++) {
            Service local = it.next();
            ids[i] = local.getId();
        }

        return ids;
    }

    @Transactional(readOnly = true)
    public PageList getServicesByPlatform(AuthzSubject subject, Integer platId, PageControl pc)
            throws ServiceNotFoundException, PlatformNotFoundException, PermissionException {
        return getServicesByPlatform(subject, platId, APPDEF_RES_TYPE_UNDEFINED, pc);
    }

    /**
     * Get platform services (children of virtual servers)
     */
    @Transactional(readOnly = true)
    public PageList getPlatformServices(AuthzSubject subject, Integer platId, PageControl pc)
            throws PlatformNotFoundException, PermissionException, ServiceNotFoundException {
        return getPlatformServices(subject, platId, APPDEF_RES_TYPE_UNDEFINED, pc);
    }

    /**
     * Get platform services (children of virtual servers) of a specified type
     */
    @Transactional(readOnly = true)
    public PageList getPlatformServices(AuthzSubject subject, Integer platId, Integer typeId, PageControl pc)
            throws PlatformNotFoundException, PermissionException, ServiceNotFoundException {
        pc = PageControl.initDefaults(pc, SortAttribute.SERVICE_NAME);
        Collection<Service> allServices = serviceDAO.findPlatformServices_orderName(platId, pc.isAscending());
        return filterAndPage(allServices, subject, typeId, pc);
    }

    /**
     * Get {@link Service}s which are children of the server, and of the
     * specified type.
     */
    @Transactional(readOnly = true)
    public List<Service> findServicesByType(Server server, ServiceType st) {
        return serviceDAO.findByServerAndType_orderName(server.getId(), st.getId());
    }

    /**
     * Get platform service POJOs
     */
    @Transactional(readOnly = true)
    public List<Service> findPlatformServicesByType(Platform p, ServiceType st) {
        return serviceDAO.findPlatformServicesByType(p, st);
    }

    /**
     * Get platform service POJOs
     */
    @Transactional(readOnly = true)
    public Collection getPlatformServices(AuthzSubject subject, Integer platId)
            throws ServiceNotFoundException, PermissionException {
        Collection<Service> services = serviceDAO.findPlatformServices_orderName(platId, true);
        return filterUnviewable(subject, services);
    }

    /**
     * Get platform services (children of virtual servers), mapped by type id of
     * a specified type
     */
    @Transactional(readOnly = true)
    public Map<Integer, List> getMappedPlatformServices(AuthzSubject subject, Integer platId, PageControl pc)
            throws PlatformNotFoundException, PermissionException, ServiceNotFoundException {
        pc = PageControl.initDefaults(pc, SortAttribute.SERVICE_NAME);

        Collection<Service> allServices = serviceDAO.findPlatformServices_orderName(platId, pc.isAscending());
        HashMap<Integer, List> retMap = new HashMap<Integer, List>();

        // Map all services by type ID
        for (Service svc : allServices) {

            Integer typeId = svc.getServiceType().getId();
            List<Service> addTo = (List<Service>) MapUtil.getOrCreate(retMap, typeId, ArrayList.class);

            addTo.add(svc);
        }

        // Page the lists before returning
        for (Map.Entry<Integer, List> entry : retMap.entrySet()) {

            Integer typeId = entry.getKey();
            List svcs = entry.getValue();

            PageControl pcCheck = svcs.size() <= pc.getPagesize() ? PageControl.PAGE_ALL : pc;

            svcs = filterAndPage(svcs, subject, typeId, pcCheck);
            entry.setValue(svcs);
        }

        return retMap;
    }

    /**
     * Get services by platform.
     */
    @Transactional(readOnly = true)
    public PageList<ServiceValue> getServicesByPlatform(AuthzSubject subject, Integer platId, Integer svcTypeId,
            PageControl pc) throws ServiceNotFoundException, PlatformNotFoundException, PermissionException {
        Collection<Service> allServices;
        pc = PageControl.initDefaults(pc, SortAttribute.SERVICE_NAME);

        switch (pc.getSortattribute()) {
        case SortAttribute.SERVICE_NAME:
            allServices = serviceDAO.findByPlatform_orderName(platId, pc.isAscending());
            break;
        case SortAttribute.SERVICE_TYPE:
            allServices = serviceDAO.findByPlatform_orderType(platId, pc.isAscending());
            break;
        default:
            throw new IllegalArgumentException("Invalid sort attribute");
        }
        return filterAndPage(allServices, subject, svcTypeId, pc);
    }

    /**
     * @return A List of ServiceValue and ServiceClusterValue objects
     *         representing all of the services that the given subject is
     *         allowed to view.
     */
    @Transactional(readOnly = true)
    public PageList getServicesByApplication(AuthzSubject subject, Integer appId, PageControl pc)
            throws ApplicationNotFoundException, ServiceNotFoundException, PermissionException {
        return getServicesByApplication(subject, appId, APPDEF_RES_TYPE_UNDEFINED, pc);
    }

    /**
     * @return A List of ServiceValue and ServiceClusterValue objects
     *         representing all of the services that the given subject is
     *         allowed to view.
     * @throws ApplicationNotFoundException if the appId is bogus
     * @throws ServiceNotFoundException if services could not be looked up
     */
    @Transactional(readOnly = true)
    public PageList getServicesByApplication(AuthzSubject subject, Integer appId, Integer svcTypeId, PageControl pc)
            throws PermissionException, ApplicationNotFoundException, ServiceNotFoundException {
        return filterAndPage(getServicesByApplication(appId, pc), subject, svcTypeId, pc);
    }

    /**
     * @return A List of Service and ServiceCluster objects representing all of
     *         the services that the given subject is allowed to view.
     * @throws ApplicationNotFoundException if the appId is bogus
     * @throws ServiceNotFoundException if services could not be looked up
     */
    @Transactional(readOnly = true)
    public List getServicesByApplication(AuthzSubject subject, Integer appId)
            throws PermissionException, ApplicationNotFoundException, ServiceNotFoundException {
        return filterUnviewable(subject, getServicesByApplication(appId, PageControl.PAGE_ALL));
    }

    private List getServicesByApplication(Integer appId, PageControl pc) throws ApplicationNotFoundException {
        try {
            // we only look up the application to validate
            // the appId param
            applicationDAO.findById(appId);
        } catch (ObjectNotFoundException e) {
            throw new ApplicationNotFoundException(appId, e);
        }

        Collection<AppService> appServiceCollection;

        pc = PageControl.initDefaults(pc, SortAttribute.SERVICE_NAME);

        switch (pc.getSortattribute()) {
        case SortAttribute.SERVICE_NAME:
        case SortAttribute.RESOURCE_NAME:
            appServiceCollection = appServiceDAO.findByApplication_orderSvcName(appId, pc.isAscending());
            break;
        case SortAttribute.SERVICE_TYPE:
            appServiceCollection = appServiceDAO.findByApplication_orderSvcType(appId, pc.isAscending());
            break;
        default:
            throw new IllegalArgumentException(
                    "Unsupported sort " + "attribute [" + pc.getSortattribute() + "] on PageControl : " + pc);
        }

        List services = new ArrayList();
        for (AppService appService : appServiceCollection) {

            if (appService.isIsGroup()) {
                services.add(appService.getResourceGroup());
            } else {
                services.add(appService.getService());
            }
        }
        return services;
    }

    /**
     * @return A List of ServiceValue and ServiceClusterValue objects
     *         representing all of the services that the given subject is
     *         allowed to view.
     */
    @Transactional(readOnly = true)
    public PageList getServiceInventoryByApplication(AuthzSubject subject, Integer appId, PageControl pc)
            throws ApplicationNotFoundException, ServiceNotFoundException, PermissionException {
        return getServiceInventoryByApplication(subject, appId, APPDEF_RES_TYPE_UNDEFINED, pc);
    }

    /**
     * Get all services by application. This is to only be used for the Evident
     * API.
     */
    @Transactional(readOnly = true)
    public PageList getFlattenedServicesByApplication(AuthzSubject subject, Integer appId, Integer typeId,
            PageControl pc) throws ApplicationNotFoundException, ServiceNotFoundException, PermissionException {
        if (typeId == null)
            typeId = APPDEF_RES_TYPE_UNDEFINED;

        final StopWatch watch = new StopWatch();
        final boolean debug = log.isDebugEnabled();

        Application appLocal;
        try {
            appLocal = applicationDAO.findById(appId);
        } catch (ObjectNotFoundException e) {
            throw new ApplicationNotFoundException(appId, e);
        }

        Collection<Service> svcCollection = new ArrayList<Service>();
        if (debug)
            watch.markTimeBegin("getAppServices");
        Collection<AppService> appSvcCollection = appLocal.getAppServices();
        if (debug)
            watch.markTimeEnd("getAppServices");

        for (AppService appService : appSvcCollection) {

            if (appService.isIsGroup()) {
                if (debug)
                    watch.markTimeBegin("getServiceCluster");
                ServiceCluster cluster = getServiceCluster(appService.getResourceGroup());
                if (debug)
                    watch.markTimeEnd("getServiceCluster");
                if (debug)
                    watch.markTimeBegin("getServices");
                svcCollection.addAll(cluster.getServices());
                if (debug)
                    watch.markTimeEnd("getServices");
            } else {
                svcCollection.add(appService.getService());
            }
        }

        if (debug)
            log.debug(watch);
        return filterAndPage(svcCollection, subject, typeId, pc);
    }

    /**
     * @return A List of ServiceValue and ServiceClusterValue objects
     *         representing all of the services that the given subject is
     *         allowed to view.
     */
    @Transactional(readOnly = true)
    public PageList getServiceInventoryByApplication(AuthzSubject subject, Integer appId, Integer svcTypeId,
            PageControl pc) throws ApplicationNotFoundException, ServiceNotFoundException, PermissionException {
        if (svcTypeId == null || svcTypeId.equals(APPDEF_RES_TYPE_UNDEFINED)) {
            List services = getUnflattenedServiceInventoryByApplication(subject, appId, pc);
            return filterAndPage(services, subject, APPDEF_RES_TYPE_UNDEFINED, pc);
        } else {
            return getFlattenedServicesByApplication(subject, appId, svcTypeId, pc);
        }
    }

    /**
     * Get all service inventory by application, including those inside an
     * associated cluster
     * 
     * @param subject The subject trying to list services.
     * @param appId Application id.
     * @return A List of ServiceValue objects representing all of the services
     *         that the given subject is allowed to view.
     */
    @Transactional(readOnly = true)
    public Integer[] getFlattenedServiceIdsByApplication(AuthzSubject subject, Integer appId)
            throws ServiceNotFoundException, PermissionException, ApplicationNotFoundException {

        final boolean debug = log.isDebugEnabled();
        final StopWatch watch = new StopWatch();
        if (debug)
            watch.markTimeBegin("getUnflattenedServiceInventoryByApplication");
        List serviceInventory = getUnflattenedServiceInventoryByApplication(subject, appId, PageControl.PAGE_ALL);
        if (debug)
            watch.markTimeEnd("getUnflattenedServiceInventoryByApplication");

        List<Integer> servicePKs = new ArrayList<Integer>();
        // flattening: open up all of the groups (if any) and get their services
        // as well
        try {
            for (Iterator iter = serviceInventory.iterator(); iter.hasNext();) {
                Object o = iter.next();
                // applications can have both clusters and services
                if (o instanceof Service) {
                    Service service = (Service) o;
                    // servers will only have these
                    servicePKs.add(service.getId());
                } else {
                    // this only happens when entId is for an application and
                    // a cluster is bound to it
                    ResourceGroup cluster = (ResourceGroup) o;
                    AppdefEntityID groupId = AppdefEntityID.newGroupID(cluster.getId());
                    // any authz resource filtering on the group members happens
                    // inside the group subsystem
                    try {
                        if (debug)
                            watch.markTimeBegin("getCompatGroupMembers");
                        List<AppdefEntityID> memberIds = GroupUtil.getCompatGroupMembers(subject, groupId, null,
                                PageControl.PAGE_ALL);
                        if (debug)
                            watch.markTimeEnd("getCompatGroupMembers");
                        for (AppdefEntityID memberEntId : memberIds) {

                            servicePKs.add(memberEntId.getId());
                        }
                    } catch (PermissionException e) {
                        // User not allowed to see this group
                        log.debug("User " + subject + " not allowed to view " + "group " + groupId);
                    }
                }
            }
        } catch (GroupNotCompatibleException e) {
            throw new InvalidAppdefTypeException("serviceInventory has groups that are not compatible", e);
        } catch (AppdefEntityNotFoundException e) {
            throw new ServiceNotFoundException("could not return all services", e);
        }
        if (debug)
            log.debug(watch);
        return (Integer[]) servicePKs.toArray(new Integer[servicePKs.size()]);
    }

    private List getUnflattenedServiceInventoryByApplication(AuthzSubject subject, Integer appId, PageControl pc)
            throws ApplicationNotFoundException, ServiceNotFoundException {
        final boolean debug = log.isDebugEnabled();
        final StopWatch watch = new StopWatch();

        try {
            applicationDAO.findById(appId);
        } catch (ObjectNotFoundException e) {
            throw new ApplicationNotFoundException(appId, e);
        }

        pc = PageControl.initDefaults(pc, SortAttribute.SERVICE_NAME);

        Collection<AppService> appServices = null;
        switch (pc.getSortattribute()) {
        case SortAttribute.SERVICE_NAME:
        case SortAttribute.RESOURCE_NAME:
        case SortAttribute.NAME:
            // TODO: Not actually sorting
            if (debug)
                watch.markTimeBegin("findByApplication_orderName");
            appServices = appServiceDAO.findByApplication_orderName(appId);
            if (debug)
                watch.markTimeEnd("findByApplication_orderName");
            break;
        case SortAttribute.SERVICE_TYPE:
            if (debug)
                watch.markTimeBegin("findByApplication_orderType");
            appServices = appServiceDAO.findByApplication_orderType(appId, pc.isAscending());
            if (debug)
                watch.markTimeEnd("findByApplication_orderType");
            break;
        default:
            throw new IllegalArgumentException(
                    "Unsupported sort attribute [" + pc.getSortattribute() + "] on PageControl : " + pc);
        }

        // XXX Call to authz, get the collection of all services
        // that we are allowed to see.
        // OR, alternatively, find everything, and then call out
        // to authz in batches to find out which ones we are
        // allowed to return.

        List services = new ArrayList(appServices.size());
        for (AppService appService : appServices) {
            if (appService.isIsGroup()) {
                if (debug)
                    watch.markTimeBegin("appService.getResourceGroup");
                services.add(appService.getResourceGroup());
                if (debug)
                    watch.markTimeEnd("appService.getResourceGroup");
            } else {
                if (debug)
                    watch.markTimeBegin("appService.getService");
                services.add(appService.getService());
                if (debug)
                    watch.markTimeEnd("appService.getService");
            }
        }
        if (debug)
            log.debug(watch);
        return services;
    }

    public void updateServiceZombieStatus(AuthzSubject subject, Service svc, boolean zombieStatus)
            throws PermissionException {
        permissionManager.checkModifyPermission(subject, svc.getEntityId());
        svc.setModifiedBy(subject.getName());
        svc.setAutodiscoveryZombie(zombieStatus);
    }

    public Service updateService(AuthzSubject subject, ServiceValue existing)
            throws PermissionException, UpdateException, AppdefDuplicateNameException, ServiceNotFoundException {
        permissionManager.checkModifyPermission(subject, existing.getEntityId());
        Service service = serviceDAO.findById(existing.getId());
        String oldName = null;

        existing.setModifiedBy(subject.getName());
        if (existing.getDescription() != null)
            existing.setDescription(existing.getDescription().trim());
        if (existing.getLocation() != null)
            existing.setLocation(existing.getLocation().trim());
        if (existing.getName() != null) {
            oldName = service.getName();
            existing.setName(existing.getName().trim());
        }

        Map<String, String> changedProps = service.changedProperties(existing);
        if (changedProps.isEmpty()) {
            log.debug("No changes found between value object and entity");
        } else {
            service.updateService(existing);
            Resource r = service.getResource();
            this.zeventManager.enqueueEventAfterCommit(
                    new ResourceContentChangedZevent(r.getId(), r.getName(), null, changedProps, oldName));
        }
        return service;
    }

    public void updateServiceTypes(String plugin, ServiceTypeInfo[] infos) throws VetoException, NotFoundException {
        final boolean debug = log.isDebugEnabled();
        StopWatch watch = new StopWatch();
        AuthzSubject overlord = authzSubjectManager.getOverlordPojo();

        // First, put all of the infos into a Hash
        HashMap<String, ServiceTypeInfo> infoMap = new HashMap<String, ServiceTypeInfo>();
        List<String> names = new ArrayList<String>();
        for (int i = 0; i < infos.length; i++) {
            infoMap.put(infos[i].getName(), infos[i]);
            names.add(infos[i].getName());
        }

        List<ServerType> types = serverTypeDAO.findByName(names);
        HashMap<String, ServerType> serverTypes = new HashMap<String, ServerType>(types.size());
        for (ServerType type : types) {
            serverTypes.put(type.getName(), type);
        }

        try {
            Collection<ServiceType> curServices = serviceTypeDAO.findByPlugin(plugin);

            for (ServiceType serviceType : curServices) {

                if (log.isDebugEnabled()) {
                    log.debug("Begin updating ServiceTypeLocal: " + serviceType.getName());
                }

                ServiceTypeInfo sinfo = (ServiceTypeInfo) infoMap.remove(serviceType.getName());

                // See if this exists
                if (sinfo == null) {
                    deleteServiceType(serviceType, overlord, resourceGroupManager, resourceManager);
                } else {
                    // Just update it
                    // XXX TODO MOVE THIS INTO THE ENTITY
                    if (!sinfo.getName().equals(serviceType.getName()))
                        serviceType.setName(sinfo.getName());

                    if (!sinfo.getDescription().equals(serviceType.getDescription()))
                        serviceType.setDescription(sinfo.getDescription());

                    if (sinfo.getInternal() != serviceType.isIsInternal())
                        serviceType.setIsInternal(sinfo.getInternal());

                    // Could be null if servertype was deleted/updated by plugin
                    ServerType svrtype = serviceType.getServerType();

                    // Check server type
                    if (svrtype == null || !sinfo.getServerName().equals(svrtype.getName())) {
                        // Lookup the server type
                        if (null == (svrtype = serverTypes.get(sinfo.getServerName()))) {
                            svrtype = serverTypeDAO.findByName(sinfo.getServerName());
                            if (svrtype == null) {
                                throw new NotFoundException("Unable to find server " + sinfo.getServerName()
                                        + " on which service '" + serviceType.getName() + "' relies");
                            }
                            serverTypes.put(svrtype.getName(), svrtype);
                        }
                        serviceType.setServerType(svrtype);
                    }
                }
            }

            // Now create the left-overs
            final ResourceType resType = resourceManager
                    .findResourceTypeByName(AuthzConstants.servicePrototypeTypeName);
            final Resource rootResource = resourceManager.findRootResource();
            final Set<String> creates = new HashSet<String>();
            for (final ServiceTypeInfo sinfo : infoMap.values()) {
                ServerType servType;
                if (null == (servType = serverTypes.get(sinfo.getServerName()))) {
                    servType = serverTypeDAO.findByName(sinfo.getServerName());
                    serverTypes.put(servType.getName(), servType);
                }
                if (creates.contains(sinfo.getName())) {
                    continue;
                }
                creates.add(sinfo.getName());
                if (debug)
                    watch.markTimeBegin("create");
                createServiceType(sinfo, plugin, servType, rootResource, resType);
                if (debug)
                    watch.markTimeEnd("create");
            }
        } finally {
            if (debug)
                log.debug(watch);
        }
    }

    public ServiceType createServiceType(ServiceTypeInfo sinfo, String plugin, ServerType servType)
            throws NotFoundException {
        Resource rootResource = resourceManager.findRootResource();
        return createServiceType(sinfo, plugin, servType, rootResource,
                resourceManager.findResourceTypeByName(AuthzConstants.servicePrototypeTypeName));
    }

    private ServiceType createServiceType(ServiceTypeInfo sinfo, String plugin, ServerType servType,
            Resource rootResource, ResourceType serviceType) throws NotFoundException {
        ServiceType stype = serviceTypeDAO.create(sinfo.getName(), plugin, sinfo.getDescription(),
                sinfo.getInternal());
        stype.setServerType(servType);
        resourceManager.createResource(authzSubjectManager.getOverlordPojo(), serviceType, rootResource,
                stype.getId(), stype.getName(), false, null);
        return stype;
    }

    public void deleteServiceType(ServiceType serviceType, AuthzSubject overlord, ResourceGroupManager resGroupMan,
            ResourceManager resMan) throws VetoException {
        Resource proto = resMan.findResourceByInstanceId(AuthzConstants.authzServiceProto, serviceType.getId());

        try {
            // Delete compatible groups of this type.
            resGroupMan.removeGroupsCompatibleWith(proto);

            // Remove all services
            Service[] services = serviceType.getServices().toArray(new Service[serviceType.getServices().size()]);
            for (int i = 0; i < services.length; i++) {
                removeService(overlord, services[i]);
            }
        } catch (PermissionException e) {
            assert false : "Overlord should not run into PermissionException";
        }

        serviceTypeDAO.remove(serviceType);

        resMan.removeResource(overlord, proto);
    }

    /**
     * Map a ResourceGroup to ServiceCluster, just temporary, should be able to
     * remove when done with the ServiceCluster to ResourceGroup Migration
     */
    @Transactional(readOnly = true)
    public ServiceCluster getServiceCluster(ResourceGroup group) {
        if (group == null) {
            return null;
        }
        ServiceCluster sc = new ServiceCluster();
        sc.setName(group.getName());
        sc.setDescription(group.getDescription());
        sc.setGroup(group);

        Collection<Resource> resources = resourceGroupManager.getMembers(group);

        Set<Service> services = new HashSet<Service>(resources.size());

        ServiceType st = null;
        for (Resource resource : resources) {

            // this should not be the case
            if (!resource.getResourceType().getId().equals(AuthzConstants.authzService)) {
                continue;
            }
            Service service = serviceDAO.findById(resource.getInstanceId());
            if (st == null) {
                st = service.getServiceType();
            }
            services.add(service);
            service.setResourceGroup(sc.getGroup());
        }
        sc.setServices(services);

        if (st == null && group.getResourcePrototype() != null) {
            st = serviceTypeDAO.findById(group.getResourcePrototype().getInstanceId());
        }

        if (st != null) {
            sc.setServiceType(st);
        }
        return sc;
    }

    /**
     * A removeService method that takes a ServiceLocal. This is called by
     * ServerManager.removeServer when cascading a delete onto services.
     */

    public void removeService(AuthzSubject subject, Service service) throws PermissionException, VetoException {
        AppdefEntityID aeid = service.getEntityId();
        permissionManager.checkRemovePermission(subject, aeid);

        // Remove service from parent Server's Services collection
        Server server = service.getServer();
        if (server != null) {
            server.getServices().remove(service);
        }

        // Remove from ServiceType collection
        service.getServiceType().getServices().remove(service);

        final ConfigResponseDB config = service.getConfigResponse();

        // remove from appdef

        serviceDAO.remove(service);

        // remove the config response
        if (config != null) {
            configResponseDAO.remove(config);
        }

        // remove custom properties
        cpropManager.deleteValues(aeid.getType(), aeid.getID());

        // Remove authz resource.
        resourceManager.removeAuthzResource(subject, aeid, service.getResource());

        serviceDAO.getSession().flush();
    }

    /**
     * Find an operation by name inside a ResourcetypeValue object
     */
    protected Operation getOperationByName(ResourceType rtV, String opName) throws PermissionException {
        Collection<Operation> ops = rtV.getOperations();
        for (Operation op : ops) {

            if (op.getName().equals(opName)) {
                return op;
            }
        }
        throw new PermissionException("Operation: " + opName + " not valid for ResourceType: " + rtV.getName());
    }

    public void handleResourceDelete(Resource resource) {
        resource.setResourceType(null);
    }

    /**
     * Returns a list of 2 element arrays. The first element is the name of the
     * service type, the second element is the # of services of that type in the
     * inventory.
     */
    @Transactional(readOnly = true)
    public List<Object[]> getServiceTypeCounts() {
        return serviceDAO.getServiceTypeCounts();
    }

    /**
     * Get the # of services within HQ inventory
     */
    @Transactional(readOnly = true)
    public Number getServiceCount() {
        return serviceDAO.getServiceCount();
    }

    @PostConstruct
    public void afterPropertiesSet() throws Exception {
        valuePager = Pager.getPager(VALUE_PROCESSOR);
    }

    public Collection<Service> getOrphanedServices() {
        return serviceDAO.getOrphanedServices();
    }

}