001package com.pusher.rest; 002 003import com.pusher.rest.data.Result; 004import org.asynchttpclient.AsyncHttpClient; 005import org.asynchttpclient.DefaultAsyncHttpClientConfig; 006import org.asynchttpclient.Request; 007import org.asynchttpclient.RequestBuilder; 008import org.asynchttpclient.util.HttpConstants; 009 010import java.io.IOException; 011import java.net.URI; 012import java.util.concurrent.CompletableFuture; 013 014import static java.nio.charset.StandardCharsets.UTF_8; 015import static org.asynchttpclient.Dsl.asyncHttpClient; 016import static org.asynchttpclient.Dsl.config; 017 018/** 019 * A library for interacting with the Pusher HTTP API asynchronously. 020 * <p> 021 * See http://github.com/pusher/pusher-http-java for an overview 022 * <p> 023 * Essentially: 024 * <pre> 025 * // Init 026 * PusherAsync pusher = new PusherAsync(APP_ID, KEY, SECRET); 027 * 028 * // Publish 029 * CompletableFuture<Result> futureTriggerResult = pusher.trigger("my-channel", "my-eventname", myPojoForSerialisation); 030 * triggerResult.thenAccept(triggerResult -> { 031 * if (triggerResult.getStatus() == Status.SUCCESS) { 032 * // request was successful 033 * } else { 034 * // something went wrong with the request 035 * } 036 * }); 037 * 038 * // Query 039 * CompletableFuture<Result> futureChannelListResult = pusher.get("/channels"); 040 * futureChannelListResult.thenAccept(triggerResult -> { 041 * if (triggerResult.getStatus() == Status.SUCCESS) { 042 * String channelListAsJson = channelListResult.getMessage(); 043 * // etc. 044 * } else { 045 * // something went wrong with the request 046 * } 047 * }); 048 * </pre> 049 * 050 * See {@link Pusher} for the synchronous implementation. 051 */ 052public class PusherAsync extends PusherAbstract<CompletableFuture<Result>> implements AutoCloseable { 053 054 private AsyncHttpClient client; 055 056 /** 057 * Construct an instance of the Pusher object through which you may interact with the Pusher API. 058 * <p> 059 * The parameters to use are found on your dashboard at https://app.pusher.com and are specific per App. 060 * <p> 061 * 062 * @param appId The ID of the App you will to interact with. 063 * @param key The App Key, the same key you give to websocket clients to identify your app when they connect to Pusher. 064 * @param secret The App Secret. Used to sign requests to the API, this should be treated as sensitive and not distributed. 065 */ 066 public PusherAsync(final String appId, final String key, final String secret) { 067 super(appId, key, secret); 068 configureHttpClient(config()); 069 } 070 071 public PusherAsync(final String url) { 072 super(url); 073 configureHttpClient(config()); 074 } 075 076 /* 077 * CONFIG 078 */ 079 080 /** 081 * Configure the AsyncHttpClient instance which will be used for making calls to the Pusher API. 082 * <p> 083 * This method allows almost complete control over all aspects of the HTTP client, including 084 * <ul> 085 * <li>proxy host</li> 086 * <li>connection pooling and reuse strategies</li> 087 * <li>automatic retry and backoff strategies</li> 088 * </ul> 089 * <p> 090 * e.g. 091 * <pre> 092 * pusher.configureHttpClient( 093 * config() 094 * .setProxyServer(proxyServer("127.0.0.1", 38080)) 095 * .setMaxRequestRetry(5) 096 * ); 097 * </pre> 098 * 099 * @param builder an {@link DefaultAsyncHttpClientConfig.Builder} with which to configure 100 * the internal HTTP client 101 */ 102 public void configureHttpClient(final DefaultAsyncHttpClientConfig.Builder builder) { 103 try { 104 close(); 105 } catch (final Exception e) { 106 // Not a lot useful we can do here 107 } 108 109 this.client = asyncHttpClient(builder); 110 } 111 112 /* 113 * REST 114 */ 115 116 @Override 117 protected CompletableFuture<Result> doGet(final URI uri) { 118 final Request request = new RequestBuilder(HttpConstants.Methods.GET) 119 .setUrl(uri.toString()) 120 .build(); 121 122 return httpCall(request); 123 } 124 125 @Override 126 protected CompletableFuture<Result> doPost(final URI uri, final String body) { 127 final Request request = new RequestBuilder(HttpConstants.Methods.POST) 128 .setUrl(uri.toString()) 129 .setBody(body) 130 .addHeader("Content-Type", "application/json") 131 .build(); 132 133 return httpCall(request); 134 } 135 136 CompletableFuture<Result> httpCall(final Request request) { 137 return client 138 .prepareRequest(request) 139 .execute() 140 .toCompletableFuture() 141 .thenApply(response -> Result.fromHttpCode(response.getStatusCode(), response.getResponseBody(UTF_8))) 142 .exceptionally(Result::fromThrowable); 143 } 144 145 @Override 146 public void close() throws Exception { 147 if (client != null && !client.isClosed()) { 148 client.close(); 149 } 150 } 151 152}