org.hyperic.hq.galerts.processor.GalertProcessorImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperic.hq.galerts.processor.GalertProcessorImpl.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, 2005, 2006], 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.processor;

import java.util.Collection;
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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.galerts.server.session.GalertDef;
import org.hyperic.hq.galerts.server.session.GalertDefDAO;
import org.hyperic.hq.hibernate.SessionManager;
import org.hyperic.hq.hibernate.SessionManager.SessionRunner;
import org.hyperic.hq.zevents.Zevent;
import org.hyperic.hq.zevents.ZeventEnqueuer;
import org.hyperic.hq.zevents.ZeventSourceId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

/**
 * This class acts as the central manager for all in-memory alerting.
 * It performs the following functions:
 * 
 * - Manages lists of active alert definitions
 * - Reloads defs as configurations change
 * - Listens to events from the zevents subsystem
 * - Listens to group membership events from the grouping subsystem 
 *  
 * In the future, it will also:
 * - Come online when a 'master' alerting node is specified in High Availability
 * 
 * Since the galerting subsystem is broken up into 2 pieces, the persisted
 * objects and the runtime objects, the 2 are split into separate packages.
 */
@Component
public class GalertProcessorImpl implements GalertProcessor {

    private final Object cfgLock = new Object();
    private final Log _log = LogFactory.getLog(GalertProcessor.class);

    private final ZeventEnqueuer _zMan;

    private GalertDefDAO _defDAO;

    // Integer ids -> MemGalertDefs
    private Map _alertDefs = new HashMap();

    // ZeventSourceId -> Set of {@link Gtrigger}s
    private Map _listeners = new HashMap();

    @Autowired
    public GalertProcessorImpl(ZeventEnqueuer zEventManager, GalertDefDAO galertDefDAO) {
        _zMan = zEventManager;
        _zMan.addBufferedGlobalListener(new EventListener(this));
        this._defDAO = galertDefDAO;
    }

    /**
     * Entry point to the processor from the {@link EventListener}
     *
     * @param events  A list of {@link Zevent}s to process
     * 
     * TODO:  This needs to be optimized so that the EventListener buffers
     *        up many events and calls this method.  The overhead of creating
     *        and checking session existance is too high on a per-event
     *        basis.
     */
    public void processEvents(final List<Zevent> events) {
        for (Iterator i = events.iterator(); i.hasNext();) {
            Zevent z = (Zevent) i.next();
            processEvent(z);
        }
    }

    private void processEvent(final Zevent event) {
        ZeventSourceId source = event.getSourceId();
        Set listenerDupe;

        synchronized (cfgLock) {
            Set listeners = (Set) _listeners.get(source);

            if (listeners == null)
                return;

            listenerDupe = new HashSet(listeners);
        }

        for (Iterator i = listenerDupe.iterator(); i.hasNext();) {
            final Gtrigger t = (Gtrigger) i.next();

            // Synchronize around all event processing for a trigger, since
            // they keep state and will need to be flushed
            synchronized (t) {
                try {
                    SessionManager.runInSession(new SessionRunner() {
                        public String getName() {
                            return "Event Processor";
                        }

                        public void run() throws Exception {
                            t.processEvent(event);
                        }
                    });
                } catch (Exception e) {
                    _log.warn("Error processing events", e);
                }
            }
        }
    }

    /**
     * Load a {@link GalertDef} into the processor, initializing the triggers,
     * strategy, etc.  If the definition is already loaded, it will be
     * re-initialized as though it were loaded for the first time.
     * 
     * If the load or reload fails, the processor will be in the same state
     * as it was prior to the call.
     * 
     * @param defId Id of a {@link GalertDef} to load.
     */
    private void handleUpdate(MemGalertDef def) {
        _log.debug("Handling load/update of alert def: " + def.getName());

        synchronized (cfgLock) {
            MemGalertDef oldDef = (MemGalertDef) _alertDefs.get(def.getId());

            // Out with the old
            if (oldDef != null) {
                removeListeners(oldDef);
            }

            // In with the new
            addListeners(def);
            _alertDefs.put(def.getId(), def);
            _log.debug("galert[id=" + def.getId() + ",name=" + def.getName() + "] loaded");
        }
    }

    /**
     * Remove an alert definition from the processor. 
     */
    private void handleUnload(Integer defId) {
        _log.debug("Handling unload of alertdef[" + defId + "]");
        synchronized (cfgLock) {
            MemGalertDef oldDef = (MemGalertDef) _alertDefs.get(defId);

            if (oldDef == null) {
                _log.warn("Attempted to unload defid=" + defId + " but it wasn't loaded");
                return;
            }
            removeListeners(oldDef);
            _alertDefs.remove(defId);
        }
    }

    private void addListeners(MemGalertDef def) {
        Map eventMap = def.getInterestedEvents();

        for (Iterator i = eventMap.entrySet().iterator(); i.hasNext();) {
            Map.Entry ent = (Map.Entry) i.next();
            Gtrigger t = (Gtrigger) ent.getKey();
            Set sourceIds = (Set) ent.getValue();

            for (Iterator j = sourceIds.iterator(); j.hasNext();) {
                ZeventSourceId sourceId = (ZeventSourceId) j.next();
                Set listeners = (Set) _listeners.get(sourceId);

                if (listeners == null) {
                    listeners = new HashSet();
                    _listeners.put(sourceId, listeners);
                }
                listeners.add(t);
            }
        }
    }

    private void removeListeners(MemGalertDef def) {
        Map eventMap = def.getInterestedEvents();

        // Nuke all the old listeners 
        for (Iterator i = eventMap.entrySet().iterator(); i.hasNext();) {
            Map.Entry ent = (Map.Entry) i.next();
            Gtrigger t = (Gtrigger) ent.getKey();
            Set sourceIds = (Set) ent.getValue();

            for (Iterator j = sourceIds.iterator(); j.hasNext();) {
                ZeventSourceId sourceId = (ZeventSourceId) j.next();
                Set listeners = (Set) _listeners.get(sourceId);

                listeners.remove(t);
            }
        }
    }

    /**
     * Called when primitive information has been updated which doens't
     * require a reload of the entire definition.
     */
    public void alertDefUpdated(GalertDef def, final String newName) {
        final Integer defId = def.getId();
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {

            public void suspend() {
            }

            public void resume() {
            }

            public void flush() {
            }

            public void beforeCompletion() {
            }

            public void beforeCommit(boolean readOnly) {
            }

            public void afterCompletion(int status) {
            }

            public void afterCommit() {
                synchronized (cfgLock) {
                    MemGalertDef memDef = (MemGalertDef) _alertDefs.get(defId);

                    if (memDef != null)
                        memDef.setName(newName);
                }
            }
        });
    }

    /**
     * Call this if an alert-def is created or updated.  The internal state
     * of the processor will be updated after the current transaction 
     * successfully commits.  
     * 
     * This method should be called for major changes to the definition, such
     * as conditions, enablement, etc., since it fully unloads the existing
     * definition and reloads it (meaning that internal state of the 
     * triggers, such as previously processed metrics, etc. will be reset)
     */
    public void loadReloadOrUnload(GalertDef def) {
        final boolean isUnload = !def.isEnabled();
        final Integer defId = def.getId();
        final MemGalertDef memDef;

        if (isUnload) {
            memDef = null;
        } else {
            memDef = new MemGalertDef(def);
        }
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {

            public void suspend() {
            }

            public void resume() {
            }

            public void flush() {
            }

            public void beforeCompletion() {
            }

            public void beforeCommit(boolean readOnly) {
            }

            public void afterCompletion(int status) {
            }

            public void afterCommit() {
                if (isUnload) {
                    handleUnload(defId);
                } else {
                    handleUpdate(memDef);
                }
            }
        });
    }

    /**
     * Call this if an alert-def is deleted.  After the transaction is
     * successfully committed, it will be removed from the processor.
     */
    public void alertDefDeleted(final Integer defId) {
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {

            public void suspend() {
            }

            public void resume() {
            }

            public void flush() {
            }

            public void beforeCompletion() {
            }

            public void beforeCommit(boolean readOnly) {
            }

            public void afterCompletion(int status) {
            }

            public void afterCommit() {
                handleUnload(defId);
            }
        });
    }

    /**
     * Called to initialize the state of the processor.  This should be
     * called during application startup with all the alert definitions
     * in the system.
     */
    @Transactional
    public void startupInitialize() {
        Collection galertDefs = _defDAO.findAll();
        for (Iterator i = galertDefs.iterator(); i.hasNext();) {
            GalertDef def = (GalertDef) i.next();
            MemGalertDef memDef;

            if (!def.isEnabled())
                continue;

            try {
                memDef = new MemGalertDef(def);
                handleUpdate(memDef);
            } catch (Exception e) {
                _log.warn("Unable to load alert definition[id=" + def.getId() + ",name=" + def.getName(), e);
            }
        }
    }

    public boolean validateAlertDef(GalertDef def) {
        try {
            new MemGalertDef(def);
            return true;
        } catch (Exception e) {
            _log.warn("Unable to create alert def [" + def + "]", e);
            return false;
        }
    }
}