/*******************************************************************************
* 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.config;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.ValidationException;
import org.opennms.core.utils.DBUtils;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.core.xml.CastorUtils;
import org.opennms.netmgt.EventConstants;
import org.opennms.netmgt.config.notifications.Header;
import org.opennms.netmgt.config.notifications.Notification;
import org.opennms.netmgt.config.notifications.Notifications;
import org.opennms.netmgt.config.notifications.Parameter;
import org.opennms.netmgt.eventd.EventUtil;
import org.opennms.netmgt.filter.FilterDaoFactory;
import org.opennms.netmgt.filter.FilterParseException;
import org.opennms.netmgt.notifd.BroadcastEventProcessor;
import org.opennms.netmgt.utils.Querier;
import org.opennms.netmgt.utils.RowProcessor;
import org.opennms.netmgt.utils.SingleResultQuerier;
import org.opennms.netmgt.xml.event.Event;
import org.opennms.netmgt.xml.event.Tticket;
import org.springframework.util.Assert;
/**
* <p>Abstract NotificationManager class.</p>
*
* @author David Hustace <david@opennms.org>
* This base class was refactored from NotificationFactory to support non-global
* references during JUnit testing and later to support distributed processes.
* @version $Id: $
*/
public abstract class NotificationManager {
/**
* Object containing all Notification objects parsed from the xml file
*/
public Notifications m_notifications;
/**
*
*/
private Header oldHeader;
/** Constant <code>PARAM_TYPE="-t"</code> */
public static final String PARAM_TYPE = "-t";
/** Constant <code>PARAM_DESTINATION="-d"</code> */
public static final String PARAM_DESTINATION = "-d";
/** Constant <code>PARAM_TEXT_MSG="-tm"</code> */
public static final String PARAM_TEXT_MSG = "-tm";
/** Constant <code>PARAM_NUM_MSG="-nm"</code> */
public static final String PARAM_NUM_MSG = "-nm";
/** Constant <code>PARAM_RESPONSE="-r"</code> */
public static final String PARAM_RESPONSE = "-r";
/** Constant <code>PARAM_NODE="-nodeid"</code> */
public static final String PARAM_NODE = "-nodeid";
/** Constant <code>PARAM_INTERFACE="-interface"</code> */
public static final String PARAM_INTERFACE = "-interface";
/** Constant <code>PARAM_SERVICE="-service"</code> */
public static final String PARAM_SERVICE = "-service";
/** Constant <code>PARAM_SUBJECT="-subject"</code> */
public static final String PARAM_SUBJECT = "-subject";
/** Constant <code>PARAM_EMAIL="-email"</code> */
public static final String PARAM_EMAIL = "-email";
/** Constant <code>PARAM_PAGER_EMAIL="-pemail"</code> */
public static final String PARAM_PAGER_EMAIL = "-pemail";
/** Constant <code>PARAM_XMPP_ADDRESS="-xmpp"</code> */
public static final String PARAM_XMPP_ADDRESS = "-xmpp";
/** Constant <code>PARAM_TEXT_PAGER_PIN="-tp"</code> */
public static final String PARAM_TEXT_PAGER_PIN = "-tp";
/** Constant <code>PARAM_NUM_PAGER_PIN="-np"</code> */
public static final String PARAM_NUM_PAGER_PIN = "-np";
/** Constant <code>PARAM_WORK_PHONE="-wphone"</code> */
public static final String PARAM_WORK_PHONE = "-wphone";
/** Constant <code>PARAM_HOME_PHONE="-hphone"</code> */
public static final String PARAM_HOME_PHONE = "-hphone";
/** Constant <code>PARAM_MOBILE_PHONE="-mphone"</code> */
public static final String PARAM_MOBILE_PHONE = "-mphone";
/** Constant <code>PARAM_TUI_PIN="-tuipin"</code> */
public static final String PARAM_TUI_PIN = "-tuipin";
/** Constant <code>PARAM_MICROBLOG_USERNAME="-ublog"</code> */
public static final String PARAM_MICROBLOG_USERNAME = "-ublog";
NotifdConfigManager m_configManager;
private DataSource m_dataSource;
/**
* <p>Constructor for NotificationManager.</p>
*
* @throws MarshalException if any.
* @throws ValidationException if any.
* @param configManager a {@link org.opennms.netmgt.config.NotifdConfigManager} object.
* @param dcf a {@link javax.sql.DataSource} object.
*/
protected NotificationManager(final NotifdConfigManager configManager, final DataSource dcf) {
m_configManager = configManager;
m_dataSource = dcf;
}
/**
* <p>parseXML</p>
*
* @param reader a {@link java.io.Reader} object.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
@Deprecated
public synchronized void parseXML(final Reader reader) throws MarshalException, ValidationException {
m_notifications = CastorUtils.unmarshal(Notifications.class, reader, true);
oldHeader = m_notifications.getHeader();
}
/**
* <p>parseXML</p>
*
* @param stream a {@link java.io.InputStream} object.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public synchronized void parseXML(final InputStream stream) throws MarshalException, ValidationException {
m_notifications = CastorUtils.unmarshal(Notifications.class, stream, true);
oldHeader = m_notifications.getHeader();
}
/**
* <p>hasUei</p>
*
* @param uei a {@link java.lang.String} object.
* @return a boolean.
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public boolean hasUei(final String uei) throws IOException, MarshalException, ValidationException {
update();
for (Notification notif : m_notifications.getNotificationCollection()) {
if (uei.equals(notif.getUei()) || "MATCH-ANY-UEI".equals(notif.getUei())) {
return true;
} else if (notif.getUei().charAt(0) == '~') {
if (uei.matches(notif.getUei().substring(1))) {
return true;
}
}
}
return false;
}
/**
* <p>getNotifForEvent</p>
*
* @param event a {@link org.opennms.netmgt.xml.event.Event} object.
* @return an array of {@link org.opennms.netmgt.config.notifications.Notification} objects.
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public Notification[] getNotifForEvent(final Event event) throws IOException, MarshalException, ValidationException {
update();
List<Notification> notifList = new ArrayList<Notification>();
boolean matchAll = getConfigManager().getNotificationMatch();
ThreadCategory log = this.log();
// This if statement will check to see if notification should be suppressed for this event.
if (event == null) {
log.warn("unable to get notification for null event!");
return null;
} else if (event.getLogmsg() != null && !(event.getLogmsg().getNotify())) {
if (log.isDebugEnabled())
log.debug("Event " + event.getUei() + " is configured to suppress notifications.");
return null;
}
for (Notification curNotif : m_notifications.getNotificationCollection()) {
if (log.isDebugEnabled())
log.debug("Checking " + event.getUei() + " against " + curNotif.getUei());
if (event.getUei().equals(curNotif.getUei()) || "MATCH-ANY-UEI".equals(curNotif.getUei())) {
// Match!
} else if (curNotif.getUei().charAt(0) == '~') {
if (event.getUei().matches(curNotif.getUei().substring(1))) {
// Match!
} else {
if (log.isDebugEnabled())
log.debug("Notification regex " + curNotif.getUei() + " failed to match event UEI: " + event.getUei());
continue;
}
} else {
if (log.isDebugEnabled())
log.debug("Event UEI " + event.getUei() + " did not match " + curNotif.getUei());
continue;
}
/**
* Check if event severity matches pattern in notification
*/
if (log.isDebugEnabled())
log.debug("Checking event severity: " + event.getSeverity() + " against notification severity: " + curNotif.getEventSeverity());
// parameter is optional, return true if not set
if (curNotif.getEventSeverity() == null) {
// Skip matching on severity
} else if (event.getSeverity().toLowerCase().matches(curNotif.getEventSeverity().toLowerCase())) {
// Severities match
} else {
if (log.isDebugEnabled())
log.debug("Event severity: " + event.getSeverity() + " did not match notification severity: " + curNotif.getEventSeverity());
continue;
}
// The notice has to be "on"
// The notice has to match a severity if configured - currHasSeverity should be true if there is no severity rule
// The notice has to match the UEI of the event or MATCH-ANY-UEI
// If all those things are true:
// Then the service has to match if configured, the interface if configured, and the node if configured.
if (curNotif.getStatus().equals("on")) {
if (nodeInterfaceServiceValid(curNotif, event)) {
boolean parmsmatched = getConfigManager().matchNotificationParameters(event, curNotif);
if (!parmsmatched) {
if (log.isDebugEnabled())
log.debug("Event " + event.getUei() + " did not match parameters for notice " + curNotif.getName());
continue;
}
// Add this notification to the return value
notifList.add(curNotif);
if (log.isDebugEnabled())
log.debug("Event " + event.getUei() + " matched notice " + curNotif.getName());
if (!matchAll)
break;
} else {
if (log.isDebugEnabled())
log.debug("Node/interface/service combination in the event was invalid");
}
} else {
if (log.isDebugEnabled())
log.debug("Current notification is turned off.");
}
}
if (!notifList.isEmpty()) {
return notifList.toArray(new Notification[0]);
} else {
return null;
}
}
private ThreadCategory log() {
return ThreadCategory.getInstance(getClass());
}
/**
* <p>getConfigManager</p>
*
* @return a {@link org.opennms.netmgt.config.NotifdConfigManager} object.
*/
protected NotifdConfigManager getConfigManager() {
return m_configManager;
}
/**
* <p>nodeInterfaceServiceValid</p>
*
* @param notif a {@link org.opennms.netmgt.config.notifications.Notification} object.
* @param event a {@link org.opennms.netmgt.xml.event.Event} object.
* @return a boolean.
*/
protected boolean nodeInterfaceServiceValid(final Notification notif, final Event event) {
Assert.notNull(notif, "notif argument must not be null");
Assert.notNull(event, "event argument must not be null");
Assert.notNull(notif.getRule(), "getRule() on notif argument must not return null");
/*
* If the event doesn't have a nodeId, interface, or service,
* return true since there is nothing on which to filter.
*/
if (event.getNodeid() == 0 && event.getInterface() == null && event.getService() == null) {
if ("MATCH-ANY-UEI".equals(notif.getUei())) {
if ("ipaddr != '0.0.0.0'".equals(notif.getRule().toLowerCase()) || "ipaddr iplike *.*.*.*".equals(notif.getRule().toLowerCase())) {
return true;
} else {
return false;
}
}
return true;
}
StringBuffer constraints = new StringBuffer();
if (event.getNodeid() != 0) {
constraints.append(" & (nodeId == " + event.getNodeid() + ")");
}
if (event.getInterface() != null
&& !"0.0.0.0".equals(event.getInterface())) {
constraints.append(" & (ipAddr == '" + event.getInterface() + "')");
if (event.getService() != null) {
constraints.append(" & (serviceName == '" + event.getService() + "')");
}
}
String rule = "((" + notif.getRule() + ")" + constraints + ")";
return isRuleMatchingFilter(notif, rule);
}
private boolean isRuleMatchingFilter(final Notification notif, final String rule) {
try {
return FilterDaoFactory.getInstance().isRuleMatching(rule);
} catch (FilterParseException e) {
log().error("Invalid filter rule for notification " + notif.getName() + ": " + notif.getRule(), e);
throw e;
}
}
/**
* @return
* @throws SQLException
*/
private Connection getConnection() throws SQLException {
return m_dataSource.getConnection();
}
/**
* This method wraps the call to the database to get a sequence notice ID
* from the database.
*
* @return int, the sequence id from the database, 0 by default if there is
* database trouble
* @throws java.sql.SQLException if any.
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public int getNoticeId() throws SQLException, IOException, MarshalException, ValidationException {
return getNxtId(m_configManager.getNextNotifIdSql());
}
/**
* <p>getUserNotifId</p>
*
* @return a int.
* @throws java.sql.SQLException if any.
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public int getUserNotifId() throws SQLException, IOException, MarshalException, ValidationException {
return getNxtId(m_configManager.getNextUserNotifIdSql());
}
private int getNxtId(final String sql) throws SQLException {
int id = 0;
Connection connection = null;
try {
connection = getConnection();
Statement stmt = connection.createStatement();
ResultSet results = stmt.executeQuery(sql);
results.next();
id = results.getInt(1);
stmt.close();
results.close();
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
}
}
}
return id;
}
/**
* This method returns a boolean indicating if the page has been responded
* to by any member of the group the page was sent to.
*
* @param noticeId a int.
* @return a boolean.
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public boolean noticeOutstanding(final int noticeId) throws IOException, MarshalException, ValidationException {
boolean outstanding = false;
Connection connection = null;
final DBUtils d = new DBUtils(getClass());
try {
connection = getConnection();
d.watch(connection);
final PreparedStatement statement = connection.prepareStatement(getConfigManager().getConfiguration().getOutstandingNoticesSql());
d.watch(statement);
statement.setInt(1, noticeId);
ResultSet results = statement.executeQuery();
d.watch(results);
// count how many rows were returned, if there is even one then the
// page
// has been responded too.
int count = 0;
while (results.next()) {
count++;
}
if (count == 0) {
outstanding = true;
}
} catch (SQLException e) {
log().error("Error getting notice status: " + e.getMessage(), e);
} finally {
d.cleanUp();
}
return outstanding;
}
/**
* <p>acknowledgeNotice</p>
*
* @param event a {@link org.opennms.netmgt.xml.event.Event} object.
* @param uei a {@link java.lang.String} object.
* @param matchList an array of {@link java.lang.String} objects.
* @return a {@link java.util.Collection} object.
* @throws java.sql.SQLException if any.
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public Collection<Integer> acknowledgeNotice(final Event event, final String uei, final String[] matchList) throws SQLException, IOException, MarshalException, ValidationException {
Connection connection = null;
List<Integer> notifIDs = new LinkedList<Integer>();
final DBUtils d = new DBUtils(getClass());
ThreadCategory log = this.log();
try {
// First get most recent event ID from notifications
// that match the matchList, then get all notifications
// with this event ID
connection = getConnection();
d.watch(connection);
int eventID = 0;
boolean wasAcked = false;
StringBuffer sql = new StringBuffer("SELECT eventid FROM notifications WHERE eventuei=? ");
for (int i = 0; i < matchList.length; i++) {
sql.append("AND ").append(matchList[i]).append("=? ");
}
sql.append("ORDER BY eventid desc limit 1");
PreparedStatement statement = connection.prepareStatement(sql.toString());
d.watch(statement);
statement.setString(1, uei);
for (int i = 0; i < matchList.length; i++) {
if (matchList[i].equals("nodeid")) {
statement.setLong(i + 2, event.getNodeid());
}
if (matchList[i].equals("interfaceid")) {
statement.setString(i + 2, event.getInterface());
}
if (matchList[i].equals("serviceid")) {
statement.setInt(i + 2, getServiceId(event.getService()));
}
}
ResultSet results = statement.executeQuery();
d.watch(results);
if (results != null && results.next()) {
eventID = results.getInt(1);
if (log.isDebugEnabled())
log.debug("EventID for notice(s) to be acked: " + eventID);
sql = new StringBuffer("SELECT notifyid, answeredby, respondtime FROM notifications WHERE eventID=?");
statement = connection.prepareStatement(sql.toString());
statement.setInt(1, eventID);
results = statement.executeQuery();
if (results != null) {
while (results.next()) {
int notifID = results.getInt(1);
String ansBy = results.getString(2);
Timestamp ts = results.getTimestamp(3);
if(ansBy == null) {
ansBy = "auto-acknowledged";
ts = new Timestamp((new Date()).getTime());
} else if(ansBy.indexOf("auto-acknowledged") > -1) {
if (log.isDebugEnabled())
log.debug("Notice has previously been auto-acknowledged. Skipping...");
continue;
} else {
wasAcked = true;
ansBy = ansBy + "/auto-acknowledged";
}
if (log.isDebugEnabled())
log.debug("Matching DOWN notifyID = " + notifID + ", was acked by user = " + wasAcked + ", ansBy = " +ansBy);
final PreparedStatement update = connection.prepareStatement(getConfigManager().getConfiguration().getAcknowledgeUpdateSql());
d.watch(update);
update.setString(1, ansBy);
update.setTimestamp(2, ts);
update.setInt(3, notifID);
update.executeUpdate();
update.close();
if(wasAcked) {
notifIDs.add(-1 * notifID);
} else {
notifIDs.add(notifID);
}
}
}
} else {
if (log.isDebugEnabled())
log.debug("No matching DOWN eventID found");
}
} finally {
d.cleanUp();
}
return notifIDs;
}
/**
* <p>getActiveNodes</p>
*
* @return a {@link java.util.List} object.
* @throws java.sql.SQLException if any.
*/
public List<Integer> getActiveNodes() throws SQLException {
String NODE_QUERY = "SELECT n.nodeid " + "FROM node n " + "WHERE n.nodetype != 'D' " + "ORDER BY n.nodelabel";
java.sql.Connection connection = null;
final List<Integer> allNodes = new ArrayList<Integer>();
final DBUtils d = new DBUtils(getClass());
try {
connection = getConnection();
d.watch(connection);
final Statement stmt = connection.createStatement();
d.watch(stmt);
final ResultSet rset = stmt.executeQuery(NODE_QUERY);
d.watch(rset);
if (rset != null) {
// Iterate through the result and build the array list
while (rset.next()) {
int nodeID = rset.getInt(1);
allNodes.add(nodeID);
}
}
return allNodes;
} finally {
d.cleanUp();
}
}
/**
* <p>getServiceNoticeStatus</p>
*
* @param nodeID a {@link java.lang.String} object.
* @param ipaddr a {@link java.lang.String} object.
* @param service a {@link java.lang.String} object.
* @return a {@link java.lang.String} object.
* @throws java.sql.SQLException if any.
*/
public String getServiceNoticeStatus(final String nodeID, final String ipaddr, final String service) throws SQLException {
String notify = "Y";
final String query = "SELECT notify FROM ifservices, service WHERE nodeid=? AND ipaddr=? AND ifservices.serviceid=service.serviceid AND service.servicename=?";
java.sql.Connection connection = null;
final DBUtils d = new DBUtils(getClass());
try {
connection = getConnection();
d.watch(connection);
final PreparedStatement statement = connection.prepareStatement(query);
d.watch(statement);
statement.setInt(1, Integer.parseInt(nodeID));
statement.setString(2, ipaddr);
statement.setString(3, service);
final ResultSet rs = statement.executeQuery();
d.watch(rs);
if (rs.next() && rs.getString("notify") != null) {
notify = rs.getString("notify");
if (notify == null)
notify = "Y";
}
return notify;
} finally {
d.cleanUp();
}
}
/**
* <p>updateNoticeWithUserInfo</p>
*
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @param userId a {@link java.lang.String} object.
* @param noticeId a int.
* @param media a {@link java.lang.String} object.
* @param contactInfo a {@link java.lang.String} object.
* @param autoNotify a {@link java.lang.String} object.
* @throws java.sql.SQLException if any.
*/
public void updateNoticeWithUserInfo(final String userId, final int noticeId, final String media, final String contactInfo, final String autoNotify) throws SQLException, MarshalException, ValidationException, IOException {
if (noticeId < 0) return;
int userNotifId = getUserNotifId();
ThreadCategory log = this.log();
if (log.isDebugEnabled()) {
log.debug("updating usersnotified: ID = " + userNotifId+ " User = " + userId + ", notice ID = " + noticeId + ", conctactinfo = " + contactInfo + ", media = " + media + ", autoNotify = " + autoNotify);
}
Connection connection = null;
final DBUtils d = new DBUtils(getClass());
try {
connection = getConnection();
d.watch(connection);
final PreparedStatement insert = connection.prepareStatement("INSERT INTO usersNotified (id, userid, notifyid, notifytime, media, contactinfo, autonotify) values (?,?,?,?,?,?,?)");
d.watch(insert);
insert.setInt(1, userNotifId);
insert.setString(2, userId);
insert.setInt(3, noticeId);
insert.setTimestamp(4, new Timestamp((new Date()).getTime()));
insert.setString(5, media);
insert.setString(6, contactInfo);
insert.setString(7, autoNotify);
insert.executeUpdate();
} finally {
d.cleanUp();
}
}
/**
* This method inserts a row into the notifications table in the database.
* This row indicates that the page has been sent out.
*
* @param queueID a {@link java.lang.String} object.
* @param notification TODO
* @param notifyId a int.
* @param params a {@link java.util.Map} object.
* @throws java.sql.SQLException if any.
*/
public void insertNotice(final int notifyId, final Map<String, String> params, final String queueID, final Notification notification) throws SQLException {
Connection connection = null;
final DBUtils d = new DBUtils(getClass());
try {
connection = getConnection();
d.watch(connection);
final PreparedStatement statement = connection.prepareStatement("INSERT INTO notifications (" +
"textmsg, numericmsg, notifyid, pagetime, nodeid, interfaceid, serviceid, eventid, " +
"eventuei, subject, queueID, notifConfigName) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
d.watch(statement);
// notifications textMsg field
String textMsg = params.get(NotificationManager.PARAM_TEXT_MSG);
if (textMsg != null && textMsg.length() > 4000) {
log().warn("textmsg too long, it will be truncated");
textMsg = textMsg.substring(0, 4000);
}
statement.setString(1, textMsg);
// notifications numericMsg field
String numMsg = params.get(NotificationManager.PARAM_NUM_MSG);
if (numMsg != null && numMsg.length() > 256) {
log().warn("numericmsg too long, it will be truncated");
numMsg = numMsg.substring(0, 256);
}
statement.setString(2, numMsg);
// notifications notifyID field
statement.setInt(3, notifyId);
// notifications pageTime field
statement.setTimestamp(4, new Timestamp((new Date()).getTime()));
// notifications nodeID field
String node = params.get(NotificationManager.PARAM_NODE);
if (node != null && !node.trim().equals("") && !node.toLowerCase().equals("null") && !node.toLowerCase().equals("%nodeid%")) {
statement.setInt(5, Integer.parseInt(node));
} else {
statement.setNull(5, Types.INTEGER);
}
// notifications interfaceID field
String ipaddr = params.get(NotificationManager.PARAM_INTERFACE);
if (ipaddr != null && !ipaddr.trim().equals("") && !ipaddr.toLowerCase().equals("null") && !ipaddr.toLowerCase().equals("%interface%")) {
statement.setString(6, ipaddr);
} else {
statement.setString(6, null);
}
// notifications serviceID field
String service = params.get(NotificationManager.PARAM_SERVICE);
if (service != null && !service.trim().equals("") && !service.toLowerCase().equals("null") && !service.toLowerCase().equals("%service%")) {
statement.setInt(7, getServiceId(service));
} else {
statement.setNull(7, Types.INTEGER);
}
// eventID field
final String eventID = params.get("eventID");
statement.setInt(8, Integer.parseInt(eventID));
statement.setString(9, params.get("eventUEI"));
// notifications subject field
statement.setString(10, params.get(NotificationManager.PARAM_SUBJECT));
// the queue this will be sent on
statement.setString(11, queueID);
statement.setString(12, notification.getName());
statement.executeUpdate();
} finally {
d.cleanUp();
}
}
/**
* This method queries the database in search of a service id for a given
* serivice name
*
* @param service
* the name of the service
* @return the serviceID of the service
*/
private int getServiceId(final String service) throws SQLException {
int serviceID = 0;
Connection connection = null;
final DBUtils d = new DBUtils(getClass());
try {
connection = getConnection();
d.watch(connection);
final PreparedStatement statement = connection.prepareStatement("SELECT serviceID from service where serviceName = ?");
d.watch(statement);
statement.setString(1, service);
final ResultSet results = statement.executeQuery();
d.watch(results);
results.next();
serviceID = results.getInt(1);
return serviceID;
} finally {
d.cleanUp();
}
}
/**
* <p>getNotifications</p>
*
* @return a {@link java.util.Map} object.
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public Map<String, Notification> getNotifications() throws IOException, MarshalException, ValidationException {
update();
Map<String, Notification> newMap = new HashMap<String, Notification>();
Notification notices[] = m_notifications.getNotification();
for (int i = 0; i < notices.length; i++) {
newMap.put(notices[i].getName(), notices[i]);
}
return Collections.unmodifiableMap(newMap);
}
/**
* <p>getServiceNames</p>
*
* @return a {@link java.util.List} object.
* @throws java.sql.SQLException if any.
*/
public List<String> getServiceNames() throws SQLException {
Connection connection = null;
List<String> services = new ArrayList<String>();
try {
connection = getConnection();
Statement stmt = connection.createStatement();
ResultSet rset = stmt.executeQuery("SELECT servicename FROM service");
if (rset != null) {
// Iterate through the result and build the array list
while (rset.next()) {
services.add(rset.getString(1));
}
}
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
}
}
}
return services;
}
/**
* <p>getNotification</p>
*
* @param name a {@link java.lang.String} object.
* @return a {@link org.opennms.netmgt.config.notifications.Notification} object.
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public Notification getNotification(final String name) throws IOException, MarshalException, ValidationException {
update();
return getNotifications().get(name);
}
/**
* <p>getNotificationNames</p>
*
* @return a {@link java.util.List} object.
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public List<String> getNotificationNames() throws IOException, MarshalException, ValidationException {
update();
List<String> notificationNames = new ArrayList<String>();
for (Notification curNotif : m_notifications.getNotificationCollection()) {
notificationNames.add(curNotif.getName());
}
return notificationNames;
}
/**
* <p>removeNotification</p>
*
* @param name a {@link java.lang.String} object.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
* @throws java.io.IOException if any.
* @throws java.lang.ClassNotFoundException if any.
*/
public synchronized void removeNotification(final String name) throws MarshalException, ValidationException, IOException, ClassNotFoundException {
m_notifications.removeNotification(getNotification(name));
saveCurrent();
}
/**
* Handles adding a new Notification.
*
* @param notice
* The Notification to add.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
* @throws java.io.IOException if any.
* @throws java.lang.ClassNotFoundException if any.
*/
public synchronized void addNotification(final Notification notice) throws MarshalException, ValidationException, IOException, ClassNotFoundException {
// remove any existing notice with the same name
m_notifications.removeNotification(getNotification(notice.getName()));
m_notifications.addNotification(notice);
saveCurrent();
}
/**
* <p>replaceNotification</p>
*
* @param oldName a {@link java.lang.String} object.
* @param newNotice a {@link org.opennms.netmgt.config.notifications.Notification} object.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
* @throws java.io.IOException if any.
* @throws java.lang.ClassNotFoundException if any.
*/
public synchronized void replaceNotification(final String oldName, final Notification newNotice) throws MarshalException, ValidationException, IOException, ClassNotFoundException {
// In order to preserve the order of the notices, we have to replace "in place".
Notification notice = getNotification(oldName);
if (notice != null) {
notice.setWriteable(newNotice.getWriteable());
notice.setName(newNotice.getName());
notice.setDescription(newNotice.getDescription());
notice.setUei(newNotice.getUei());
notice.setRule(newNotice.getRule());
notice.setDestinationPath(newNotice.getDestinationPath());
notice.setNoticeQueue(newNotice.getNoticeQueue());
notice.setTextMessage(newNotice.getTextMessage());
notice.setSubject(newNotice.getSubject());
notice.setNumericMessage(newNotice.getNumericMessage());
notice.setStatus(newNotice.getStatus());
notice.setVarbind(newNotice.getVarbind());
Parameter parameters[] = newNotice.getParameter();
for (int i = 0; i < parameters.length; i++) {
Parameter newParam = new Parameter();
newParam.setName(parameters[i].getName());
newParam.setValue(parameters[i].getValue());
notice.addParameter(newParam);
}
saveCurrent();
}
else
addNotification(newNotice);
}
/**
* Sets the status on an individual notification configuration and saves to
* xml.
*
* @param name
* The name of the notification.
* @param status
* The status (either "on" or "off").
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
* @throws java.io.IOException if any.
* @throws java.lang.ClassNotFoundException if any.
*/
public synchronized void updateStatus(final String name, final String status) throws MarshalException, ValidationException, IOException, ClassNotFoundException {
if ("on".equals(status) || "off".equals(status)) {
Notification notice = getNotification(name);
notice.setStatus(status);
saveCurrent();
} else
throw new IllegalArgumentException("Status must be on|off, not " + status);
}
/**
* <p>saveCurrent</p>
*
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
* @throws java.io.IOException if any.
* @throws java.lang.ClassNotFoundException if any.
*/
public synchronized void saveCurrent() throws MarshalException, ValidationException, IOException, ClassNotFoundException {
m_notifications.setHeader(rebuildHeader());
// Marshal to a string first, then write the string to the file. This
// way the original configuration
// isn't lost if the XML from the marshal is hosed.
StringWriter stringWriter = new StringWriter();
Marshaller.marshal(m_notifications, stringWriter);
String xmlString = stringWriter.toString();
saveXML(xmlString);
update();
}
/**
* <p>saveXML</p>
*
* @param xmlString a {@link java.lang.String} object.
* @throws java.io.IOException if any.
*/
protected abstract void saveXML(String xmlString) throws IOException;
/**
*
*/
private Header rebuildHeader() {
Header header = oldHeader;
header.setCreated(EventConstants.formatToString(new Date()));
return header;
}
/**
* <p>update</p>
*
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public abstract void update() throws IOException, MarshalException, ValidationException;
/**
* <p>rebuildParameterMap</p>
*
* @param notifId a int.
* @param resolutionPrefix a {@link java.lang.String} object.
* @param skipNumericPrefix a boolean.
* @return a {@link java.util.Map} object.
* @throws java.lang.Exception if any.
*/
public Map<String, String> rebuildParameterMap(final int notifId, final String resolutionPrefix, final boolean skipNumericPrefix) throws Exception {
final Map<String, String> parmMap = new HashMap<String, String>();
Querier querier = new Querier(m_dataSource, "select notifications.*, service.* from notifications left outer join service on notifications.serviceID = service.serviceID where notifyId = ?") {
public void processRow(ResultSet rs) throws SQLException {
/*
* Note, getString on results is valid for any SQL data type except the new SQL types:
* Blog, Clob, Array, Struct, Ref
* of which we have none in this table so this row processor is using getString
* to correctly align with annotated types in the map.
*/
parmMap.put(
NotificationManager.PARAM_TEXT_MSG,
BroadcastEventProcessor.expandNotifParms(
resolutionPrefix,
Collections.singletonMap("noticeid", String.valueOf(notifId))
) + rs.getString("textMsg")
);
if (skipNumericPrefix) {
parmMap.put(
NotificationManager.PARAM_NUM_MSG,
rs.getString("numericMsg")
);
} else {
parmMap.put(
NotificationManager.PARAM_NUM_MSG,
BroadcastEventProcessor.expandNotifParms(
resolutionPrefix,
Collections.singletonMap("noticeid", String.valueOf(notifId))
) + rs.getString("numericMsg")
);
}
parmMap.put(
NotificationManager.PARAM_SUBJECT,
BroadcastEventProcessor.expandNotifParms(
resolutionPrefix,
Collections.singletonMap("noticeid", String.valueOf(notifId))
) + rs.getString("subject")
);
parmMap.put(NotificationManager.PARAM_NODE, rs.getString("nodeID"));
parmMap.put(NotificationManager.PARAM_INTERFACE, rs.getString("interfaceID"));
parmMap.put(NotificationManager.PARAM_SERVICE, rs.getString("serviceName"));
parmMap.put("noticeid", rs.getString("notifyID"));
parmMap.put("eventID", rs.getString("eventID"));
parmMap.put("eventUEI", rs.getString("eventUEI"));
Notification notification = null;
try {
notification = getNotification(rs.getObject("notifConfigName").toString());
} catch (MarshalException e) {
} catch (ValidationException e) {
} catch (IOException e) {
}
if (notification != null) {
addNotificationParams(parmMap, notification);
}
}
};
querier.execute(notifId);
return parmMap;
}
/**
* Adds additional parameters defined by the user in the notificaiton
* configuration XML.
*
* @param paramMap a {@link java.util.Map} object.
* @param notification a {@link org.opennms.netmgt.config.notifications.Notification} object.
*/
public static void addNotificationParams(final Map<String, String> paramMap, final Notification notification) {
Collection<Parameter> parameters = notification.getParameterCollection();
for (Parameter parameter : parameters) {
paramMap.put(parameter.getName(), parameter.getValue());
}
}
/**
* <p>forEachUserNotification</p>
*
* @param notifId a int.
* @param rp a {@link org.opennms.netmgt.utils.RowProcessor} object.
*/
public void forEachUserNotification(final int notifId, final RowProcessor rp) {
final Querier querier = new Querier(m_dataSource, "select * from usersNotified where notifyId = ? order by notifytime", rp);
querier.execute(notifId);
}
/**
* <p>getQueueForNotification</p>
*
* @param notifId a int.
* @return a {@link java.lang.String} object.
*/
public String getQueueForNotification(final int notifId) {
final SingleResultQuerier querier = new SingleResultQuerier(m_dataSource, "select queueID from notifications where notifyId = ?");
querier.execute(notifId);
return (String)querier.getResult();
}
/**
* <p>expandMapValues</p>
*
* @param map a {@link java.util.Map} object.
* @param event a {@link org.opennms.netmgt.xml.event.Event} object.
*/
public static void expandMapValues(Map<String, String> map, final Event event) {
for (String key : map.keySet()) {
String mapValue = map.get(key);
if (mapValue == null) {
continue;
}
String expandedValue = EventUtil.expandParms(map.get(key), event);
if (expandedValue == null) {
// Don't use this value to replace the existing value if it's null
} else {
map.put(key, expandedValue);
}
}
}
/**
* In the absence of DAOs and ORMs this creates an Event object from the persisted
* record.
*
* @param eventid a int.
* @return a populated Event object
*/
public Event getEvent(final int eventid) {
// don't switch using event builder since this event is read from the database
final Event event = new Event();
Querier querier = new Querier(m_dataSource, "select * from events where eventid = ?", new RowProcessor() {
public void processRow(ResultSet rs) throws SQLException {
event.setDbid(rs.getInt("eventid"));
event.setUei(rs.getString("eventuei"));
event.setNodeid(rs.getLong("nodeid"));
event.setTime(rs.getString("eventtime"));
event.setHost(rs.getString("eventhost"));
event.setInterface(rs.getString("ipaddr"));
event.setSnmphost(rs.getString("eventsnmphost"));
event.setService(getServiceName(rs.getInt("serviceid")));
event.setCreationTime(rs.getString("eventcreatetime"));
event.setSeverity(rs.getString("eventseverity"));
event.setPathoutage(rs.getString("eventpathoutage"));
Tticket tticket = new Tticket();
tticket.setContent(rs.getString("eventtticket"));
tticket.setState(rs.getString("eventtticketstate"));
event.setTticket(tticket);
event.setSource(rs.getString("eventsource"));
}
private String getServiceName(int serviceid) {
SingleResultQuerier querier = new SingleResultQuerier(m_dataSource, "select servicename from service where serviceid = ?");
return (String)querier.getResult();
}
});
querier.execute(eventid);
return event;
}
}