001package com.pusher.rest; 002 003import com.pusher.rest.data.Result; 004import org.apache.http.HttpResponse; 005import org.apache.http.client.config.RequestConfig; 006import org.apache.http.client.methods.HttpGet; 007import org.apache.http.client.methods.HttpPost; 008import org.apache.http.client.methods.HttpRequestBase; 009import org.apache.http.entity.StringEntity; 010import org.apache.http.impl.DefaultConnectionReuseStrategy; 011import org.apache.http.impl.client.CloseableHttpClient; 012import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy; 013import org.apache.http.impl.client.HttpClientBuilder; 014import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; 015 016import java.io.ByteArrayOutputStream; 017import java.io.IOException; 018import java.net.URI; 019 020/** 021 * A library for interacting with the Pusher HTTP API. 022 * <p> 023 * See http://github.com/pusher/pusher-http-java for an overview 024 * <p> 025 * Essentially: 026 * <pre> 027 * // Init 028 * Pusher pusher = new Pusher(APP_ID, KEY, SECRET); 029 * // Publish 030 * Result triggerResult = pusher.trigger("my-channel", "my-eventname", myPojoForSerialisation); 031 * if (triggerResult.getStatus() != Status.SUCCESS) { 032 * if (triggerResult.getStatus().shouldRetry()) { 033 * // Temporary, let's schedule a retry 034 * } 035 * else { 036 * // Something is wrong with our request 037 * } 038 * } 039 * 040 * // Query 041 * Result channelListResult = pusher.get("/channels"); 042 * if (channelListResult.getStatus() == Status.SUCCESS) { 043 * String channelListAsJson = channelListResult.getMessage(); 044 * // etc 045 * } 046 * </pre> 047 * 048 * See {@link PusherAsync} for the asynchronous implementation. 049 */ 050public class Pusher extends PusherAbstract<Result> implements AutoCloseable { 051 052 private int requestTimeout = 4000; // milliseconds 053 054 private CloseableHttpClient 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 * @param appId The ID of the App you will to interact with. 062 * @param key The App Key, the same key you give to websocket clients to identify your app when they connect to Pusher. 063 * @param secret The App Secret. Used to sign requests to the API, this should be treated as sensitive and not distributed. 064 */ 065 public Pusher(final String appId, final String key, final String secret) { 066 super(appId, key, secret); 067 configureHttpClient(defaultHttpClientBuilder()); 068 } 069 070 public Pusher(final String url) { 071 super(url); 072 configureHttpClient(defaultHttpClientBuilder()); 073 } 074 075 /* 076 * CONFIG 077 */ 078 079 /** 080 * Default: 4000 081 * 082 * @param requestTimeout the request timeout in milliseconds 083 */ 084 public void setRequestTimeout(final int requestTimeout) { 085 this.requestTimeout = requestTimeout; 086 } 087 088 /** 089 * Returns an HttpClientBuilder with the settings used by default applied. You may apply 090 * further configuration (for example an HTTP proxy), override existing configuration 091 * (for example, the connection manager which handles connection pooling for reuse) and 092 * then call {@link #configureHttpClient(HttpClientBuilder)} to have this configuration 093 * applied to all subsequent calls. 094 * 095 * @see #configureHttpClient(HttpClientBuilder) 096 * 097 * @return an {@link org.apache.http.impl.client.HttpClientBuilder} with the default settings applied 098 */ 099 public static HttpClientBuilder defaultHttpClientBuilder() { 100 return HttpClientBuilder.create() 101 .setConnectionManager(new PoolingHttpClientConnectionManager()) 102 .setConnectionReuseStrategy(new DefaultConnectionReuseStrategy()) 103 .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()) 104 .disableRedirectHandling(); 105 } 106 107 /** 108 * Configure the HttpClient instance which will be used for making calls to the Pusher API. 109 * <p> 110 * This method allows almost complete control over all aspects of the HTTP client, including 111 * <ul> 112 * <li>proxy host</li> 113 * <li>connection pooling and reuse strategies</li> 114 * <li>automatic retry and backoff strategies</li> 115 * </ul> 116 * It is <strong>strongly</strong> recommended that you take the value of {@link #defaultHttpClientBuilder()} 117 * as a base, apply your custom config to that and then pass the builder in here, to ensure 118 * that sensible defaults for configuration areas you are not setting explicitly are retained. 119 * <p> 120 * e.g. 121 * <pre> 122 * pusher.configureHttpClient( 123 * Pusher.defaultHttpClientBuilder() 124 * .setProxy(new HttpHost("proxy.example.com")) 125 * .disableAutomaticRetries() 126 * ); 127 * </pre> 128 * 129 * @see #defaultHttpClientBuilder() 130 * 131 * @param builder an {@link org.apache.http.impl.client.HttpClientBuilder} with which to configure 132 * the internal HTTP client 133 */ 134 public void configureHttpClient(final HttpClientBuilder builder) { 135 try { 136 close(); 137 } catch (final Exception e) { 138 // Not a lot useful we can do here 139 } 140 141 this.client = builder.build(); 142 } 143 144 /* 145 * REST 146 */ 147 148 @Override 149 protected Result doGet(final URI uri) { 150 return httpCall(new HttpGet(uri)); 151 } 152 153 @Override 154 protected Result doPost(final URI uri, final String body) { 155 final StringEntity bodyEntity = new StringEntity(body, "UTF-8"); 156 bodyEntity.setContentType("application/json"); 157 158 final HttpPost request = new HttpPost(uri); 159 request.setEntity(bodyEntity); 160 161 return httpCall(request); 162 } 163 164 Result httpCall(final HttpRequestBase request) { 165 final RequestConfig config = RequestConfig.custom() 166 .setSocketTimeout(requestTimeout) 167 .setConnectionRequestTimeout(requestTimeout) 168 .setConnectTimeout(requestTimeout) 169 .build(); 170 request.setConfig(config); 171 172 try { 173 final HttpResponse response = client.execute(request); 174 175 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 176 response.getEntity().writeTo(baos); 177 final String responseBody = new String(baos.toByteArray(), "UTF-8"); 178 179 return Result.fromHttpCode(response.getStatusLine().getStatusCode(), responseBody); 180 } 181 catch (final IOException e) { 182 return Result.fromException(e); 183 } 184 } 185 186 @Override 187 public void close() throws Exception { 188 if (client != null) { 189 client.close(); 190 } 191 } 192 193}