org.opennms.ng.services.collectd.Collectd.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.ng.services.collectd.Collectd.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2006-2012 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2012 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) 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 OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.ng.services.collectd;

import java.net.InetAddress;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.opennms.core.logging.Logging;
import org.opennms.core.utils.ConfigFileConstants;
import org.opennms.netmgt.EventConstants;
import org.opennms.netmgt.capsd.EventUtils;
import org.opennms.netmgt.capsd.InsufficientInformationException;
import org.opennms.netmgt.collectd.CollectdInstrumentation;
import org.opennms.netmgt.collectd.CollectionInitializationException;
import org.opennms.netmgt.collectd.ServiceCollector;
import org.opennms.netmgt.config.SnmpEventInfo;
import org.opennms.netmgt.config.SnmpPeerFactory;
import org.opennms.netmgt.config.ThreshdConfigFactory;
import org.opennms.netmgt.config.ThresholdingConfigFactory;
import org.opennms.netmgt.config.collectd.Collector;
import org.opennms.netmgt.model.AbstractEntityVisitor;
import org.opennms.netmgt.model.OnmsIpInterface;
import org.opennms.netmgt.model.OnmsMonitoredService;
import org.opennms.netmgt.model.events.EventBuilder;
import org.opennms.netmgt.model.events.EventIpcManager;
import org.opennms.netmgt.model.events.EventListener;
import org.opennms.netmgt.xml.event.Event;
import org.opennms.netmgt.xml.event.Parm;
import org.opennms.netmgt.xml.event.Value;
import org.opennms.ng.persistence.dao.OnmsIpInterfaceDao;
import org.opennms.ng.persistence.dao.OnmsNodeDao;
import org.opennms.ng.persistence.entities.OnmsNode;
import org.opennms.ng.services.collectdconfig.CollectdPackage;
import org.opennms.ng.services.scheduler.LegacyScheduler;
import org.opennms.ng.services.scheduler.ReadyRunnable;
import org.opennms.ng.services.scheduler.Scheduler;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import static org.opennms.core.utils.InetAddressUtils.str;

/**
 * <p>Collectd class.</p>
 *
 * @author ranger
 * @version $Id: $
 */
public class Collectd implements EventListener {

    private static final Logger LOG = LoggerFactory.getLogger(Collectd.class);
    private static CollectdInstrumentation s_instrumentation = null;
    /**
     * Instantiated service collectors specified in config file
     */
    private final Map<String, ServiceCollector> m_collectors = new HashMap<String, ServiceCollector>(4);
    /**
     * List of all CollectableService objects.
     */
    private final List<CollectableService> m_collectableServices;
    /**
     * Reference to the collection scheduler
     */
    private volatile Scheduler m_scheduler;
    /**
     * Indicates if scheduling of existing interfaces has been completed
     */
    private volatile CollectorConfigDao m_collectorConfigDao;
    private volatile OnmsIpInterfaceDao m_ifaceDao;
    private final SchedulingCompletedFlag m_schedulingCompletedFlag = new SchedulingCompletedFlag();
    private volatile EventIpcManager m_eventIpcManager;
    private volatile TransactionTemplate m_transTemplate;
    private volatile OnmsNodeDao m_nodeDao;
    /**
     * Log4j category
     */
    final static String LOG4J_CATEGORY = "collectd";

    /**
     * Constructor.
     */
    public Collectd() {

        m_collectableServices = Collections.synchronizedList(new LinkedList<CollectableService>());
    }

    /**
     * <p>instrumentation</p>
     *
     * @return a {@link CollectdInstrumentation} object.
     */
    public static CollectdInstrumentation instrumentation() {
        if (s_instrumentation == null) {
            String className = System.getProperty("org.opennms.collectd.instrumentationClass",
                    DefaultCollectdInstrumentation.class.getName());
            try {
                s_instrumentation = (CollectdInstrumentation) ClassUtils
                        .forName(className, Thread.currentThread().getContextClassLoader()).newInstance();
            } catch (Throwable e) {
                s_instrumentation = new DefaultCollectdInstrumentation();
            }
        }

        return s_instrumentation;
    }

    public static String getLoggingCategory() {
        return LOG4J_CATEGORY;
    }

    /**
     * <p>onInit</p>
     */
    public void onInit() {
        Assert.notNull(m_collectorConfigDao, "collectorConfigDao must not be null");
        Assert.notNull(m_eventIpcManager, "eventIpcManager must not be null");
        Assert.notNull(m_transTemplate, "transTemplate must not be null");
        Assert.notNull(m_ifaceDao, "ifaceDao must not be null");
        Assert.notNull(m_nodeDao, "nodeDao must not be null");

        LOG.debug("init: Initializing collection daemon");

        // make sure the instrumentation gets initialized
        instrumentation();

        instantiateCollectors();

        getScheduler().schedule(0, ifScheduler());

        installMessageSelectors();
        onStart();
    }

    private void installMessageSelectors() {
        // Add the EventListeners for the UEIs in which this service is
        // interested
        List<String> ueiList = new ArrayList<String>();

        // nodeGainedService
        ueiList.add(EventConstants.NODE_GAINED_SERVICE_EVENT_UEI);

        // primarySnmpInterfaceChanged
        ueiList.add(EventConstants.PRIMARY_SNMP_INTERFACE_CHANGED_EVENT_UEI);

        // reinitializePrimarySnmpInterface
        ueiList.add(EventConstants.REINITIALIZE_PRIMARY_SNMP_INTERFACE_EVENT_UEI);

        // interfaceReparented
        ueiList.add(EventConstants.INTERFACE_REPARENTED_EVENT_UEI);

        // nodeDeleted
        ueiList.add(EventConstants.NODE_DELETED_EVENT_UEI);

        // duplicateNodeDeleted
        ueiList.add(EventConstants.DUP_NODE_DELETED_EVENT_UEI);

        // interfaceDeleted
        ueiList.add(EventConstants.INTERFACE_DELETED_EVENT_UEI);

        // serviceDeleted
        ueiList.add(EventConstants.SERVICE_DELETED_EVENT_UEI);

        // outageConfigurationChanged
        ueiList.add(EventConstants.SCHEDOUTAGES_CHANGED_EVENT_UEI);

        // configureSNMP
        ueiList.add(EventConstants.CONFIGURE_SNMP_EVENT_UEI);

        // thresholds configuration change
        ueiList.add(EventConstants.THRESHOLDCONFIG_CHANGED_EVENT_UEI);

        // daemon configuration change
        ueiList.add(EventConstants.RELOAD_DAEMON_CONFIG_UEI);

        // node category membership changes
        ueiList.add(EventConstants.NODE_CATEGORY_MEMBERSHIP_CHANGED_EVENT_UEI);

        getEventIpcManager().addEventListener(this, ueiList);
    }

