Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HZN-956: Implement browser notification #2284

Merged
merged 12 commits into from Feb 6, 2019
Merged
Expand Up @@ -217,4 +217,18 @@
<switch>-tm</switch>
</argument>
</command>
</notification-commands>
<command binary="false">
<name>browser</name>
<execute>org.opennms.netmgt.notifd.BrowserNotificationStrategy</execute>
<comment>Sending Notification to the Browser</comment>
<argument streamed="false">
<switch>-d</switch>
</argument>
<argument streamed="false">
<switch>-subject</switch>
</argument>
<argument streamed="false">
<switch>-tm</switch>
</argument>
</command>
</notification-commands>
Expand Up @@ -8,6 +8,7 @@
_{opennms-product-name}_ uses notifications to make users aware of an event.
Common notification methods are email and paging, but notification mechanisms also exist for:

* Browser based desktop notifications
* Arbitrary HTTP GET and POST operations
* Arbitrary external commands
* Asterisk call origination
Expand Down
@@ -1,6 +1,9 @@
[[releasenotes-24]]
== What's New in OpenNMS Horizon 24

* Added support for browser notifications to notifd.
The browser notification can be added to a notification path and desktop notifications will pop-up for currently logged in users if a notice is delivered.

=== System Requirements

* *Java 8*: OpenNMS Horizon 24 requires Java 8 as the runtime environment.
Expand Down
@@ -0,0 +1,84 @@
/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2009-2014 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2014 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 Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero 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.notifd;

import java.util.List;
import java.util.UUID;

import org.opennms.netmgt.config.NotificationManager;
import org.opennms.netmgt.model.notifd.Argument;
import org.opennms.netmgt.model.notifd.NotificationStrategy;
import org.opennms.netmgt.notifd.browser.BrowserNotificationDispatcher;
import org.opennms.netmgt.notifd.browser.BrowserNotificationMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Send notifications to the browser.
*/
public class BrowserNotificationStrategy implements NotificationStrategy {

private static final Logger LOG = LoggerFactory.getLogger(BrowserNotificationStrategy.class);

public BrowserNotificationStrategy() {
}

@Override
public int send(final List<Argument> arguments) {
String user = null;
String head = null;
String body = null;

for (final Argument argument : arguments) {
switch (argument.getSwitch()) {
case NotificationManager.PARAM_DESTINATION:
user = argument.getValue();
break;

case NotificationManager.PARAM_SUBJECT:
head = argument.getValue();
break;

case NotificationManager.PARAM_TEXT_MSG:
case NotificationManager.PARAM_NUM_MSG:
body = argument.getValue();
break;
}
}

final BrowserNotificationMessage message = new BrowserNotificationMessage();
message.setId(UUID.randomUUID().toString());
message.setHead(head);
message.setBody(body);

BrowserNotificationDispatcher.getInstance().notify(user, message);

return 0;
}
}
@@ -0,0 +1,74 @@
/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2018 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2018 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 Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero 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.notifd.browser;

import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;

import com.google.common.collect.Sets;

public class BrowserNotificationDispatcher {

public static class Handler {
public final String user;
public final Consumer<BrowserNotificationMessage> consumer;

private Handler(final String user,
final Consumer<BrowserNotificationMessage> consumer) {
this.user = Objects.requireNonNull(user);
this.consumer = Objects.requireNonNull(consumer);
}
}

private final Set<Handler> handlers = Sets.newConcurrentHashSet();

public Handler subscribe(final String user, final Consumer<BrowserNotificationMessage> consumer) {
final Handler handler = new Handler(user, consumer);
this.handlers.add(handler);

return handler;
}

public void unsubscribe(final Handler handler) {
this.handlers.remove(handler);
}

public void notify(final String user, final BrowserNotificationMessage notification) {
this.handlers.stream()
.filter(handler -> handler.user.equals(user))
.forEach(handler -> handler.consumer.accept(notification));
}

private final static BrowserNotificationDispatcher INSTANCE = new BrowserNotificationDispatcher();

public static BrowserNotificationDispatcher getInstance() {
return INSTANCE;
}
}
@@ -0,0 +1,60 @@
/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2018 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2018 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 Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero 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.notifd.browser;

public class BrowserNotificationMessage {
private String id;
private String head;
private String body;

public String getId() {
return this.id;
}

public void setId(final String id) {
this.id = id;
}

public String getHead() {
return this.head;
}

public void setHead(final String head) {
this.head = head;
}

public String getBody() {
return this.body;
}

public void setBody(final String body) {
this.body = body;
}

}
11 changes: 11 additions & 0 deletions opennms-webapp/pom.xml
Expand Up @@ -450,11 +450,22 @@
<!-- indirect OpenNMS dependencies that belong in $OPENNMS_HOME/lib -->
<!-- these should all be <scope>${onmsLibScope}</scope> and also included in opennms-base-assembly -->

<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-servlet</artifactId>
<version>${jettyVersion}</version>
</dependency>

<dependency>
<groupId>org.opennms</groupId>
<artifactId>opennms-dao</artifactId>
<scope>${onmsLibScope}</scope>
</dependency>
<dependency>
<groupId>org.opennms</groupId>
<artifactId>opennms-services</artifactId>
<scope>${onmsLibScope}</scope>
</dependency>
<dependency>
<groupId>org.opennms.features.enlinkd</groupId>
<artifactId>org.opennms.features.enlinkd.persistence.api</artifactId>
Expand Down
@@ -0,0 +1,102 @@
/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2018 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2018 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 Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero 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.notification;

import java.io.IOException;
import java.util.Objects;

import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.json.JSONObject;
import org.opennms.netmgt.notifd.browser.BrowserNotificationDispatcher;
import org.opennms.netmgt.notifd.browser.BrowserNotificationMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.sf.json.JSONSerializer;

public class NotificationStreamServlet extends WebSocketServlet {
private static final Logger LOG = LoggerFactory.getLogger(NotificationStreamServlet.class);

public NotificationStreamServlet() {
}

@Override
public void configure(final WebSocketServletFactory factory) {
factory.setCreator((request, response) -> {
final String user = request.getHttpServletRequest().getRemoteUser();

return new NotificationStreamSocket(user);
});
}

public class NotificationStreamSocket extends WebSocketAdapter {
private final String user;

private BrowserNotificationDispatcher.Handler handler;

public NotificationStreamSocket(final String user) {
this.user = Objects.requireNonNull(user);
}

@Override
public void onWebSocketConnect(final Session session) {
super.onWebSocketConnect(session);

this.handler = BrowserNotificationDispatcher.getInstance().subscribe(this.user, this::sendNotification);
}

@Override
public void onWebSocketClose(final int statusCode, final String reason) {
super.onWebSocketClose(statusCode, reason);
BrowserNotificationDispatcher.getInstance().unsubscribe(this.handler);
}

@Override
public void onWebSocketError(final Throwable cause) {
super.onWebSocketError(cause);
BrowserNotificationDispatcher.getInstance().unsubscribe(this.handler);
}

private void sendNotification(final BrowserNotificationMessage message) {
final JSONObject json = new JSONObject();
json.append("id", message.getId());
json.append("head", message.getHead());
json.append("body", message.getBody());

try {
this.getRemote().sendString(json.toString());
} catch (final IOException e) {
LOG.error("Failed to send out notification", e);
}
}
}
}