/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2006-2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 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.netmgt.capsd; import java.net.InetAddress; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; import org.opennms.core.utils.DBUtils; import org.opennms.core.utils.InetAddressUtils; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.EventConstants; import org.opennms.netmgt.config.CapsdConfigFactory; import org.opennms.netmgt.config.DataSourceFactory; import org.opennms.netmgt.model.events.annotations.EventHandler; import org.opennms.netmgt.model.events.annotations.EventListener; import org.opennms.netmgt.xml.event.Event; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; /** * <p>BroadcastEventProcessor class.</p> * * @author <a href="mailto:matt@opennms.org">Matt Brozowski </a> * @author <a href="http://www.opennms.org/">OpenNMS </a> */ @EventListener(name="Capsd:BroadcastEventProcessor") public class BroadcastEventProcessor implements InitializingBean { /** * SQL statement used to add an interface/server mapping into the database; */ private static String SQL_ADD_INTERFACE_TO_SERVER = "INSERT INTO serverMap VALUES (?, ?)"; /** * SQL statement used to add an interface/service mapping into the database. */ private static String SQL_ADD_SERVICE_TO_MAPPING = "INSERT INTO serviceMap VALUES (?, ?)"; /** * SQL statement used to delete all services mapping to a specified * interface from the database. */ private static String SQL_DELETE_ALL_SERVICES_INTERFACE_MAPPING = "DELETE FROM serviceMap WHERE ipaddr = ?"; /** * SQL statement used to delete an interface/server mapping from the * database. */ private static String SQL_DELETE_INTERFACE_ON_SERVER = "DELETE FROM serverMap WHERE ipaddr = ? AND servername = ?"; /** * SQL statement used to delete an interface/service mapping from the * database. */ private static String SQL_DELETE_SERVICE_INTERFACE_MAPPING = "DELETE FROM serviceMap WHERE ipaddr = ? AND servicemapname = ?"; /** * SQL statement used to verify if an ipinterface with the specified ip * address exists in the database and retrieve the nodeid if exists. */ private static String SQL_QUERY_IPADDRESS_EXIST = "SELECT nodeid FROM ipinterface WHERE ipaddr = ? AND isManaged !='D'"; /** * SQL statement used to query the 'node' and 'ipinterface' tables to verify * if a specified ipaddr and node label have already exist in the database. */ private static String SQL_QUERY_IPINTERFACE_EXIST = "SELECT nodelabel, ipaddr FROM node, ipinterface WHERE node.nodeid = ipinterface.nodeid AND node.nodelabel = ? AND ipinterface.ipaddr = ? AND isManaged !='D' AND nodeType !='D'"; /** * SQL statement used to query if a node with the specified nodelabel exist * in the database, and the nodeid from the database if exists. */ private static String SQL_QUERY_NODE_EXIST = "SELECT nodeid, dpname FROM node WHERE nodelabel = ? AND nodeType !='D'"; /** * SQL statement used to verify if an ifservice with the specified ip * address and service name exists in the database. */ private static String SQL_QUERY_SERVICE_EXIST = "SELECT nodeid FROM ifservices, service WHERE ifservices.serviceid = service.serviceid AND ipaddr = ? AND servicename = ? AND status !='D'"; /** * SQL statement used to query if an interface/service mapping already * exists in the database. */ private static String SQL_QUERY_SERVICE_MAPPING_EXIST = "SELECT * FROM serviceMap WHERE ipaddr = ? AND servicemapname = ?"; /** * SQL query to retrieve nodeid of a particulary interface address */ private static String SQL_RETRIEVE_NODEID = "select nodeid from ipinterface where ipaddr=? and isManaged!='D'"; /** * SQL statement used to retrieve the serviced id from the database with a * specified service name. */ private static String SQL_RETRIEVE_SERVICE_ID = "SELECT serviceid FROM service WHERE servicename = ?"; /** * Determines if deletePropagation is enabled in the Outage Manager. * * @return true if deletePropagation is enable, false otherwise */ public static boolean isPropagationEnabled() { return CapsdConfigFactory.getInstance().getDeletePropagationEnabled(); } /** * Convenience method checking Capsd's config for status of XmlRpc API * * @return Returns the xmlrpc. */ public static boolean isXmlRpcEnabled() { return CapsdConfigFactory.getInstance().getXmlrpc().equals("true"); } /** * local openNMS server name */ private String m_localServer = null; /** * The Capsd rescan scheduler */ private Scheduler m_scheduler; /** * The location where suspectInterface events are enqueued for processing. */ private ExecutorService m_suspectQ; private SuspectEventProcessorFactory m_suspectEventProcessorFactory; /** * Counts the number of interfaces on the node other than a given interface * * @param dbConn * the database connection * @param nodeid * the node to check interfaces for * @param ipAddr * the interface not to include in the count * @return the numer of interfaces other than the given one * @throws SQLException * if an error occurs talking to the database */ private int countOtherInterfacesOnNode(Connection dbConn, long nodeId, String ipAddr) throws SQLException { final String DB_COUNT_OTHER_INTERFACES_ON_NODE = "SELECT count(*) FROM ipinterface WHERE nodeID=? and ipAddr != ? and isManaged != 'D'"; PreparedStatement stmt = null; ResultSet rs = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement(DB_COUNT_OTHER_INTERFACES_ON_NODE); d.watch(stmt); stmt.setLong(1, nodeId); stmt.setString(2, ipAddr); rs = stmt.executeQuery(); d.watch(rs); int count = 0; while (rs.next()) { count = rs.getInt(1); } if (log().isDebugEnabled()) log().debug("countServicesForInterface: count services for interface " + nodeId + "/" + ipAddr + ": found " + count); return count; } finally { d.cleanUp(); } } /** * Counts the number of other non deleted services associated with the * interface defined by nodeid/ipAddr * * @param dbConn * the database connection * @param nodeId * the node to chck * @param ipAddr * the interface to check * @param service * the name of the service not to include * @return the number of non deleted services, other than serviceId */ private int countOtherServicesOnInterface(Connection dbConn, long nodeId, String ipAddr, String service) throws SQLException { final String DB_COUNT_OTHER_SERVICES_ON_IFACE = "SELECT count(*) FROM ifservices, service " + "WHERE ifservices.serviceId = service.serviceId AND ifservices.status != 'D' " + "AND ifservices.nodeID=? AND ifservices.ipAddr=? AND service.servicename != ?"; PreparedStatement stmt = null; ResultSet rs = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement(DB_COUNT_OTHER_SERVICES_ON_IFACE); d.watch(stmt); stmt.setLong(1, nodeId); stmt.setString(2, ipAddr); stmt.setString(3, service); rs = stmt.executeQuery(); d.watch(rs); int count = 0; while (rs.next()) { count = rs.getInt(1); } if (log().isDebugEnabled()) log().debug("countServicesForInterface: count services for interface " + nodeId + "/" + ipAddr + ": found " + count); return count; } finally { d.cleanUp(); } } /** * Counts the number of non deleted services on a node on interfaces other * than a given interface * * @param dbConn * the database connection * @param nodeId * the nodeid to check * @param ipAddr * the address of the interface not to include * @return the number of non deleted services on other interfaces */ private int countServicesOnOtherInterfaces(Connection dbConn, long nodeId, String ipAddr) throws SQLException { final String DB_COUNT_SERVICES_ON_OTHER_INTERFACES = "SELECT count(*) FROM ifservices WHERE nodeID=? and ipAddr != ? and status != 'D'"; PreparedStatement stmt = null; ResultSet rs = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement(DB_COUNT_SERVICES_ON_OTHER_INTERFACES); d.watch(stmt); stmt.setLong(1, nodeId); stmt.setString(2, ipAddr); rs = stmt.executeQuery(); d.watch(rs); int count = 0; while (rs.next()) { count = rs.getInt(1); } if (log().isDebugEnabled()) log().debug("countServicesOnOtherInterfaces: count services for node " + nodeId + ": found " + count); return count; } finally { d.cleanUp(); } } /** * Helper method used to create add an interface to a node. * * @param dbConn * @param nodeLabel * @param ipaddr * @return a LinkedList of events to be sent * @throws SQLException * @throws FailedOperationException */ private List<Event> createInterfaceOnNode(Connection dbConn, String nodeLabel, String ipaddr) throws SQLException, FailedOperationException { PreparedStatement stmt = null; ResultSet rs = null; final DBUtils d = new DBUtils(getClass()); try { // There is no ipinterface associated with the specified nodeLabel // exist in the database. Verify if a node with the nodeLabel already // exist in the database. If not, create a node with the nodeLabel and add it // to the database, and also add the ipaddress associated with this node to // the database. If the node with the nodeLabel exists in the node // table, just add the ip address to the database. stmt = dbConn.prepareStatement(SQL_QUERY_NODE_EXIST); d.watch(stmt); stmt.setString(1, nodeLabel); rs = stmt.executeQuery(); d.watch(rs); List<Event> eventsToSend = new LinkedList<Event>(); while (rs.next()) { if (log().isDebugEnabled()) log().debug("addInterfaceHandler: add interface: " + ipaddr + " to the database."); // Node already exists. Add the ipaddess to the ipinterface // table InetAddress ifaddr; try { ifaddr = InetAddressUtils.addr(ipaddr); } catch (final IllegalArgumentException e) { throw new FailedOperationException("unable to resolve host " + ipaddr + ": " + e.getMessage(), e); } int nodeId = rs.getInt(1); String dpName = rs.getString(2); DbIpInterfaceEntry ipInterface = DbIpInterfaceEntry.create(nodeId, ifaddr); ipInterface.setHostname(ifaddr.getHostName()); ipInterface.setManagedState(DbIpInterfaceEntry.STATE_MANAGED); ipInterface.setPrimaryState(DbIpInterfaceEntry.SNMP_NOT_ELIGIBLE); ipInterface.store(dbConn); // create a nodeEntry DbNodeEntry nodeEntry = DbNodeEntry.get(nodeId, dpName); Event newEvent = EventUtils.createNodeGainedInterfaceEvent(nodeEntry, ifaddr); eventsToSend.add(newEvent); } return eventsToSend; } finally { d.cleanUp(); } } /** * This method add a node with the specified node label to the database. If * also adds in interface with the given ipaddress to the node, if the * ipaddr is not null * * @param conn * The JDBC Database connection. * @param nodeLabel * the node label to identify the node to create. * @param ipaddr * the ipaddress to be added into the ipinterface table. * @throws SQLException * if a database error occurs * @throws FailedOperationException * if the ipaddr is not resolvable */ private List<Event> createNodeWithInterface(Connection conn, String nodeLabel, String ipaddr) throws SQLException, FailedOperationException { if (nodeLabel == null) return Collections.emptyList(); if (log().isDebugEnabled()) log().debug("addNode: Add a node " + nodeLabel + " to the database"); List<Event> eventsToSend = new LinkedList<Event>(); DbNodeEntry node = DbNodeEntry.create(); Date now = new Date(); node.setCreationTime(now); node.setNodeType(DbNodeEntry.NODE_TYPE_ACTIVE); node.setLabel(nodeLabel); node.setLabelSource(DbNodeEntry.LABEL_SOURCE_USER); node.store(conn); Event newEvent = EventUtils.createNodeAddedEvent(node); eventsToSend.add(newEvent); if (ipaddr != null) if (log().isDebugEnabled()) log().debug("addNode: Add an IP Address " + ipaddr + " to the database"); // add the ipaddess to the database InetAddress ifaddress; try { ifaddress = InetAddressUtils.addr(ipaddr); } catch (final IllegalArgumentException e) { throw new FailedOperationException("unable to resolve host " + ipaddr + ": " + e.getMessage(), e); } DbIpInterfaceEntry ipInterface = DbIpInterfaceEntry.create(node.getNodeId(), ifaddress); ipInterface.setHostname(ifaddress.getHostName()); ipInterface.setManagedState(DbIpInterfaceEntry.STATE_MANAGED); ipInterface.setPrimaryState(DbIpInterfaceEntry.SNMP_NOT_ELIGIBLE); ipInterface.store(conn); Event gainIfEvent = EventUtils.createNodeGainedInterfaceEvent(node, ifaddress); eventsToSend.add(gainIfEvent); return eventsToSend; } /** * Helper method to add an interface to a node. * * @param dbConn * @param nodeLabel * @param ipaddr * @return eventsToSend * A List Object containing events to be sent * @throws SQLException * @throws FailedOperationException */ private List<Event> doAddInterface(Connection dbConn, String nodeLabel, String ipaddr) throws SQLException, FailedOperationException { List<Event> eventsToSend; if (interfaceExists(dbConn, nodeLabel, ipaddr)) { if (log().isDebugEnabled()) { log().debug("addInterfaceHandler: node " + nodeLabel + " with IPAddress " + ipaddr + " already exist in the database."); } eventsToSend = Collections.emptyList(); } else if (nodeExists(dbConn, nodeLabel)) { eventsToSend = createInterfaceOnNode(dbConn, nodeLabel, ipaddr); } else { // The node does not exist in the database, add the node and // the ipinterface into the database. eventsToSend = createNodeWithInterface(dbConn, nodeLabel, ipaddr); } return eventsToSend; } /** * Perform the buld of the work for processing an addNode event * * @param dbConn * the database connection * @param nodeLabel * the label for the node to add * @param ipaddr * an interface on the node (may be null if no interface is * supplied) * @return a list of events that need to be sent in response to these * changes * @throws SQLException * if a database error occurrs * @throws FailedOperationException * if other errors occur */ private List<Event> doAddNode(Connection dbConn, String nodeLabel, String ipaddr) throws SQLException, FailedOperationException { List<Event> eventsToSend; if (!nodeExists(dbConn, nodeLabel)) { // the node does not exist in the database. Add the node with the // specified // node label and add the ipaddress to the database. eventsToSend = createNodeWithInterface(dbConn, nodeLabel, ipaddr); } else { eventsToSend = Collections.emptyList(); if (log().isDebugEnabled()) { log().debug("doAddNode: node " + nodeLabel + " with IPAddress " + ipaddr + " already exist in the database."); } } return eventsToSend; } /** * Helper method used to update the mapping of interfaces to services. * * @param dbConn * @param ipaddr * @param serviceName * @param action * @param txNo * @return a list of events that need to be sent in response to these * changes * @throws SQLException * @throws FailedOperationException */ private List<Event> doAddServiceMapping(Connection dbConn, String ipaddr, String serviceName, long txNo) throws SQLException, FailedOperationException { PreparedStatement stmt = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement(SQL_ADD_SERVICE_TO_MAPPING); d.watch(stmt); stmt.setString(1, ipaddr); stmt.setString(2, serviceName); stmt.executeUpdate(); if (log().isDebugEnabled()) { log().debug("updateServiceHandler: add service " + serviceName + " to interface: " + ipaddr); } return doChangeService(dbConn, ipaddr, serviceName, "ADD", txNo); } finally { d.cleanUp(); } } /** * Helper method used to add a service to an interface. * * @param dbConn * @param sourceUei * @param ipaddr * @param serviceName * @param serviceId * @param txNo * @throws SQLException * @throws FailedOperationException */ private List<Event> doAddServiceToInterface(Connection dbConn, String ipaddr, String serviceName, int serviceId, long txNo) throws SQLException, FailedOperationException { PreparedStatement stmt = null; ResultSet rs = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement(SQL_QUERY_IPADDRESS_EXIST); d.watch(stmt); stmt.setString(1, ipaddr); rs = stmt.executeQuery(); d.watch(rs); List<Event> eventsToSend = new LinkedList<Event>(); while (rs.next()) { if (log().isDebugEnabled()) { log().debug("changeServiceHandler: add service " + serviceName + " to interface: " + ipaddr); } InetAddress inetAddr; try { inetAddr = InetAddressUtils.addr(ipaddr); } catch (final IllegalArgumentException e) { throw new FailedOperationException("unable to resolve host " + ipaddr + ": " + e.getMessage(), e); } final int nodeId = rs.getInt(1); // insert service DbIfServiceEntry service = DbIfServiceEntry.create(nodeId, inetAddr, serviceId); service.setSource(DbIfServiceEntry.SOURCE_PLUGIN); service.setStatus(DbIfServiceEntry.STATUS_ACTIVE); service.setNotify(DbIfServiceEntry.NOTIFY_ON); service.store(dbConn, true); // Create a nodeGainedService event to eventd. DbNodeEntry nodeEntry = DbNodeEntry.get(nodeId); Event newEvent = EventUtils.createNodeGainedServiceEvent(nodeEntry, inetAddr, serviceName, txNo); eventsToSend.add(newEvent); } return eventsToSend; } finally { d.cleanUp(); } } /** * Helper method used to change the state of a service for an interface. Currently, add or delete. * * @param dbConn * @param sourceUei * @param ipaddr * @param serviceName * @param action * @param txNo * @throws SQLException * @throws FailedOperationException */ private List<Event> doChangeService(Connection dbConn, String ipaddr, String serviceName, String action, long txNo) throws SQLException, FailedOperationException { List<Event> eventsToSend = null; int serviceId = verifyServiceExists(dbConn, serviceName); if (action.equalsIgnoreCase("DELETE")) { eventsToSend = new LinkedList<Event>(); // find the node Id associated with the serviceName and interface int[] nodeIds = findNodeIdForServiceAndInterface(dbConn, ipaddr, serviceName); for (int i = 0; i < nodeIds.length; i++) { int nodeId = nodeIds[i]; // delete the service from the database eventsToSend.addAll(doDeleteService(dbConn, "OpenNMS.Capsd", nodeId, ipaddr, serviceName, txNo)); } } else if (action.equalsIgnoreCase("ADD")) { eventsToSend = doAddServiceToInterface(dbConn, ipaddr, serviceName, serviceId, txNo); } else { eventsToSend = Collections.emptyList(); } return eventsToSend; } /** * Helper method used to create mapping of interface to service. * * @param dbConn * @param nodeLabel * @param ipaddr * @param hostName * @param txNo * @return Collections.singletonList() * A List containing event(s) to send. * @throws SQLException */ private List<Event> doCreateInterfaceMappings(Connection dbConn, String nodeLabel, String ipaddr, String hostName, long txNo) throws SQLException { PreparedStatement stmt = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement(SQL_ADD_INTERFACE_TO_SERVER); d.watch(stmt); stmt.setString(1, ipaddr); stmt.setString(2, hostName); stmt.executeUpdate(); if (log().isDebugEnabled()) { log().debug("updateServerHandler: added interface " + ipaddr + " into NMS server: " + hostName); } // Create a addInterface event and process it. // FIXME: do I need to make a direct call here? Event newEvent = EventUtils.createAddInterfaceEvent("OpenNMS.Capsd", nodeLabel, ipaddr, hostName, txNo); return Collections.singletonList(newEvent); } finally { d.cleanUp(); } } /** * Mark as deleted the specified interface and its associated services, if * delete propagation is enable and the interface is the only one on the * node, delete the node as well. * * @param dbConn * the database connection * @param source * the source for any events that must be sent * @param nodeid * the id of the node the interface resides on * @param ipAddr * the ip address of the interface to be deleted * @param txNo * a transaction number to associate with the deletion * @return a list of events that need to be sent w.r.t. this deletion * @throws SQLException * if any database errors occur */ private List<Event> doDeleteInterface(Connection dbConn, String source, long nodeid, String ipAddr, long txNo) throws SQLException { return doDeleteInterface( dbConn, source, nodeid, ipAddr, -1, txNo); } /** * Mark as deleted the specified interface and its associated services, and/or * also the snmpinterface, if it exists. If delete propagation is enabled and * the interface is the only one on the node, delete the node as well. * * @param dbConn * the database connection * @param source * the source for any events that must be sent * @param nodeid * the id of the node the interface resides on * @param ipAddr * the ip address of the interface to be deleted * @param ifIndex * the ifIndex of the interface to be deleted * @param txNo * a transaction number to associate with the deletion * @return a list of events that need to be sent w.r.t. this deletion * @throws SQLException * if any database errors occur */ private List<Event> doDeleteInterface(Connection dbConn, String source, long nodeid, String ipAddr, int ifIndex, long txNo) throws SQLException { List<Event> eventsToSend = new LinkedList<Event>(); // if this is the last ip interface for the node then delete the node // instead if (!EventUtils.isNonIpInterface(ipAddr) && isPropagationEnabled() && countOtherInterfacesOnNode(dbConn, nodeid, ipAddr) == 0) { // there are no other ifs for this node so delete the node eventsToSend = doDeleteNode(dbConn, source, nodeid, txNo); } else { if (!EventUtils.isNonIpInterface(ipAddr)) { eventsToSend.addAll(markAllServicesForInterfaceDeleted(dbConn, source, nodeid, ipAddr, txNo)); } eventsToSend.addAll(markInterfaceDeleted(dbConn, source, nodeid, ipAddr, ifIndex, txNo)); } deleteAlarmsForInterface(dbConn, nodeid, ipAddr); if (ifIndex > -1) { deleteAlarmsForSnmpInterface(dbConn, nodeid, ifIndex); } return eventsToSend; } /** * Helper method to remove all mappings of services to @param nodeLabel, @param ipaddr * * @param dbConn * @param nodeLabel * @param ipaddr * @param hostName * @param txNo * @param log * @return eventsToSend * a list of events to be sent. * @throws SQLException */ private List<Event> doDeleteInterfaceMappings(Connection dbConn, String nodeLabel, String ipaddr, String hostName, long txNo) throws SQLException { PreparedStatement stmt = null; final DBUtils d = new DBUtils(getClass()); try { List<Event> eventsToSend = new LinkedList<Event>(); // Delete all services on the specified interface in // interface/service // mapping // if (log().isDebugEnabled()) { log().debug("updateServer: delete all services on the interface: " + ipaddr + " in the interface/service mapping."); } stmt = dbConn.prepareStatement(SQL_DELETE_ALL_SERVICES_INTERFACE_MAPPING); d.watch(stmt); stmt.setString(1, ipaddr); stmt.executeUpdate(); // Delete the interface on interface/server mapping if (log().isDebugEnabled()) { log().debug("updateServer: delete interface: " + ipaddr + " on NMS server: " + hostName); } stmt = dbConn.prepareStatement(SQL_DELETE_INTERFACE_ON_SERVER); stmt.setString(1, ipaddr); stmt.setString(2, hostName); stmt.executeUpdate(); // Now mark the interface as deleted (and its services as well) long[] nodeIds = findNodeIdsForInterfaceAndLabel(dbConn, nodeLabel, ipaddr); for (int i = 0; i < nodeIds.length; i++) { long nodeId = nodeIds[i]; eventsToSend.addAll(doDeleteInterface(dbConn, "OpenNMS.Capsd", nodeId, ipaddr, txNo)); } return eventsToSend; } finally { d.cleanUp(); } } /** * Mark as deleted the specified node, its associated interfaces and * services. * * @param dbConn * the connection to the database * @param source * the source for any events to send * @param nodeid * the nodeid to be deleted * @param txNo * a transaction id to associate with this deletion * * @return the list of events that need to be sent in response to the node * being deleted * @throws SQLException * if any exception occurs communicating with the database */ private List<Event> doDeleteNode(Connection dbConn, String source, long nodeid, long txNo) throws SQLException { List<Event> eventsToSend = new LinkedList<Event>(); eventsToSend.addAll(markInterfacesAndServicesDeleted(dbConn, source, nodeid, txNo)); eventsToSend.addAll(markNodeDeleted(dbConn, source, nodeid, txNo)); //Note: left this call to deleteAlarmsForNode because I wanted to indicate that alarms are now //deleted by the DB with a delete cascade fk constraint on the alarm table //when the node is actually deleted. We have to leave this in here for Capsd because //it only flags the node as deleted whereas the provisioner actually deletes the node. deleteAlarmsForNode(dbConn, nodeid); return eventsToSend; } private void deleteAlarmsForNode(Connection dbConn, long nodeId) throws SQLException { PreparedStatement stmt = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement("DELETE FROM alarms WHERE nodeid = ?"); d.watch(stmt); stmt.setLong(1, nodeId); int count = stmt.executeUpdate(); log().debug("deleteAlarmsForNode: deleted: "+count+" alarms for node: "+nodeId); } finally { d.cleanUp(); } } private void deleteAlarmsForInterface(Connection dbConn, long nodeId, String ipAddr) throws SQLException { PreparedStatement stmt = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement("DELETE FROM alarms WHERE nodeid = ? AND ipaddr = ?"); d.watch(stmt); stmt.setLong(1, nodeId); stmt.setString(2, ipAddr); int count = stmt.executeUpdate(); log().debug("deleteAlarmsForInterace: deleted: "+count+" alarms for interface: "+ipAddr); } finally { d.cleanUp(); } } /** * Delete alarms for the specified snmp interface * * @param dbConn * the connection to the database * @param nodeid * the nodeid for the interface to be deleted * @param ifIndex * the ifIndex for the interface to be deleted * @throws SQLException * if any exception occurs communicating with the database */ private void deleteAlarmsForSnmpInterface(Connection dbConn, long nodeId, int ifIndex) throws SQLException { PreparedStatement stmt = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement("DELETE FROM alarms WHERE nodeid = ? AND ifindex = ?"); d.watch(stmt); stmt.setLong(1, nodeId); stmt.setInt(2, ifIndex); int count = stmt.executeUpdate(); if (log().isDebugEnabled()) { log().debug("deleteAlarmsForSnmpInterace: deleted: "+count+" alarms for node " + nodeId + "ifIndex: "+ifIndex); } } finally { d.cleanUp(); } } private void deleteAlarmsForService(Connection dbConn, long nodeId, String ipAddr, String service) throws SQLException { PreparedStatement stmt = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement("DELETE FROM alarms " + "WHERE nodeid = ? " + " AND ipaddr = ? " + " AND serviceid " + " IN (SELECT serviceid " + "FROM service " + "WHERE servicename = ?)"); d.watch(stmt); stmt.setLong(1, nodeId); stmt.setString(2, ipAddr); stmt.setString(3, service); int count = stmt.executeUpdate(); log().debug("deleteAlarmsForService: deleted: "+count+" alarms for service: "+service); } finally { d.cleanUp(); } } /** * Mark as deleted the specified service, if this is the only service on an * interface or node and deletePropagation is enabled, the interface or node * is marked as deleted as well. * * @param dbConn * the connection to the database * @param source * the source for any events to send * @param nodeid * the nodeid that the service resides on * @param ipAddr * the interface that the service resides on * @param service * the name of the service * @param txNo * a transaction id to associate with this deletion * * @return the list of events that need to be sent in response to the * service being deleted * @throws SQLException * if any exception occurs communicating with the database */ private List<Event> doDeleteService(Connection dbConn, String source, long nodeid, String ipAddr, String service, long txNo) throws SQLException { List<Event> eventsToSend = new LinkedList<Event>(); if (isPropagationEnabled()) { // if this is the last service for the interface or the last service // for the node then send delete events for the interface or node // instead int otherSvcsOnIfCnt = countOtherServicesOnInterface(dbConn, nodeid, ipAddr, service); if (otherSvcsOnIfCnt == 0 && countServicesOnOtherInterfaces(dbConn, nodeid, ipAddr) == 0) { // no services on this interface or any other interface on this // node so delete // node log().debug("Propagating service delete to node " + nodeid); eventsToSend.addAll(doDeleteNode(dbConn, source, nodeid, txNo)); } else if (otherSvcsOnIfCnt == 0) { // no services on this interface so delete interface log().debug("Propagting service delete to interface " + nodeid + "/" + ipAddr); eventsToSend.addAll(doDeleteInterface(dbConn, source, nodeid, ipAddr, txNo)); } else { log().debug("No need to Propagate service delete " + nodeid + "/" + ipAddr + "/" + service); // otherwise just mark the service as deleted and send a // serviceDeleted event eventsToSend.addAll(markServiceDeleted(dbConn, source, nodeid, ipAddr, service, txNo)); } } else { log().debug("Propagation disabled: deleting only service " + nodeid + "/" + ipAddr + "/" + service); // otherwise just mark the service as deleted and send a // serviceDeleted event eventsToSend.addAll(markServiceDeleted(dbConn, source, nodeid, ipAddr, service, txNo)); } deleteAlarmsForService(dbConn, nodeid, ipAddr, service); return eventsToSend; } /** * Helper method to handle the processes involved after receiving a delete service event. * FIXME: see FIXME in javadoc of the doUpdateService method. * * @param dbConn * @param ipaddr * @param serviceName * @param action * @param txNo * @return An object of type List that may contain events that need to be sent. * @throws SQLException * @throws FailedOperationException */ private List<Event> doDeleteServiceMapping(Connection dbConn, String ipaddr, String serviceName, long txNo) throws SQLException, FailedOperationException { PreparedStatement stmt = null; final DBUtils d = new DBUtils(getClass()); try { if (log().isDebugEnabled()) { log().debug("handleUpdateService: delete service: " + serviceName + " on IPAddress: " + ipaddr); } stmt = dbConn.prepareStatement(SQL_DELETE_SERVICE_INTERFACE_MAPPING); d.watch(stmt); stmt.setString(1, ipaddr); stmt.setString(2, serviceName); stmt.executeUpdate(); return doChangeService(dbConn, ipaddr, serviceName, "DELETE", txNo); } finally { d.cleanUp(); } } private List<Event> doUpdateServer(Connection dbConn, String nodeLabel, String ipaddr, String action, String hostName, long txNo) throws SQLException, FailedOperationException { boolean exists = existsInServerMap(dbConn, hostName, ipaddr); //TODO: this logic changed from stable, verify that it should not be backported if ("DELETE".equalsIgnoreCase(action)) { return doDeleteInterfaceMappings(dbConn, nodeLabel, ipaddr, hostName, txNo); } else if ("ADD".equalsIgnoreCase(action)) { if (exists) throw new FailedOperationException("Could not add interface "+ipaddr+" to NMS server: "+hostName+" because it already exists!"); else return doCreateInterfaceMappings(dbConn, nodeLabel, ipaddr, hostName, txNo); } else { log().error("updateServerHandler: could not process interface: " + ipaddr + " on NMS server: " + hostName+": action "+action+" unknown"); throw new FailedOperationException("Undefined operation "+action+" for updateServer event!"); } } /** * This method adds/updates a service for @param nodeLabel, @param ipaddr, @param serviceName. * FIXME: Found inconsistency in the sub-types of List returned. Need to verify issues with jUnit tests. * * @param dbConn * @param ipaddr * @param serviceName * @param action * @param txNo * @return An object of type List containing events to be sent. * @throws SQLException * @throws FailedOperationException */ private List<Event> doUpdateService(Connection dbConn, String nodeLabel, String ipaddr, String serviceName, String action, long txNo) throws SQLException, FailedOperationException { List<Event> eventsToSend; verifyServiceExists(dbConn, serviceName); verifyInterfaceExists(dbConn, nodeLabel, ipaddr); boolean mapExists = serviceMappingExists(dbConn, ipaddr, serviceName); if (mapExists && "DELETE".equalsIgnoreCase(action)) { // the mapping exists and should be deleted eventsToSend = doDeleteServiceMapping(dbConn, ipaddr, serviceName, txNo); } else if (!mapExists && "ADD".equalsIgnoreCase(action)) { // we need to add the mapping, it doesn't exist eventsToSend = doAddServiceMapping(dbConn, ipaddr, serviceName, txNo); } else { eventsToSend = Collections.emptyList(); } return eventsToSend; } /** * Helper method to check if a service exists for a host and IP address. * * FIXME: Very inconsistent naming in the current "model": nodelabel, hostname, servername, etc. * right here this simple method indicates a problem with the model. * * @param dbConn * @param hostName * @param ipaddr * @return A logical evaluation of a service existing for hostname and IP address * @throws SQLException */ private boolean existsInServerMap(Connection dbConn, String hostName, String ipaddr) throws SQLException { PreparedStatement stmt = null; final DBUtils d = new DBUtils(getClass()); try { /** * SQL statement used to query if an interface/server mapping * already exists in the database. */ final String SQL_QUERY_INTERFACE_ON_SERVER = "SELECT count(*) FROM serverMap WHERE ipaddr = ? AND servername = ?"; // Verify if the interface already exists on the NMS server stmt = dbConn.prepareStatement(SQL_QUERY_INTERFACE_ON_SERVER); d.watch(stmt); stmt.setString(1, ipaddr); stmt.setString(2, hostName); ResultSet rs = stmt.executeQuery(); int count = 0; while (rs.next()) { count = rs.getInt(1); } return count > 0; } finally { d.cleanUp(); } } /** * Helper method that returns the node id for the @param ipaddr and @param serviceName. * FIXME: Notice how some of these methods return node id arrays as int(s) and some as long(s). * * @param dbConn * @param ipaddr * @param serviceName * @return An int Array of node ids. * @throws SQLException */ private int[] findNodeIdForServiceAndInterface(Connection dbConn, String ipaddr, String serviceName) throws SQLException { int[] nodeIds; PreparedStatement stmt = null; ResultSet rs = null; final DBUtils d = new DBUtils(getClass()); try { // Verify if the specified service already exist. stmt = dbConn.prepareStatement(SQL_QUERY_SERVICE_EXIST); d.watch(stmt); stmt.setString(1, ipaddr); stmt.setString(2, serviceName); rs = stmt.executeQuery(); d.watch(rs); List<Integer> nodeIdList = new LinkedList<Integer>(); while (rs.next()) { if (log().isDebugEnabled()) { log().debug("changeService: service " + serviceName + " on IPAddress " + ipaddr + " already exists in the database."); } int nodeId = rs.getInt(1); nodeIdList.add(nodeId); } nodeIds = new int[nodeIdList.size()]; int i = 0; for(Integer n : nodeIdList) { nodeIds[i++] = n.intValue(); } return nodeIds; } finally { d.cleanUp(); } } /** * Helper method for retrieving list of node ids that match @param nodeLabel and @param ipAddr * * @param dbConn * @param nodeLabel * @param ipAddr * @return Array of node ids from the db (JDBC) * @throws SQLException */ private long[] findNodeIdsForInterfaceAndLabel(Connection dbConn, String nodeLabel, String ipAddr) throws SQLException { PreparedStatement stmt = null; ResultSet rs = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement("SELECT node.nodeid FROM node, ipinterface WHERE node.nodeid = ipinterface.nodeid AND node.nodelabel = ? AND ipinterface.ipaddr = ? AND isManaged !='D' AND nodeType !='D'"); d.watch(stmt); stmt.setString(1, nodeLabel); stmt.setString(2, ipAddr); rs = stmt.executeQuery(); d.watch(rs); List<Long> nodeIdList = new LinkedList<Long>(); while (rs.next()) { nodeIdList.add(rs.getLong(1)); } long[] nodeIds = new long[nodeIdList.size()]; int i = 0; for(Long nodeId : nodeIdList) { nodeIds[i++] = nodeId.longValue(); } return nodeIds; } finally { d.cleanUp(); } } /** * Return an id for this event listener * * @return a {@link java.lang.String} object. */ public String getName() { return "Capsd:BroadcastEventProcessor"; } /** * Process the event, add the specified interface into database. If the * associated node does not exist in the database yet, add a node into the * database. * * @param event * The event to process. * @throws org.opennms.netmgt.capsd.InsufficientInformationException * if the event is missing essential information * @throws org.opennms.netmgt.capsd.FailedOperationException * if the operation fails (because of database error for * example) */ @EventHandler(uei=EventConstants.ADD_INTERFACE_EVENT_UEI) public void handleAddInterface(Event event) throws InsufficientInformationException, FailedOperationException { EventUtils.checkInterface(event); EventUtils.requireParm(event, EventConstants.PARM_NODE_LABEL); if (isXmlRpcEnabled()) EventUtils.requireParm(event, EventConstants.PARM_TRANSACTION_NO); if (log().isDebugEnabled()) log().debug("addInterfaceHandler: processing addInterface event for " + event.getInterface()); String nodeLabel = EventUtils.getParm(event, EventConstants.PARM_NODE_LABEL); long txNo = EventUtils.getLongParm(event, EventConstants.PARM_TRANSACTION_NO, -1L); // First make sure the specified node label and ipaddress do not exist // in the database // before trying to add them in. Connection dbConn = null; List<Event> eventsToSend = null; try { dbConn = getConnection(); dbConn.setAutoCommit(false); eventsToSend = doAddInterface(dbConn, nodeLabel, event.getInterface()); } catch (SQLException sqlE) { log().error("addInterfaceHandler: SQLException during add node and ipaddress to the database.", sqlE); throw new FailedOperationException("Database error: " + sqlE.getMessage(), sqlE); } finally { if (dbConn != null) try { if (eventsToSend != null) { dbConn.commit(); for(Event e : eventsToSend) { EventUtils.sendEvent(e, event.getUei(), txNo, isXmlRpcEnabled()); } } else { dbConn.rollback(); } } catch (SQLException ex) { log().error("handleAddInterface: Threw Exception during commit: ", ex); throw new FailedOperationException("Database error: " + ex.getMessage(), ex); } finally { if (dbConn != null) try { dbConn.setAutoCommit(true); //TODO:verify this dbConn.close(); } catch (SQLException ex) { log().error("handleAddInterface: Threw Exception during close: ", ex); } } } } private Connection getConnection() throws SQLException { return DataSourceFactory.getInstance().getConnection(); } /** * Process an addNode event. * * @param event * The event to process. * @throws org.opennms.netmgt.capsd.InsufficientInformationException * if the event is missing information * @throws org.opennms.netmgt.capsd.FailedOperationException * if an error occurs during processing */ @EventHandler(uei=EventConstants.ADD_NODE_EVENT_UEI) public void handleAddNode(Event event) throws InsufficientInformationException, FailedOperationException { EventUtils.requireParm(event, EventConstants.PARM_NODE_LABEL); if (isXmlRpcEnabled()) { EventUtils.requireParm(event, EventConstants.PARM_TRANSACTION_NO); } String ipaddr = event.getInterface(); String nodeLabel = EventUtils.getParm(event, EventConstants.PARM_NODE_LABEL); long txNo = EventUtils.getLongParm(event, EventConstants.PARM_TRANSACTION_NO, -1L); log().debug("addNodeHandler: processing addNode event for " + ipaddr); Connection dbConn = null; List<Event> eventsToSend = null; try { dbConn = getConnection(); dbConn.setAutoCommit(false); eventsToSend = doAddNode(dbConn, nodeLabel, ipaddr); } catch (SQLException sqlE) { log().error("addNodeHandler: SQLException during add node and ipaddress to tables", sqlE); throw new FailedOperationException("database error: " + sqlE.getMessage(), sqlE); } finally { if (dbConn != null) try { if (eventsToSend != null) { dbConn.commit(); for(Event e : eventsToSend) { EventUtils.sendEvent(e, event.getUei(), txNo, isXmlRpcEnabled()); } } else { dbConn.rollback(); } } catch (SQLException ex) { log().error("handleAddNode: Threw Exception during commit: ", ex); throw new FailedOperationException("database error: " + ex.getMessage(), ex); } finally { if (dbConn != null) try { dbConn.close(); } catch (SQLException ex) { log().error("handleAddNode: Threw Exception during close: ", ex); } } } } /** * Process the event, add or remove a specified service from an interface. * An 'action' parameter wraped in the event will tell which action to take * to the service. * * @param event * The event to process. * @throws org.opennms.netmgt.capsd.FailedOperationException if any. * @throws org.opennms.netmgt.capsd.InsufficientInformationException if any. */ @EventHandler(uei=EventConstants.CHANGE_SERVICE_EVENT_UEI) public void handleChangeService(Event event) throws InsufficientInformationException, FailedOperationException { EventUtils.checkInterface(event); EventUtils.checkService(event); EventUtils.requireParm(event, EventConstants.PARM_ACTION); if (isXmlRpcEnabled()) { EventUtils.requireParm(event, EventConstants.PARM_TRANSACTION_NO); } String action = EventUtils.getParm(event, EventConstants.PARM_ACTION); long txNo = EventUtils.getLongParm(event, EventConstants.PARM_TRANSACTION_NO, -1L); log().debug("changeServiceHandler: processing changeService event on: " + event.getInterface()); Connection dbConn = null; List<Event> eventsToSend = null; try { dbConn = getConnection(); dbConn.setAutoCommit(false); eventsToSend = doChangeService(dbConn, event.getInterface(), event.getService(), action, txNo); } catch (SQLException sqlE) { log().error("SQLException during changeService on database.", sqlE); throw new FailedOperationException("exeption processing changeService: " + sqlE.getMessage(), sqlE); } finally { if (dbConn != null) try { if (eventsToSend != null) { dbConn.commit(); for(Event e : eventsToSend) { EventUtils.sendEvent(e, event.getUei(), txNo, isXmlRpcEnabled()); } } else { dbConn.rollback(); } } catch (SQLException ex) { log().error("handleChangeService: Exception thrown during commit/rollback: ", ex); throw new FailedOperationException("exeption processing changeService: " + ex.getMessage(), ex); } finally { if (dbConn != null) try { dbConn.close(); } catch (SQLException ex) { log().error("handleChangeService: Exception thrown closing connection: "+ex); } } } } /** * Handle a deleteInterface Event. Here we process the event by marking all * the appropriate data rows as deleted. * * @param event * The event indicating what interface to delete * @throws org.opennms.netmgt.capsd.InsufficientInformationException * if the required information is not part of the event * @throws org.opennms.netmgt.capsd.FailedOperationException if any. */ @EventHandler(uei=EventConstants.DELETE_INTERFACE_EVENT_UEI) public void handleDeleteInterface(Event event) throws InsufficientInformationException, FailedOperationException { // validate event EventUtils.checkEventId(event); EventUtils.checkInterfaceOrIfIndex(event); EventUtils.checkNodeId(event); int ifIndex = -1; if(event.hasIfIndex()) { ifIndex = event.getIfIndex(); } if (isXmlRpcEnabled()) EventUtils.requireParm(event, EventConstants.PARM_TRANSACTION_NO); // log the event if (log().isDebugEnabled()) log().debug("handleDeleteInterface: Event\n" + "uei\t\t" + event.getUei() + "\neventid\t\t" + event.getDbid() + "\nnodeId\t\t" + event.getNodeid() + "\nipaddr\t\t" + (event.getInterface() != null ? event.getInterface() : "N/A" ) + "\nifIndex\t\t" + (ifIndex > -1 ? ifIndex : "N/A" ) + "\neventtime\t" + (event.getTime() != null ? event.getTime() : "<null>")); long txNo = EventUtils.getLongParm(event, EventConstants.PARM_TRANSACTION_NO, -1L); // update the database Connection dbConn = null; List<Event> eventsToSend = null; try { dbConn = getConnection(); dbConn.setAutoCommit(false); String source = (event.getSource() == null ? "OpenNMS.Capsd" : event.getSource()); eventsToSend = doDeleteInterface(dbConn, source, event.getNodeid(), event.getInterface(), ifIndex, txNo); } catch (SQLException ex) { log().error("handleDeleteInterface: Database error deleting interface on node " + event.getNodeid() + " with ip address " + (event.getInterface() != null ? event.getInterface() : "null") + " and ifIndex "+ (event.hasIfIndex() ? event.getIfIndex() : "null"), ex); throw new FailedOperationException("database error: " + ex.getMessage(), ex); } finally { if (dbConn != null) try { if (eventsToSend != null) { dbConn.commit(); for(Event e : eventsToSend) { EventUtils.sendEvent(e, event.getUei(), txNo, isXmlRpcEnabled()); } } else { dbConn.rollback(); } } catch (SQLException ex) { log().error("handleDeleteInterface: Exception thrown during commit/rollback: ", ex); throw new FailedOperationException("exeption processing delete interface: " + ex.getMessage(), ex); } finally { if (dbConn != null) try { dbConn.close(); } catch (SQLException ex) { log().error("handleDeleteInterface: Exception thrown closing connection: ", ex); } } } } /** * Handle a deleteNode Event. Here we process the event by marking all the * appropriate data rows as deleted. * * @param event * The event indicating what node to delete * @throws org.opennms.netmgt.capsd.InsufficientInformationException * if the required information is not part of the event * @throws org.opennms.netmgt.capsd.FailedOperationException if any. */ @EventHandler(uei=EventConstants.DELETE_NODE_EVENT_UEI) public void handleDeleteNode(Event event) throws InsufficientInformationException, FailedOperationException { // validate event EventUtils.checkEventId(event); EventUtils.checkNodeId(event); if (isXmlRpcEnabled()) EventUtils.requireParm(event, EventConstants.PARM_TRANSACTION_NO); // log the event long nodeid = event.getNodeid(); if (log().isDebugEnabled()) log().debug("handleDeleteNode: Event\n" + "uei\t\t" + event.getUei() + "\neventid\t\t" + event.getDbid() + "\nnodeId\t\t" + nodeid + "\neventtime\t" + (event.getTime() != null ? event.getTime() : "<null>")); long txNo = EventUtils.getLongParm(event, EventConstants.PARM_TRANSACTION_NO, -1L); // update the database Connection dbConn = null; List<Event> eventsToSend = null; try { dbConn = getConnection(); dbConn.setAutoCommit(false); String source = (event.getSource() == null ? "OpenNMS.Capsd" : event.getSource()); eventsToSend = doDeleteNode(dbConn, source, nodeid, txNo); } catch (SQLException ex) { log().error("handleDeleteService: Database error deleting service " + event.getService() + " on ipAddr " + event.getInterface() + " for node " + nodeid, ex); throw new FailedOperationException("database error: " + ex.getMessage(), ex); } finally { if (dbConn != null) try { if (eventsToSend != null) { dbConn.commit(); for(Event e : eventsToSend) { EventUtils.sendEvent(e, event.getUei(), txNo, isXmlRpcEnabled()); } } else { dbConn.rollback(); } } catch (SQLException ex) { log().error("handleDeleteNode: Exception thrown during commit/rollback: ", ex); throw new FailedOperationException("exeption processing deleteNode: " + ex.getMessage(), ex); } finally { if (dbConn != null) try { dbConn.close(); } catch (SQLException ex) { log().error("handleDeleteNode: Exception thrown closing connection: ",ex); } } } } /** * Handle a deleteService Event. Here we process the event by marking all * the appropriate data rows as deleted. * * @param event * The event indicating what service to delete * @throws org.opennms.netmgt.capsd.InsufficientInformationException * if the required information is not part of the event * @throws org.opennms.netmgt.capsd.FailedOperationException if any. */ @EventHandler(uei=EventConstants.DELETE_SERVICE_EVENT_UEI) public void handleDeleteService(Event event) throws InsufficientInformationException, FailedOperationException { // validate event EventUtils.checkEventId(event); EventUtils.checkNodeId(event); EventUtils.checkInterface(event); EventUtils.checkService(event); // log the event if (log().isDebugEnabled()) log().debug("handleDeleteService: Event\nuei\t\t" + event.getUei() + "\neventid\t\t" + event.getDbid() + "\nnodeid\t\t" + event.getNodeid() + "\nipaddr\t\t" + event.getInterface() + "\nservice\t\t" + event.getService() + "\neventtime\t" + (event.getTime() != null ? event.getTime() : "<null>")); long txNo = EventUtils.getLongParm(event, EventConstants.PARM_TRANSACTION_NO, -1L); // update the database Connection dbConn = null; List<Event> eventsToSend = null; try { dbConn = getConnection(); dbConn.setAutoCommit(false); String source = (event.getSource() == null ? "OpenNMS.Capsd" : event.getSource()); eventsToSend = doDeleteService(dbConn, source, event.getNodeid(), event.getInterface(), event.getService(), txNo); } catch (SQLException ex) { log().error("handleDeleteService: Database error deleting service " + event.getService() + " on ipAddr " + event.getInterface() + " for node " + event.getNodeid(), ex); throw new FailedOperationException("database error: " + ex.getMessage(), ex); } finally { if (dbConn != null) try { if (eventsToSend != null) { dbConn.commit(); for(Event e : eventsToSend) { EventUtils.sendEvent(e, event.getUei(), txNo, isXmlRpcEnabled()); } } else { dbConn.rollback(); } } catch (SQLException ex) { log().error("handleDeleteService: Exception thrown during commit/rollback: ", ex); throw new FailedOperationException("exeption processing deleteService: " + ex.getMessage(), ex); } finally { if (dbConn != null) try { dbConn.close(); } catch (SQLException ex) { log().error("handleDeleteService: Exception thrown closing connection: ", ex); } } } } /** * This helper method removes a deleted node from Capsd's re-scan schedule. Doesn't remove it * from the new suspect scan schedule. * * @param event a {@link org.opennms.netmgt.xml.event.Event} object. * @throws org.opennms.netmgt.capsd.InsufficientInformationException if any. */ @EventHandler(uei=EventConstants.DUP_NODE_DELETED_EVENT_UEI) public void handleDupNodeDeleted(Event event) throws InsufficientInformationException { EventUtils.checkNodeId(event); // Remove the deleted node from the scheduler m_scheduler.unscheduleNode(event.getNodeid().intValue()); } /** * Helper method that takes IP address from the force rescan event, * retrieves the nodeid (JDBC) and adds it to the rescan schedule for immediate * processing when the next thread is available. * * @param event a {@link org.opennms.netmgt.xml.event.Event} object. * @throws org.opennms.netmgt.capsd.InsufficientInformationException if any. */ @EventHandler(uei=EventConstants.FORCE_RESCAN_EVENT_UEI) public void handleForceRescan(Event event) throws InsufficientInformationException { // If the event has a node identifier use it otherwise // will need to use the interface to lookup the node id // from the database Long nodeid = -1L; if (event.hasNodeid()) nodeid = event.getNodeid(); else { // Extract interface from the event and use it to // lookup the node identifier associated with the // interface from the database. // // ensure the ipaddr is set EventUtils.checkInterface(event); // Get database connection and retrieve nodeid Connection dbc = null; PreparedStatement stmt = null; ResultSet rs = null; final DBUtils d = new DBUtils(getClass()); try { dbc = getConnection(); d.watch(dbc); // Retrieve node id stmt = dbc.prepareStatement(SQL_RETRIEVE_NODEID); d.watch(stmt); stmt.setString(1, event.getInterface()); rs = stmt.executeQuery(); d.watch(rs); if (rs.next()) { nodeid = rs.getLong(1); } } catch (SQLException sqlE) { log().error("handleForceRescan: Database error during nodeid retrieval for interface " + event.getInterface(), sqlE); } finally { d.cleanUp(); } } if (nodeid == null || nodeid == -1) { log().error("handleForceRescan: Nodeid retrieval for interface " + event.getInterface() + " failed. Unable to perform rescan."); return; } // discard this forceRescan if one is already enqueued for the same node ID if (RescanProcessor.isRescanQueuedForNode(nodeid.intValue())) { log().info("Ignoring forceRescan event for node " + nodeid + " because a forceRescan for that node already exists in the queue"); return; } // Rescan the node. m_scheduler.forceRescan(nodeid.intValue()); } /** * Helper method to add a node from the new suspect event Event to the suspect scan schedule. * * @param event a {@link org.opennms.netmgt.xml.event.Event} object. * @throws org.opennms.netmgt.capsd.InsufficientInformationException if any. */ @EventHandler(uei=EventConstants.NEW_SUSPECT_INTERFACE_EVENT_UEI) public void handleNewSuspect(final Event event) throws InsufficientInformationException { // ensure the event has an interface EventUtils.checkInterface(event); final String interfaceValue = event.getInterface(); // discard this newSuspect if one is already enqueued for the same IP address if (SuspectEventProcessor.isScanQueuedForAddress(interfaceValue)) { log().info("Ignoring newSuspect event for interface " + interfaceValue + " because a newSuspect scan for that interface already exists in the queue"); return; } // new suspect event try { if (log().isDebugEnabled()) log().debug("onMessage: Adding interface to suspectInterface Q: " + interfaceValue); m_suspectQ.execute(m_suspectEventProcessorFactory.createSuspectEventProcessor(interfaceValue)); } catch (final Throwable ex) { log().error("onMessage: Failed to add interface to suspect queue", ex); } } /** * Adds the node from the event to the rescan schedule. * * @param event a {@link org.opennms.netmgt.xml.event.Event} object. * @throws org.opennms.netmgt.capsd.InsufficientInformationException if any. */ @EventHandler(uei=EventConstants.NODE_ADDED_EVENT_UEI) public void handleNodeAdded(Event event) throws InsufficientInformationException { EventUtils.checkNodeId(event); // Schedule the new node. try { m_scheduler.scheduleNode(event.getNodeid().intValue()); } catch (SQLException sqlE) { log().error("onMessage: SQL exception while attempting to schedule node " + event.getNodeid(), sqlE); } } /** * Handles the process of removing a deleted node from the rescan schedule. * * @param event a {@link org.opennms.netmgt.xml.event.Event} object. * @throws org.opennms.netmgt.capsd.InsufficientInformationException if any. */ @EventHandler(uei=EventConstants.NODE_DELETED_EVENT_UEI) public void handleNodeDeleted(Event event) throws InsufficientInformationException { EventUtils.checkNodeId(event); // Remove the deleted node from the scheduler m_scheduler.unscheduleNode(event.getNodeid().intValue()); } /** * Handles the process of adding/updating a node. * TODO: Change the name of this to something that makes more sense. This impacts the UEI of the named event * for consistency and clearity, however, being called "Server" is unclear to itself. Change the event from * "Server" to "Node" and all related methods. * * @param event a {@link org.opennms.netmgt.xml.event.Event} object. * @throws org.opennms.netmgt.capsd.InsufficientInformationException if any. * @throws org.opennms.netmgt.capsd.FailedOperationException if any. */ @EventHandler(uei=EventConstants.UPDATE_SERVER_EVENT_UEI) public void handleUpdateServer(Event event) throws InsufficientInformationException, FailedOperationException { // If there is no interface or NMS server found then it cannot be // processed EventUtils.checkInterface(event); EventUtils.checkHost(event); EventUtils.requireParm(event, EventConstants.PARM_ACTION); EventUtils.requireParm(event, EventConstants.PARM_NODE_LABEL); if (isXmlRpcEnabled()) { EventUtils.requireParm(event, EventConstants.PARM_TRANSACTION_NO); } String action = EventUtils.getParm(event, EventConstants.PARM_ACTION); String nodeLabel = EventUtils.getParm(event, EventConstants.PARM_NODE_LABEL); long txNo = EventUtils.getLongParm(event, EventConstants.PARM_TRANSACTION_NO, -1L); if (log().isDebugEnabled()) log().debug("updateServerHandler: processing updateServer event for: " + event.getInterface() + " on OpenNMS server: " + m_localServer); Connection dbConn = null; List<Event> eventsToSend = null; try { dbConn = getConnection(); dbConn.setAutoCommit(false); eventsToSend = doUpdateServer(dbConn, nodeLabel, event.getInterface(), action, m_localServer, txNo); } catch (SQLException sqlE) { log().error("SQLException during updateServer on database.", sqlE); throw new FailedOperationException("SQLException during updateServer on database.", sqlE); } finally { if (dbConn != null) try { if (eventsToSend != null) { dbConn.commit(); for(Event e : eventsToSend) { EventUtils.sendEvent(e, event.getUei(), txNo, isXmlRpcEnabled()); } } else { dbConn.rollback(); } } catch (SQLException ex) { log().error("handleUpdateServer: Exception thrown during commit/rollback: ", ex); throw new FailedOperationException("SQLException during updateServer on database.", ex); } finally { if (dbConn != null) try { dbConn.close(); } catch (SQLException ex) { log().error("handleUpdateServer: Exception thrown closing connection: ", ex); } } } } /** * Process the event, add or remove a specified interface/service pair into * the database. this event will cause an changeService event with the * specified action. An 'action' parameter wraped in the event will tell * which action to take to the service on the specified interface. The * ipaddress of the interface, the service name must be included in the * event. * * @param event * The event to process. * @throws org.opennms.netmgt.capsd.InsufficientInformationException * if there is missing information in the event * @throws org.opennms.netmgt.capsd.FailedOperationException * if the operation fails for some reason */ @EventHandler(uei=EventConstants.UPDATE_SERVICE_EVENT_UEI) public void handleUpdateService(Event event) throws InsufficientInformationException, FailedOperationException { EventUtils.checkInterface(event); EventUtils.checkService(event); EventUtils.requireParm(event, EventConstants.PARM_NODE_LABEL); EventUtils.requireParm(event, EventConstants.PARM_ACTION); if (isXmlRpcEnabled()) { EventUtils.requireParm(event, EventConstants.PARM_TRANSACTION_NO); } long txNo = EventUtils.getLongParm(event, EventConstants.PARM_TRANSACTION_NO, -1L); String action = EventUtils.getParm(event, EventConstants.PARM_ACTION); String nodeLabel = EventUtils.getParm(event, EventConstants.PARM_NODE_LABEL); if (log().isDebugEnabled()) log().debug("handleUpdateService: processing updateService event for : " + event.getService() + " on : " + event.getInterface()); List<Event> eventsToSend = null; Connection dbConn = null; try { dbConn = getConnection(); dbConn.setAutoCommit(false); eventsToSend = doUpdateService(dbConn, nodeLabel, event.getInterface(), event.getService(), action, txNo); } catch (SQLException sqlE) { log().error("SQLException during handleUpdateService on database.", sqlE); throw new FailedOperationException(sqlE.getMessage()); } finally { if (dbConn != null) try { if (eventsToSend != null) { dbConn.commit(); for(Event e : eventsToSend) { EventUtils.sendEvent(e, event.getUei(), txNo, isXmlRpcEnabled()); } } else { dbConn.rollback(); } } catch (SQLException ex) { log().error("handleUpdateService: Exception thrown during commit/rollback: ", ex); throw new FailedOperationException(ex.getMessage()); } finally { if (dbConn != null) try { dbConn.close(); } catch (SQLException ex) { log().error("handleUpdateService: Exception thrown during close: ",ex); } } } } /** * Returns true if and only an interface with the given ipaddr on a node * with the give label exists * * @param dbConn * a database connection * @param nodeLabel * the label of the node the interface must reside on * @param ipaddr * the ip address the interface should have * @return true iff the interface exists * @throws SQLException * if a database error occurs */ private boolean interfaceExists(Connection dbConn, String nodeLabel, String ipaddr) throws SQLException { PreparedStatement stmt = null; ResultSet rs = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement(SQL_QUERY_IPINTERFACE_EXIST); d.watch(stmt); stmt.setString(1, nodeLabel); stmt.setString(2, ipaddr); rs = stmt.executeQuery(); d.watch(rs); return rs.next(); } finally { d.cleanUp(); } } /** * Mark all the services associated with a given interface as deleted and * create service deleted events for each one that gets deleted * * @param dbConn * the database connection * @param nodeId * the node that interface resides on * @param ipAddr * the ipAddress of the interface * @param txNo * a transaction number that can be associated with this deletion * @return a List of serviceDeleted events, one for each service marked * @throws SQLException * if a database error occurs */ private List<Event> markAllServicesForInterfaceDeleted(Connection dbConn, String source, long nodeId, String ipAddr, long txNo) throws SQLException { PreparedStatement stmt = null; ResultSet rs = null; final DBUtils d = new DBUtils(getClass()); try { List<Event> eventsToSend = new LinkedList<Event>(); final String DB_FIND_SERVICES_FOR_INTERFACE = "SELECT DISTINCT service.serviceName FROM ifservices as ifservices, service as service WHERE ifservices.nodeID = ? and ifservices.ipAddr = ? and ifservices.status != 'D' and ifservices.serviceID = service.serviceID"; stmt = dbConn.prepareStatement(DB_FIND_SERVICES_FOR_INTERFACE); d.watch(stmt); stmt.setLong(1, nodeId); stmt.setString(2, ipAddr); rs = stmt.executeQuery(); d.watch(rs); Set<String> services = new HashSet<String>(); while (rs.next()) { String serviceName = rs.getString(1); log().debug("found service " + serviceName + " for ipAddr " + ipAddr + " node " + nodeId); services.add(serviceName); } final String DB_MARK_SERVICES_FOR_INTERFACE = "UPDATE ifservices SET status = 'D' where ifservices.nodeID = ? and ifservices.ipAddr = ?"; stmt = dbConn.prepareStatement(DB_MARK_SERVICES_FOR_INTERFACE); d.watch(stmt); stmt.setLong(1, nodeId); stmt.setString(2, ipAddr); stmt.executeUpdate(); for(String serviceName : services) { log().debug("creating event for service " + serviceName + " for ipAddr " + ipAddr + " node " + nodeId); eventsToSend.add(EventUtils.createServiceDeletedEvent(source, nodeId, ipAddr, serviceName, txNo)); } if (log().isDebugEnabled()) log().debug("markServicesDeleted: marked service deleted: " + nodeId + "/" + ipAddr); return eventsToSend; } finally { d.cleanUp(); } } /** * Mark the given interface deleted * * @param dbConn * the database connection * @param source * the source for any events set * @param nodeId * the id the interface resides on * @param ipAddr * the ipAddress of the interface * @param txNo * a transaction no to associate with this deletion * @return a List containing an interfaceDeleted event for the interface if * it was actually marked * @throws SQLException * if a database error occurs */ private List<Event> markInterfaceDeleted(Connection dbConn, String source, long nodeId, String ipAddr, long txNo) throws SQLException { return markInterfaceDeleted(dbConn, source, nodeId, ipAddr, -1, txNo); } /** * Mark the given interface deleted * * @param dbConn * the database connection * @param source * the source for any events set * @param nodeId * the id the interface resides on * @param ipAddr * the ipAddress of the interface * @param ifIndex * the ifIndex of the interface * @param txNo * a transaction no to associate with this deletion * @return a List containing an interfaceDeleted event for the interface if * it was actually marked * @throws SQLException * if a database error occurs */ private List<Event> markInterfaceDeleted(Connection dbConn, String source, long nodeId, String ipAddr, int ifIndex, long txNo) throws SQLException { final String DB_FIND_INTERFACE = "UPDATE ipinterface SET isManaged = 'D' WHERE nodeid = ? and ipAddr = ? and isManaged != 'D'"; final String DB_FIND_SNMPINTERFACE = "UPDATE snmpinterface SET snmpcollect = 'D' WHERE nodeid = ? and snmpifindex = ? and snmpcollect != 'D'"; PreparedStatement stmt = null; final DBUtils d = new DBUtils(getClass()); int countip = 0; int countsnmp = 0; try { if(!EventUtils.isNonIpInterface(ipAddr)) { stmt = dbConn.prepareStatement(DB_FIND_INTERFACE); d.watch(stmt); stmt.setLong(1, nodeId); stmt.setString(2, ipAddr); countip = stmt.executeUpdate(); } if (ifIndex > -1) { stmt = dbConn.prepareStatement(DB_FIND_SNMPINTERFACE); d.watch(stmt); stmt.setLong(1, nodeId); stmt.setInt(2, ifIndex); countsnmp = stmt.executeUpdate(); } if (countip > 0) { if (log().isDebugEnabled()) { log().debug("markInterfaceDeleted: marked ip interface deleted: node = " + nodeId + ", IP address = " + ipAddr); } } if (countsnmp > 0) { if (log().isDebugEnabled()) { log().debug("markInterfaceDeleted: marked snmp interface deleted: node = " + nodeId + ", ifIndex = " + ifIndex); } } if (countip > 0 || countsnmp > 0) { return Collections.singletonList(EventUtils.createInterfaceDeletedEvent(source, nodeId, ipAddr, ifIndex, txNo)); } else { if (log().isDebugEnabled()) { log().debug("markInterfaceDeleted: Interface not found: node = " + nodeId + ", with ip address " + (ipAddr != null ? ipAddr : "null") + ", and ifIndex " + (ifIndex > -1 ? ifIndex : "N/A")); } return Collections.emptyList(); } } finally { d.cleanUp(); } } /** * Marks all the interfaces and services for a given node deleted and * constructs events for each. The order of events is significant * representing the hierarchy, service events preceed the event for the * interface the service is on * * @param dbConn * the database connection * @param source * the source for use in the constructed events * @param nodeId * the node whose interfaces and services are to be deleted * @param txNo * a transaction number to associate with this deletion * @return a List of events indicating which nodes and services have been * deleted * * @throws SQLException */ private List<Event> markInterfacesAndServicesDeleted(Connection dbConn, String source, long nodeId, long txNo) throws SQLException { List<Event> eventsToSend = new LinkedList<Event>(); final String DB_FIND_IFS_FOR_NODE = "SELECT ipinterface.ipaddr FROM ipinterface WHERE ipinterface.nodeid = ? and ipinterface.ismanaged != 'D'"; PreparedStatement stmt = null; ResultSet rs = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement(DB_FIND_IFS_FOR_NODE); d.watch(stmt); stmt.setLong(1, nodeId); rs = stmt.executeQuery(); d.watch(rs); Set<String> ipAddrs = new HashSet<String>(); while (rs.next()) { String ipAddr = rs.getString(1); log().debug("found interface " + ipAddr + " for node " + nodeId); ipAddrs.add(ipAddr); } for(String ipAddr : ipAddrs) { log().debug("deleting interface " + ipAddr + " for node " + nodeId); eventsToSend.addAll(markAllServicesForInterfaceDeleted(dbConn, source, nodeId, ipAddr, txNo)); eventsToSend.addAll(markInterfaceDeleted(dbConn, source, nodeId, ipAddr, txNo)); } return eventsToSend; } finally { d.cleanUp(); } } /** * Marks a node deleted and creates an event for it if necessary. * * @param dbConn * the database connection * @param source * the source to use for constructed events * @param nodeId * the node to delete * @param txNo * a transaction number to associate with this deletion * @return a List containing the node deleted event if necessary * @throws SQLException * if a database error occurs */ private List<Event> markNodeDeleted(Connection dbConn, String source, long nodeId, long txNo) throws SQLException { final String DB_FIND_INTERFACE = "UPDATE node SET nodeType = 'D' WHERE nodeid = ? and nodeType != 'D'"; PreparedStatement stmt = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement(DB_FIND_INTERFACE); d.watch(stmt); stmt.setLong(1, nodeId); int count = stmt.executeUpdate(); log().debug("markServicesDeleted: marked service deleted: " + nodeId); if (count > 0) return Collections.singletonList(EventUtils.createNodeDeletedEvent(source, nodeId, txNo)); else return Collections.emptyList(); } finally { d.cleanUp(); } } /** * Marks the service deleted in the database and returns a serviceDeleted * event for the service, if and only if the service existed * * @param dbConn * the database connection * @param source * the source for any events sent * @param nodeId * the node the service resides on * @param ipAddr * the interface the service resides on * @param service * the name of the service * @param txNo * a transaction number to associate with this deletion * @return a List containing a service deleted event. * @throws SQLException * if an error occurs communicating with the database */ private List<Event> markServiceDeleted(Connection dbConn, String source, long nodeId, String ipAddr, String service, long txNo) throws SQLException { PreparedStatement stmt = null; final String DB_MARK_SERVICE_DELETED = "UPDATE ifservices SET status='D' " + "FROM service " + "WHERE ifservices.serviceID = service.serviceID " + "AND ifservices.nodeID=? AND ifservices.ipAddr=? AND service.serviceName=?"; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement(DB_MARK_SERVICE_DELETED); d.watch(stmt); stmt.setLong(1, nodeId); stmt.setString(2, ipAddr); stmt.setString(3, service); int count = stmt.executeUpdate(); if (log().isDebugEnabled()) log().debug("markServiceDeleted: marked service deleted: " + nodeId + "/" + ipAddr + "/" + service); if (count > 0) return Collections.singletonList(EventUtils.createServiceDeletedEvent(source, nodeId, ipAddr, service, txNo)); else return Collections.emptyList(); } finally { d.cleanUp(); } } /** * Returns true if and only a node with the give label exists * * @param dbConn * a database connection * @param nodeLabel * the label to check * @return true iff the node exists * @throws SQLException * if a database error occurs */ private boolean nodeExists(Connection dbConn, String nodeLabel) throws SQLException { PreparedStatement stmt = null; ResultSet rs = null; final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement(SQL_QUERY_NODE_EXIST); d.watch(stmt); stmt.setString(1, nodeLabel); rs = stmt.executeQuery(); d.watch(rs); return rs.next(); } finally { d.cleanUp(); } } /** * JDBC Query to service map table. This will soon be cleaned up with Hibernate/DAO code. * * @param dbConn * @param ipaddr * @param serviceName * @return * @throws SQLException */ private boolean serviceMappingExists(Connection dbConn, String ipaddr, String serviceName) throws SQLException { boolean mapExists; PreparedStatement stmt = null; // Verify if the specified service already exists on the // interface/service // mapping. final DBUtils d = new DBUtils(getClass()); try { stmt = dbConn.prepareStatement(SQL_QUERY_SERVICE_MAPPING_EXIST); d.watch(stmt); stmt.setString(1, ipaddr); stmt.setString(2, serviceName); ResultSet rs = stmt.executeQuery(); d.watch(rs); mapExists = rs.next(); return mapExists; } finally { d.cleanUp(); } } /** * JDBC Query using @param serviceName to determine if the serviceName is indeed as * service name in the service table. * * FIXME: This sucks: * 1) No transaction management * 2) Such a small table should be cached and accessed via a synchronized call. Quit * going to the DB for such trivial information. This is a performance killer. Especially * since our current db factory (factories) use the synchonized DriverManager JDBC class. * * @param dbConn * @param serviceName * @return * @throws SQLException * @throws FailedOperationException */ private int verifyServiceExists(Connection dbConn, String serviceName) throws SQLException, FailedOperationException { PreparedStatement stmt = null; ResultSet rs = null; final DBUtils d = new DBUtils(getClass()); try { // Retrieve the serviceId stmt = dbConn.prepareStatement(SQL_RETRIEVE_SERVICE_ID); d.watch(stmt); stmt.setString(1, serviceName); rs = stmt.executeQuery(); d.watch(rs); int serviceId = -1; while (rs.next()) { log().debug("verifyServiceExists: retrieve serviceid for service " + serviceName); serviceId = rs.getInt(1); } if (serviceId < 0) { if (log().isDebugEnabled()) log().debug("verifyServiceExists: the specified service: " + serviceName + " does not exist in the database."); throw new FailedOperationException("Invalid service: " + serviceName); } return serviceId; } finally { d.cleanUp(); } } private void verifyInterfaceExists(Connection dbConn, String nodeLabel, String ipaddr) throws SQLException, FailedOperationException { if (!interfaceExists(dbConn, nodeLabel, ipaddr)) throw new FailedOperationException("Interface "+ipaddr+" does not exist on a node with nodeLabel "+nodeLabel); } /** * <p>setSuspectEventProcessorFactory</p> * * @param suspectEventProcessorFactory a {@link org.opennms.netmgt.capsd.SuspectEventProcessorFactory} object. */ public void setSuspectEventProcessorFactory(SuspectEventProcessorFactory suspectEventProcessorFactory) { m_suspectEventProcessorFactory = suspectEventProcessorFactory; } /** * <p>setScheduler</p> * * @param scheduler a {@link org.opennms.netmgt.capsd.Scheduler} object. */ public void setScheduler(Scheduler scheduler) { m_scheduler = scheduler; } /** * <p>setSuspectQueue</p> * * @param suspectQ a {@link java.util.concurrent.ExecutorService} object. */ public void setSuspectQueue(ExecutorService suspectQ) { m_suspectQ = suspectQ; } /** * <p>setLocalServer</p> * * @param localServer a {@link java.lang.String} object. */ public void setLocalServer(String localServer) { m_localServer = localServer; } /** * <p>afterPropertiesSet</p> * * @throws java.lang.Exception if any. */ @Override public void afterPropertiesSet() throws Exception { Assert.state(m_suspectEventProcessorFactory != null, "The suspectEventProcessor must be set"); Assert.state(m_scheduler != null, "The schedule must be set"); Assert.state(m_suspectQ != null, "The suspectQueue must be set"); Assert.state(m_localServer != null, "The localServer must be set"); } private ThreadCategory log() { return ThreadCategory.getInstance(getClass()); } }