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

com.datatorrent.stram.util.PubSubWebSocketClient Maven / Gradle / Ivy

There is a newer version: 3.7.0
Show newest version
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.datatorrent.stram.util;

import java.io.IOException;
import java.net.URI;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;

import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.apex.shaded.ning19.com.ning.http.client.AsyncCompletionHandler;
import org.apache.apex.shaded.ning19.com.ning.http.client.AsyncHttpClient;
import org.apache.apex.shaded.ning19.com.ning.http.client.AsyncHttpClient.BoundRequestBuilder;
import org.apache.apex.shaded.ning19.com.ning.http.client.AsyncHttpClientConfigBean;
import org.apache.apex.shaded.ning19.com.ning.http.client.Response;
import org.apache.apex.shaded.ning19.com.ning.http.client.cookie.Cookie;
import org.apache.apex.shaded.ning19.com.ning.http.client.ws.WebSocket;
import org.apache.apex.shaded.ning19.com.ning.http.client.ws.WebSocketTextListener;
import org.apache.apex.shaded.ning19.com.ning.http.client.ws.WebSocketUpgradeHandler;

import com.google.common.base.Throwables;

import com.datatorrent.api.Component;
import com.datatorrent.api.Context;
import com.datatorrent.common.util.JacksonObjectMapperProvider;
import com.datatorrent.common.util.NameableThreadFactory;
import com.datatorrent.common.util.PubSubMessage;
import com.datatorrent.common.util.PubSubMessageCodec;

/**
 * 

Abstract PubSubWebSocketClient class.

* * @since 0.3.2 */ public abstract class PubSubWebSocketClient implements Component { private final AsyncHttpClient client; private WebSocket connection; private final ObjectMapper mapper; private final PubSubMessageCodec codec; private URI uri; private int ioThreadMultiplier; private String loginUrl; private String userName; private String password; private final AtomicReference throwable; private class PubSubWebSocket implements WebSocketTextListener { @Override public void onMessage(String message) { PubSubMessage pubSubMessage; try { pubSubMessage = codec.parseMessage(message); PubSubWebSocketClient.this.onMessage(pubSubMessage.getType().getIdentifier(), pubSubMessage.getTopic(), pubSubMessage.getData()); } catch (JsonParseException jpe) { logger.warn("Ignoring unparseable JSON message: {}", message, jpe); } catch (JsonMappingException jme) { logger.warn("Ignoring JSON mapping in message: {}", message, jme); } catch (IOException ex) { onError(ex); } } @Override public void onOpen(WebSocket ws) { PubSubWebSocketClient.this.onOpen(ws); } @Override public void onClose(WebSocket ws) { PubSubWebSocketClient.this.onClose(ws); } @Override public void onError(Throwable t) { PubSubWebSocketClient.this.onError(t); } } /** *

Constructor for PubSubWebSocketClient.

*/ public PubSubWebSocketClient() { throwable = new AtomicReference(); ioThreadMultiplier = 1; mapper = (new JacksonObjectMapperProvider()).getContext(null); codec = new PubSubMessageCodec(mapper); AsyncHttpClientConfigBean config = new AsyncHttpClientConfigBean(); config.setIoThreadMultiplier(ioThreadMultiplier); config.setApplicationThreadPool(Executors.newCachedThreadPool(new NameableThreadFactory("AsyncHttpClient"))); client = new AsyncHttpClient(config); } /** *

Setter for the field * uri.

* * @param uri */ public void setUri(URI uri) { this.uri = uri; } public void setIoThreadMultiplier(int ioThreadMultiplier) { this.ioThreadMultiplier = ioThreadMultiplier; } public void setLoginUrl(String url) { this.loginUrl = url; } public void setUserName(String userName) { this.userName = userName; } public void setPassword(String password) { this.password = password; } /** *

openConnection.

* * @param timeoutMillis * @throws IOException * @throws ExecutionException * @throws InterruptedException * @throws TimeoutException */ public void openConnection(long timeoutMillis) throws IOException, ExecutionException, InterruptedException, TimeoutException { throwable.set(null); List cookies = null; if (loginUrl != null && userName != null && password != null) { // get the session key first before attempting web socket JSONObject json = new JSONObject(); try { json.put("userName", userName); json.put("password", password); } catch (JSONException ex) { throw new RuntimeException(ex); } Response response = client.preparePost(loginUrl).setHeader("Content-Type", "application/json").setBody(json.toString()).execute().get(); cookies = response.getCookies(); } BoundRequestBuilder brb = client.prepareGet(uri.toString()); if (cookies != null) { for (Cookie cookie : cookies) { brb.addCookie(cookie); } } connection = brb.execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new PubSubWebSocket()).build()).get(timeoutMillis, TimeUnit.MILLISECONDS); } public void openConnectionAsync() throws IOException { throwable.set(null); if (loginUrl != null && userName != null && password != null) { // get the session key first before attempting web socket JSONObject json = new JSONObject(); try { json.put("userName", userName); json.put("password", password); } catch (JSONException ex) { throw new RuntimeException(ex); } client.preparePost(loginUrl).setHeader("Content-Type", "application/json").setBody(json.toString()).execute(new AsyncCompletionHandler() { @Override public Response onCompleted(Response response) throws Exception { List cookies = response.getCookies(); BoundRequestBuilder brb = client.prepareGet(uri.toString()); if (cookies != null) { for (Cookie cookie : cookies) { brb.addCookie(cookie); } } connection = brb.execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new PubSubWebSocket()).build()).get(); return response; } }); } else { final PubSubWebSocket webSocket = new PubSubWebSocket() { @Override public void onOpen(WebSocket ws) { connection = ws; super.onOpen(ws); } }; client.prepareGet(uri.toString()).execute( new WebSocketUpgradeHandler.Builder().addWebSocketListener(webSocket).build()); } } /** * * @return true if the connection is open; false otherwise. */ public boolean isConnectionOpen() { if (connection == null) { return false; } return connection.isOpen(); } /** * Before the websocket is used, it's recommended to call this method to ensure that * any exceptions caught while processing the messages received are acknowledged. * @throws IOException The reason because of which connection cannot be used. */ public void assertUsable() throws IOException { if (throwable.get() == null) { if (connection == null) { throw new IOException("Connection is not open"); } return; } Throwable t = throwable.get(); if (t instanceof IOException) { throw (IOException)t; } else { throw Throwables.propagate(t); } } /** *

publish.

* * @param topic * @param data * @throws IOException */ public void publish(String topic, Object data) throws IOException { assertUsable(); connection.sendMessage(PubSubMessageCodec.constructPublishMessage(topic, data, codec)); } /** *

subscribe.

* * @param topic * @throws IOException */ public void subscribe(String topic) throws IOException { assertUsable(); connection.sendMessage(PubSubMessageCodec.constructSubscribeMessage(topic, codec)); } /** *

unsubscribe.

* * @param topic * @throws IOException */ public void unsubscribe(String topic) throws IOException { assertUsable(); connection.sendMessage(PubSubMessageCodec.constructUnsubscribeMessage(topic, codec)); } /** *

subscribeNumSubscribers.

* * @param topic * @throws IOException */ public void subscribeNumSubscribers(String topic) throws IOException { assertUsable(); connection.sendMessage(PubSubMessageCodec.constructSubscribeNumSubscribersMessage(topic, codec)); } /** *

unsubscribeNumSubscribers.

* * @param topic * @throws IOException */ public void unsubscribeNumSubscribers(String topic) throws IOException { assertUsable(); connection.sendMessage(PubSubMessageCodec.constructUnsubscribeNumSubscribersMessage(topic, codec)); } /** *

onOpen.

* * @param ws */ public abstract void onOpen(WebSocket ws); /** *

onMessage.

* * @param type * @param topic * @param data */ public abstract void onMessage(String type, String topic, Object data); /** *

onClose.

* * @param ws */ public abstract void onClose(WebSocket ws); @Override public void setup(Context context) { } @Override public void teardown() { if (connection != null) { connection.close(); } if (client != null) { client.close(); } throwable.set(null); } private void onError(Throwable t) { throwable.set(t); } private static final Logger logger = LoggerFactory.getLogger(PubSubWebSocketClient.class); }