blob: b3782a25b6d2c11ce9598e49160543e452fb657d [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 Huntc54cd1b2015-05-11 13:43:44 -0700110 log.debug("process {} event...", eventType);
Simon Huntda580882015-05-12 20:58:18 -0700111 requestHandler.process(sid, payload);
Simon Hunta0ddb022015-05-01 09:53:01 -0700112 }
113 }
114
Simon Huntd2747a02015-04-30 22:41:16 -0700115 /**
116 * Initializes the handler with the user interface connection and
117 * service directory context.
118 *
119 * @param connection user interface connection
120 * @param directory service directory
121 */
122 public void init(UiConnection connection, ServiceDirectory directory) {
123 this.connection = connection;
124 this.directory = directory;
Simon Huntda580882015-05-12 20:58:18 -0700125
126 Collection<RequestHandler> handlers = createRequestHandlers();
127 checkNotNull(handlers, "Handlers cannot be null");
128 checkArgument(!handlers.isEmpty(), "Handlers cannot be empty");
129
130 for (RequestHandler h : handlers) {
131 h.setParent(this);
132 handlerMap.put(h.eventType(), h);
133 }
Simon Huntd2747a02015-04-30 22:41:16 -0700134 }
135
136 /**
137 * Destroys the message handler context.
138 */
139 public void destroy() {
140 this.connection = null;
141 this.directory = null;
Simon Huntda580882015-05-12 20:58:18 -0700142 handlerMap.clear();
Simon Huntd2747a02015-04-30 22:41:16 -0700143 }
144
145 /**
146 * Returns the user interface connection with which this handler was primed.
147 *
148 * @return user interface connection
149 */
150 public UiConnection connection() {
151 return connection;
152 }
153
154 /**
155 * Returns the user interface connection with which this handler was primed.
156 *
157 * @return user interface connection
158 */
159 public ServiceDirectory directory() {
160 return directory;
161 }
162
163 /**
164 * Returns implementation of the specified service class.
165 *
166 * @param serviceClass service class
167 * @param <T> type of service
168 * @return implementation class
169 * @throws org.onlab.osgi.ServiceNotFoundException if no implementation found
170 */
171 protected <T> T get(Class<T> serviceClass) {
172 return directory.get(serviceClass);
173 }
174
Simon Huntda580882015-05-12 20:58:18 -0700175 /**
176 * Returns a freshly minted object node.
177 *
178 * @return new object node
179 */
180 protected ObjectNode objectNode() {
181 return mapper.createObjectNode();
182 }
183
184 /**
185 * Returns a freshly minted array node.
186 *
187 * @return new array node
188 */
189 protected ArrayNode arrayNode() {
190 return mapper.createArrayNode();
191 }
Simon Huntd2747a02015-04-30 22:41:16 -0700192}