    /**
     * <p>getEventIpcManager</p>
     *
     * @return a {@link org.opennms.netmgt.model.events.EventIpcManager} object.
     */
    public EventIpcManager getEventIpcManager() {
        return m_eventIpcManager;
    }

    /**
     * <p>setEventIpcManager</p>
     *
     * @param eventIpcManager a {@link org.opennms.netmgt.model.events.EventIpcManager} object.
     */
    public void setEventIpcManager(EventIpcManager eventIpcManager) {
        m_eventIpcManager = eventIpcManager;
    }

    private ReadyRunnable ifScheduler() {
        // Schedule existing interfaces for data collection

        ReadyRunnable interfaceScheduler = new ReadyRunnable() {

            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void run() {
                Logging.withPrefix(LOG4J_CATEGORY, new Runnable() {
                    @Override
                    public void run() {
                        try {
                            scheduleExistingInterfaces();
                        } catch (SQLException e) {
                            LOG.error("start: Failed to schedule existing interfaces", e);
                        } finally {
                            setSchedulingCompleted(true);
                        }
                    }
                });
            }
        };
        return interfaceScheduler;
    }

    private void createScheduler() {
        Logging.withPrefix("collectd", new Runnable() {
            @Override
            public void run() {
                // Create a scheduler
                try {
                    LOG.debug("init: Creating collectd scheduler");
                    setScheduler(new LegacyScheduler("Collectd", getCollectorConfigDao().getSchedulerThreads()));
                } catch (final RuntimeException e) {
                    LOG.error("init: Failed to create collectd scheduler", e);
                    throw e;
                }
            }
        });
    }

    /**
     * {@inheritDoc}
     */
    protected void onStart() {
        // start the scheduler
        try {
            LOG.debug("start: Starting collectd scheduler");

            getScheduler().start();
        } catch (RuntimeException e) {
            LOG.error("start: Failed to start scheduler", e);
            throw e;
        }
    }

    /**
     * {@inheritDoc}
     */
    public void onStop() {
        getScheduler().stop();
        deinstallMessageSelectors();

        setScheduler(null);
    }

    /**
     * {@inheritDoc}
     */
    protected void onPause() {
        getScheduler().pause();
    }

    /**
     * {@inheritDoc}
     */
    protected void onResume() {
        getScheduler().resume();
    }

    /**
     * Schedule existing interfaces for data collection.
     *
     * @throws SQLException if database errors encountered.
     */
    private void scheduleExistingInterfaces() throws SQLException {

        instrumentation().beginScheduleExistingInterfaces();
        try {
            //TODO - throws NPE.
            m_transTemplate.execute(new TransactionCallbackWithoutResult() {

                @Override
                public void doInTransactionWithoutResult(TransactionStatus status) {

                    // Loop through collectors and schedule for each one present
                    for (String name : getCollectorNames()) {
                        scheduleInterfacesWithService(name);
                    }
                }
            });
        } finally {
            instrumentation().endScheduleExistingInterfaces();
        }
    }

    private void scheduleInterfacesWithService(String svcName) {
        instrumentation().beginScheduleInterfacesWithService(svcName);
        try {
            LOG.info("scheduleInterfacesWithService: svcName = {}", svcName);

            Collection<OnmsIpInterface> ifsWithServices = findInterfacesWithService(svcName);
            for (OnmsIpInterface iface : ifsWithServices) {
                scheduleInterface(iface, svcName, true);
            }
        } finally {
            instrumentation().endScheduleInterfacesWithService(svcName);
        }
    }

    private Collection<OnmsIpInterface> findInterfacesWithService(String svcName) {
        instrumentation().beginFindInterfacesWithService(svcName);
        int count = -1;
        try {
            Collection<OnmsIpInterface> ifaces = getIpInterfaceDao().findByServiceType(svcName);
            count = ifaces.size();
            return ifaces;
        } finally {
            instrumentation().endFindInterfacesWithService(svcName, count);
        }
    }

    /**
     * This method is responsible for scheduling the specified
     * node/address/svcname tuple for data collection.
     *
     * @param nodeId    Node id
     * @param ipAddress IP address
     * @param svcName   Service name
     * @param existing  True if called by scheduleExistingInterfaces(), false
     *                  otheriwse
     */
    private void scheduleInterface(int nodeId, String ipAddress, String svcName, boolean existing) {

        OnmsIpInterface iface = getIpInterface(nodeId, ipAddress);
        if (iface == null) {
            LOG.error("Unable to find interface with address {} on node {}", ipAddress, nodeId);
            return;
        }

        OnmsMonitoredService svc = iface.getMonitoredServiceByServiceType(svcName);
        if (svc == null) {
            LOG.error("Unable to find service {} on interface with address {} on node {}", svcName, ipAddress,
                    nodeId);
            return;
        }

        scheduleInterface(iface, svc.getServiceType().getName(), existing);
    }

    private void scheduleNode(final int nodeId, final boolean existing) {

        getCollectorConfigDao().rebuildPackageIpListMap();

        OnmsNode node = m_nodeDao.getHierarchy(nodeId);
        node.visit(new AbstractEntityVisitor() {

            @Override
            public void visitMonitoredService(OnmsMonitoredService monSvc) {
                scheduleInterface((OnmsIpInterface) monSvc.getIpInterface(), monSvc.getServiceName(), existing);
            }
        });
    }

