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

com.zipwhip.api.ZipwhipNetworkSupport Maven / Gradle / Ivy

The newest version!
package com.zipwhip.api;

import com.zipwhip.api.response.JsonResponseParser;
import com.zipwhip.api.response.ResponseParser;
import com.zipwhip.api.response.ServerResponse;
import com.zipwhip.api.response.StringServerResponse;
import com.zipwhip.concurrent.DefaultObservableFuture;
import com.zipwhip.concurrent.ExecutorFactory;
import com.zipwhip.concurrent.ObservableFuture;
import com.zipwhip.events.Observer;
import com.zipwhip.lifecycle.CascadingDestroyableBase;
import com.zipwhip.lifecycle.DestroyableBase;
import com.zipwhip.util.CollectionUtil;
import com.zipwhip.util.InputRunnable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * A base class for future implementation to extend.
 * 

* It takes all the non-API specific stuff out of ZipwhipClient implementations. *

* If some class wants to communicate with Zipwhip, then it needs to extend this * class. This class gives functionality that can be used to parse Zipwhip API. * This naming convention was copied from Spring (JmsSupport) base class. */ public abstract class ZipwhipNetworkSupport extends CascadingDestroyableBase { protected static final Logger LOGGER = LoggerFactory.getLogger(ZipwhipNetworkSupport.class); private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; /** * The default timeout when connecting to Zipwhip */ public static final long DEFAULT_TIMEOUT_SECONDS = 45; public static final String SESSION_GET = "session/get"; public static final String PRESENCE_GET = "presence/get"; public static final String CONTACT_LIST = "contact/list"; public static final String CONTACT_DELETE = "contact/delete"; public static final String CONTACT_SAVE = "contact/save"; public static final String CONTACT_GET = "contact/get"; public static final String MESSAGE_SEND = "message/send"; public static final String MESSAGE_LIST = "message/list"; public static final String MESSAGE_READ = "message/read"; public static final String MESSAGE_DELETE = "message/delete"; public static final String MESSAGE_GET = "message/get"; public static final String CONVERSATION_LIST = "conversation/list"; public static final String CONVERSATION_READ = "conversation/read"; public static final String CONVERSATION_DELETE = "conversation/delete"; public static final String CONVERSATION_GET = "conversation/get"; public static final String DEVICE_SAVE = "device/save"; public static final String DEVICE_GET = "device/get"; public static final String DEVICE_LIST = "device/list"; public static final String DEVICE_DELETE = "device/delete"; public static final String GROUP_SAVE = "group/save"; public static final String GROUP_ADD_MEMBER = "group/addMember"; public static final String SIGNALS_DISCONNECT = "signals/disconnect"; public static final String SIGNALS_CONNECT = "signals/connect"; public static final String SIGNALS_VERIFY = "signal/verify"; public static final String SIGNAL_SEND = "signal/send"; public static final String USER_ENROLL = "user/enroll"; public static final String USER_DEACT = "user/deact"; public static final String USER_SAVE = "user/save"; public static final String USER_GET = "user/get"; public static final String USER_EXISTS = "user/exists"; public static final String USER_UNENROLL = "user/unenroll"; public static final String CARBON_ENABLE = "v1/carbon/enable"; public static final String CARBON_ENABLED = "v1/carbon/enabled"; public static final String CARBON_ENABLED_VENDOR = "carbon/enabled"; public static final String CARBON_INSTALLED = "carbon/installed"; public static final String CARBON_SUGGEST = "carbon/suggest"; public static final String CARBON_REGISTER = "carbon/register"; public static final String CARBON_V2_REGISTER = "carbon/v2/register"; public static final String CARBON_STATS = "carbon/stats"; public static final String CARBON_ACCEPTED_TCS = "carbon/acceptedTCs"; public static final String CHALLENGE_REQUEST = "session/v2/challenge"; public static final String CHALLENGE_CONFIRM = "session/challenge/confirm"; public static final String TEXTLINE_PROVISION = "textline/provision"; public static final String TEXTLINE_ENROLL = "textline/enroll"; public static final String TEXTLINE_UNENROLL = "textline/unenroll"; public static final String FACE_IMAGE = "face/image"; public static final String FACE_NAME = "face/name"; public static final String ATTACHMENT_LIST = "messageAttachment/list"; public static final String HOSTED_CONTENT_GET = "hostedContent/get"; public static final String HOSTED_CONTENT_SAVE = "hostedContent/save"; public static final String TINY_URL_RESERVE = "tinyUrl/reserve"; public static final String TINY_URL_SAVE = "tinyUrl/save"; /** * A runnable for for for executing asynchronous server responses. */ private static final InputRunnable> FORWARD_RUNNABLE = new InputRunnable>() { @Override public void run(ParsableServerResponse object) { object.getFuture().setSuccess(object.getServerResponse()); } }; /** * This importantTaskExecutor really matters. This is the importantTaskExecutor that runs client code. I mean, the guys that call us. * They are observing our web calls via this importantTaskExecutor. If it's too small, and they are too slow, it'll backlog. */ protected final Executor callbackExecutor; protected ApiConnection connection; protected ResponseParser responseParser; /** * Create a new default {@code ZipwhipNetworkSupport} */ public ZipwhipNetworkSupport() { this(null); } public ZipwhipNetworkSupport(ApiConnection connection) { this(null, connection); } public ZipwhipNetworkSupport(Executor callbackExecutor, ApiConnection connection) { if (connection == null) { connection = new HttpConnection(); } if (callbackExecutor == null){ callbackExecutor = ExecutorFactory.newInstance("ZipwhipNetworkSupport-callbacks"); this.link(new DestroyableBase() { @Override protected void onDestroy() { ((ExecutorService)ZipwhipNetworkSupport.this.callbackExecutor).shutdownNow(); } }); } this.callbackExecutor = callbackExecutor; setConnection(connection); link(connection); setResponseParser(new JsonResponseParser()); } public ApiConnection getConnection() { return connection; } public void setConnection(ApiConnection connection) { if (this.connection != null) { unlink(this.connection); } this.connection = connection; link(this.connection); } public ResponseParser getResponseParser() { return responseParser; } public void setResponseParser(ResponseParser responseParser) { this.responseParser = responseParser; } protected ServerResponse executeSync(final String method, final Map params) throws Exception { return get(executeAsync(method, params, true, FORWARD_RUNNABLE)); } protected ServerResponse executeSync(final String method, final Map params, boolean requiresAuthentication) throws Exception { return get(executeAsync(method, params, requiresAuthentication, FORWARD_RUNNABLE)); } protected ServerResponse executeSync(final String method, final Map params, List files) throws Exception { return get(executeAsync(method, params, files, true, FORWARD_RUNNABLE)); } protected ServerResponse executeSync(final String method, final Map params, List files, boolean requiresAuthentication) throws Exception { return get(executeAsync(method, params, files, requiresAuthentication, FORWARD_RUNNABLE)); } protected ObservableFuture executeAsync(String method, Map params) throws Exception { return executeAsync(method, params, true, null); } protected ObservableFuture executeAsync(String method, Map params, boolean requiresAuthentication, final InputRunnable> businessLogic) throws Exception { return executeAsync(method, params, null, requiresAuthentication, businessLogic); } protected ObservableFuture executeAsync(String method, Map params, List files, boolean requiresAuthentication, final InputRunnable> businessLogic) throws Exception { if (requiresAuthentication && !connection.isAuthenticated()) { throw new Exception("The connection is not authenticated, can't continue."); } final ObservableFuture result = new DefaultObservableFuture(this, callbackExecutor); final ObservableFuture responseFuture; if (CollectionUtil.exists(files)) { responseFuture = getConnection().send(method, params, files); } else { responseFuture = getConnection().send(method, params); } responseFuture.addObserver(new Observer>() { /** * This code will execute in the "workerExecutor" of the connection. * If you pass in a bogus/small importantTaskExecutor to him, our code will lag. * * @param sender The sender might not be the same object every time. * @param item Rich object representing the notification. */ @Override public void notify(Object sender, ObservableFuture item) { // The network is done! let's check for our cake! if (!item.isDone()) { return; } if (item.isCancelled()) { // this will execute in the "callbackExecutor" result.cancel(); return; } if (!item.isSuccess()) { // this will execute in the "callbackExecutor" result.setFailure(item.getCause()); return; } String responseString = item.getResult(); ServerResponse serverResponse; try { serverResponse = responseParser.parse(responseString); } catch (Exception e) { LOGGER.error("Problem parsing json response", e); // this will execute in the "callbackExecutor" result.setFailure(e); return; } try { checkAndThrowError(serverResponse); } catch (Exception e) { // this will execute in the "callbackExecutor" result.setFailure(e); return; } try { if (businessLogic != null){ businessLogic.run(new ParsableServerResponse(result, serverResponse)); } else { result.setSuccess(null); } } catch (Exception e) { LOGGER.error("Problem with running the business logic conversion", e); // this will execute in the "callbackExecutor" result.setFailure(e); } } }); return result; } protected ObservableFuture executeAsyncBinaryResponse(String method, Map params, boolean requiresAuthentication) throws Exception { if (requiresAuthentication && !connection.isAuthenticated()) { throw new Exception("The connection is not authenticated, can't continue."); } final ObservableFuture result = new DefaultObservableFuture(this, callbackExecutor); final ObservableFuture responseFuture = getConnection().sendBinaryResponse(method, params); responseFuture.addObserver(new Observer>() { @Override public void notify(Object sender, ObservableFuture item) { // The network is done! let's check for our cake! if (!item.isDone()) { return; } if (item.isCancelled()) { // this will execute in the "callbackExecutor" result.cancel(); return; } if (!item.isSuccess()) { // this will execute in the "callbackExecutor" result.setFailure(item.getCause()); return; } byte[] bytes; try { bytes = toByteArray(item.getResult()); } catch (IOException e) { result.setFailure(e); return; } result.setSuccess(bytes); } }); return result; } protected void checkAndThrowError(ServerResponse serverResponse) throws Exception { if (serverResponse == null) { // A null response from the server is OK return; } if (!serverResponse.isSuccess()) { throwError(serverResponse); } } protected void throwError(ServerResponse serverResponse) throws Exception { if (serverResponse instanceof StringServerResponse) { StringServerResponse string = (StringServerResponse) serverResponse; throw new Exception(string.response); } else { throw new Exception(serverResponse.getRaw()); } } protected static byte[] toByteArray(InputStream input) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; int numBytes; while (-1 != (numBytes = input.read(buffer))) { output.write(buffer, 0, numBytes); } return output.toByteArray(); } protected T get(ObservableFuture task) throws Exception { task.await(DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS); if (!task.isSuccess()) { throw new Exception(task.getCause()); } return task.getResult(); } protected boolean success(ServerResponse serverResponse) { return (serverResponse != null) && serverResponse.isSuccess(); } protected static class ParsableServerResponse { private ObservableFuture future; private ServerResponse serverResponse; private ParsableServerResponse(ObservableFuture future, ServerResponse serverResponse) { this.future = future; this.serverResponse = serverResponse; } public ObservableFuture getFuture() { return future; } public ServerResponse getServerResponse() { return serverResponse; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy