blob: 9b11b42d75a21725c8f126bf5f92abefa785e767 [file] [log] [blame]
Thomas Vachuska3553b302015-03-07 14:49:43 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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;
19import com.fasterxml.jackson.databind.node.ObjectNode;
20import org.eclipse.jetty.websocket.WebSocket;
21import org.onlab.osgi.ServiceDirectory;
22import org.onosproject.ui.UiConnection;
23import org.onosproject.ui.UiExtensionService;
24import org.onosproject.ui.UiMessageHandler;
25import org.slf4j.Logger;
26import org.slf4j.LoggerFactory;
27
28import java.io.IOException;
29import java.util.HashMap;
30import java.util.Map;
31
32/**
33 * Web socket capable of interacting with the GUI.
34 */
35public class UiWebSocket
36 implements UiConnection, WebSocket.OnTextMessage, WebSocket.OnControl {
37
38 private static final Logger log = LoggerFactory.getLogger(UiWebSocket.class);
39
40 private static final long MAX_AGE_MS = 15000;
41
42 private static final byte PING = 0x9;
43 private static final byte PONG = 0xA;
44 private static final byte[] PING_DATA = new byte[]{(byte) 0xde, (byte) 0xad};
45
46 private final ServiceDirectory directory;
47
48 private Connection connection;
49 private FrameConnection control;
50
51 private final ObjectMapper mapper = new ObjectMapper();
52
53 private long lastActive = System.currentTimeMillis();
54
55 private Map<String, UiMessageHandler> handlers;
56
57 /**
58 * Creates a new web-socket for serving data to GUI.
59 *
60 * @param directory service directory
61 */
62 public UiWebSocket(ServiceDirectory directory) {
63 this.directory = directory;
64 }
65
66 /**
67 * Issues a close on the connection.
68 */
69 synchronized void close() {
70 destroyHandlers();
71 if (connection.isOpen()) {
72 connection.close();
73 }
74 }
75
76 /**
77 * Indicates if this connection is idle.
78 *
79 * @return true if idle or closed
80 */
81 synchronized boolean isIdle() {
82 boolean idle = (System.currentTimeMillis() - lastActive) > MAX_AGE_MS;
83 if (idle || (connection != null && !connection.isOpen())) {
84 return true;
85 } else if (connection != null) {
86 try {
87 control.sendControl(PING, PING_DATA, 0, PING_DATA.length);
88 } catch (IOException e) {
89 log.warn("Unable to send ping message due to: ", e);
90 }
91 }
92 return false;
93 }
94
95 @Override
96 public void onOpen(Connection connection) {
97 log.info("GUI client connected");
98 this.connection = connection;
99 this.control = (FrameConnection) connection;
100 createHandlers();
101 }
102
103 @Override
104 public synchronized void onClose(int closeCode, String message) {
105 destroyHandlers();
106 log.info("GUI client disconnected");
107 }
108
109 @Override
110 public boolean onControl(byte controlCode, byte[] data, int offset, int length) {
111 lastActive = System.currentTimeMillis();
112 return true;
113 }
114
115 @Override
116 public void onMessage(String data) {
117 lastActive = System.currentTimeMillis();
118 try {
119 ObjectNode message = (ObjectNode) mapper.reader().readTree(data);
120 String type = message.path("type").asText("unknown");
121 UiMessageHandler handler = handlers.get(type);
122 if (handler != null) {
123 handler.process(message);
124 } else {
125 log.warn("No GUI message handler for type {}", type);
126 }
127 } catch (Exception e) {
128 log.warn("Unable to parse GUI message {} due to {}", data, e);
129 log.debug("Boom!!!", e);
130 }
131 }
132
133 @Override
134 public void sendMessage(ObjectNode message) {
135 try {
136 if (connection.isOpen()) {
137 connection.sendMessage(message.toString());
138 }
139 } catch (IOException e) {
140 log.warn("Unable to send message {} to GUI due to {}", message, e);
141 log.debug("Boom!!!", e);
142 }
143 }
144
145 // Creates new message handlers.
146 private void createHandlers() {
147 handlers = new HashMap<>();
148 UiExtensionService service = directory.get(UiExtensionService.class);
149 service.getExtensions().forEach(ext -> ext.messageHandlerFactory().newHandlers().forEach(handler -> {
150 handler.init(this, directory);
151 handler.messageTypes().forEach(type -> handlers.put(type, handler));
152 }));
153 }
154
155 // Destroys message handlers.
156 private synchronized void destroyHandlers() {
157 handlers.forEach((type, handler) -> handler.destroy());
158 handlers.clear();
159 }
160}
161