blob: 4faae280b560bd566efafca25544eebff5c9d0a7 [file] [log] [blame]
Thomas Vachuska3553b302015-03-07 14:49:43 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Thomas Vachuska3553b302015-03-07 14:49:43 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.ui.impl;
17
Thomas Vachuskafa74dd72016-03-20 19:11:12 -070018import com.fasterxml.jackson.databind.node.ObjectNode;
Thomas Vachuskaf3897232018-04-25 12:50:32 -040019import com.google.common.collect.ImmutableSet;
Charles Chan43455b82018-04-23 10:52:48 -070020import com.google.common.collect.Sets;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070021import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
22import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
Thomas Vachuska3553b302015-03-07 14:49:43 -080023import org.onlab.osgi.DefaultServiceDirectory;
24import org.onlab.osgi.ServiceDirectory;
25
26import javax.servlet.ServletException;
27import javax.servlet.http.HttpServletRequest;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070028import javax.servlet.http.HttpServletResponse;
29import java.io.IOException;
Simon Huntea327082016-04-11 16:49:07 -070030import java.security.Principal;
Thomas Vachuska3553b302015-03-07 14:49:43 -080031import java.util.Set;
32import java.util.Timer;
33import java.util.TimerTask;
34
35/**
36 * Web socket servlet capable of creating web sockets for the user interface.
37 */
38public class UiWebSocketServlet extends WebSocketServlet {
39
40 private static final long PING_DELAY_MS = 5000;
41
Thomas Vachuska51f540f2015-05-27 17:26:57 -070042 private static UiWebSocketServlet instance;
Ray Milkey06297ed2018-01-22 17:13:41 -080043 private static final Object INSTANCE_LOCK = new Object();
Thomas Vachuska51f540f2015-05-27 17:26:57 -070044
Ray Milkey2014cdb2018-05-16 09:12:24 -070045 private static ServiceDirectory directory = new DefaultServiceDirectory();
Thomas Vachuska3553b302015-03-07 14:49:43 -080046
Charles Chan43455b82018-04-23 10:52:48 -070047 private final Set<UiWebSocket> sockets = Sets.newConcurrentHashSet();
Thomas Vachuska3553b302015-03-07 14:49:43 -080048 private final Timer timer = new Timer();
49 private final TimerTask pruner = new Pruner();
Ray Milkey2014cdb2018-05-16 09:12:24 -070050 private static boolean isStopped = false;
Thomas Vachuska3553b302015-03-07 14:49:43 -080051
Ray Milkeyd84f89b2018-08-17 14:54:17 -070052 @Override
53 public void configure(WebSocketServletFactory webSocketServletFactory) {
54
55 }
56
Thomas Vachuska51f540f2015-05-27 17:26:57 -070057 /**
58 * Closes all currently open UI web-sockets.
59 */
60 public static void closeAll() {
Ray Milkey06297ed2018-01-22 17:13:41 -080061 synchronized (INSTANCE_LOCK) {
62 if (instance != null) {
Ray Milkey2014cdb2018-05-16 09:12:24 -070063 isStopped = true;
Ray Milkey06297ed2018-01-22 17:13:41 -080064 instance.sockets.forEach(UiWebSocket::close);
65 instance.sockets.clear();
66 instance.pruner.cancel();
67 instance.timer.cancel();
68 }
Thomas Vachuska51f540f2015-05-27 17:26:57 -070069 }
70 }
71
Thomas Vachuska3553b302015-03-07 14:49:43 -080072 @Override
73 public void init() throws ServletException {
74 super.init();
Ray Milkey06297ed2018-01-22 17:13:41 -080075 synchronized (INSTANCE_LOCK) {
76 instance = this;
77 }
Thomas Vachuska3553b302015-03-07 14:49:43 -080078 timer.schedule(pruner, PING_DELAY_MS, PING_DELAY_MS);
79 }
80
81 @Override
Ray Milkeyd84f89b2018-08-17 14:54:17 -070082 public void service(HttpServletRequest request, HttpServletResponse response)
83 throws ServletException, IOException {
84 super.service(request, response);
Thomas Vachuska4e0e0932015-07-24 10:14:34 -070085 if (isStopped) {
Ray Milkeyd84f89b2018-08-17 14:54:17 -070086 return;
Thomas Vachuska4e0e0932015-07-24 10:14:34 -070087 }
Thomas Vachuska0af26912016-03-21 21:37:30 -070088
89 // FIXME: Replace this with globally shared opaque token to allow secure failover
Simon Huntea327082016-04-11 16:49:07 -070090 Principal p = request.getUserPrincipal();
91 String userName = p != null ? p.getName() : FAKE_USERNAME;
92
Thomas Vachuska0af26912016-03-21 21:37:30 -070093 UiWebSocket socket = new UiWebSocket(directory, userName);
Thomas Vachuskaf3897232018-04-25 12:50:32 -040094 sockets.add(socket);
Thomas Vachuska3553b302015-03-07 14:49:43 -080095 }
96
Simon Huntea327082016-04-11 16:49:07 -070097 // FIXME: This should not be necessary
98 private static final String FAKE_USERNAME = "UI-user";
99
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700100 /**
101 * Sends the specified message to all the GUI clients.
102 *
103 * @param type message type
104 * @param payload message payload
105 */
106 static void sendToAll(String type, ObjectNode payload) {
Ray Milkey06297ed2018-01-22 17:13:41 -0800107 synchronized (INSTANCE_LOCK) {
108 if (instance != null) {
109 instance.sockets.forEach(ws -> ws.sendMessage(type, payload));
110 }
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700111 }
112 }
113
Thomas Vachuska0af26912016-03-21 21:37:30 -0700114 /**
115 * Sends the specified message to all the GUI clients of the specified user.
116 *
117 * @param userName user name
118 * @param type message type
119 * @param payload message payload
120 */
121 static void sendToUser(String userName, String type, ObjectNode payload) {
Ray Milkey06297ed2018-01-22 17:13:41 -0800122 synchronized (INSTANCE_LOCK) {
123 if (instance != null) {
124 instance.sockets.stream().filter(ws -> userName.equals(ws.userName()))
125 .forEach(ws -> ws.sendMessage(type, payload));
126 }
Thomas Vachuska0af26912016-03-21 21:37:30 -0700127 }
128 }
129
Thomas Vachuska3553b302015-03-07 14:49:43 -0800130 // Task for pruning web-sockets that are idle.
131 private class Pruner extends TimerTask {
132 @Override
133 public void run() {
Thomas Vachuskaf3897232018-04-25 12:50:32 -0400134 ImmutableSet<UiWebSocket> set = ImmutableSet.copyOf(sockets);
135 set.stream().filter(UiWebSocket::isIdle).forEach(s -> {
136 sockets.remove(s);
137 s.close();
138 });
Thomas Vachuska3553b302015-03-07 14:49:43 -0800139 }
140 }
141}