blob: 0ed48ad638917bb51154b20ff8991b6242838ff0 [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
18import com.fasterxml.jackson.databind.ObjectMapper;
Thomas Vachuskab6acc7b2015-03-11 09:11:21 -070019import com.fasterxml.jackson.databind.node.ArrayNode;
Thomas Vachuska3553b302015-03-07 14:49:43 -080020import com.fasterxml.jackson.databind.node.ObjectNode;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070021import org.eclipse.jetty.websocket.api.Session;
Thomas Vachuskac0f757a2018-10-23 11:41:34 -070022import org.eclipse.jetty.websocket.api.WebSocketAdapter;
Thomas Vachuska3553b302015-03-07 14:49:43 -080023import org.onlab.osgi.ServiceDirectory;
Thomas Vachuskafc52fec2015-05-18 19:13:56 -070024import org.onlab.osgi.ServiceNotFoundException;
pierventre55a5f392021-11-05 15:37:32 +010025import org.onlab.packet.IpAddress;
Thomas Vachuskab6acc7b2015-03-11 09:11:21 -070026import org.onosproject.cluster.ClusterService;
27import org.onosproject.cluster.ControllerNode;
Simon Hunt8add9ee2016-09-20 17:05:07 -070028import org.onosproject.ui.GlyphConstants;
Thomas Vachuska3553b302015-03-07 14:49:43 -080029import org.onosproject.ui.UiConnection;
Simon Hunt2d7cd6f2017-05-04 13:04:50 -070030import org.onosproject.ui.UiExtension;
Thomas Vachuska3553b302015-03-07 14:49:43 -080031import org.onosproject.ui.UiExtensionService;
Laszlo Papp759f0d32018-03-05 13:24:30 +000032import org.onosproject.ui.UiGlyph;
Simon Hunta0ddb022015-05-01 09:53:01 -070033import org.onosproject.ui.UiMessageHandler;
Simon Hunt7715e892016-04-12 19:55:32 -070034import org.onosproject.ui.UiMessageHandlerFactory;
Simon Hunt1169c952017-06-05 11:20:11 -070035import org.onosproject.ui.UiSessionToken;
36import org.onosproject.ui.UiTokenService;
Simon Hunt22c35df2017-04-26 17:28:42 -070037import org.onosproject.ui.UiTopo2OverlayFactory;
Thomas Vachuska92b016b2016-05-20 11:37:57 -070038import org.onosproject.ui.UiTopoLayoutService;
Simon Hunte05cae42015-07-23 17:35:24 -070039import org.onosproject.ui.UiTopoOverlayFactory;
Simon Hunt537bc762016-12-20 12:15:13 -080040import org.onosproject.ui.impl.topo.Topo2Jsonifier;
Simon Hunt22c35df2017-04-26 17:28:42 -070041import org.onosproject.ui.impl.topo.Topo2OverlayCache;
Simon Hunt2d7cd6f2017-05-04 13:04:50 -070042import org.onosproject.ui.impl.topo.Topo2TrafficMessageHandler;
Simon Hunt22c35df2017-04-26 17:28:42 -070043import org.onosproject.ui.impl.topo.Topo2ViewMessageHandler;
Sean Condonadeb7162019-04-13 20:56:14 +010044import org.onosproject.ui.impl.topo.Traffic2Overlay;
Thomas Vachuska92b016b2016-05-20 11:37:57 -070045import org.onosproject.ui.impl.topo.UiTopoSession;
46import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel;
Simon Hunt879ce452017-08-10 23:32:00 -070047import org.onosproject.ui.lion.LionBundle;
Thomas Vachuska92b016b2016-05-20 11:37:57 -070048import org.onosproject.ui.model.topo.UiTopoLayout;
Thomas Vachuska3553b302015-03-07 14:49:43 -080049import org.slf4j.Logger;
50import org.slf4j.LoggerFactory;
51
52import java.io.IOException;
Thomas Vachuskac0f757a2018-10-23 11:41:34 -070053import java.nio.ByteBuffer;
Thomas Vachuska3553b302015-03-07 14:49:43 -080054import java.util.HashMap;
Simon Hunt1f4365d2017-06-21 17:25:09 -070055import java.util.Locale;
Thomas Vachuska3553b302015-03-07 14:49:43 -080056import java.util.Map;
57
Thomas Vachuskac0f757a2018-10-23 11:41:34 -070058import static org.onosproject.ui.impl.UiWebSocketServlet.PING_DELAY_MS;
59
Thomas Vachuska3553b302015-03-07 14:49:43 -080060/**
Simon Hunt7092cc42016-04-06 18:40:17 -070061 * Web socket capable of interacting with the Web UI.
Thomas Vachuska3553b302015-03-07 14:49:43 -080062 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070063
Thomas Vachuskac0f757a2018-10-23 11:41:34 -070064public class UiWebSocket extends WebSocketAdapter implements UiConnection {
Thomas Vachuska3553b302015-03-07 14:49:43 -080065
66 private static final Logger log = LoggerFactory.getLogger(UiWebSocket.class);
67
Simon Hunt7715e892016-04-12 19:55:32 -070068 private static final String EVENT = "event";
Simon Hunt7715e892016-04-12 19:55:32 -070069 private static final String PAYLOAD = "payload";
70 private static final String UNKNOWN = "unknown";
Simon Hunt1169c952017-06-05 11:20:11 -070071 private static final String AUTHENTICATION = "authentication";
72 private static final String TOKEN = "token";
73 private static final String ERROR = "error";
Simon Hunt7715e892016-04-12 19:55:32 -070074
75 private static final String ID = "id";
76 private static final String IP = "ip";
77 private static final String CLUSTER_NODES = "clusterNodes";
78 private static final String USER = "user";
79 private static final String BOOTSTRAP = "bootstrap";
80
Simon Huntd6d3ad32017-06-21 15:27:06 -070081 private static final String UBERLION = "uberlion";
82 private static final String LION = "lion";
Simon Hunt1f4365d2017-06-21 17:25:09 -070083 private static final String LOCALE = "locale";
Simon Huntd6d3ad32017-06-21 15:27:06 -070084
Simon Huntd7395c82016-10-20 17:54:01 -070085 private static final String TOPO = "topo";
Thomas Vachuska92b016b2016-05-20 11:37:57 -070086
Laszlo Papp759f0d32018-03-05 13:24:30 +000087 private static final String GLYPHS = "glyphs";
88
Thomas Vachuska1a989c12015-06-09 18:29:22 -070089 private static final long MAX_AGE_MS = 30_000;
Thomas Vachuska3553b302015-03-07 14:49:43 -080090
Thomas Vachuskac0f757a2018-10-23 11:41:34 -070091 private static final byte[] PING_DATA = new byte[]{(byte) 0xde, (byte) 0xad};
92 private static final ByteBuffer PING = ByteBuffer.wrap(PING_DATA);
93
Simon Hunt7092cc42016-04-06 18:40:17 -070094 private final ObjectMapper mapper = new ObjectMapper();
Thomas Vachuska3553b302015-03-07 14:49:43 -080095 private final ServiceDirectory directory;
Thomas Vachuska92b016b2016-05-20 11:37:57 -070096 private final UiTopoSession topoSession;
Thomas Vachuska3553b302015-03-07 14:49:43 -080097
Thomas Vachuska0af26912016-03-21 21:37:30 -070098 private String userName;
Thomas Vachuska92b016b2016-05-20 11:37:57 -070099 private String currentView;
Thomas Vachuska3553b302015-03-07 14:49:43 -0800100
Thomas Vachuska3553b302015-03-07 14:49:43 -0800101 private long lastActive = System.currentTimeMillis();
102
Simon Hunta0ddb022015-05-01 09:53:01 -0700103 private Map<String, UiMessageHandler> handlers;
Simon Hunte05cae42015-07-23 17:35:24 -0700104 private TopoOverlayCache overlayCache;
Simon Hunt22c35df2017-04-26 17:28:42 -0700105 private Topo2OverlayCache overlay2Cache;
Thomas Vachuska3553b302015-03-07 14:49:43 -0800106
Simon Hunt879ce452017-08-10 23:32:00 -0700107 private Map<String, LionBundle> lionBundleMap;
108
Simon Hunt1169c952017-06-05 11:20:11 -0700109 private UiSessionToken sessionToken;
110
111
Thomas Vachuska3553b302015-03-07 14:49:43 -0800112 /**
Simon Huntcda9c032016-04-11 10:32:54 -0700113 * Creates a new web-socket for serving data to the Web UI.
Thomas Vachuska3553b302015-03-07 14:49:43 -0800114 *
115 * @param directory service directory
Simon Hunt7715e892016-04-12 19:55:32 -0700116 * @param userName user name of the logged-in user
Thomas Vachuska3553b302015-03-07 14:49:43 -0800117 */
Thomas Vachuska0af26912016-03-21 21:37:30 -0700118 public UiWebSocket(ServiceDirectory directory, String userName) {
Thomas Vachuska3553b302015-03-07 14:49:43 -0800119 this.directory = directory;
Thomas Vachuska0af26912016-03-21 21:37:30 -0700120 this.userName = userName;
Simon Hunt537bc762016-12-20 12:15:13 -0800121
Simon Hunt95f4b422017-03-03 13:49:05 -0800122 Topo2Jsonifier t2json = new Topo2Jsonifier(directory, userName);
Simon Hunt537bc762016-12-20 12:15:13 -0800123 UiSharedTopologyModel sharedModel = directory.get(UiSharedTopologyModel.class);
124 UiTopoLayoutService layoutService = directory.get(UiTopoLayoutService.class);
Simon Huntbbd0f462017-01-10 14:50:22 -0800125
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800126 sharedModel.injectJsonifier(t2json);
Simon Huntbbd0f462017-01-10 14:50:22 -0800127 topoSession = new UiTopoSession(this, t2json, sharedModel, layoutService);
Simon Hunt1169c952017-06-05 11:20:11 -0700128 sessionToken = null;
Thomas Vachuska0af26912016-03-21 21:37:30 -0700129 }
130
131 @Override
132 public String userName() {
133 return userName;
Thomas Vachuska3553b302015-03-07 14:49:43 -0800134 }
135
Thomas Vachuska92b016b2016-05-20 11:37:57 -0700136 @Override
137 public UiTopoLayout currentLayout() {
138 return topoSession.currentLayout();
139 }
140
141 @Override
142 public void setCurrentLayout(UiTopoLayout topoLayout) {
143 topoSession.setCurrentLayout(topoLayout);
144 }
145
146 @Override
147 public String currentView() {
148 return currentView;
149 }
150
151 @Override
152 public void setCurrentView(String viewId) {
153 currentView = viewId;
154 topoSession.enableEvent(viewId.equals(TOPO));
155 }
156
Simon Huntd6d3ad32017-06-21 15:27:06 -0700157 private ObjectNode objectNode() {
158 return mapper.createObjectNode();
159 }
160
161 private ArrayNode arrayNode() {
162 return mapper.createArrayNode();
163 }
164
Thomas Vachuska3553b302015-03-07 14:49:43 -0800165 /**
Simon Huntd5b96732016-07-08 13:22:27 -0700166 * Provides a reference to the topology session.
167 *
168 * @return topo session reference
169 */
170 public UiTopoSession topoSession() {
171 return topoSession;
172 }
173
174 /**
Thomas Vachuska3553b302015-03-07 14:49:43 -0800175 * Issues a close on the connection.
176 */
Thomas Vachuskac0f757a2018-10-23 11:41:34 -0700177 void close() {
Simon Hunte05cae42015-07-23 17:35:24 -0700178 destroyHandlersAndOverlays();
Thomas Vachuskac0f757a2018-10-23 11:41:34 -0700179 if (isConnected()) {
180 getSession().close();
Thomas Vachuska3553b302015-03-07 14:49:43 -0800181 }
182 }
183
184 /**
185 * Indicates if this connection is idle.
186 *
187 * @return true if idle or closed
188 */
Thomas Vachuskac0f757a2018-10-23 11:41:34 -0700189 boolean isIdle() {
Simon Huntda580882015-05-12 20:58:18 -0700190 long quietFor = System.currentTimeMillis() - lastActive;
191 boolean idle = quietFor > MAX_AGE_MS;
Thomas Vachuskac0f757a2018-10-23 11:41:34 -0700192 if (idle || isNotConnected()) {
Simon Huntda580882015-05-12 20:58:18 -0700193 log.debug("IDLE (or closed) websocket [{} ms]", quietFor);
Thomas Vachuska3553b302015-03-07 14:49:43 -0800194 return true;
Thomas Vachuskac0f757a2018-10-23 11:41:34 -0700195
196 } else if (isConnected() && quietFor > PING_DELAY_MS) {
197 try {
198 getRemote().sendPing(PING);
199 lastActive = System.currentTimeMillis();
200 } catch (IOException e) {
201 log.warn("Unable to send ping message due to: ", e);
202 }
Thomas Vachuska3553b302015-03-07 14:49:43 -0800203 }
204 return false;
205 }
206
Thomas Vachuskac0f757a2018-10-23 11:41:34 -0700207 @Override
208 public void onWebSocketConnect(Session session) {
209 super.onWebSocketConnect(session);
Thomas Vachuskafc52fec2015-05-18 19:13:56 -0700210 try {
Thomas Vachuska92b016b2016-05-20 11:37:57 -0700211 topoSession.init();
Simon Hunte05cae42015-07-23 17:35:24 -0700212 createHandlersAndOverlays();
Simon Hunt7715e892016-04-12 19:55:32 -0700213 sendBootstrapData();
Simon Huntd6d3ad32017-06-21 15:27:06 -0700214 sendUberLionBundle();
Thomas Vachuskac0f757a2018-10-23 11:41:34 -0700215 lastActive = System.currentTimeMillis();
Simon Hunt7715e892016-04-12 19:55:32 -0700216 log.info("GUI client connected -- user <{}>", userName);
Thomas Vachuskafc52fec2015-05-18 19:13:56 -0700217
218 } catch (ServiceNotFoundException e) {
Brian O'Connor75deea62015-06-24 16:09:17 -0400219 log.warn("Unable to open GUI connection; services have been shut-down", e);
Thomas Vachuskac0f757a2018-10-23 11:41:34 -0700220 getSession().close();
Thomas Vachuskafc52fec2015-05-18 19:13:56 -0700221 }
Thomas Vachuska3553b302015-03-07 14:49:43 -0800222 }
223
Thomas Vachuskac0f757a2018-10-23 11:41:34 -0700224 @Override
225 public void onWebSocketClose(int closeCode, String reason) {
dvaddireb09fdf32017-07-04 11:42:53 +0530226 try {
Thomas Vachuska3ed75852018-01-17 11:53:21 -0800227 try {
228 tokenService().revokeToken(sessionToken);
229 log.info("Session token revoked");
230 } catch (ServiceNotFoundException e) {
231 log.error("Unable to reference UiTokenService");
232 }
233 sessionToken = null;
Simon Hunt1169c952017-06-05 11:20:11 -0700234
Thomas Vachuska3ed75852018-01-17 11:53:21 -0800235 topoSession.destroy();
236 destroyHandlersAndOverlays();
237 } catch (Exception e) {
238 log.warn("Unexpected error", e);
239 }
Thomas Vachuskac0f757a2018-10-23 11:41:34 -0700240 super.onWebSocketClose(closeCode, reason);
Simon Huntda580882015-05-12 20:58:18 -0700241 log.info("GUI client disconnected [close-code={}, message={}]",
Thomas Vachuskac0f757a2018-10-23 11:41:34 -0700242 closeCode, reason);
Thomas Vachuska3553b302015-03-07 14:49:43 -0800243 }
244
Thomas Vachuskac0f757a2018-10-23 11:41:34 -0700245 @Override
246 public void onWebSocketText(String data) {
Thomas Vachuska3553b302015-03-07 14:49:43 -0800247 lastActive = System.currentTimeMillis();
248 try {
249 ObjectNode message = (ObjectNode) mapper.reader().readTree(data);
Simon Hunt7715e892016-04-12 19:55:32 -0700250 String type = message.path(EVENT).asText(UNKNOWN);
Simon Hunt1169c952017-06-05 11:20:11 -0700251
Thomas Vachuskad3585742018-08-14 10:06:39 -0700252 if (sessionToken == null) {
253 authenticate(type, message);
Thomas Vachuska3553b302015-03-07 14:49:43 -0800254 } else {
Thomas Vachuskad3585742018-08-14 10:06:39 -0700255 UiMessageHandler handler = handlers.get(type);
256 if (handler != null) {
257 log.debug("RX message: {}", message);
258 handler.process(message);
259 } else {
260 log.warn("No GUI message handler for type {}", type);
261 }
Thomas Vachuska3553b302015-03-07 14:49:43 -0800262 }
Simon Hunt1169c952017-06-05 11:20:11 -0700263
Thomas Vachuskaaddf6342019-03-11 12:50:01 -0700264 } catch (Error | Exception e) {
Andrea Campanellabdeeda12019-08-02 16:12:05 +0200265 log.warn("Unable to parse GUI message {} due to", data, e);
Thomas Vachuska3553b302015-03-07 14:49:43 -0800266 log.debug("Boom!!!", e);
267 }
268 }
269
270 @Override
Thomas Vachuskac0f757a2018-10-23 11:41:34 -0700271 public void onWebSocketBinary(byte[] payload, int offset, int length) {
272 lastActive = System.currentTimeMillis();
273 log.warn("Binary messages are currently not supported");
274 }
275
276 @Override
Thomas Vachuska35fa3d42015-04-30 10:11:47 -0700277 public synchronized void sendMessage(ObjectNode message) {
Thomas Vachuska3553b302015-03-07 14:49:43 -0800278 try {
Thomas Vachuskac0f757a2018-10-23 11:41:34 -0700279 if (isConnected()) {
280 getRemote().sendString(message.toString());
Simon Huntd5b96732016-07-08 13:22:27 -0700281 log.debug("TX message: {}", message);
Thomas Vachuska3553b302015-03-07 14:49:43 -0800282 }
283 } catch (IOException e) {
284 log.warn("Unable to send message {} to GUI due to {}", message, e);
285 log.debug("Boom!!!", e);
286 }
287 }
288
Thomas Vachuskab6acc7b2015-03-11 09:11:21 -0700289 @Override
Simon Huntc5368182017-01-10 13:32:04 -0800290 public synchronized void sendMessage(String type, ObjectNode payload) {
Simon Huntd6d3ad32017-06-21 15:27:06 -0700291 ObjectNode message = objectNode();
Simon Hunt7715e892016-04-12 19:55:32 -0700292 message.put(EVENT, type);
Simon Huntd6d3ad32017-06-21 15:27:06 -0700293 message.set(PAYLOAD, payload != null ? payload : objectNode());
Thomas Vachuskab6acc7b2015-03-11 09:11:21 -0700294 sendMessage(message);
Thomas Vachuskab6acc7b2015-03-11 09:11:21 -0700295 }
296
Thomas Vachuska3553b302015-03-07 14:49:43 -0800297 // Creates new message handlers.
Simon Hunte05cae42015-07-23 17:35:24 -0700298 private synchronized void createHandlersAndOverlays() {
Simon Hunt7715e892016-04-12 19:55:32 -0700299 log.debug("Creating handlers and overlays...");
Thomas Vachuska3553b302015-03-07 14:49:43 -0800300 handlers = new HashMap<>();
Simon Hunte05cae42015-07-23 17:35:24 -0700301 overlayCache = new TopoOverlayCache();
Simon Hunt22c35df2017-04-26 17:28:42 -0700302 overlay2Cache = new Topo2OverlayCache();
Simon Hunte05cae42015-07-23 17:35:24 -0700303
Simon Hunt2d7cd6f2017-05-04 13:04:50 -0700304 Map<Class<?>, UiMessageHandler> handlerInstances = new HashMap<>();
Thomas Vachuska3553b302015-03-07 14:49:43 -0800305 UiExtensionService service = directory.get(UiExtensionService.class);
Simon Hunt879ce452017-08-10 23:32:00 -0700306 lionBundleMap = generateLionMap(service);
307
Thomas Vachuska329af532015-03-10 02:08:33 -0700308 service.getExtensions().forEach(ext -> {
309 UiMessageHandlerFactory factory = ext.messageHandlerFactory();
310 if (factory != null) {
311 factory.newHandlers().forEach(handler -> {
Thomas Vachuskac4178cc2015-12-10 11:43:32 -0800312 try {
313 handler.init(this, directory);
Simon Hunt879ce452017-08-10 23:32:00 -0700314 injectLionBundles(handler, lionBundleMap);
Thomas Vachuskac4178cc2015-12-10 11:43:32 -0800315 handler.messageTypes().forEach(type -> handlers.put(type, handler));
Simon Hunt2d7cd6f2017-05-04 13:04:50 -0700316 handlerInstances.put(handler.getClass(), handler);
Simon Hunte05cae42015-07-23 17:35:24 -0700317
Thomas Vachuskac4178cc2015-12-10 11:43:32 -0800318 } catch (Exception e) {
319 log.warn("Unable to setup handler {} due to", handler, e);
Simon Hunte05cae42015-07-23 17:35:24 -0700320 }
Thomas Vachuska329af532015-03-10 02:08:33 -0700321 });
322 }
Simon Hunt2d7cd6f2017-05-04 13:04:50 -0700323 registerOverlays(ext);
Thomas Vachuska329af532015-03-10 02:08:33 -0700324 });
Simon Hunt2d7cd6f2017-05-04 13:04:50 -0700325
326 handlerCrossConnects(handlerInstances);
327
Sean Condonadeb7162019-04-13 20:56:14 +0100328 overlay2Cache.switchOverlay(null, Traffic2Overlay.OVERLAY_ID);
329
330 log.debug("#handlers = {}, #overlays = Topo: {}, Topo2: {}",
331 handlers.size(), overlayCache.size(), overlay2Cache.size());
Simon Hunt879ce452017-08-10 23:32:00 -0700332 }
333
334 private Map<String, LionBundle> generateLionMap(UiExtensionService service) {
335 Map<String, LionBundle> bundles = new HashMap<>();
336 service.getExtensions().forEach(ext -> {
337 ext.lionBundles().forEach(lb -> bundles.put(lb.id(), lb));
338 });
339 return bundles;
340 }
341
342 private void injectLionBundles(UiMessageHandler handler,
343 Map<String, LionBundle> lionBundleMap) {
344 handler.requiredLionBundles().forEach(lbid -> {
345 LionBundle lb = lionBundleMap.get(lbid);
346 if (lb != null) {
347 handler.cacheLionBundle(lb);
348 } else {
349 log.warn("handler {}: Lion bundle {} non existent!",
350 handler.getClass().getName(), lbid);
351 }
352 });
Simon Hunt2d7cd6f2017-05-04 13:04:50 -0700353 }
354
Simon Hunt1169c952017-06-05 11:20:11 -0700355 private void authenticate(String type, ObjectNode message) {
356 if (!AUTHENTICATION.equals(type)) {
Thomas Vachuskabf5d1fe2018-04-25 15:49:00 -0400357 log.warn("WebSocket not authenticated: {}", message);
Thomas Vachuskad3585742018-08-14 10:06:39 -0700358 sendMessage(ERROR, notAuthorized(null));
Thomas Vachuskabf5d1fe2018-04-25 15:49:00 -0400359 close();
Simon Hunt1169c952017-06-05 11:20:11 -0700360 return;
361 }
362
363 String tokstr = message.path(PAYLOAD).path(TOKEN).asText(UNKNOWN);
364 UiSessionToken token = new UiSessionToken(tokstr);
365
366 if (tokenService().isTokenValid(token)) {
367 sessionToken = token;
368 log.info("Session token authenticated");
369 log.debug("WebSocket authenticated: {}", message);
370 } else {
371 log.warn("Invalid Authentication Token: {}", message);
372 sendMessage(ERROR, notAuthorized(token));
373 }
374 }
375
376 private ObjectNode notAuthorized(UiSessionToken token) {
Simon Huntd6d3ad32017-06-21 15:27:06 -0700377 return objectNode()
Simon Hunt1169c952017-06-05 11:20:11 -0700378 .put("message", "invalid authentication token")
Thomas Vachuskad3585742018-08-14 10:06:39 -0700379 .put("badToken", token != null ? token.toString() : "null");
Simon Hunt1169c952017-06-05 11:20:11 -0700380 }
381
Simon Hunt2d7cd6f2017-05-04 13:04:50 -0700382 private void registerOverlays(UiExtension ext) {
383 UiTopoOverlayFactory overlayFactory = ext.topoOverlayFactory();
384 if (overlayFactory != null) {
385 overlayFactory.newOverlays().forEach(overlayCache::add);
386 }
387
388 UiTopo2OverlayFactory overlay2Factory = ext.topo2OverlayFactory();
389 if (overlay2Factory != null) {
390 overlay2Factory.newOverlays().forEach(overlay2Cache::add);
391 }
392 }
393
394 private void handlerCrossConnects(Map<Class<?>, UiMessageHandler> handlers) {
395 TopologyViewMessageHandler topomh = (TopologyViewMessageHandler)
396 handlers.get(TopologyViewMessageHandler.class);
397 if (topomh != null) {
398 topomh.setOverlayCache(overlayCache);
399 }
400
401 Topo2ViewMessageHandler topo2mh = (Topo2ViewMessageHandler)
402 handlers.get(Topo2ViewMessageHandler.class);
403 if (topo2mh != null) {
404 topo2mh.setOverlayCache(overlay2Cache);
405
406 // We also need a link to Topo2Traffic
407 Topo2TrafficMessageHandler topo2traffic = (Topo2TrafficMessageHandler)
408 handlers.get(Topo2TrafficMessageHandler.class);
409 if (topo2traffic != null) {
410 topo2mh.setTrafficHandler(topo2traffic);
Sean Condonadeb7162019-04-13 20:56:14 +0100411 topo2traffic.setOverlayCache(overlay2Cache);
Simon Hunt2d7cd6f2017-05-04 13:04:50 -0700412 } else {
413 log.error("No topo2 traffic handler found");
414 }
415 }
Thomas Vachuska3553b302015-03-07 14:49:43 -0800416 }
417
418 // Destroys message handlers.
Simon Hunte05cae42015-07-23 17:35:24 -0700419 private synchronized void destroyHandlersAndOverlays() {
Simon Hunt7715e892016-04-12 19:55:32 -0700420 log.debug("Destroying handlers and overlays...");
Thomas Vachuska3553b302015-03-07 14:49:43 -0800421 handlers.forEach((type, handler) -> handler.destroy());
422 handlers.clear();
Simon Hunte05cae42015-07-23 17:35:24 -0700423
424 if (overlayCache != null) {
425 overlayCache.destroy();
426 overlayCache = null;
427 }
Simon Hunte6f64612017-04-28 00:01:48 -0700428 if (overlay2Cache != null) {
429 overlay2Cache.destroy();
430 overlay2Cache = null;
431 }
Thomas Vachuska3553b302015-03-07 14:49:43 -0800432 }
Thomas Vachuskab6acc7b2015-03-11 09:11:21 -0700433
Simon Hunt7715e892016-04-12 19:55:32 -0700434 // Sends initial information (username and cluster member information)
435 // to allow GUI to display logged-in user, and to be able to
436 // fail-over to an alternate cluster member if necessary.
437 private void sendBootstrapData() {
Thomas Vachuskab6acc7b2015-03-11 09:11:21 -0700438 ClusterService service = directory.get(ClusterService.class);
Simon Huntd6d3ad32017-06-21 15:27:06 -0700439 ArrayNode instances = arrayNode();
Thomas Vachuskab6acc7b2015-03-11 09:11:21 -0700440
441 for (ControllerNode node : service.getNodes()) {
pierventre55a5f392021-11-05 15:37:32 +0100442 IpAddress nodeIp = node.ip();
Simon Huntd6d3ad32017-06-21 15:27:06 -0700443 ObjectNode instance = objectNode()
Simon Hunt7715e892016-04-12 19:55:32 -0700444 .put(ID, node.id().toString())
pierventre55a5f392021-11-05 15:37:32 +0100445 .put(IP, nodeIp != null ? nodeIp.toString() : node.host())
Simon Hunt8add9ee2016-09-20 17:05:07 -0700446 .put(GlyphConstants.UI_ATTACHED,
Simon Hunt22c35df2017-04-26 17:28:42 -0700447 node.equals(service.getLocalNode()));
Thomas Vachuskab6acc7b2015-03-11 09:11:21 -0700448 instances.add(instance);
449 }
450
Laszlo Papp759f0d32018-03-05 13:24:30 +0000451 ArrayNode glyphInstances = arrayNode();
452 UiExtensionService uiExtensionService = directory.get(UiExtensionService.class);
453 for (UiGlyph glyph : uiExtensionService.getGlyphs()) {
454 ObjectNode glyphInstance = objectNode()
455 .put(GlyphConstants.ID, glyph.id())
456 .put(GlyphConstants.VIEWBOX, glyph.viewbox())
457 .put(GlyphConstants.PATH, glyph.path());
458 glyphInstances.add(glyphInstance);
459 }
460
Simon Huntd6d3ad32017-06-21 15:27:06 -0700461 ObjectNode payload = objectNode();
Simon Hunt7715e892016-04-12 19:55:32 -0700462 payload.set(CLUSTER_NODES, instances);
Laszlo Papp759f0d32018-03-05 13:24:30 +0000463 payload.set(GLYPHS, glyphInstances);
Simon Hunt7715e892016-04-12 19:55:32 -0700464 payload.put(USER, userName);
Laszlo Papp759f0d32018-03-05 13:24:30 +0000465
Simon Huntc5368182017-01-10 13:32:04 -0800466 sendMessage(BOOTSTRAP, payload);
Thomas Vachuskab6acc7b2015-03-11 09:11:21 -0700467 }
468
Simon Hunt1169c952017-06-05 11:20:11 -0700469 private UiTokenService tokenService() {
dvaddireb09fdf32017-07-04 11:42:53 +0530470 return directory.get(UiTokenService.class);
Simon Hunt1169c952017-06-05 11:20:11 -0700471 }
Simon Huntd6d3ad32017-06-21 15:27:06 -0700472
473 // sends the collated localization bundle data up to the client.
474 private void sendUberLionBundle() {
475 UiExtensionService service = directory.get(UiExtensionService.class);
476 ObjectNode lion = objectNode();
477
478 service.getExtensions().forEach(ext -> {
479 ext.lionBundles().forEach(lb -> {
480 ObjectNode lionMap = objectNode();
Simon Hunt879ce452017-08-10 23:32:00 -0700481 lb.getItems().forEach(item -> lionMap.put(item.key(), item.value()));
Simon Huntd6d3ad32017-06-21 15:27:06 -0700482 lion.set(lb.id(), lionMap);
483 });
484 });
485
486 ObjectNode payload = objectNode();
487 payload.set(LION, lion);
Simon Hunt1f4365d2017-06-21 17:25:09 -0700488 payload.put(LOCALE, Locale.getDefault().toString());
Simon Huntd6d3ad32017-06-21 15:27:06 -0700489 sendMessage(UBERLION, payload);
490 }
Thomas Vachuska3553b302015-03-07 14:49:43 -0800491}