/*
* 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.galerts.server.session;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hibernate.PageInfo;
import org.hyperic.hq.appdef.shared.AppdefEntityID;
import org.hyperic.hq.authz.server.session.AuthzSubject;
import org.hyperic.hq.authz.server.session.Resource;
import org.hyperic.hq.authz.server.session.ResourceGroup;
import org.hyperic.hq.authz.server.session.SubjectDeleteRequestedEvent;
import org.hyperic.hq.authz.server.session.events.group.GroupDeleteRequestedEvent;
import org.hyperic.hq.authz.server.session.events.group.GroupMembersChangedEvent;
import org.hyperic.hq.authz.server.session.events.group.GroupRelatedEvent;
import org.hyperic.hq.authz.server.shared.ResourceDeletedException;
import org.hyperic.hq.authz.shared.PermissionException;
import org.hyperic.hq.authz.shared.ResourceGroupManager;
import org.hyperic.hq.common.server.session.Crispo;
import org.hyperic.hq.common.shared.CrispoManager;
import org.hyperic.hq.escalation.server.session.Escalatable;
import org.hyperic.hq.escalation.server.session.Escalation;
import org.hyperic.hq.escalation.shared.EscalationManager;
import org.hyperic.hq.events.AlertAuxLog;
import org.hyperic.hq.events.AlertAuxLogProvider;
import org.hyperic.hq.events.AlertSeverity;
import org.hyperic.hq.events.EventConstants;
import org.hyperic.hq.events.server.session.Action;
import org.hyperic.hq.galerts.processor.GalertProcessor;
import org.hyperic.hq.galerts.processor.Gtrigger;
import org.hyperic.hq.galerts.shared.GalertManager;
import org.hyperic.util.config.ConfigResponse;
import org.hyperic.util.pager.PageControl;
import org.hyperic.util.pager.PageList;
import org.hyperic.util.pager.Pager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
*
*/
@Service
@Transactional
public class GalertManagerImpl implements GalertManager, ApplicationListener<ApplicationEvent> {
private final Log _log = LogFactory.getLog(GalertManagerImpl.class);
private ExecutionStrategyTypeInfoDAO _stratTypeDAO;
private final GalertDefDAO _defDAO;
private final GalertAuxLogDAO _auxLogDAO;
private final GalertLogDAO _logDAO;
private final GalertActionLogDAO _actionLogDAO;
private final CrispoManager crispoManager;
private final EscalationManager escalationManager;
private final GalertProcessor galertProcessor;
private final ResourceGroupManager resourceGroupManager;
@Autowired
public GalertManagerImpl(ExecutionStrategyTypeInfoDAO stratTypeDAO, GalertDefDAO defDAO,
GalertAuxLogDAO auxLogDAO, GalertLogDAO logDAO,
GalertActionLogDAO actionLogDAO, CrispoManager crispoManager,
EscalationManager escalationManager,
ResourceGroupManager resourceGroupManager, GalertProcessor gAlertProcessor) {
_stratTypeDAO = stratTypeDAO;
_defDAO = defDAO;
_auxLogDAO = auxLogDAO;
_logDAO = logDAO;
_actionLogDAO = actionLogDAO;
this.crispoManager = crispoManager;
this.escalationManager = escalationManager;
this.resourceGroupManager = resourceGroupManager;
this.galertProcessor = gAlertProcessor;
}
/**
* Update basic properties of an alert definition
*
* If any of the passed params are non-null, they will be updated with the
* new value
*
*
*/
public void update(GalertDef def, String name, String desc, AlertSeverity severity,
Boolean enabled) {
boolean seriousUpdate = false;
boolean updateName = false;
if (def.isDeleted()) {
throw new IllegalArgumentException("Unable to update a def "
+ "which has already been " + "deleted");
}
if (name != null) {
def.setName(name);
updateName = true;
}
if (desc != null) {
def.setDescription(desc);
}
if (severity != null) {
def.setSeverity(severity);
}
if (enabled != null) {
def.setEnabled(enabled.booleanValue());
seriousUpdate = true;
}
if (seriousUpdate) {
def.setMtime(System.currentTimeMillis());
galertProcessor.loadReloadOrUnload(def);
} else if (updateName) {
def.setMtime(System.currentTimeMillis());
galertProcessor.alertDefUpdated(def, name);
}
}
/**
* Update the escalation of an alert def
*
*/
public void update(GalertDef def, Escalation escalation) {
def.setEscalation(escalation);
def.setMtime(System.currentTimeMillis());
// End any escalation we were previously doing.
escalationManager.endEscalation(def);
// If the alert def was in the middle of an escalation, ending the
// escalation will prevent users from being aware that the alert def
// has fired. Thus, they might not fix the alert and reset the triggers.
// We'll reload the alert def so that the triggers are reset forcefully.
galertProcessor.loadReloadOrUnload(def);
}
/**
* Enable/disable an alert def
*
*/
public void enable(GalertDef def, boolean enable) {
update(def, null, null, null, Boolean.valueOf(enable));
}
/**
* Enable/disable an alert def by id
*/
public void enable(Integer id, boolean enable) {
// TODO: Add permission check
GalertDef def = findById(id);
enable(def, enable);
}
/**
* Find all alert definitions for the specified group
*
*/
@Transactional(readOnly = true)
public PageList<GalertDef> findAlertDefs(ResourceGroup g, PageControl pc) {
Pager pager = Pager.getDefaultPager();
// TODO: G
return pager.seek(_defDAO.findAll(g), pc);
}
/**
* Find all group alert definitions.
*
* @param minSeverity Minimum severity for returned defs
* @param enabled If non-null specifies the nature of the 'enabled' flag for
* the results.
* @param pInfo Paging information. Must contain a sort field from
* {@link GalertDefSortField}
*
*/
@Transactional(readOnly = true)
public List<GalertDef> findAlertDefs(AuthzSubject subj, AlertSeverity minSeverity,
Boolean enabled, PageInfo pInfo) {
return _defDAO.findAll(subj, minSeverity, enabled, pInfo);
}
/**
*
*/
@Transactional(readOnly = true)
public Collection<ExecutionStrategyTypeInfo> findAllStrategyTypes() {
return _stratTypeDAO.findAll();
}
/**
*
*/
@Transactional(readOnly = true)
public ExecutionStrategyTypeInfo findStrategyType(Integer id) {
return _stratTypeDAO.findById(id);
}
/**
*
*/
@Transactional(readOnly = true)
public ExecutionStrategyTypeInfo findStrategyType(ExecutionStrategyType t) {
return _stratTypeDAO.find(t);
}
/**
*
*/
@Transactional(readOnly = true)
public GalertDef findById(Integer id) {
return _defDAO.findById(id);
}
/**
*
*/
@Transactional(readOnly = true)
public GalertAuxLog findAuxLogById(Integer id) {
return _auxLogDAO.findById(id);
}
/**
* Retrieve the Gtriggers for a partition in the given galert def.
*
* @param id The galert def id.
* @param partition The partition.
* @return The list of Gtriggers.
*
*/
@Transactional(readOnly = true)
public List<Gtrigger> getTriggersById(Integer id, GalertDefPartition partition) {
List<Gtrigger> triggers = new ArrayList<Gtrigger>();
GalertDef def = findById(id);
ExecutionStrategyInfo strategy = def.getStrategy(partition);
if (strategy != null) {
List<GtriggerInfo> triggerInfos = strategy.getTriggers();
for (GtriggerInfo triggerInfo : triggerInfos) {
Gtrigger trigger = triggerInfo.getTrigger();
trigger.setGroup(def.getGroup());
triggers.add(trigger);
}
}
return triggers;
}
/**
* Save the alert log and associated auxillary log information to the DB.
*
* DevNote: Since the GalertAuxLog table needs to be written first (for
* foreign-key from the auxType tables), we first traverse all the logs and
* save them. Then, we perform the same traversal and save the specific
* logs.
*
*
*/
public GalertLog createAlertLog(GalertDef def, ExecutionReason reason)
throws ResourceDeletedException {
Resource r = def.getResource();
if (r == null || r.isInAsyncDeleteState()) {
throw ResourceDeletedException.newInstance(r);
}
Map<GalertAuxLog, AlertAuxLog> gAuxLogToAuxLog = new HashMap<GalertAuxLog, AlertAuxLog>(); // Stores
// real
// logs
// to
// auxType
// logs
GalertLog newLog = new GalertLog(def, reason, System.currentTimeMillis());
addAuxLogChildren(newLog, null, reason.getAuxLogs(), gAuxLogToAuxLog);
_logDAO.save(newLog);
for (Map.Entry<GalertAuxLog, AlertAuxLog> ent : gAuxLogToAuxLog.entrySet()) {
GalertAuxLog gAuxLog = ent.getKey();
AlertAuxLog auxLog = ent.getValue();
AlertAuxLogProvider provider = auxLog.getProvider();
if (provider != null)
provider.save(gAuxLog.getId().intValue(), auxLog);
}
def.setLastFired(new Long(System.currentTimeMillis()));
return newLog;
}
private void addAuxLogChildren(GalertLog alert, GalertAuxLog parent, List<AlertAuxLog> auxLogs,
Map<GalertAuxLog, AlertAuxLog> gAuxLogToAuxLog) {
for (AlertAuxLog auxLog : auxLogs) {
GalertAuxLog newLog;
newLog = alert.addAuxLog(auxLog, parent);
gAuxLogToAuxLog.put(newLog, auxLog);
addAuxLogChildren(alert, newLog, auxLog.getChildren(), gAuxLogToAuxLog);
}
}
/**
*
*/
public void createActionLog(GalertLog alert, String detail, Action action, AuthzSubject subject) {
GalertActionLog log = alert.createActionLog(detail, action, subject);
_actionLogDAO.save(log);
}
/**
*
*/
@Transactional(readOnly = true)
public List<GalertLog> findAlertLogs(GalertDef def) {
return _logDAO.findAll(def.getGroup());
}
/**
*
*/
@Transactional(readOnly = true)
public GalertLog findLastFixedByDef(GalertDef def) {
try {
return _logDAO.findLastByDefinition(def, true);
} catch (Exception e) {
return null;
}
}
/**
* Simply sets the 'fixed' flag on an alert
*
*/
public void fixAlert(GalertLog alert) {
alert.setFixed(true);
}
/**
*
*/
@Transactional(readOnly = true)
public Escalatable findEscalatableAlert(Integer id) {
GalertLog alert = _logDAO.findById(id);
_logDAO.getSession().refresh(alert);
return GalertEscalatableCreator.createEscalatable(alert);
}
/**
*
*/
@Transactional(readOnly = true)
public GalertLog findAlertLog(Integer id) {
return _logDAO.findById(id);
}
/**
*
*/
@Transactional(readOnly = true)
public List<GalertLog> findAlertLogs(ResourceGroup group) {
return _logDAO.findAll(group);
}
/**
*
*/
@Transactional(readOnly = true)
public PageList<GalertLog> findAlertLogsByTimeWindow(ResourceGroup group, long begin, long end,
PageControl pc) {
return _logDAO.findByTimeWindow(group, begin, end, pc);
}
/**
*
*/
public List<GalertLog> findUnfixedAlertLogsByTimeWindow(ResourceGroup group, long begin,
long end) {
return _logDAO.findUnfixedByTimeWindow(group, begin, end);
}
/**
* @see findAlerts
* @return a list of {@link Escalatable}s
*
*/
public List<Escalatable> findEscalatables(AuthzSubject subj, int count, int priority,
long timeRange, long endTime,
List<AppdefEntityID> includes)
throws PermissionException {
List<GalertLog> alerts = findAlerts(subj, count, priority, timeRange, endTime, includes);
List<Escalatable> res = new ArrayList<Escalatable>(alerts.size());
for (GalertLog alert : alerts) {
res.add(GalertEscalatableCreator.createEscalatable(alert));
}
return res;
}
/**
* Find group alerts based on a set of criteria
*
* @param subj Subject doing the finding
* @param count Max # of alerts to return
* @param priority A value from {@link EventConstants}
* @param timeRange the amount of milliseconds prior to current that the
* alerts will be contained in. e.g. the beginning of the time range
* will be (current - timeRante)
* @param includes A list of entity IDs to include in the result. If null
* then ignore and return all.
*
* @return a list of {@link GalertLog}s
*
*/
public List<GalertLog> findAlerts(AuthzSubject subj, int count, int priority, long timeRange,
long endTime, List<AppdefEntityID> includes)
throws PermissionException {
PageInfo pInfo = PageInfo.create(0, count, GalertLogSortField.DATE, false);
if (priority == EventConstants.PRIORITY_ALL) {
// if priority is "all" set the severity code as low
// this is essentially "all"
priority = AlertSeverity.LOW.getCode();
}
AlertSeverity s = AlertSeverity.findByCode(priority);
List<GalertLog> alerts = _logDAO.findByCreateTimeAndPriority(subj.getId(),
endTime - timeRange, endTime, s, false, false, null, null, pInfo);
List<GalertLog> result = new ArrayList<GalertLog>();
for (GalertLog l : alerts) {
GalertDef def = l.getAlertDef();
if (def.getResource().isInAsyncDeleteState()) {
continue;
}
// Filter by appdef entity
AppdefEntityID aeid = def.getAppdefID();
if (includes != null && !includes.contains(aeid))
continue;
result.add(l);
}
return result;
}
/**
*
*/
@Transactional(readOnly = true)
public List<GalertLog> findAlerts(AuthzSubject subj, AlertSeverity severity, long timeRange,
long endTime, boolean inEsc, boolean notFixed,
Integer groupId, PageInfo pInfo) {
return findAlerts(subj, severity, timeRange, endTime, inEsc, notFixed, groupId, null, pInfo);
}
/**
*
*/
@Transactional(readOnly = true)
public List<GalertLog> findAlerts(AuthzSubject subj, AlertSeverity severity, long timeRange,
long endTime, boolean inEsc, boolean notFixed,
Integer groupId, Integer galertDefId, PageInfo pInfo) {
return _logDAO.findByCreateTimeAndPriority(subj.getId(), endTime - timeRange, endTime,
severity, inEsc, notFixed, groupId, galertDefId, pInfo);
}
/**
* Get the number of alerts for the given array of AppdefEntityID's
*
*/
public int[] fillAlertCount(AuthzSubject subj, AppdefEntityID[] ids, int[] counts)
throws PermissionException {
for (int i = 0; i < ids.length; i++) {
if (ids[i].isGroup()) {
ResourceGroup group = resourceGroupManager.findResourceGroupById(subj, ids[i]
.getId());
counts[i] = _logDAO.countAlerts(group).intValue();
}
}
return counts;
}
/**
* fill the number of alerts for the given array of AppdefEntityID's , mapping AppdefEntityID to it's alerts count
*
*/
public void fillAlertCount(AuthzSubject subj, AppdefEntityID[] ids, Map<AppdefEntityID, Integer> counts)
throws PermissionException {
for (int i = 0; i < ids.length; i++) {
if (ids[i].isGroup()) {
ResourceGroup group = resourceGroupManager.findResourceGroupById(subj, ids[i]
.getId());
counts.put(ids[i], _logDAO.countAlerts(group).intValue());
}
}
}
/**
*
*/
public void deleteAlertLog(GalertLog log) {
_logDAO.remove(log);
}
/**
*
*/
public void deleteAlertLogs(ResourceGroup group) {
_logDAO.removeAll(group);
}
/**
* Register an execution strategy.
*
*/
public ExecutionStrategyTypeInfo registerExecutionStrategy(ExecutionStrategyType stratType) {
ExecutionStrategyTypeInfo info = _stratTypeDAO.find(stratType);
if (info != null) {
_log.warn("Execution strategy type [" + stratType.getClass().getName() +
"] already registered");
return info;
}
info = new ExecutionStrategyTypeInfo(stratType);
_stratTypeDAO.save(info);
return info;
}
/**
* Unregister an execution strategy. This will fail if any alert definitions
* are currently using the strategy
*
*/
public void unregisterExecutionStrategy(ExecutionStrategyType sType) {
ExecutionStrategyTypeInfo info = _stratTypeDAO.find(sType);
if (info == null) {
_log.warn("Execution strategy [" + sType.getClass().getName() +
"] already unregistered");
return;
}
if (_defDAO.countByStrategy(info) != 0) {
throw new IllegalArgumentException("Unable to unregister [ " +
sType.getClass().getName() +
"] alert defs are using it");
}
_stratTypeDAO.remove(info);
}
/**
* Configure triggers for a given partition.
*
* @param triggerInfos A list of {@link GtriggerTypeInfo}s
* @param configs A list of {@link ConfigResponse}s, one for each trigger
* info
*
*
*/
public void configureTriggers(GalertDef def, GalertDefPartition partition,
List<GtriggerTypeInfo> triggerInfos, List<ConfigResponse> configs) {
ExecutionStrategyInfo strat;
if (triggerInfos.size() != configs.size()) {
throw new IllegalArgumentException("Must be a config for each " + "trigger");
}
strat = def.getStrategy(partition);
// Delete the old triggers if there were any
List<Crispo> crispos = new ArrayList<Crispo>();
for (GtriggerInfo t : strat.getTriggers()) {
crispos.add(t.getConfigCrispo());
_defDAO.remove(t);
}
for (Iterator<Crispo> i = crispos.iterator(); i.hasNext();) {
crispoManager.deleteCrispo(i.next());
}
strat.clearTriggers();
// Now add the new triggers
Iterator<ConfigResponse> j = configs.iterator();
for (GtriggerTypeInfo typeInfo : triggerInfos) {
ConfigResponse config = j.next();
Crispo crispo = crispoManager.create(config);
GtriggerInfo t = strat.addTrigger(typeInfo, crispo, def.getGroup(), partition);
_defDAO.save(t);
}
galertProcessor.loadReloadOrUnload(def);
}
/**
*
*/
public ExecutionStrategyInfo addPartition(GalertDef def, GalertDefPartition partition,
ExecutionStrategyTypeInfo stratType,
ConfigResponse stratConfig) {
Crispo stratCrispo = crispoManager.create(stratConfig);
ExecutionStrategyInfo res = def.addPartition(partition, stratType, stratCrispo);
_stratTypeDAO.save(res);
galertProcessor.loadReloadOrUnload(def);
return res;
}
/**
*
*/
public GalertDef createAlertDef(AuthzSubject subject, String name, String description,
AlertSeverity severity, boolean enabled, ResourceGroup group) {
GalertDef def;
def = new GalertDef(name, description, severity, enabled, group);
_defDAO.save(def);
galertProcessor.validateAlertDef(def);
return def;
}
/**
* Reload an alert definition. Probably should only be called internally
* here.
*
*
*/
public void reloadAlertDef(GalertDef def) {
galertProcessor.loadReloadOrUnload(def);
}
/**
* Mark an alert definition as deleted. This will remove it from all
* dialogues, but will leave all the data (specific alerts) in place.
*
*
*/
public void markDefDeleted(GalertDef def) {
update(def, null, null, null, Boolean.FALSE);
def.setEscalation(null);
def.setDeleted(true);
}
/**
* Delete an alert definition along with all logs which are tied to it.
*
*
*/
public void nukeAlertDef(GalertDef def) {
List<Crispo> nukeCrispos = new ArrayList<Crispo>();
Integer defId = def.getId();
for (AlertAuxLogProvider p : AlertAuxLogProvider.findAll()) {
p.deleteAll(def);
}
_auxLogDAO.removeAll(def);
// Kill the logs
_logDAO.removeAll(def);
for (ExecutionStrategyInfo strat : def.getStrategies()) {
// Reconfigure the def to have 0 triggers (i.e. nuke the instances)
// TODO: G (Collections.emptyList() should have worked, but at least
// Eclipse issues an error)
configureTriggers(def, strat.getPartition(), Collections.EMPTY_LIST,
Collections.EMPTY_LIST);
nukeCrispos.add(strat.getConfigCrispo());
}
_defDAO.remove(def);
for (Crispo c : nukeCrispos) {
crispoManager.deleteCrispo(c);
}
galertProcessor.alertDefDeleted(defId);
}
/**
* Returns a list of {@link GalertDef}s using the passed escalation.
*
*/
@Transactional(readOnly = true)
public Collection<GalertDef> getUsing(Escalation e) {
return _defDAO.getUsing(e);
}
/**
* Start an escalation for a group alert definition.
*
*
*/
public void startEscalation(GalertDef def, ExecutionReason reason) {
escalationManager.startEscalation(def, new GalertEscalatableCreator(this, def, reason));
}
/**
* Remove all the galert defs associated with this resource group.
*
*
*/
public void processGroupDeletion(ResourceGroup g) {
Collection<GalertDef> defs = _defDAO.findAbsolutelyAllGalertDefs(g);
for (GalertDef def : defs) {
_log.debug("Cascade deleting GalertDef[" + def.getName() + "]");
nukeAlertDef(def);
}
}
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof GroupDeleteRequestedEvent) {
processGroupDeletion(((GroupDeleteRequestedEvent) event).getGroup());
} else if (event instanceof GroupMembersChangedEvent) {
groupMembersChanged(((GroupRelatedEvent) event).getGroup());
} else if (event instanceof SubjectDeleteRequestedEvent) {
_actionLogDAO.handleSubjectRemoval(((SubjectDeleteRequestedEvent) event).getSubject());
}
}
private void groupMembersChanged(ResourceGroup g) {
Collection<GalertDef> defs = findAlertDefs(g, PageControl.PAGE_ALL);
for (GalertDef def : defs) {
reloadAlertDef(def);
}
_log.debug("Group members changed for group [" + g + "]");
}
@PostConstruct
public void initialize() {
galertProcessor.startupInitialize();
// Make sure the escalation enumeration is loaded and registered so
// that the escalations run
GalertEscalationAlertType.class.getClass();
// Make sure we have the aux-log provider loaded
GalertAuxLogProvider.class.toString();
}
@PreDestroy
public void destroy() {
this._stratTypeDAO = null;
}//EOM
}