/**
* 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) [2009-2010], VMware, 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.bizapp.server.session;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.ObjectNotFoundException;
import org.hyperic.hq.appdef.Agent;
import org.hyperic.hq.appdef.server.session.Application;
import org.hyperic.hq.appdef.server.session.Platform;
import org.hyperic.hq.appdef.server.session.Server;
import org.hyperic.hq.appdef.server.session.Service;
import org.hyperic.hq.appdef.shared.AgentManager;
import org.hyperic.hq.appdef.shared.AppdefEntityID;
import org.hyperic.hq.appdef.shared.ApplicationManager;
import org.hyperic.hq.appdef.shared.PlatformManager;
import org.hyperic.hq.appdef.shared.PlatformNotFoundException;
import org.hyperic.hq.appdef.shared.ResourceTypeCleanupZevent;
import org.hyperic.hq.appdef.shared.ResourcesCleanupZevent;
import org.hyperic.hq.appdef.shared.ServerManager;
import org.hyperic.hq.appdef.shared.ServiceManager;
import org.hyperic.hq.authz.server.session.AuthzSubject;
import org.hyperic.hq.authz.server.session.GroupMember;
import org.hyperic.hq.authz.server.session.Resource;
import org.hyperic.hq.authz.server.session.ResourceGroup;
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.ResourceGroupManager;
import org.hyperic.hq.authz.shared.ResourceManager;
import org.hyperic.hq.bizapp.shared.AppdefBoss;
import org.hyperic.hq.common.ApplicationException;
import org.hyperic.hq.common.VetoException;
import org.hyperic.hq.common.server.session.Audit;
import org.hyperic.hq.common.shared.AuditManager;
import org.hyperic.hq.escalation.server.session.EscalationState;
import org.hyperic.hq.escalation.shared.EscalationManager;
import org.hyperic.hq.events.server.session.AlertDefinition;
import org.hyperic.hq.events.shared.AlertManager;
import org.hyperic.hq.measurement.shared.MeasurementManager;
import org.hyperic.hq.zevents.Zevent;
import org.hyperic.hq.zevents.ZeventEnqueuer;
import org.hyperic.hq.zevents.ZeventListener;
import org.hyperic.util.timer.StopWatch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
@Transactional
public class ResourceCleanupEventListener implements ZeventListener<ResourcesCleanupZevent>, ResourceCleanupEventListenerRegistrar {
private AppdefBoss appdefBoss;
private final Log log = LogFactory.getLog(ResourceCleanupEventListener.class);
private ZeventEnqueuer zEventManager;
private AuthzSubjectManager authzSubjectManager;
private ApplicationManager applicationManager;
private ResourceGroupManager resourceGroupManager;
private ServiceManager serviceManager;
private ServerManager serverManager;
private PlatformManager platformManager;
private AgentManager agentManager;
private MeasurementManager measurementManager;
private ResourceManager resourceManager;
private AlertManager alertManager;
private AuditManager auditManager;
private EscalationManager escalationManager;
@Autowired
public ResourceCleanupEventListener(AppdefBoss appdefBoss, ZeventEnqueuer zEventManager,
AuthzSubjectManager authzSubjectManager,
ApplicationManager applicationManager,
ResourceGroupManager resourceGroupManager,
ServiceManager serviceManager, ServerManager serverManager,
PlatformManager platformManager, AgentManager agentManager,
MeasurementManager measurementManager,
ResourceManager resourceManager, AlertManager alertManager,
AuditManager auditManager, EscalationManager escalationManager) {
this.appdefBoss = appdefBoss;
this.zEventManager = zEventManager;
this.authzSubjectManager = authzSubjectManager;
this.applicationManager = applicationManager;
this.resourceGroupManager = resourceGroupManager;
this.serviceManager = serviceManager;
this.serverManager = serverManager;
this.platformManager = platformManager;
this.agentManager = agentManager;
this.measurementManager = measurementManager;
this.resourceManager = resourceManager;
this.alertManager = alertManager;
this.auditManager = auditManager;
this.escalationManager = escalationManager;
}
/*
* Field 1: (0-59) second
* Field 2: (0-59) minute
* Field 3: (0-23) hour
* Field 4: (1-31) day of the month
* Field 5: (1-12) month of the year
* Field 6: (0-6) day of the week - 1=Monday
*/
@Scheduled(cron="0 50 9 * * *")
public void runCleanup() {
zEventManager.enqueueEventAfterCommit(new ResourcesCleanupZevent());
}
public void registerResourceCleanupListener() {
// Add listener to remove alert definition and alerts after resources
// are deleted.
HashSet<Class<? extends Zevent>> events = new HashSet<Class<? extends Zevent>>();
events.add(ResourcesCleanupZevent.class);
events.add(ResourceTypeCleanupZevent.class);
zEventManager.addBufferedListener(events, this);
zEventManager.enqueueEventAfterCommit(new ResourcesCleanupZevent());
}
public void processEvents(List<ResourcesCleanupZevent> events) {
log.info("starting removeDeletedResources");
final StopWatch watch = new StopWatch();
final Collection<String> typeNames = new ArrayList<String>();
for (final ResourcesCleanupZevent e : events) {
if (e instanceof ResourceTypeCleanupZevent) {
typeNames.addAll(((ResourceTypeCleanupZevent) e).getTypeNames());
}
}
if (events != null && !events.isEmpty()) {
try {
Map<Integer,List<AppdefEntityID>> agentCache = buildAsyncDeleteAgentCache(events);
removeDeletedResources(agentCache, typeNames);
final AuthzSubject overlord = authzSubjectManager.getOverlordPojo();
if (!typeNames.isEmpty()) {
resourceManager.removeResourceTypes(typeNames);
}
removeOrphanedPlatforms(overlord);
removeOrphanedServers(overlord);
removeOrphanedServices(overlord);
removeOrphanedAlertDefs();
removeOrphanedResourceGroupMembers();
removeOrphanedAudits();
removeOrphanedEscalationStates(overlord);
removeOrphanedResources(overlord);
} catch (Exception e) {
log.error("removeDeletedResources failed", e);
}
}
log.info("completed removeDeletedResources " + watch);
}
private void removeOrphanedResources(AuthzSubject overlord) {
final Collection<Resource> resources = resourceManager.getOrphanedResources();
if (!resources.isEmpty()) {
log.info("cleaning up " + resources.size() + " orphaned resources");
}
for (final Resource r : resources) {
try {
resourceManager.removeResource(overlord, r);
} catch (VetoException e) {
log.error(e,e);
}
}
}
private void removeOrphanedEscalationStates(AuthzSubject overlord) {
final Collection<EscalationState> escalationStates = escalationManager.getOrphanedEscalationStates();
if (!escalationStates.isEmpty()) {
log.info("cleaning up " + escalationStates.size() + " orphaned escalation states");
}
for (final EscalationState e : escalationStates) {
escalationManager.removeEscalationState(e);
}
}
private void removeOrphanedAudits() {
final Collection<Audit> audits = auditManager.getOrphanedAudits();
if (!audits.isEmpty()) {
log.info("cleaning up " + audits.size() + " orphaned audits");
}
for (final Audit a : audits) {
auditManager.deleteAudit(a);
}
}
private void removeOrphanedResourceGroupMembers() {
final Collection<GroupMember> members = resourceGroupManager.getOrphanedResourceGroupMembers();
if (!members.isEmpty()) {
log.info("cleaning up " + members.size() + " orphaned group members");
}
for (final GroupMember m : members) {
resourceGroupManager.removeGroupMember(m);
}
}
private void removeOrphanedAlertDefs() {
final Collection<AlertDefinition> alertDefs = alertManager.getOrphanedAlertDefs();
if (!alertDefs.isEmpty()) {
log.info("cleaning up " + alertDefs.size() + " orphaned alert definitions");
}
for (final AlertDefinition def : alertDefs) {
try {
alertManager.deleteAlertDef(def);
} catch (ObjectNotFoundException e) {
log.warn(e);
log.debug(e,e);
}
}
}
private void removeOrphanedPlatforms(AuthzSubject overlord) {
final Collection<Platform> platforms = platformManager.getOrphanedPlatforms();
if (!platforms.isEmpty()) {
log.info("cleaning up " + platforms.size() + " orphaned platforms");
}
for (Platform platform : platforms) {
try {
platform = platformManager.getPlatformById(platform.getId());
platformManager.removePlatform(overlord, platform);
} catch (ObjectNotFoundException e) {
log.warn(e);
log.debug(e,e);
} catch (PlatformNotFoundException e) {
log.warn(e);
log.debug(e,e);
} catch (PermissionException e) {
log.warn(e);
log.debug(e,e);
} catch (VetoException e) {
log.warn(e);
log.debug(e,e);
}
}
}
private void removeOrphanedServers(AuthzSubject overlord) {
final Collection<Server> servers = serverManager.getOrphanedServers();
if (!servers.isEmpty()) {
log.info("cleaning up " + servers.size() + " orphaned servers");
}
for (Server server : servers) {
try {
server = serverManager.getServerById(server.getId());
serverManager.removeServer(overlord, server);
} catch (ObjectNotFoundException e) {
log.warn(e);
log.debug(e,e);
} catch (PermissionException e) {
log.warn(e);
log.debug(e,e);
} catch (VetoException e) {
log.warn(e);
log.debug(e,e);
}
}
}
private void removeOrphanedServices(AuthzSubject overlord) {
final Collection<Service> services = serviceManager.getOrphanedServices();
if (!services.isEmpty()) {
log.info("cleaning up " + services.size() + " orphaned services");
}
for (Service service : services) {
try {
service = serviceManager.getServiceById(service.getId());
serviceManager.removeService(overlord, service);
} catch (ObjectNotFoundException e) {
log.warn(e);
log.debug(e,e);
} catch (PermissionException e) {
log.warn(e);
log.debug(e,e);
} catch (VetoException e) {
log.warn(e);
log.debug(e,e);
}
}
}
/**
* @param zevents {@link List} of {@link ResourcesCleanupZevent}
*
* @return {@link Map} of {@link Integer} of agentIds
* to {@link List} of {@link AppdefEntityID}s
*/
@SuppressWarnings("unchecked")
private Map<Integer,List<AppdefEntityID>> buildAsyncDeleteAgentCache(List<ResourcesCleanupZevent> zevents) {
Map<Integer,List<AppdefEntityID>> masterCache = new HashMap<Integer,List<AppdefEntityID>>();
for (ResourcesCleanupZevent z : zevents) {
if (z.getAgents() != null) {
Map<Integer,List<AppdefEntityID>> cache = z.getAgents();
for (Integer agentId : cache.keySet() ) {
List<AppdefEntityID> newResources = cache.get(agentId);
List<AppdefEntityID> resources = masterCache.get(agentId);
if (resources == null) {
resources = newResources;
} else {
resources.addAll(newResources);
}
masterCache.put(agentId, resources);
}
}
}
return masterCache;
}
@SuppressWarnings("unchecked")
private void removeDeletedResources(Map<Integer, List<AppdefEntityID>> agentCache, Collection<String> typeNames)
throws ApplicationException, VetoException {
final boolean debug = log.isDebugEnabled();
final StopWatch watch = new StopWatch();
final AuthzSubject subject = authzSubjectManager.findSubjectById(AuthzConstants.overlordId);
if (debug) watch.markTimeBegin("unscheduleMeasurementsForAsyncDelete");
unscheduleMeasurementsForAsyncDelete(agentCache);
if (debug) watch.markTimeEnd("unscheduleMeasurementsForAsyncDelete");
// Look through services, servers, platforms, applications, and groups
if (debug) watch.markTimeBegin("removeApplications");
Collection<Application> applications = applicationManager.findDeletedApplications();
removeApplications(subject, applications);
if (debug) watch.markTimeEnd("removeApplications");
if (debug) watch.markTimeBegin("removeResourceGroups");
Collection<ResourceGroup> groups = resourceGroupManager.findDeletedGroups();
removeResourceGroups(subject, groups);
if (debug) watch.markTimeEnd("removeResourceGroups");
typeNames = (typeNames == null) ? Collections.EMPTY_LIST : typeNames;
if (debug) watch.markTimeBegin("removeGroupsCompatibleWith");
for (String name : typeNames) {
resourceGroupManager.removeGroupsCompatibleWith(name);
}
if (debug) watch.markTimeEnd("removeGroupsCompatibleWith");
Collection<Service> services = serviceManager.findDeletedServices();
removeServices(subject, services);
Collection<Server> servers = serverManager.findDeletedServers();
removeServers(subject, servers);
if (debug) watch.markTimeBegin("removePlatforms");
Collection<Platform> platforms = platformManager.findDeletedPlatforms();
removePlatforms(subject, platforms);
if (debug) watch.markTimeEnd("removePlatforms");
if (debug) log.debug("removeDeletedResources: " + watch);
}
/**
* Disable measurements and unschedule from the agent in bulk with the agent
* cache info because the resources have been de-referenced from the agent
*
* @param agentCache {@link Map} of {@link Integer} of agentIds to
* {@link List} of {@link AppdefEntityID}s
*/
private void unscheduleMeasurementsForAsyncDelete(Map<Integer, List<AppdefEntityID>> agentCache) {
if (agentCache == null) {
return;
}
try {
AuthzSubject subject = authzSubjectManager.findSubjectById(AuthzConstants.overlordId);
for (Integer agentId : agentCache.keySet()) {
Agent agent = agentManager.getAgent(agentId);
List<AppdefEntityID> resources = agentCache.get(agentId);
measurementManager.disableMeasurementsForDeletion(subject, agent, (AppdefEntityID[]) resources
.toArray(new AppdefEntityID[resources.size()]));
}
} catch (Exception e) {
log.error("Error unscheduling measurements during async delete", e);
}
}
private final void removeApplications(AuthzSubject subject, Collection<Application> applications) {
for (Application application : applications) {
try {
applicationManager.removeApplication(subject, application.getId());
} catch (Exception e) {
log.error("Unable to remove application: " + e, e);
}
}
if (log.isDebugEnabled()) {
log.debug("Removed " + applications.size() + " applications");
}
}
private final void removeResourceGroups(AuthzSubject subject, Collection<ResourceGroup> groups) {
for (ResourceGroup group : groups) {
try {
resourceGroupManager.removeResourceGroup(subject, group.getId());
} catch (Exception e) {
log.error("Unable to remove group: " + e, e);
}
}
if (log.isDebugEnabled()) {
log.debug("Removed " + groups.size() + " resource groups");
}
}
private void removePlatforms(AuthzSubject subject, Collection<Platform> platforms) {
for (Platform platform : platforms) {
try {
//removeServers(subject, platform.getServers());
appdefBoss.removePlatform(subject, platform.getId());
} catch (Exception e) {
log.error("Unable to remove platform: " + e, e);
}
}
}
private final void removeServers(AuthzSubject subject, Collection<Server> servers) {
final StopWatch watch = new StopWatch();
watch.markTimeBegin("removeServers");
final List<Server> svrs = new ArrayList<Server>(servers);
// can't use iterator for loop here. Since we are modifying the
// internal hibernate collection, which this collection is based on,
// it will throw a ConcurrentModificationException
// This occurs even if you disassociate the Collection by trying
// something like new ArrayList(servers). Not sure why.
for (int i = 0; i < svrs.size(); i++) {
try {
final Server server = svrs.get(i);
//removeServices(subject, server.getServices());
appdefBoss.removeServer(subject, server.getId());
} catch (Exception e) {
log.error("Unable to remove server: " + e, e);
}
}
watch.markTimeEnd("removeServers");
if (log.isDebugEnabled()) {
log.debug("Removed " + servers.size() + " services");
}
}
private final void removeServices(AuthzSubject subject, Collection<Service> services) {
final StopWatch watch = new StopWatch();
watch.markTimeBegin("removeServices");
final List<Service> svcs = new ArrayList<Service>(services);
// can't use iterator for loop here. Since we are modifying the
// internal hibernate collection, which this collection is based on,
// it will throw a ConcurrentModificationException
// This occurs even if you disassociate the Collection by trying
// something like new ArrayList(services). Not sure why.
for (int i = 0; i < svcs.size(); i++) {
try {
final Service service = svcs.get(i);
appdefBoss.removeService(subject, service.getId());
} catch (Exception e) {
log.error("Unable to remove service: " + e, e);
}
}
watch.markTimeEnd("removeServices");
if (log.isDebugEnabled()) {
log.debug("Removed " + services.size() + " services");
}
}
public String toString() {
return "ResourceCleanupEventListener";
}
}