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