blob: 609c28bae23b7b63c0e67f33f71422d33dfc56b1 [file] [log] [blame]
Simon Huntd2747a02015-04-30 22:41:16 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Simon Huntd2747a02015-04-30 22:41:16 -07003 *
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;
17
18import com.fasterxml.jackson.databind.ObjectMapper;
Simon Huntda580882015-05-12 20:58:18 -070019import com.fasterxml.jackson.databind.node.ArrayNode;
Simon Huntd2747a02015-04-30 22:41:16 -070020import com.fasterxml.jackson.databind.node.ObjectNode;
21import org.onlab.osgi.ServiceDirectory;
Simon Huntc54cd1b2015-05-11 13:43:44 -070022import org.slf4j.Logger;
23import org.slf4j.LoggerFactory;
Simon Huntd2747a02015-04-30 22:41:16 -070024
25import java.util.Collection;
26import java.util.Collections;
27import java.util.HashMap;
28import java.util.Map;
29import java.util.Set;
30
31import static com.google.common.base.Preconditions.checkArgument;
32import static com.google.common.base.Preconditions.checkNotNull;
33
34/**
Simon Huntda580882015-05-12 20:58:18 -070035 * Abstraction of an entity capable of processing JSON messages from the user
Simon Huntd2747a02015-04-30 22:41:16 -070036 * interface client.
37 * <p>
Simon Huntda580882015-05-12 20:58:18 -070038 * The message structure is:
Simon Huntd2747a02015-04-30 22:41:16 -070039 * </p>
40 * <pre>
41 * {
Simon Hunt8a0429a2017-01-06 16:52:47 -080042 * "event": "<em>event-type</em>",
Simon Huntd2747a02015-04-30 22:41:16 -070043 * "payload": {
44 * <em>arbitrary JSON object structure</em>
45 * }
46 * }
47 * </pre>
Simon Huntda580882015-05-12 20:58:18 -070048 * On {@link #init initialization} the handler will create and cache
49 * {@link RequestHandler} instances, each of which are bound to a particular
50 * <em>event-type</em>. On {@link #process arrival} of a new message,
51 * the <em>event-type</em> is determined, and the message dispatched to the
52 * corresponding <em>RequestHandler</em>'s
53 * {@link RequestHandler#process process} method.
Simon Huntd2747a02015-04-30 22:41:16 -070054 */
Simon Hunta0ddb022015-05-01 09:53:01 -070055public abstract class UiMessageHandler {
Simon Huntd2747a02015-04-30 22:41:16 -070056
Simon Huntc54cd1b2015-05-11 13:43:44 -070057 private final Logger log = LoggerFactory.getLogger(getClass());
Simon Huntd2747a02015-04-30 22:41:16 -070058 private final Map<String, RequestHandler> handlerMap = new HashMap<>();
Thomas Vachuska26be4f32016-03-31 01:10:27 -070059
Simon Huntda580882015-05-12 20:58:18 -070060 private final ObjectMapper mapper = new ObjectMapper();
Simon Huntd2747a02015-04-30 22:41:16 -070061
62 private UiConnection connection;
63 private ServiceDirectory directory;
64
Simon Huntd2747a02015-04-30 22:41:16 -070065
Simon Huntd2747a02015-04-30 22:41:16 -070066 /**
Simon Huntda580882015-05-12 20:58:18 -070067 * Subclasses must create and return the collection of request handlers
68 * for the message types they handle.
69 * <p>
70 * Note that request handlers should be stateless. When we are
71 * {@link #destroy destroyed}, we will simply drop our references to them
72 * and allow them to be garbage collected.
Simon Huntd2747a02015-04-30 22:41:16 -070073 *
74 * @return the message handler instances
75 */
Simon Huntda580882015-05-12 20:58:18 -070076 protected abstract Collection<RequestHandler> createRequestHandlers();
Simon Huntd2747a02015-04-30 22:41:16 -070077
78 /**
79 * Returns the set of message types which this handler is capable of
80 * processing.
81 *
82 * @return set of message types
83 */
84 public Set<String> messageTypes() {
85 return Collections.unmodifiableSet(handlerMap.keySet());
86 }
87
88 /**
89 * Processes a JSON message from the user interface client.
90 *
91 * @param message JSON message
92 */
93 public void process(ObjectNode message) {
94 String type = JsonUtils.eventType(message);
Simon Huntd2747a02015-04-30 22:41:16 -070095 ObjectNode payload = JsonUtils.payload(message);
Simon Hunt8a0429a2017-01-06 16:52:47 -080096 exec(type, payload);
Simon Huntd2747a02015-04-30 22:41:16 -070097 }
98
99 /**
100 * Finds the appropriate handler and executes the process method.
101 *
102 * @param eventType event type
Simon Huntd2747a02015-04-30 22:41:16 -0700103 * @param payload message payload
104 */
Simon Hunt8a0429a2017-01-06 16:52:47 -0800105 void exec(String eventType, ObjectNode payload) {
Simon Huntda580882015-05-12 20:58:18 -0700106 RequestHandler requestHandler = handlerMap.get(eventType);
107 if (requestHandler != null) {
Simon Hunt8a0429a2017-01-06 16:52:47 -0800108 requestHandler.process(payload);
Simon Hunte05cae42015-07-23 17:35:24 -0700109 } else {
110 log.warn("no request handler for event type {}", eventType);
Simon Hunta0ddb022015-05-01 09:53:01 -0700111 }
112 }
113
Simon Huntd2747a02015-04-30 22:41:16 -0700114 /**
115 * Initializes the handler with the user interface connection and
116 * service directory context.
117 *
118 * @param connection user interface connection
119 * @param directory service directory
120 */
121 public void init(UiConnection connection, ServiceDirectory directory) {
122 this.connection = connection;
123 this.directory = directory;
Simon Huntda580882015-05-12 20:58:18 -0700124
125 Collection<RequestHandler> handlers = createRequestHandlers();
126 checkNotNull(handlers, "Handlers cannot be null");
127 checkArgument(!handlers.isEmpty(), "Handlers cannot be empty");
128
129 for (RequestHandler h : handlers) {
130 h.setParent(this);
131 handlerMap.put(h.eventType(), h);
132 }
Simon Huntd2747a02015-04-30 22:41:16 -0700133 }
134
135 /**
136 * Destroys the message handler context.
137 */
138 public void destroy() {
139 this.connection = null;
140 this.directory = null;
Simon Huntda580882015-05-12 20:58:18 -0700141 handlerMap.clear();
Simon Huntd2747a02015-04-30 22:41:16 -0700142 }
143
144 /**
145 * Returns the user interface connection with which this handler was primed.
146 *
147 * @return user interface connection
148 */
149 public UiConnection connection() {
150 return connection;
151 }
152
153 /**
Simon Hunt8add9ee2016-09-20 17:05:07 -0700154 * Returns the service directory with which this handler was primed.
Simon Huntd2747a02015-04-30 22:41:16 -0700155 *
Simon Hunt8add9ee2016-09-20 17:05:07 -0700156 * @return service directory
Simon Huntd2747a02015-04-30 22:41:16 -0700157 */
158 public ServiceDirectory directory() {
159 return directory;
160 }
161
162 /**
Simon Hunt8add9ee2016-09-20 17:05:07 -0700163 * Returns an implementation of the specified service class.
Simon Huntd2747a02015-04-30 22:41:16 -0700164 *
165 * @param serviceClass service class
166 * @param <T> type of service
167 * @return implementation class
168 * @throws org.onlab.osgi.ServiceNotFoundException if no implementation found
169 */
170 protected <T> T get(Class<T> serviceClass) {
171 return directory.get(serviceClass);
172 }
173
Simon Huntda580882015-05-12 20:58:18 -0700174 /**
175 * Returns a freshly minted object node.
176 *
177 * @return new object node
178 */
179 protected ObjectNode objectNode() {
180 return mapper.createObjectNode();
181 }
182
183 /**
184 * Returns a freshly minted array node.
185 *
186 * @return new array node
187 */
188 protected ArrayNode arrayNode() {
189 return mapper.createArrayNode();
190 }
Simon Hunt52560662015-08-27 22:46:44 -0700191
192 /**
193 * Sends the specified data to the client.
194 * It is expected that the data is in the prescribed JSON format for
195 * events to the client.
196 *
197 * @param data data to be sent
198 */
199 protected synchronized void sendMessage(ObjectNode data) {
200 UiConnection connection = connection();
201 if (connection != null) {
202 connection.sendMessage(data);
203 }
204 }
Simon Huntd2747a02015-04-30 22:41:16 -0700205}