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 /** 072 * Construct an instance of the Pusher object through which you may interact with the Pusher API. 073 * <p> 074 * The parameters to use are found on your dashboard at https://app.pusher.com and are specific per App. 075 * <p> 076 * 077 * @param appId The ID of the App you will to interact with. 078 * @param key The App Key, the same key you give to websocket clients to identify your app when they connect to Pusher. 079 * @param secret The App Secret. Used to sign requests to the API, this should be treated as sensitive and not distributed. 080 * @param encryptionMasterKeyBase64 32 byte key, base64 encoded. This key, along with the channel name, are used to derive per-channel encryption keys. 081 */ 082 public PusherAsync(final String appId, final String key, final String secret, final String encryptionMasterKeyBase64) { 083 super(appId, key, secret, encryptionMasterKeyBase64); 084 configureHttpClient(config()); 085 } 086 087 public PusherAsync(final String url) { 088 super(url); 089 configureHttpClient(config()); 090 } 091 092 /* 093 * CONFIG 094 */ 095 096 /** 097 * Configure the AsyncHttpClient instance which will be used for making calls to the Pusher API. 098 * <p> 099 * This method allows almost complete control over all aspects of the HTTP client, including 100 * <ul> 101 * <li>proxy host</li> 102 * <li>connection pooling and reuse strategies</li> 103 * <li>automatic retry and backoff strategies</li> 104 * </ul> 105 * <p> 106 * e.g. 107 * <pre> 108 * pusher.configureHttpClient( 109 * config() 110 * .setProxyServer(proxyServer("127.0.0.1", 38080)) 111 * .setMaxRequestRetry(5) 112 * ); 113 * </pre> 114 * 115 * @param builder an {@link DefaultAsyncHttpClientConfig.Builder} with which to configure 116 * the internal HTTP client 117 */ 118 public void configureHttpClient(final DefaultAsyncHttpClientConfig.Builder builder) { 119 try { 120 close(); 121 } catch (final Exception e) { 122 // Not a lot useful we can do here 123 } 124 125 this.client = asyncHttpClient(builder); 126 } 127 128 /* 129 * REST 130 */ 131 132 @Override 133 protected CompletableFuture<Result> doGet(final URI uri) { 134 final Request request = new RequestBuilder(HttpConstants.Methods.GET) 135 .setUrl(uri.toString()) 136 .build(); 137 138 return httpCall(request); 139 } 140 141 @Override 142 protected CompletableFuture<Result> doPost(final URI uri, final String body) { 143 final Request request = new RequestBuilder(HttpConstants.Methods.POST) 144 .setUrl(uri.toString()) 145 .setBody(body) 146 .addHeader("Content-Type", "application/json") 147 .build(); 148 149 return httpCall(request); 150 } 151 152 CompletableFuture<Result> httpCall(final Request request) { 153 return client 154 .prepareRequest(request) 155 .execute() 156 .toCompletableFuture() 157 .thenApply(response -> Result.fromHttpCode(response.getStatusCode(), response.getResponseBody(UTF_8))) 158 .exceptionally(Result::fromThrowable); 159 } 160 161 @Override 162 public void close() throws Exception { 163 if (client != null && !client.isClosed()) { 164 client.close(); 165 } 166 } 167 168}