blob: 7f3117d67b53ea3955081b911133685446ae537f [file] [log] [blame]
Simon Huntd2747a02015-04-30 22:41:16 -07001/*
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;
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 * {
42 * "type": "<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 Huntda580882015-05-12 20:58:18 -070096 // TODO: remove sid
97 exec(type, 0, payload);
Simon Huntd2747a02015-04-30 22:41:16 -070098 }
99
100 /**
101 * Finds the appropriate handler and executes the process method.
102 *
103 * @param eventType event type
104 * @param sid sequence identifier
105 * @param payload message payload
106 */
Simon Huntc54cd1b2015-05-11 13:43:44 -0700107 // TODO: remove sid from signature
Simon Huntd2747a02015-04-30 22:41:16 -0700108 void exec(String eventType, long sid, ObjectNode payload) {
Simon Huntda580882015-05-12 20:58:18 -0700109 RequestHandler requestHandler = handlerMap.get(eventType);
110 if (requestHandler != null) {
Simon Huntda580882015-05-12 20:58:18 -0700111 requestHandler.process(sid, payload);
Simon Hunte05cae42015-07-23 17:35:24 -0700112 } else {
113 log.warn("no request handler for event type {}", eventType);
Simon Hunta0ddb022015-05-01 09:53:01 -0700114 }
115 }
116
Simon Huntd2747a02015-04-30 22:41:16 -0700117 /**
118 * Initializes the handler with the user interface connection and
119 * service directory context.
120 *
121 * @param connection user interface connection
122 * @param directory service directory
123 */
124 public void init(UiConnection connection, ServiceDirectory directory) {
125 this.connection = connection;
126 this.directory = directory;
Simon Huntda580882015-05-12 20:58:18 -0700127
128 Collection<RequestHandler> handlers = createRequestHandlers();
129 checkNotNull(handlers, "Handlers cannot be null");
130 checkArgument(!handlers.isEmpty(), "Handlers cannot be empty");
131
132 for (RequestHandler h : handlers) {
133 h.setParent(this);
134 handlerMap.put(h.eventType(), h);
135 }
Simon Huntd2747a02015-04-30 22:41:16 -0700136 }
137
138 /**
139 * Destroys the message handler context.
140 */
141 public void destroy() {
142 this.connection = null;
143 this.directory = null;
Simon Huntda580882015-05-12 20:58:18 -0700144 handlerMap.clear();
Simon Huntd2747a02015-04-30 22:41:16 -0700145 }
146
147 /**
148 * Returns the user interface connection with which this handler was primed.
149 *
150 * @return user interface connection
151 */
152 public UiConnection connection() {
153 return connection;
154 }
155
156 /**
157 * Returns the user interface connection with which this handler was primed.
158 *
159 * @return user interface connection
160 */
161 public ServiceDirectory directory() {
162 return directory;
163 }
164
165 /**
166 * Returns implementation of the specified service class.
167 *
168 * @param serviceClass service class
169 * @param <T> type of service
170 * @return implementation class
171 * @throws org.onlab.osgi.ServiceNotFoundException if no implementation found
172 */
173 protected <T> T get(Class<T> serviceClass) {
174 return directory.get(serviceClass);
175 }
176
Simon Huntda580882015-05-12 20:58:18 -0700177 /**
178 * Returns a freshly minted object node.
179 *
180 * @return new object node
181 */
182 protected ObjectNode objectNode() {
183 return mapper.createObjectNode();
184 }
185
186 /**
187 * Returns a freshly minted array node.
188 *
189 * @return new array node
190 */
191 protected ArrayNode arrayNode() {
192 return mapper.createArrayNode();
193 }
Simon Hunt52560662015-08-27 22:46:44 -0700194
195 /**
196 * Sends the specified data to the client.
197 * It is expected that the data is in the prescribed JSON format for
198 * events to the client.
199 *
200 * @param data data to be sent
201 */
202 protected synchronized void sendMessage(ObjectNode data) {
203 UiConnection connection = connection();
204 if (connection != null) {
205 connection.sendMessage(data);
206 }
207 }
Simon Huntd2747a02015-04-30 22:41:16 -0700208}