All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.onosproject.ui.UiMessageHandler Maven / Gradle / Ivy

There is a newer version: 2.7.0
Show newest version
/*
 * Copyright 2015-present Open Networking Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.onosproject.ui;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onlab.osgi.ServiceDirectory;
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.CodecService;
import org.onosproject.codec.JsonCodec;
import org.onosproject.ui.lion.LionBundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Abstraction of an entity capable of processing JSON messages from the user
 * interface client.
 * 

* The message structure is: *

*
 * {
 *     "event": "event-type",
 *     "payload": {
 *         arbitrary JSON object structure
 *     }
 * }
 * 
* On {@link #init initialization} the handler will create and cache * {@link RequestHandler} instances, each of which are bound to a particular * event-type. On {@link #process arrival} of a new message, * the event-type is determined, and the message dispatched to the * corresponding RequestHandler's * {@link RequestHandler#process process} method. *

* For convenience the implementation includes methods to obtain JSON * generating objects (mapper, objectNode, arrayNode) as well as a * JsonCodecContext for preparing and digesting messages to the UI * client. */ public abstract class UiMessageHandler { private final Logger log = LoggerFactory.getLogger(getClass()); private final Map handlerMap = new HashMap<>(); private final Map cachedLionBundles = new HashMap<>(); private final ObjectMapper mapper = new ObjectMapper(); private UiConnection connection; private ServiceDirectory directory; private MessageCodecContext codecContext; /** * Subclasses must create and return the collection of request handlers * for the message types they handle. *

* Note that request handlers should be stateless. When we are * {@link #destroy destroyed}, we will simply drop our references to them * and allow them to be garbage collected. * * @return the message handler instances */ protected abstract Collection createRequestHandlers(); /** * Returns the set of message types which this handler is capable of * processing. * * @return set of message types */ public Set messageTypes() { return Collections.unmodifiableSet(handlerMap.keySet()); } /** * Processes a JSON message from the user interface client. * * @param message JSON message */ public void process(ObjectNode message) { String type = JsonUtils.eventType(message); ObjectNode payload = JsonUtils.payload(message); exec(type, payload); } /** * Finds the appropriate handler and executes the process method. * * @param eventType event type * @param payload message payload */ void exec(String eventType, ObjectNode payload) { RequestHandler requestHandler = handlerMap.get(eventType); if (requestHandler != null) { requestHandler.process(payload); } else { log.warn("no request handler for event type {}", eventType); } } /** * Initializes the handler with the user interface connection and * service directory context. * * @param connection user interface connection * @param directory service directory */ public void init(UiConnection connection, ServiceDirectory directory) { this.connection = connection; this.directory = directory; Collection handlers = createRequestHandlers(); checkNotNull(handlers, "Handlers cannot be null"); checkArgument(!handlers.isEmpty(), "Handlers cannot be empty"); for (RequestHandler h : handlers) { h.setParent(this); handlerMap.put(h.eventType(), h); } } /** * Destroys the message handler context. */ public void destroy() { this.connection = null; this.directory = null; handlerMap.clear(); } /** * Returns the user interface connection with which this handler was primed. * * @return user interface connection */ public UiConnection connection() { return connection; } /** * Returns the service directory with which this handler was primed. * * @return service directory */ public ServiceDirectory directory() { return directory; } /** * Returns an implementation of the specified service class. * * @param serviceClass service class * @param type of service * @return implementation class * @throws org.onlab.osgi.ServiceNotFoundException if no implementation found */ protected T get(Class serviceClass) { return directory.get(serviceClass); } /** * Returns the set of identifiers for localization bundles that the * message handler would like injected into itself, so that it can use * those bundles in composing localized data to ship to the client. *

* This default implementation returns an empty set. *

* Subclasses that wish to have localization bundles injected should * override this method and return the set of bundle identifiers. * * @return the set of identifiers of required localization bundles */ public Set requiredLionBundles() { return Collections.emptySet(); } /** * Invoked during initialization to cache any requested localization * bundles in the handler's context, so that it may subsequently look * up localization strings when composing data for the client. * * @param bundle the bundle to cache */ public void cacheLionBundle(LionBundle bundle) { cachedLionBundles.put(bundle.id(), bundle); } /** * Returns the localization bundle with the given identifier, if we * requested to have it cached during initialization; null otherwise. * * @param id the lion bundle identifier * @return the associated lion bundle */ protected LionBundle getLionBundle(String id) { return cachedLionBundles.get(id); } /** * Returns a freshly minted object node. * * @return new object node */ protected ObjectNode objectNode() { return mapper.createObjectNode(); } /** * Returns a freshly minted array node. * * @return new array node */ protected ArrayNode arrayNode() { return mapper.createArrayNode(); } /** * Sends the specified data to the client. * It is expected that the data is in the prescribed JSON format for * events to the client. * * @param data data to be sent */ protected synchronized void sendMessage(ObjectNode data) { UiConnection connection = connection(); if (connection != null) { connection.sendMessage(data); } } /** * Obtain a CodecContext to be used in encoding and decoding objects * that have a registered JsonCodec for their class. This method * instantiates a private inner class which is returned on * subsequent calls. * * @return a CodecContext. */ protected CodecContext getJsonCodecContext() { if (codecContext != null) { return codecContext; } codecContext = new MessageCodecContext(); return codecContext; } private class MessageCodecContext implements CodecContext { CodecService cs = get(CodecService.class); @Override public ObjectMapper mapper() { return mapper; } @Override public JsonCodec codec(Class entityClass) { return cs.getCodec(entityClass); } @Override public T getService(Class serviceClass) { return get(serviceClass); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy