/*******************************************************************************
* 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.web.admin.notification.noticeWizard;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.ValidationException;
import org.opennms.core.resource.Vault;
import org.opennms.core.utils.DBUtils;
import org.opennms.netmgt.config.NotifdConfigFactory;
import org.opennms.netmgt.config.NotificationFactory;
import org.opennms.netmgt.config.notifications.Notification;
import org.opennms.netmgt.config.notifications.Parameter;
import org.opennms.netmgt.config.notifications.Varbind;
import org.opennms.netmgt.dao.FilterDao;
import org.opennms.netmgt.filter.FilterDaoFactory;
import org.opennms.netmgt.filter.FilterParseException;
import org.opennms.web.api.Util;
/**
* A servlet that handles the data comming in from the notification wizard jsps.
*
* @author <A HREF="mailto:jason@opennms.org">Jason Johns </A>
* @author <A HREF="http://www.opennms.org/">OpenNMS </A>
* @author <A HREF="mailto:jason@opennms.org">Jason Johns </A>
* @author <A HREF="http://www.opennms.org/">OpenNMS </A>
* @version $Id: $
* @since 1.8.1
*/
public class NotificationWizardServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = -8394875468854510137L;
//SOURCE_PAGE_EVENTS_VIEW is more of a tag than an actual page - can't be used for navigation as is
/** Constant <code>SOURCE_PAGE_OTHER_WEBUI="eventslist"</code> */
public static final String SOURCE_PAGE_OTHER_WEBUI = "eventslist";
/** Constant <code>SOURCE_PAGE_NOTICES="eventNotices.jsp"</code> */
public static final String SOURCE_PAGE_NOTICES = "eventNotices.jsp";
/** Constant <code>SOURCE_PAGE_NOTIFS_FOR_UEI="notifsForUEI.jsp"</code> */
public static final String SOURCE_PAGE_NOTIFS_FOR_UEI = "notifsForUEI.jsp";
/** Constant <code>SOURCE_PAGE_UEIS="chooseUeis.jsp"</code> */
public static final String SOURCE_PAGE_UEIS = "chooseUeis.jsp";
/** Constant <code>SOURCE_PAGE_RULE="buildRule.jsp"</code> */
public static final String SOURCE_PAGE_RULE = "buildRule.jsp";
/** Constant <code>SOURCE_PAGE_VALIDATE="validateRule.jsp"</code> */
public static final String SOURCE_PAGE_VALIDATE = "validateRule.jsp";
/** Constant <code>SOURCE_PAGE_PATH_OUTAGE="buildPathOutage.jsp"</code> */
public static final String SOURCE_PAGE_PATH_OUTAGE = "buildPathOutage.jsp";
/** Constant <code>SOURCE_PAGE_VALIDATE_PATH_OUTAGE="validatePathOutage.jsp"</code> */
public static final String SOURCE_PAGE_VALIDATE_PATH_OUTAGE = "validatePathOutage.jsp";
/** Constant <code>SOURCE_PAGE_PATH="choosePath.jsp"</code> */
public static final String SOURCE_PAGE_PATH = "choosePath.jsp";
/** Constant <code>SOURCE_PAGE_NOTIFICATION_INDEX="../index.jsp"</code> */
public static final String SOURCE_PAGE_NOTIFICATION_INDEX = "../index.jsp";
private static final String SQL_DELETE_CRITICAL_PATH = "DELETE FROM pathoutage WHERE nodeid=?";
private static final String SQL_SET_CRITICAL_PATH = "INSERT INTO pathoutage (nodeid, criticalpathip, criticalpathservicename) VALUES (?, ?, ?)";
/** {@inheritDoc} */
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String sourcePage = request.getParameter("sourcePage");
HttpSession user = request.getSession(true);
/*
* FIXME: Why do we do this for every request in doPost instead of
* once in init?
*/
try {
NotifdConfigFactory.init();
} catch (Throwable e) {
throw new ServletException("Failed to initialize NotifdConfigFactory: " + e, e);
}
try {
NotificationFactory.init();
} catch (Throwable e) {
throw new ServletException("Failed to initialize NotificationFactory: " + e, e);
}
String redirect;
if (sourcePage.equals(SOURCE_PAGE_NOTICES)) {
redirect = processNotices(request, user);
} else if (sourcePage.equals(SOURCE_PAGE_UEIS)) {
redirect = processUeis(request, user);
} else if (sourcePage.equals(SOURCE_PAGE_RULE)) {
redirect = processRule(request, user);
} else if (sourcePage.equals(SOURCE_PAGE_VALIDATE)) {
redirect = processValidate(request, user);
} else if (sourcePage.equals(SOURCE_PAGE_PATH)) {
redirect = processPath(request, user);
} else if (sourcePage.equals(SOURCE_PAGE_PATH_OUTAGE)) {
redirect = processPathOutage(request);
} else if (sourcePage.equals(SOURCE_PAGE_VALIDATE_PATH_OUTAGE)) {
redirect = processValidatePathOutage(request);
} else if (sourcePage.equals(SOURCE_PAGE_OTHER_WEBUI)) {
redirect = processOtherWebUi(request, user);
} else if (sourcePage.equals(SOURCE_PAGE_NOTIFS_FOR_UEI)) {
redirect = processNotificationsForUei(request, user);
} else {
// FIXME: What do we do if there is no sourcePage match?
redirect = "";
}
if (redirect.equals("")) {
throw new ServletException("no redirect specified for this wizard!");
}
response.sendRedirect(redirect);
}
private String processNotices(HttpServletRequest request, HttpSession user) throws ServletException {
String userAction = request.getParameter("userAction");
if (userAction.equals("delete")) {
try {
getNotificationFactory().removeNotification(request.getParameter("notice"));
} catch (Throwable e) {
throw new ServletException("Couldn't save/reload notifications configuration file: " + e, e);
}
return SOURCE_PAGE_NOTICES;
} else if (userAction.equals("edit")) {
return edit(request, user);
} else if (userAction.equals("new")) {
user.setAttribute("newNotice", buildNewNotification("off"));
return SOURCE_PAGE_UEIS;
} else if (userAction.equals("on") || userAction.equals("off")) {
try {
getNotificationFactory().updateStatus(request.getParameter("notice"), userAction);
} catch (Throwable e) {
throw new ServletException("Couldn't save/reload notifications configuration file: " + e, e);
}
return SOURCE_PAGE_NOTICES;
} else {
// FIXME: We should do something if we hit this
return "";
}
}
private String processUeis(HttpServletRequest request, HttpSession user) {
Notification newNotice = (Notification) user.getAttribute("newNotice");
newNotice.setUei(request.getParameter("uei"));
Map<String, Object> params = new HashMap<String, Object>();
params.put("newRule", toSingleQuote(newNotice.getRule()));
return SOURCE_PAGE_RULE + makeQueryString(params);
}
private String processValidate(HttpServletRequest request, HttpSession user) {
String userAction = request.getParameter("userAction");
if (userAction.equals("rebuild")) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("newRule", request.getParameter("newRule"));
String services[] = request.getParameterValues("services");
if (services != null) {
params.put("services", services);
}
params.put("mode", "rebuild");
return SOURCE_PAGE_RULE + makeQueryString(params);
} else {
Notification newNotice = (Notification) user.getAttribute("newNotice");
newNotice.setRule(request.getParameter("newRule"));
return SOURCE_PAGE_PATH;
}
}
private String processRule(HttpServletRequest request, HttpSession user) {
String ruleString = request.getParameter("newRule");
ruleString = toSingleQuote(ruleString);
ruleString = stripExtraWhite(ruleString);
ruleString = stripServices(ruleString);
ruleString = checkParens(ruleString);
StringBuffer rule = new StringBuffer(ruleString);
String services[] = request.getParameterValues("services");
if (services != null) {
rule.append(" & ").append(" (");
for (int i = 0; i < services.length; i++) {
rule.append("is").append(services[i]);
if (i < services.length - 1) {
rule.append(" | ");
}
}
rule.append(" )");
}
String notServices[] = request.getParameterValues("notServices");
if (notServices != null) {
rule.append(" & ").append(" (");
for (int i = 0; i < notServices.length; i++) {
rule.append("!is").append(notServices[i]);
if (i < notServices.length - 1) {
rule.append(" & ");
}
}
rule.append(" )");
}
Map<String, Object> params = new HashMap<String, Object>();
params.put("newRule", rule.toString());
if (services != null) {
params.put("services", services);
}
if (notServices != null) {
params.put("notServices", notServices);
}
// page to redirect to, either validate or skip validation
String redirectPage = request.getParameter("nextPage");
// now lets see if the rule is syntactically valid
try {
getFilterDao().validateRule(rule.toString());
} catch (FilterParseException e) {
// page to redirect to if the rule is invalid
params.put("mode", "failed");
redirectPage = SOURCE_PAGE_RULE;
}
// save the rule if we are bypassing validation
if (redirectPage.equals(SOURCE_PAGE_PATH)) {
Notification newNotice = (Notification) user.getAttribute("newNotice");
newNotice.setRule(rule.toString());
}
return redirectPage + makeQueryString(params);
}
private String processPath(HttpServletRequest request, HttpSession user) throws ServletException {
Notification newNotice = (Notification) user.getAttribute("newNotice");
newNotice.setDestinationPath(request.getParameter("path"));
String description = request.getParameter("description");
if (description != null && !description.trim().equals("")) {
newNotice.setDescription(description);
} else {
newNotice.setDescription(null);
}
newNotice.setTextMessage(request.getParameter("textMsg"));
String subject = request.getParameter("subject");
if (subject != null && !subject.trim().equals("")) {
newNotice.setSubject(subject);
} else {
newNotice.setSubject(null);
}
String numMessage = request.getParameter("numMsg");
if (numMessage != null && !numMessage.trim().equals("")) {
newNotice.setNumericMessage(numMessage);
} else {
newNotice.setNumericMessage(null);
}
String oldName = newNotice.getName();
newNotice.setName(request.getParameter("name"));
String varbindName = request.getParameter("varbindName");
String varbindValue = request.getParameter("varbindValue");
Varbind varbind=newNotice.getVarbind();
if (varbindName != null && !varbindName.trim().equals("") && varbindValue != null && !varbindValue.trim().equals("")) {
if (varbind == null) {
varbind = new Varbind();
newNotice.setVarbind(varbind);
}
varbind.setVbname(varbindName);
varbind.setVbvalue(varbindValue);
} else {
// Must do this to allow clearing out varbind definitions
newNotice.setVarbind(null);
}
try {
// replacing a path with a new name.
getNotificationFactory().replaceNotification(oldName, newNotice);
} catch (Throwable e) {
throw new ServletException("Couldn't save/reload notification configuration file.", e);
}
String suppliedReturnPage=(String)user.getAttribute("noticeWizardReturnPage");
if (suppliedReturnPage != null && !suppliedReturnPage.equals("")) {
// Remove this attribute once we have consumed it, else the user may later
// get returned to a potentially unexpected page here
user.removeAttribute("noticeWizardReturnPage");
return suppliedReturnPage;
} else {
return SOURCE_PAGE_NOTICES;
}
}
private String processPathOutage(HttpServletRequest request) {
String newRule = request.getParameter("newRule");
newRule = toSingleQuote(newRule);
newRule = stripExtraWhite(newRule);
newRule = stripServices(newRule);
newRule = checkParens(newRule);
String redirectPage = SOURCE_PAGE_VALIDATE_PATH_OUTAGE;
String criticalIp = request.getParameter("criticalIp");
Map<String, Object> params = new HashMap<String, Object>();
if (newRule != null) {
params.put("newRule", newRule);
}
if (request.getParameter("criticalSvc") != null) {
params.put("criticalSvc", request.getParameter("criticalSvc"));
}
if (request.getParameter("showNodes") != null) {
params.put("showNodes", request.getParameter("showNodes"));
}
if (criticalIp != null && !criticalIp.equals("")) {
params.put("criticalIp", criticalIp);
try {
getFilterDao().validateRule("IPADDR IPLIKE " + criticalIp);
} catch (FilterParseException e) {
// page to redirect to if the critical IP is invalid
params.put("mode", "Critical path IP failed");
redirectPage = SOURCE_PAGE_PATH_OUTAGE;
}
}
try {
getFilterDao().validateRule(newRule);
} catch (FilterParseException e) {
// page to redirect to if the rule is invalid
params.put("mode", "Current rule failed");
redirectPage = SOURCE_PAGE_PATH_OUTAGE;
}
return redirectPage + makeQueryString(params);
}
private String processValidatePathOutage(HttpServletRequest request) {
String redirectPage = SOURCE_PAGE_NOTIFICATION_INDEX;
String userAction = request.getParameter("userAction");
String criticalIp = request.getParameter("criticalIp");
String criticalSvc = request.getParameter("criticalSvc");
String newRule = request.getParameter("newRule");
Map<String, Object> params = new HashMap<String, Object>();
if (userAction != null && userAction.equals("rebuild")) {
params.put("newRule", newRule);
params.put("criticalIp", criticalIp);
params.put("criticalSvc", criticalSvc);
if (request.getParameter("showNodes") != null) {
params.put("showNodes", request.getParameter("showNodes"));
}
redirectPage = SOURCE_PAGE_PATH_OUTAGE;
} else {
try {
updatePaths(newRule, criticalIp, criticalSvc);
} catch (FilterParseException e) {
params.put("mode", "Update failed");
redirectPage = SOURCE_PAGE_PATH_OUTAGE;
} catch (SQLException e) {
params.put("mode", "Update failed");
redirectPage = SOURCE_PAGE_PATH_OUTAGE;
}
}
return redirectPage + makeQueryString(params);
}
private String processOtherWebUi(HttpServletRequest request, HttpSession user) throws ServletException {
/*
* We've come from elsewhere in the Web UI page, and will have a UEI.
* If there are existing notices for this UEI, then go to a page listing them allowing editing.
* If there are none, then create a notice, populate the UEI, and go to the buildRule page.
*/
user.setAttribute("noticeWizardReturnPage", request.getParameter("returnPage"));
String uei = request.getParameter("uei");
boolean hasUei;
try {
hasUei = getNotificationFactory().hasUei(uei);
} catch (IOException e) {
throw new ServletException("IOException while checking if there is an existing notification for UEI "+uei, e);
} catch (MarshalException e) {
throw new ServletException("Marshalling Exception while checking if there is an existing notification for UEI "+uei, e);
} catch (ValidationException e) {
throw new ServletException("Validation Exception while checking if there is an existing notification for UEI "+uei, e);
}
if (hasUei) {
//There are existing notifications for this UEI - goto a listing page
Map<String, Object> params = new HashMap<String, Object>();
params.put("uei", uei);
return SOURCE_PAGE_NOTIFS_FOR_UEI + makeQueryString(params);
} else {
return newNotifWithUEI(request, user);
}
}
private String processNotificationsForUei(HttpServletRequest request, HttpSession user) throws ServletException {
String userAction = request.getParameter("userAction");
if ("edit".equals(userAction)) {
return edit(request, user);
} else if ("new".equals(userAction)) {
return newNotifWithUEI(request, user);
} else {
// FIXME: What do we do here if neither of the userActions match?
return "";
}
}
private String newNotifWithUEI(HttpServletRequest request, HttpSession user) {
String uei = request.getParameter("uei");
Notification newNotice = buildNewNotification("on");
newNotice.setUei(uei);
Map<String, Object> params = new HashMap<String, Object>();
params.put("newRule", toSingleQuote(newNotice.getRule()));
user.setAttribute("newNotice", newNotice);
return SOURCE_PAGE_RULE + makeQueryString(params);
}
private Notification buildNewNotification(String status) {
Notification notice = new Notification();
notice.setRule("IPADDR IPLIKE *.*.*.*");
notice.setNumericMessage("111-%noticeid%");
notice.setSubject("Notice #%noticeid%");
notice.setStatus(status);
return notice;
}
/**
* Common code for two source pages that can't really be considered the same
*/
private String edit(HttpServletRequest request, HttpSession user) throws ServletException {
Notification oldNotice;
try {
oldNotice = getNotificationFactory().getNotification(request.getParameter("notice"));
} catch (Throwable e) {
throw new ServletException("couldn't get a copy of the notification to edit.", e);
}
// copy the old path into the new path
Notification newNotice = copyNotice(oldNotice);
user.setAttribute("newNotice", newNotice);
return SOURCE_PAGE_UEIS;
}
/**
*
*/
private Notification copyNotice(Notification oldNotice) {
Notification newNotice = new Notification();
newNotice.setName(oldNotice.getName());
newNotice.setWriteable(oldNotice.getWriteable());
newNotice.setDescription(oldNotice.getDescription());
newNotice.setUei(oldNotice.getUei());
newNotice.setRule(oldNotice.getRule());
newNotice.setDestinationPath(oldNotice.getDestinationPath());
newNotice.setNoticeQueue(oldNotice.getNoticeQueue());
newNotice.setTextMessage(oldNotice.getTextMessage());
newNotice.setSubject(oldNotice.getSubject());
newNotice.setNumericMessage(oldNotice.getNumericMessage());
newNotice.setStatus(oldNotice.getStatus());
newNotice.setVarbind(oldNotice.getVarbind());
Parameter parameters[] = oldNotice.getParameter();
for (Parameter parameter : parameters) {
Parameter newParam = new Parameter();
newParam.setName(parameter.getName());
newParam.setValue(parameter.getValue());
newNotice.addParameter(newParam);
}
return newNotice;
}
// FIXME: Is this a duplicate of a similar method elsewhere?
private String makeQueryString(Map<String, Object> map) {
StringBuffer buffer = new StringBuffer();
String separator = "?";
Iterator<String> i = map.keySet().iterator();
while (i.hasNext()) {
String key = i.next();
Object value = map.get(key);
if (value instanceof String[]) {
String[] list = (String[]) value;
for (int j = 0; j < list.length; j++) {
buffer.append(separator).append(key).append("=").append(Util.encode(list[j]));
separator = "&";
}
} else {
buffer.append(separator).append(key).append("=").append(Util.encode((String) value));
}
separator = "&";
}
return buffer.toString();
}
private static String toSingleQuote(String rule) {
StringBuffer buffer = new StringBuffer(rule);
for (int i = 0; (i < buffer.length()); i++) {
if ((i < buffer.length() - 5) && (buffer.substring(i, i + 6).equals("""))) {
buffer.replace(i, i + 6, "'");
} else if (buffer.charAt(i) == '"') {
buffer.replace(i, i + 1, "'");
}
}
return buffer.toString();
}
private static String stripExtraWhite(String s) {
Pattern pattern1 = Pattern.compile("\\s+");
Matcher matcher1 = pattern1.matcher(s);
String mys1 = matcher1.replaceAll(" ");
Pattern pattern2 = Pattern.compile("^\\s");
Matcher matcher2 = pattern2.matcher(mys1);
String mys2 = matcher2.replaceAll("");
Pattern pattern3 = Pattern.compile("\\s$");
Matcher matcher3 = pattern3.matcher(mys2);
return matcher3.replaceAll("");
}
private static String stripServices(String s) {
String myregex = "\\s*\\&\\s*\\(\\s*\\!?is.+";
Pattern pattern = Pattern.compile(myregex);
Matcher matcher = pattern.matcher(s);
return matcher.replaceAll("");
}
private static String checkParens(String rule) {
if (rule.length() == 0) {
return rule;
} else if ((rule.charAt(0) != '(') || (rule.charAt(rule.length() - 1) != ')')) {
return "(" + rule + ")";
} else {
return rule;
}
}
private void deleteCriticalPath(int node, Connection conn) throws SQLException {
PreparedStatement stmt = null;
final DBUtils d = new DBUtils(getClass());
try {
stmt = conn.prepareStatement(SQL_DELETE_CRITICAL_PATH);
d.watch(stmt);
stmt.setInt(1, node);
stmt.execute();
} finally {
d.cleanUp();
}
}
private void setCriticalPath(int node, String criticalIp, String criticalSvc, Connection conn) throws SQLException {
PreparedStatement stmt = null;
final DBUtils d = new DBUtils(getClass());
try {
stmt = conn.prepareStatement(SQL_SET_CRITICAL_PATH);
d.watch(stmt);
stmt.setInt(1, node);
stmt.setString(2, criticalIp);
stmt.setString(3, criticalSvc);
stmt.execute();
} finally {
d.cleanUp();
}
}
private void updatePaths(String rule, String criticalIp, String criticalSvc)
throws FilterParseException, SQLException {
Connection conn = Vault.getDbConnection();
SortedMap<Integer, String> nodes = getFilterDao().getNodeMap(rule);
try {
Iterator<Integer> i = nodes.keySet().iterator();
while (i.hasNext()) {
Integer key = i.next();
deleteCriticalPath(key.intValue(), conn);
if (criticalIp != null && !criticalIp.equals("")) {
setCriticalPath(key.intValue(), criticalIp, criticalSvc, conn);
}
}
} finally {
Vault.releaseDbConnection(conn);
}
}
private FilterDao getFilterDao() {
return FilterDaoFactory.getInstance();
}
private NotificationFactory getNotificationFactory() {
return NotificationFactory.getInstance();
}
}