Java tutorial
/** * Copyright 2016 IBM Corp. All Rights Reserved. * * 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 com.ibm.watson.self.topics; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Request.Builder; import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; import okhttp3.ws.WebSocket; import okhttp3.ws.WebSocketCall; import okhttp3.ws.WebSocketListener; import okio.Buffer; /** * A client to make WebSocket connections to the TopicManager. This * enables the user to access publish/subscribe events on the instance */ public class TopicClient implements WebSocketListener { /* Logging */ private static Logger logger = LogManager.getLogger(TopicClient.class.getName()); /* Variables */ private WebSocket socket; private boolean socketOpen; private OkHttpClient client; private final Map<String, IEvent> subscriptionMap = new HashMap<String, IEvent>(); private static TopicClient instance = null; private String selfId; private String token; private String host; private String port; private WebSocketListener listener; private WebSocketCall call; private boolean disconnected = false; public TopicClient() { this.client = configureHttpClient(); this.socketOpen = false; this.listener = this; } /** * Sometimes the TopicClient needs to be re-instantiated */ public static TopicClient getInstance() { if (instance == null) { instance = new TopicClient(); } return instance; } private OkHttpClient configureHttpClient() { logger.entry(); OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.connectTimeout(60, TimeUnit.SECONDS); builder.writeTimeout(60, TimeUnit.SECONDS); builder.readTimeout(90, TimeUnit.SECONDS); return logger.exit(builder.build()); } /** * Connect to WebSocket server` * @param host - ip address * @param port - port number * @param selfId - unique self id * @param token - bearer token for authentication * @return if connection has been established or not * @throws IOException * @throws DeploymentException */ public boolean connect(String host, String port) { logger.entry(); this.host = host; this.port = port; Builder builder = new Request.Builder() .url(TopicConstants.WS + this.host + TopicConstants.COLON + this.port + TopicConstants.STREAM); builder.addHeader(TopicConstants.SELF_ID, this.selfId); builder.addHeader(TopicConstants.TOKEN, this.token); this.call = WebSocketCall.create(this.client, builder.build()); this.call.enqueue(this.listener); return logger.exit(true); } /** * Send off the message via the WebSocket connection * @param message: message in the form of a json */ public void sendMessage(JsonObject message) { logger.entry(); try { if (this.socketOpen) { message.addProperty(TopicConstants.ORIGIN, this.selfId + TopicConstants.ROOT); socket.sendMessage(RequestBody.create(WebSocket.TEXT, message.toString())); } else logger.info("Not Connected!"); } catch (IOException e) { logger.error(e.getMessage()); } logger.exit(); } private void sendMessage(JsonObject wrapperObject, byte[] data) { logger.entry(); wrapperObject.addProperty(TopicConstants.DATA, data.length); wrapperObject.addProperty(TopicConstants.ORIGIN, this.selfId + TopicConstants.ROOT); try { byte[] header = wrapperObject.toString().getBytes(TopicConstants.UTF8); byte[] frame = new byte[header.length + data.length + 1]; System.arraycopy(header, 0, frame, 0, header.length); System.arraycopy(data, 0, frame, header.length + 1, data.length); if (this.socketOpen) { socket.sendMessage(RequestBody.create(WebSocket.BINARY, frame)); } else logger.info("Not Connected!"); } catch (Exception e) { logger.error(e.getMessage()); } logger.exit(); } /** * Publish data to the topic * @param path: the path * @param data: data in the form of key/ val pairs * @param persisted: true if data needs to be persisted */ public void publish(String path, String data, boolean persisted) { logger.entry(); JsonObject wrapperObject = new JsonObject(); JsonArray pathArray = new JsonArray(); pathArray.add(new JsonPrimitive(path)); wrapperObject.add(TopicConstants.TARGETS, pathArray); wrapperObject.addProperty(TopicConstants.MSG, TopicConstants.PUBLISH_AT); wrapperObject.addProperty(TopicConstants.DATA, data); wrapperObject.addProperty(TopicConstants.BINARY, false); wrapperObject.addProperty(TopicConstants.PERSISTED, persisted); this.sendMessage(wrapperObject); logger.exit(); } /** * Publish binary data to the topic * @param path: the path * @param data: binary data * @param persisted: true if data needs to be persisted */ public void publish(String path, byte[] data, boolean persisted) { logger.entry(); JsonObject wrapperObject = new JsonObject(); JsonArray pathArray = new JsonArray(); pathArray.add(new JsonPrimitive(path)); wrapperObject.add(TopicConstants.TARGETS, pathArray); wrapperObject.addProperty(TopicConstants.MSG, TopicConstants.PUBLISH_AT); wrapperObject.addProperty(TopicConstants.BINARY, true); wrapperObject.addProperty(TopicConstants.PERSISTED, persisted); this.sendMessage(wrapperObject, data); logger.exit(); } /** * Subscribe to a topic if possible * @param path: the path * @param event: the event to subscribe to */ public void subscribe(String path, IEvent event) { logger.entry(); if (!subscriptionMap.containsKey(path)) { subscriptionMap.put(path, event); } JsonObject wrapperObject = new JsonObject(); JsonArray wrapperArray = new JsonArray(); wrapperArray.add(new JsonPrimitive(path)); wrapperObject.add(TopicConstants.TARGETS, wrapperArray); wrapperObject.addProperty(TopicConstants.MSG, TopicConstants.SUBSCRIBE); this.sendMessage(wrapperObject); logger.exit(); } /** * Unsubscribe from a given topic * @param path: the path * @param event: the event to unsubscribe from * @return: true if successfully unscubscribed */ public boolean unsubscribe(String path, IEvent event) { logger.entry(); if (subscriptionMap.containsKey(path)) { subscriptionMap.remove(path); JsonObject wrapperObject = new JsonObject(); JsonArray wrapperArray = new JsonArray(); wrapperArray.add(new JsonPrimitive(path)); wrapperObject.add(TopicConstants.TARGETS, wrapperArray); wrapperObject.addProperty(TopicConstants.MSG, TopicConstants.UNSUBSCRIBE); this.sendMessage(wrapperObject); return logger.exit(true); } return logger.exit(false); } /** * Check if socket is open. * @return */ public boolean isConnected() { return this.socketOpen; } /** * Add authorization header to HTTP handshake request. Socket must not be open. * @param authorization */ public void setHeaders(String selfId, String token) { logger.entry(); this.selfId = selfId; this.token = token; logger.exit(); } public void onClose(int arg0, String arg1) { logger.entry(); this.socketOpen = false; logger.info("closing websocket"); onReconnect(); logger.exit(); } public void onFailure(IOException arg0, Response arg1) { logger.entry(); this.socketOpen = false; onReconnect(); logger.exit(); } private void onReconnect() { if (this.socket != null) { try { this.disconnected = true; for (String topic : subscriptionMap.keySet()) { subscriptionMap.get(topic).onDisconnect(); } logger.info("Client reconnecting in 5 seconds..."); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } connect(this.host, this.port); } } /** * Handle incoming messages * @param message: the incoming message */ public void onMessage(ResponseBody message) throws IOException { logger.entry(); String response = message.string(); JsonParser parser = new JsonParser(); JsonObject wrapperObject = parser.parse(response).getAsJsonObject(); if (!wrapperObject.has(TopicConstants.BINARY)) { return; } else if (!wrapperObject.get(TopicConstants.BINARY).getAsBoolean()) { if (subscriptionMap.containsKey(wrapperObject.get(TopicConstants.TOPIC).getAsString())) { subscriptionMap.get(wrapperObject.get(TopicConstants.TOPIC).getAsString()) .onEvent(wrapperObject.get(TopicConstants.DATA).getAsString()); } } logger.exit(); } public void onOpen(WebSocket socket, Response arg1) { logger.entry(); this.socket = socket; this.socketOpen = true; logger.info("opening websocket!"); if (this.disconnected) { for (String topic : subscriptionMap.keySet()) { subscriptionMap.get(topic).onReconnect(); } this.disconnected = false; } logger.exit(); } public void onPong(Buffer arg0) { logger.info("OnPong() called"); } }