    private OnmsIpInterface getIpInterface(int nodeId, String ipAddress) {
        OnmsNode node = m_nodeDao.read(nodeId);
        //TODO - Check casting here.
        OnmsIpInterface iface = (OnmsIpInterface) node.getIpInterfaceByIpAddress(ipAddress);
        return iface;
    }

    private void scheduleInterface(OnmsIpInterface iface, String svcName, boolean existing) {

        final String ipAddress = str(iface.getIpAddress());
        if (ipAddress == null) {
            LOG.warn("Unable to schedule interface {}, could not determine IP address.", iface);
            return;
        }

        instrumentation().beginScheduleInterface(iface.getNode().getId(), ipAddress, svcName);
        try {

            Collection<CollectionSpecification> matchingSpecs = getSpecificationsForInterface(iface, svcName);
            StringBuffer sb;

            LOG.debug("scheduleInterface: found {} matching specs for interface: {}", matchingSpecs.size(), iface);

            for (CollectionSpecification spec : matchingSpecs) {

                if (existing == false) {
                    /*
                     * It is possible that both a nodeGainedService and a
                     * primarySnmpInterfaceChanged event are generated for an
                     * interface during a rescan. To handle this scenario we must
                     * verify that the ipAddress/pkg pair identified by this event
                     * does not already exist in the collectable services list.
                     */
                    if (alreadyScheduled(iface, spec)) {
                        LOG.debug(
                                "scheduleInterface: svc/pkgName {}/{} already in collectable service list, skipping.",
                                iface, spec);
                        continue;
                    }
                }

                try {
                    /*
                     * Criteria checks have all passed. The interface/service pair
                     * can be scheduled.
                     */
                    LOG.debug("scheduleInterface: now scheduling interface: {}/{}", iface, svcName);
                    CollectableService cSvc = null;

                    /*
                     * Create a new SnmpCollector object representing this node,
                     * interface, service and package pairing
                     */

                    cSvc = new CollectableService(iface, m_ifaceDao, spec, getScheduler(),
                            m_schedulingCompletedFlag, m_transTemplate.getTransactionManager());

                    // Add new collectable service to the collectable service list.
                    m_collectableServices.add(cSvc);

                    // Schedule the collectable service for immediate collection
                    getScheduler().schedule(0, cSvc.getReadyRunnable());

                    LOG.debug("scheduleInterface: {}/{} collection, scheduled", iface, svcName);
                } catch (CollectionInitializationException e) {
                    sb = new StringBuffer();
                    sb.append("scheduleInterface: Unable to schedule ");
                    sb.append(iface);
                    sb.append('/');
                    sb.append(svcName);
                    sb.append(", reason: ");
                    sb.append(e.getMessage());

                    // Only log the stack trace if TRACE level logging is enabled.
                    // Fixes bug NMS-3324.
                    // http://issues.opennms.org/browse/NMS-3324
                    if (LOG.isTraceEnabled()) {
                        LOG.trace(sb.toString(), e);
                    } else {
                        LOG.info(sb.toString());
                    }
                } catch (Throwable t) {
                    LOG.error("scheduleInterface: Uncaught exception, failed to schedule interface {}/{}.", iface,
                            svcName, t);
                }
            } // end while more specifications exist
        } finally {
            instrumentation().endScheduleInterface(iface.getNode().getId(), ipAddress, svcName);
        }
    }

    /**
     * <p>getSpecificationsForInterface</p>
     *
     * @param iface   a {@link org.opennms.netmgt.model.OnmsIpInterface} object.
     * @param svcName a {@link java.lang.String} object.
     * @return a {@link java.util.Collection} object.
     */
    public Collection<CollectionSpecification> getSpecificationsForInterface(OnmsIpInterface iface,
            String svcName) {
        Collection<CollectionSpecification> matchingPkgs = new LinkedList<CollectionSpecification>();

        /*
         * Compare interface/service pair against each collectd package
         * For each match, create new SnmpCollector object and
         * schedule it for collection
         */
        for (CollectdPackage wpkg : getCollectorConfigDao().getPackages()) {
            /*
             * Make certain the the current service is in the package
             * and enabled!
             */
            if (!wpkg.serviceInPackageAndEnabled(svcName)) {
                LOG.debug(
                        "getSpecificationsForInterface: address/service: {}/{} not scheduled, service is not enabled or does not exist in package: {}",
                        iface, svcName, wpkg.getName());
                continue;
            }

            // Is the interface in the package?
            final String ipAddress = str(iface.getIpAddress());
            if (!wpkg.interfaceInPackage(ipAddress)) {
                LOG.debug(
                        "getSpecificationsForInterface: address/service: {}/{} not scheduled, interface does not belong to package: {}",
                        iface, svcName, wpkg.getName());
                continue;
            }

            LOG.debug(
                    "getSpecificationsForInterface: address/service: {}/{} scheduled, interface does belong to package: {}",
                    iface, svcName, wpkg.getName());

            matchingPkgs.add(new CollectionSpecification(wpkg, svcName, getServiceCollector(svcName)));
        }
        return matchingPkgs;
    }

    /**
     * Returns true if specified address/pkg pair is already represented in
     * the collectable services list. False otherwise.
     *
     * @param iface TODO
     * @param spec  TODO
     */
    private boolean alreadyScheduled(OnmsIpInterface iface, CollectionSpecification spec) {
        String ipAddress = str(iface.getIpAddress());

        if (ipAddress == null) {
            LOG.warn("Cannot determine if interface {} is already scheduled.  Unable to look up IP address.",
                    iface);
            return false;
        }

        String svcName = spec.getServiceName();
        String pkgName = spec.getPackageName();
        StringBuffer sb;
        boolean isScheduled = false;

        if (LOG.isDebugEnabled()) {
            sb = new StringBuffer();
            sb.append("alreadyScheduled: determining if interface: ");
            sb.append(iface);
            sb.append(" is already scheduled.");
        }

        synchronized (m_collectableServices) {
            for (CollectableService cSvc : m_collectableServices) {
                InetAddress addr = (InetAddress) cSvc.getAddress();
                if (str(addr).equals(ipAddress) && cSvc.getPackageName().equals(pkgName)
                        && cSvc.getServiceName().equals(svcName)) {
                    isScheduled = true;
                    break;
                }
            }
        }

        if (LOG.isDebugEnabled()) {
            sb = new StringBuffer();
            sb.append("alreadyScheduled: interface ");
            sb.append(iface);
            sb.append("already scheduled check: ");
            sb.append(isScheduled);
        }
        return isScheduled;
    }

    /**
     * @param schedulingCompleted The schedulingCompleted to set.
     */
    private void setSchedulingCompleted(boolean schedulingCompleted) {
        m_schedulingCompletedFlag.setSchedulingCompleted(schedulingCompleted);
    }

    private void refreshServicePackages() {
        for (CollectableService thisService : m_collectableServices) {
            thisService.refreshPackage(getCollectorConfigDao());
        }
    }

    private List<CollectableService> getCollectableServices() {
        return m_collectableServices;
    }

    @Override
    public String getName() {
        return "COLLECTD";
    }

    /**
     * {@inheritDoc}
     * <p/>
     * This method is invoked by the JMS topic session when a new event is
     * available for processing. Currently only text based messages are
     * processed by this callback. Each message is examined for its Universal
     * Event Identifier and the appropriate action is taking based on each
     * UEI.
     */
    @Override
    public void onEvent(final Event event) {

        Logging.withPrefix(getName(), new Runnable() {

            @Override
            public void run() {
                m_transTemplate.execute(new TransactionCallbackWithoutResult() {

                    @Override
                    public void doInTransactionWithoutResult(TransactionStatus status) {
                        onEventInTransaction(event);
                    }
                });
            }
        });
    }

    private void onEventInTransaction(Event event) {
        // print out the uei
        //
        LOG.debug("received event, uei = {}", event.getUei());

        try {
            if (event.getUei().equals(EventConstants.SCHEDOUTAGES_CHANGED_EVENT_UEI)) {
                handleScheduledOutagesChanged(event);
            } else {
                if (event.getUei().equals(EventConstants.CONFIGURE_SNMP_EVENT_UEI)) {
                    handleConfigureSNMP(event);
                } else {
                    if (event.getUei().equals(EventConstants.NODE_GAINED_SERVICE_EVENT_UEI)) {
                        handleNodeGainedService(event);
                    } else {
                        if (event.getUei().equals(EventConstants.PRIMARY_SNMP_INTERFACE_CHANGED_EVENT_UEI)) {
                            handlePrimarySnmpInterfaceChanged(event);
                        } else {
                            if (event.getUei()
                                    .equals(EventConstants.REINITIALIZE_PRIMARY_SNMP_INTERFACE_EVENT_UEI)) {
                                handleReinitializePrimarySnmpInterface(event);
                            } else {
                                if (event.getUei().equals(EventConstants.INTERFACE_REPARENTED_EVENT_UEI)) {
                                    handleInterfaceReparented(event);
                                } else {
                                    if (event.getUei().equals(EventConstants.NODE_DELETED_EVENT_UEI)) {
                                        handleNodeDeleted(event);
                                    } else {
                                        if (event.getUei().equals(EventConstants.DUP_NODE_DELETED_EVENT_UEI)) {
                                            handleDupNodeDeleted(event);
                                        } else {
                                            if (event.getUei().equals(EventConstants.INTERFACE_DELETED_EVENT_UEI)) {
                                                handleInterfaceDeleted(event);
                                            } else {
                                                if (event.getUei()
                                                        .equals(EventConstants.SERVICE_DELETED_EVENT_UEI)) {
                                                    handleServiceDeleted(event);
                                                } else {
                                                    if (event.getUei()
                                                            .equals(EventConstants.RELOAD_DAEMON_CONFIG_UEI)) {
                                                        handleReloadDaemonConfig(event);
                                                    } else {
                                                        if (event.getUei().equals(
                                                                EventConstants.NODE_CATEGORY_MEMBERSHIP_CHANGED_EVENT_UEI)) {
                                                            handleNodeCategoryMembershipChanged(event);
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (InsufficientInformationException e) {
            handleInsufficientInfo(e);
        }
    }

    /**
     * <p>handleInsufficientInfo</p>
     *
     * @param e a {@link org.opennms.netmgt.capsd.InsufficientInformationException} object.
     */
    protected void handleInsufficientInfo(InsufficientInformationException e) {
        LOG.info(e.getMessage());
    }

    private void handleDupNodeDeleted(Event event) throws InsufficientInformationException {
        handleNodeDeleted(event);
    }

    private void handleScheduledOutagesChanged(Event event) {
        try {
            LOG.info("Reloading Collectd config factory");

            //TODO - RELOAD COLLECTD CONFIG.
            // CollectdConfigFactory.reload();
            refreshServicePackages();
        } catch (Throwable e) {
            LOG.error("Failed to reload CollectdConfigFactory", e);
        }
    }

    /**
     * </p>
     * Closes the current connections to the Java Message Queue if they are
     * still active. This call may be invoked more than once safely and may be
     * invoked during object finalization.
     * </p>
     */
    private void deinstallMessageSelectors() {
        getEventIpcManager().removeEventListener(this);
    }

    /**
     * This method is responsible for handling configureSNMP events.
     *
     * @param event The event to process.
     */
    private void handleConfigureSNMP(final Event event) {
        LOG.debug("configureSNMPHandler: processing configure SNMP event...", event);

        SnmpEventInfo info = null;
        try {
            info = new SnmpEventInfo(event);

            if (StringUtils.isBlank(info.getFirstIPAddress())) {
                LOG.error("configureSNMPHandler: event contained invalid firstIpAddress. {}", event);
                return;
            }

            LOG.debug("configureSNMPHandler: processing configure SNMP event: {}", info);
            SnmpPeerFactory.getInstance().define(info);
            SnmpPeerFactory.saveCurrent();
            LOG.debug("configureSNMPHandler: process complete. {}", info);
        } catch (Throwable e) {
            LOG.error("configureSNMPHandler: ", e);
        }
    }

    /**
     * This method is responsible for handling interfaceDeleted events.
     *
     * @param event The event to process.
     * @throws InsufficientInformationException
     */
    private void handleInterfaceDeleted(Event event) throws InsufficientInformationException {
        EventUtils.checkNodeId(event);

        String ipAddr = event.getInterface();
        if (EventUtils.isNonIpInterface(ipAddr)) {
            LOG.debug("handleInterfaceDeleted: the deleted interface was a non-ip interface. Nothing to do here.");
            return;
        }

        Long nodeId = event.getNodeid();

        // Iterate over the collectable services list and mark any entries
        // which match the deleted nodeId/IP address pair for deletion
        synchronized (getCollectableServices()) {
            CollectableService cSvc = null;
            ListIterator<CollectableService> liter = getCollectableServices().listIterator();
            while (liter.hasNext()) {
                cSvc = liter.next();

                // Only interested in entries with matching nodeId and IP
                // address
                InetAddress addr = (InetAddress) cSvc.getAddress();
                if (!(cSvc.getNodeId() == nodeId && addr.getHostName().equals(ipAddr))) {
                    continue;
                }

                synchronized (cSvc) {
                    // Retrieve the CollectorUpdates object associated with
                    // this CollectableService if one exists.
                    CollectorUpdates updates = cSvc.getCollectorUpdates();

                    // Now set the update's deletion flag so the next
                    // time it is selected for execution by the scheduler
                    // the collection will be skipped and the service will not
                    // be rescheduled.
                    LOG.debug(
                            "Marking CollectableService for deletion because an interface was deleted:  Service nodeid={}, "
                                    + "deleted node:{}service address:{}deleted interface:{}",
                            cSvc.getNodeId(), nodeId, addr.getHostName(), ipAddr);

                    updates.markForDeletion();
                }

                // Now safe to remove the collectable service from
                // the collectable services list
                liter.remove();
            }
        }

        LOG.debug("interfaceDeletedHandler: processing of interfaceDeleted event for {}/{} completed", nodeId,
                ipAddr);
    }

    /**
     * This method is responsible for processing 'interfacReparented' events.
     * An 'interfaceReparented' event will have old and new nodeId parms
     * associated with it. All CollectableService objects in the service
     * updates map which match the event's interface address and the SNMP
     * service have a reparenting update associated with them. When the
     * scheduler next pops one of these services from an interval queue for
     * collection all of the RRDs associated with the old nodeId are moved
     * under the new nodeId and the nodeId of the collectable service is
     * updated to reflect the interface's new parent nodeId.
     *
     * @param event The event to process.
     * @throws InsufficientInformationException
     */
    private void handleInterfaceReparented(Event event) throws InsufficientInformationException {
        EventUtils.checkNodeId(event);
        EventUtils.checkInterface(event);

        LOG.debug("interfaceReparentedHandler:  processing interfaceReparented event for {}", event.getInterface());

        // Verify that the event has an interface associated with it
        if (event.getInterface() == null) {
            return;
        }

        // Extract the old and new nodeId's from the event parms
        String oldNodeIdStr = null;
        String newNodeIdStr = null;
        String parmName = null;
        Value parmValue = null;
        String parmContent = null;

        for (Parm parm : event.getParmCollection()) {
            parmName = parm.getParmName();
            parmValue = parm.getValue();
            if (parmValue == null) {
                continue;
            } else {
                parmContent = parmValue.getContent();
            }

            // old nodeid
            if (parmName.equals(EventConstants.PARM_OLD_NODEID)) {
                oldNodeIdStr = parmContent;
            }

            // new nodeid
            else {
                if (parmName.equals(EventConstants.PARM_NEW_NODEID)) {
                    newNodeIdStr = parmContent;
                }
            }
        }

        // Only proceed provided we have both an old and a new nodeId
        //
        if (oldNodeIdStr == null || newNodeIdStr == null) {
            LOG.warn("interfaceReparentedHandler: old and new nodeId parms are required, unable to process.");
            return;
        }

        // Iterate over the CollectableService objects in the services
        // list looking for entries which share the same interface
        // address as the reparented interface. Mark any matching objects
        // for reparenting.
        //
        // The next time the service is scheduled for execution it
        // will move all of the RRDs associated
        // with the old nodeId under the new nodeId and update the service's
        // SnmpMonitor.NodeInfo attribute to reflect the new nodeId. All
        // subsequent collections will then be updating the appropriate RRDs.
        //
        OnmsIpInterface iface = null;
        synchronized (getCollectableServices()) {
            CollectableService cSvc = null;
            Iterator<CollectableService> iter = getCollectableServices().iterator();
            while (iter.hasNext()) {
                cSvc = iter.next();

                InetAddress addr = (InetAddress) cSvc.getAddress();
                if (addr.equals(event.getInterfaceAddress())) {
                    synchronized (cSvc) {
                        // Got a match!
                        LOG.debug("interfaceReparentedHandler: got a CollectableService match for {}",
                                event.getInterface());

                        // Retrieve the CollectorUpdates object associated
                        // with
                        // this CollectableService.
                        CollectorUpdates updates = cSvc.getCollectorUpdates();
                        if (iface == null) {
                            iface = getIpInterface(event.getNodeid().intValue(), event.getInterface());
                        }

                        // Now set the reparenting flag
                        updates.markForReparenting(oldNodeIdStr, newNodeIdStr, iface);
                        LOG.debug("interfaceReparentedHandler: marking {} for reparenting for service SNMP.",
                                event.getInterface());
                    }
                }
            }
        }

        LOG.debug("interfaceReparentedHandler: processing of interfaceReparented event for interface {} completed.",
                event.getInterface());
    }

    /**
     * This method is responsible for handling nodeDeleted events.
     *
     * @param event The event to process.
     * @throws InsufficientInformationException
     */
    private void handleNodeDeleted(Event event) throws InsufficientInformationException {
        EventUtils.checkNodeId(event);
        EventUtils.checkInterface(event);

        Long nodeId = event.getNodeid();

        unscheduleNodeAndMarkForDeletion(nodeId);

        LOG.debug("nodeDeletedHandler: processing of nodeDeleted event for nodeid {} completed.", nodeId);
    }

    /**
     * This method is responsible for handling nodeDeleted events.
     *
     * @param event The event to process.
     * @throws InsufficientInformationException
     */
    private void handleNodeCategoryMembershipChanged(Event event) throws InsufficientInformationException {
        EventUtils.checkNodeId(event);

        Long nodeId = event.getNodeid();

        unscheduleNodeAndMarkForDeletion(nodeId);

        LOG.debug("nodeCategoryMembershipChanged: unscheduling nodeid {} completed.", nodeId);

        scheduleNode(nodeId.intValue(), true);
    }

    private void unscheduleNodeAndMarkForDeletion(Long nodeId) {
        // Iterate over the collectable service list and mark any entries
        // which match the deleted nodeId for deletion.
        synchronized (getCollectableServices()) {
            CollectableService cSvc = null;
            final ListIterator<CollectableService> liter = getCollectableServices().listIterator();
            while (liter.hasNext()) {
                cSvc = liter.next();

                // Only interested in entries with matching nodeId
                if (!(cSvc.getNodeId() == nodeId)) {
                    continue;
                }

                synchronized (cSvc) {
                    // Retrieve the CollectorUpdates object associated
                    // with this CollectableService.
                    CollectorUpdates updates = cSvc.getCollectorUpdates();

                    // Now set the update's deletion flag so the next
                    // time it is selected for execution by the scheduler
                    // the collection will be skipped and the service will not
                    // be rescheduled.
                    LOG.debug(
                            "Marking CollectableService for deletion because a node was deleted:  Service nodeid={}, deleted node:{}",
                            cSvc.getNodeId(), nodeId);
                    updates.markForDeletion();
                }

                // Now safe to remove the collectable service from
                // the collectable services list
                liter.remove();
            }
        }
    }

    /**
     * Process the event, construct a new CollectableService object
     * representing the node/interface combination, and schedule the interface
     * for collection. If any errors occur scheduling the interface no error
     * is returned.
     *
     * @param event The event to process.
     * @throws InsufficientInformationException
     */
    private void handleNodeGainedService(Event event) throws InsufficientInformationException {
        EventUtils.checkNodeId(event);
        EventUtils.checkInterface(event);
        EventUtils.checkService(event);
        // Schedule the interface
        //
        scheduleForCollection(event);
    }

    private void handleReloadDaemonConfig(Event event) {
        final String daemonName = "Threshd";
        boolean isTarget = false;
        for (Parm parm : event.getParmCollection()) {
            if (EventConstants.PARM_DAEMON_NAME.equals(parm.getParmName())
                    && daemonName.equalsIgnoreCase(parm.getValue().getContent())) {
                isTarget = true;
                break;
            }
        }
        if (isTarget) {
            String thresholdsFile = ConfigFileConstants
                    .getFileName(ConfigFileConstants.THRESHOLDING_CONF_FILE_NAME);
            String threshdFile = ConfigFileConstants.getFileName(ConfigFileConstants.THRESHD_CONFIG_FILE_NAME);
            String targetFile = thresholdsFile; // Default
            for (Parm parm : event.getParmCollection()) {
                if (EventConstants.PARM_CONFIG_FILE_NAME.equals(parm.getParmName())
                        && threshdFile.equalsIgnoreCase(parm.getValue().getContent())) {
                    targetFile = threshdFile;
                }
            }
            EventBuilder ebldr = null;
            try {
                // Reloading Factories
                if (targetFile.equals(thresholdsFile)) {
                    ThresholdingConfigFactory.reload();
                }
                if (targetFile.equals(threshdFile)) {
                    ThreshdConfigFactory.reload();
                    ThresholdingConfigFactory.reload(); // This is required if the threshold packages has been changed.
                }
                // Sending the threshold configuration change event
                ebldr = new EventBuilder(EventConstants.THRESHOLDCONFIG_CHANGED_EVENT_UEI, "Collectd");
                getEventIpcManager().sendNow(ebldr.getEvent());
                // Updating thresholding visitors to use the new configuration
                LOG.debug("handleReloadDaemonConfig: Reloading thresholding configuration in collectd");
                synchronized (m_collectableServices) {
                    for (CollectableService service : m_collectableServices) {
                        service.reinitializeThresholding();
                    }
                }
                // Preparing successful event
                ebldr = new EventBuilder(EventConstants.RELOAD_DAEMON_CONFIG_SUCCESSFUL_UEI, "Collectd");
                ebldr.addParam(EventConstants.PARM_DAEMON_NAME, daemonName);
                ebldr.addParam(EventConstants.PARM_CONFIG_FILE_NAME, targetFile);
            } catch (Throwable e) {
                // Preparing failed event
                LOG.error("handleReloadDaemonConfig: Error reloading/processing thresholds configuration: {}",
                        e.getMessage(), e);
                ebldr = new EventBuilder(EventConstants.RELOAD_DAEMON_CONFIG_FAILED_UEI, "Collectd");
                ebldr.addParam(EventConstants.PARM_DAEMON_NAME, daemonName);
                ebldr.addParam(EventConstants.PARM_CONFIG_FILE_NAME, targetFile);
                ebldr.addParam(EventConstants.PARM_REASON, e.getMessage());
            } finally {
                if (ebldr != null) {
                    getEventIpcManager().sendNow(ebldr.getEvent());
                }
            }
        }
    }

    private void scheduleForCollection(Event event) {
        // This moved to here from the scheduleInterface() for better behavior
        // during initialization

        getCollectorConfigDao().rebuildPackageIpListMap();

        scheduleInterface(event.getNodeid().intValue(), event.getInterface(), event.getService(), false);
    }

    /**
     * Process the 'primarySnmpInterfaceChanged' event. Extract the old and
     * new primary SNMP interface addresses from the event parms. Any
     * CollectableService objects located in the collectable services list
     * which match the IP address of the old primary interface and have a
     * service name of "SNMP" are flagged for deletion. This will ensure that
     * the old primary interface is no longer collected against. Finally the
     * new primary SNMP interface is scheduled. The packages are examined and
     * new CollectableService objects are created, initialized and scheduled
     * for collection.
     *
     * @param event The event to process.
     * @throws InsufficientInformationException
     */
    private void handlePrimarySnmpInterfaceChanged(Event event) throws InsufficientInformationException {
        EventUtils.checkNodeId(event);
        EventUtils.checkInterface(event);

        LOG.debug("primarySnmpInterfaceChangedHandler:  processing primary SNMP interface changed event...");

        // Currently only support SNMP data collection.
        //
        if (!event.getService().equals("SNMP")) {
            return;
        }

        // Extract the old and new primary SNMP interface addresses from the
        // event parms.
        //
        String oldPrimaryIfAddr = null;
        String parmName = null;
        Value parmValue = null;
        String parmContent = null;

        for (Parm parm : event.getParmCollection()) {
            parmName = parm.getParmName();
            parmValue = parm.getValue();
            if (parmValue == null) {
                continue;
            } else {
                parmContent = parmValue.getContent();
            }

            // old primary SNMP interface (optional parameter)
            if (parmName.equals(EventConstants.PARM_OLD_PRIMARY_SNMP_ADDRESS)) {
                oldPrimaryIfAddr = parmContent;
            }
        }

        if (oldPrimaryIfAddr != null) {
            // Mark the service for deletion so that it will not be
            // rescheduled
            // for
            // collection.
            //
            // Iterate over the CollectableService objects in the service
            // updates map
            // and mark any which have the same interface address as the old
            // primary SNMP interface and a service name of "SNMP" for
            // deletion.
            //
            synchronized (getCollectableServices()) {
                CollectableService cSvc = null;
                ListIterator<CollectableService> liter = getCollectableServices().listIterator();
                while (liter.hasNext()) {
                    cSvc = liter.next();

                    final InetAddress addr = (InetAddress) cSvc.getAddress();
                    final String addrString = str(addr);
                    if (addrString != null && addrString.equals(oldPrimaryIfAddr)) {
                        synchronized (cSvc) {
                            // Got a match! Retrieve the CollectorUpdates
                            // object
                            // associated
                            // with this CollectableService.
                            CollectorUpdates updates = cSvc.getCollectorUpdates();

                            // Now set the deleted flag
                            updates.markForDeletion();
                            LOG.debug("primarySnmpInterfaceChangedHandler: marking {} as deleted for service SNMP.",
                                    oldPrimaryIfAddr);
                        }

                        // Now safe to remove the collectable service from
                        // the collectable services list
                        liter.remove();
                    }
                }
            }
        }

        // Now we can schedule the new service...
        //
        scheduleForCollection(event);

        LOG.debug(
                "primarySnmpInterfaceChangedHandler: processing of primarySnmpInterfaceChanged event for nodeid {} completed.",
                event.getNodeid());
    }

    /**
     * Process the event. This event is generated when a managed node which
     * supports SNMP gains a new interface. In this situation the
     * CollectableService object representing the primary SNMP interface of
     * the node must be reinitialized. The CollectableService object
     * associated with the primary SNMP interface for the node will be marked
     * for reinitialization. Reinitializing the CollectableService object
     * consists of calling the ServiceCollector.release() method followed by
     * the ServiceCollector.initialize() method which will refresh attributes
     * such as the interface key list and number of interfaces (both of which
     * most likely have changed). Reinitialization will take place the next
     * time the CollectableService is popped from an interval queue for
     * collection. If any errors occur scheduling the service no error is
     * returned.
     *
     * @param event The event to process.
     * @throws InsufficientInformationException
     */
    private void handleReinitializePrimarySnmpInterface(Event event) throws InsufficientInformationException {
        EventUtils.checkNodeId(event);
        EventUtils.checkInterface(event);

        Long nodeid = event.getNodeid();
        String ipAddress = event.getInterface();

        // Mark the primary SNMP interface for reinitialization in
        // order to update any modified attributes associated with
        // the collectable service..
        //
        // Iterate over the CollectableService objects in the
        // updates map and mark any which have the same interface
        // address for reinitialization
        //
        OnmsIpInterface iface = null;
        synchronized (getCollectableServices()) {
            Iterator<CollectableService> iter = getCollectableServices().iterator();
            while (iter.hasNext()) {
                CollectableService cSvc = iter.next();

                final InetAddress addr = (InetAddress) cSvc.getAddress();
                final String addrString = str(addr);
                LOG.debug("Comparing CollectableService ip address = {} and event ip interface = {}", addrString,
                        ipAddress);
                if (addrString != null && addrString.equals(ipAddress)) {
                    synchronized (cSvc) {
                        if (iface == null) {
                            iface = getIpInterface(nodeid.intValue(), ipAddress);
                        }
                        // Got a match! Retrieve the CollectorUpdates object
                        // associated
                        // with this CollectableService.
                        CollectorUpdates updates = cSvc.getCollectorUpdates();

                        // Now set the reinitialization flag
                        updates.markForReinitialization(iface);
                        LOG.debug(
                                "reinitializePrimarySnmpInterfaceHandler: marking {} for reinitialization for service SNMP.",
                                ipAddress);
                    }
                }
            }
        }
    }

    /**
     * This method is responsible for handling serviceDeleted events.
     *
     * @param event The event to process.
     * @throws InsufficientInformationException
     */
    private void handleServiceDeleted(Event event) throws InsufficientInformationException {
        EventUtils.checkNodeId(event);
        EventUtils.checkInterface(event);
        EventUtils.checkService(event);

        //INCORRECT; we now support all *sorts* of data collection.  This is *way* out of date
        // Currently only support SNMP data collection.
        //
        //if (!event.getService().equals("SNMP"))
        //    return;

        Long nodeId = event.getNodeid();
        String ipAddr = event.getInterface();
        String svcName = event.getService();

        // Iterate over the collectable services list and mark any entries
        // which match the nodeId/ipAddr of the deleted service
        // for deletion.
        synchronized (getCollectableServices()) {
            CollectableService cSvc = null;
            ListIterator<CollectableService> liter = getCollectableServices().listIterator();
            while (liter.hasNext()) {
                cSvc = liter.next();

                // Only interested in entries with matching nodeId, IP address
                // and service
                InetAddress addr = (InetAddress) cSvc.getAddress();

                //WATCH the brackets; there used to be an extra close bracket after the ipAddr comparison which borked this whole expression
                if (!(cSvc.getNodeId() == nodeId && addr.getHostName().equals(ipAddr)
                        && cSvc.getServiceName().equals(svcName))) {
                    continue;
                }

                synchronized (cSvc) {
                    // Retrieve the CollectorUpdates object associated with
                    // this CollectableService if one exists.
                    CollectorUpdates updates = cSvc.getCollectorUpdates();

                    // Now set the update's deletion flag so the next
                    // time it is selected for execution by the scheduler
                    // the collection will be skipped and the service will not
                    // be rescheduled.
                    LOG.debug(
                            "Marking CollectableService for deletion because a service was deleted:  Service nodeid={}, deleted node:{}, "
                                    + "service address:{}, deleted interface:{}, service servicename:{}, deleted service name:{}, event source {}",
                            cSvc.getNodeId(), nodeId, addr.getHostName(), ipAddr, cSvc.getServiceName(), svcName,
                            event.getSource());
                    updates.markForDeletion();
                }

                // Now safe to remove the collectable service from
                // the collectable services list
                liter.remove();
            }
        }

        LOG.debug("serviceDeletedHandler: processing of serviceDeleted event for {}/{}/{} completed.", nodeId,
                ipAddr, svcName);
    }

    private Scheduler getScheduler() {
        if (m_scheduler == null) {
            createScheduler();
        }
        return m_scheduler;
    }

    /**
     * <p>setScheduler</p>
     *
     * @param scheduler a {@link org.opennms.netmgt.scheduler.Scheduler} object.
     */
    public void setScheduler(Scheduler scheduler) {
        m_scheduler = scheduler;
    }

    private CollectorConfigDao getCollectorConfigDao() {
        return m_collectorConfigDao;
    }

    /**
     * <p>setCollectorConfigDao</p>
     *
     * @param collectorConfigDao a {@link org.opennms.netmgt.dao.api.CollectorConfigDao} object.
     */
    public void setCollectorConfigDao(CollectorConfigDao collectorConfigDao) {
        m_collectorConfigDao = collectorConfigDao;
    }

    private OnmsIpInterfaceDao getIpInterfaceDao() {
        return m_ifaceDao;
    }

    /**
     * <p>setIpInterfaceDao</p>
     *
     * @param ifSvcDao a {@link org.opennms.netmgt.dao.api.IpInterfaceDao} object.
     */
    public void setIpInterfaceDao(OnmsIpInterfaceDao ifSvcDao) {
        m_ifaceDao = ifSvcDao;
    }

    /**
     * <p>setTransactionTemplate</p>
     *
     * @param transTemplate a {@link org.springframework.transaction.support.TransactionTemplate} object.
     */
    public void setTransactionTemplate(TransactionTemplate transTemplate) {
        m_transTemplate = transTemplate;
    }

    /**
     * <p>setNodeDao</p>
     *
     * @param nodeDao a {@link org.opennms.netmgt.dao.api.NodeDao} object.
     */
    public void setNodeDao(OnmsNodeDao nodeDao) {
        m_nodeDao = nodeDao;
    }

    /**
     * <p>setServiceCollector</p>
     *
     * @param svcName   a {@link java.lang.String} object.
     * @param collector a {@link ServiceCollector} object.
     */
    public void setServiceCollector(String svcName, ServiceCollector collector) {
        m_collectors.put(svcName, collector);
    }

    /**
     * <p>getServiceCollector</p>
     *
     * @param svcName a {@link java.lang.String} object.
     * @return a {@link ServiceCollector} object.
     */
    public ServiceCollector getServiceCollector(String svcName) {
        return m_collectors.get(svcName);
    }

    /**
     * <p>getCollectorNames</p>
     *
     * @return a {@link java.util.Set} object.
     */
    public Set<String> getCollectorNames() {
        return m_collectors.keySet();
    }

    private void instantiateCollectors() {
        LOG.debug("instantiateCollectors: Loading collectors");

        /*
         * Load up an instance of each collector from the config
         * so that the event processor will have them for
         * new incoming events to create collectable service objects.
         */
        Collection<Collector> collectors = getCollectorConfigDao().getCollectors();
        for (Collector collector : collectors) {
            String svcName = collector.getService();
            try {
                LOG.debug("instantiateCollectors: Loading collector {}, classname {}", svcName,
                        collector.getClassName());
                Class<?> cc = bundleContext.getBundle().loadClass(collector.getClassName());

                //Class.forName(collector.getClassName());
                ServiceCollector sc = (ServiceCollector) cc.newInstance();

                sc.initialize(Collections.<String, String>emptyMap());

                setServiceCollector(svcName, sc);
            } catch (Throwable t) {
                LOG.warn("instantiateCollectors: Failed to load collector {} for service {}",
                        collector.getClassName(), svcName, t);
            }
        }
    }

    static class SchedulingCompletedFlag {
        volatile boolean m_schedulingCompleted = false;

        public synchronized boolean isSchedulingCompleted() {
            return m_schedulingCompleted;
        }

        public synchronized void setSchedulingCompleted(boolean schedulingCompleted) {
            m_schedulingCompleted = schedulingCompleted;
        }
    }

    private BundleContext bundleContext;

    public void setBundleContext(BundleContext bundleContext) {
        this.bundleContext = bundleContext;
    }
}