blob: 9b9a406c7933acaf3e423d09904ac60074945f10 [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<>();
Simon Huntda580882015-05-12 20:58:18 -070059 private final ObjectMapper mapper = new ObjectMapper();
Simon Huntd2747a02015-04-30 22:41:16 -070060
61 private UiConnection connection;
62 private ServiceDirectory directory;
63
Simon Huntd2747a02015-04-30 22:41:16 -070064
Simon Huntd2747a02015-04-30 22:41:16 -070065 /**
Simon Huntda580882015-05-12 20:58:18 -070066 * Subclasses must create and return the collection of request handlers
67 * for the message types they handle.
68 * <p>
69 * Note that request handlers should be stateless. When we are
70 * {@link #destroy destroyed}, we will simply drop our references to them
71 * and allow them to be garbage collected.
Simon Huntd2747a02015-04-30 22:41:16 -070072 *
73 * @return the message handler instances
74 */
Simon Huntda580882015-05-12 20:58:18 -070075 protected abstract Collection<RequestHandler> createRequestHandlers();
Simon Huntd2747a02015-04-30 22:41:16 -070076
77 /**
78 * Returns the set of message types which this handler is capable of
79 * processing.
80 *
81 * @return set of message types
82 */
83 public Set<String> messageTypes() {
84 return Collections.unmodifiableSet(handlerMap.keySet());
85 }
86
87 /**
88 * Processes a JSON message from the user interface client.
89 *
90 * @param message JSON message
91 */
92 public void process(ObjectNode message) {
93 String type = JsonUtils.eventType(message);
Simon Huntd2747a02015-04-30 22:41:16 -070094 ObjectNode payload = JsonUtils.payload(message);
Simon Huntda580882015-05-12 20:58:18 -070095 // TODO: remove sid
96 exec(type, 0, 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
103 * @param sid sequence identifier
104 * @param payload message payload
105 */
Simon Huntc54cd1b2015-05-11 13:43:44 -0700106 // TODO: remove sid from signature
Simon Huntd2747a02015-04-30 22:41:16 -0700107 void exec(String eventType, long sid, ObjectNode payload) {
Simon Huntda580882015-05-12 20:58:18 -0700108 RequestHandler requestHandler = handlerMap.get(eventType);
109 if (requestHandler != null) {
Simon Huntda580882015-05-12 20:58:18 -0700110 requestHandler.process(sid, payload);
Simon Hunte05cae42015-07-23 17:35:24 -0700111 } else {
112 log.warn("no request handler for event type {}", eventType);
Simon Hunta0ddb022015-05-01 09:53:01 -0700113 }
114 }
115
Simon Huntd2747a02015-04-30 22:41:16 -0700116 /**
117 * Initializes the handler with the user interface connection and
118 * service directory context.
119 *
120 * @param connection user interface connection
121 * @param directory service directory
122 */
123 public void init(UiConnection connection, ServiceDirectory directory) {
124 this.connection = connection;
125 this.directory = directory;
Simon Huntda580882015-05-12 20:58:18 -0700126
127 Collection<RequestHandler> handlers = createRequestHandlers();
128 checkNotNull(handlers, "Handlers cannot be null");
129 checkArgument(!handlers.isEmpty(), "Handlers cannot be empty");
130
131 for (RequestHandler h : handlers) {
132 h.setParent(this);
133 handlerMap.put(h.eventType(), h);
134 }
Simon Huntd2747a02015-04-30 22:41:16 -0700135 }
136
137 /**
138 * Destroys the message handler context.
139 */
140 public void destroy() {
141 this.connection = null;
142 this.directory = null;
Simon Huntda580882015-05-12 20:58:18 -0700143 handlerMap.clear();
Simon Huntd2747a02015-04-30 22:41:16 -0700144 }
145
146 /**
147 * Returns the user interface connection with which this handler was primed.
148 *
149 * @return user interface connection
150 */
151 public UiConnection connection() {
152 return connection;
153 }
154
155 /**
156 * Returns the user interface connection with which this handler was primed.
157 *
158 * @return user interface connection
159 */
160 public ServiceDirectory directory() {
161 return directory;
162 }
163
164 /**
165 * Returns implementation of the specified service class.
166 *
167 * @param serviceClass service class
168 * @param <T> type of service
169 * @return implementation class
170 * @throws org.onlab.osgi.ServiceNotFoundException if no implementation found
171 */
172 protected <T> T get(Class<T> serviceClass) {
173 return directory.get(serviceClass);
174 }
175
Simon Huntda580882015-05-12 20:58:18 -0700176 /**
177 * Returns a freshly minted object node.
178 *
179 * @return new object node
180 */
181 protected ObjectNode objectNode() {
182 return mapper.createObjectNode();
183 }
184
185 /**
186 * Returns a freshly minted array node.
187 *
188 * @return new array node
189 */
190 protected ArrayNode arrayNode() {
191 return mapper.createArrayNode();
192 }
Simon Hunt52560662015-08-27 22:46:44 -0700193
194 /**
195 * Sends the specified data to the client.
196 * It is expected that the data is in the prescribed JSON format for
197 * events to the client.
198 *
199 * @param data data to be sent
200 */
201 protected synchronized void sendMessage(ObjectNode data) {
202 UiConnection connection = connection();
203 if (connection != null) {
204 connection.sendMessage(data);
205 }
206 }
Simon Huntd2747a02015-04-30 22:41:16 -0700207}