001package com.pusher.client; 002 003import java.io.IOException; 004import java.io.InputStream; 005import java.util.Properties; 006 007/** 008 * Configuration for a {@link com.pusher.client.Pusher} instance. 009 */ 010public class PusherOptions { 011 012 private static final String SRC_LIB_DEV_VERSION = "@version@"; 013 private static final String LIB_DEV_VERSION = "0.0.0-dev"; 014 public static final String LIB_VERSION = readVersionFromProperties(); 015 016 private static final String URI_SUFFIX = "?client=java-client&protocol=5&version=" + LIB_VERSION; 017 private static final String WS_SCHEME = "ws"; 018 private static final String WSS_SCHEME = "wss"; 019 020 private static final int WS_PORT = 80; 021 private static final int WSS_PORT = 443; 022 private static final String PUSHER_DOMAIN = "pusher.com"; 023 024 private static final long DEFAULT_ACTIVITY_TIMEOUT = 120000; 025 private static final long DEFAULT_PONG_TIMEOUT = 30000; 026 027 // Note that the primary cluster lives on a different domain 028 // (others are subdomains of pusher.com). This is not an oversight. 029 // Legacy reasons. 030 private String host = "ws.pusherapp.com"; 031 private int wsPort = WS_PORT; 032 private int wssPort = WSS_PORT; 033 private boolean encrypted = true; 034 private long activityTimeout = DEFAULT_ACTIVITY_TIMEOUT; 035 private long pongTimeout = DEFAULT_PONG_TIMEOUT; 036 private Authorizer authorizer; 037 038 /** 039 * Gets whether an encrypted (SSL) connection should be used when connecting 040 * to Pusher. 041 * 042 * @return true if an encrypted connection should be used; otherwise false. 043 */ 044 public boolean isEncrypted() { 045 return encrypted; 046 } 047 048 /** 049 * Sets whether an encrypted (SSL) connection should be used when connecting to 050 * Pusher. 051 * 052 * @param encrypted Whether to use an SSL connection 053 * @return this, for chaining 054 */ 055 public PusherOptions setEncrypted(final boolean encrypted) { 056 this.encrypted = encrypted; 057 return this; 058 } 059 060 /** 061 * Gets the authorizer to be used when authenticating private and presence 062 * channels. 063 * 064 * @return the authorizer 065 */ 066 public Authorizer getAuthorizer() { 067 return authorizer; 068 } 069 070 /** 071 * Sets the authorizer to be used when authenticating private and presence 072 * channels. 073 * 074 * @param authorizer 075 * The authorizer to be used. 076 * @return this, for chaining 077 */ 078 public PusherOptions setAuthorizer(final Authorizer authorizer) { 079 this.authorizer = authorizer; 080 return this; 081 } 082 083 /** 084 * The host to which connections will be made. 085 * 086 * Note that if you wish to connect to a standard Pusher cluster, the 087 * convenience method setCluster will set the host and ports correctly from 088 * a single argument. 089 * 090 * @param host The host 091 * @return this, for chaining 092 */ 093 public PusherOptions setHost(final String host) { 094 this.host = host; 095 return this; 096 } 097 098 /** 099 * The port to which unencrypted connections will be made. 100 * 101 * Note that if you wish to connect to a standard Pusher cluster, the 102 * convenience method setCluster will set the host and ports correctly from 103 * a single argument. 104 * 105 * @param wsPort port number 106 * @return this, for chaining 107 */ 108 public PusherOptions setWsPort(final int wsPort) { 109 this.wsPort = wsPort; 110 return this; 111 } 112 113 /** 114 * The port to which encrypted connections will be made. 115 * 116 * Note that if you wish to connect to a standard Pusher cluster, the 117 * convenience method setCluster will set the host and ports correctly from 118 * a single argument. 119 * 120 * @param wssPort port number 121 * @return this, for chaining 122 */ 123 public PusherOptions setWssPort(final int wssPort) { 124 this.wssPort = wssPort; 125 return this; 126 } 127 128 public PusherOptions setCluster(final String cluster) { 129 host = "ws-" + cluster + "." + PUSHER_DOMAIN; 130 wsPort = WS_PORT; 131 wssPort = WSS_PORT; 132 return this; 133 } 134 135 /** 136 * The number of milliseconds of inactivity at which a "ping" will be 137 * triggered to check the connection. 138 * 139 * The default value is 120,000 (2 minutes). On some connections, where 140 * intermediate hops between the application and Pusher are aggressively 141 * culling connections they consider to be idle, a lower value may help 142 * preserve the connection. 143 * 144 * @param activityTimeout 145 * time to consider connection idle, in milliseconds 146 * @return this, for chaining 147 */ 148 public PusherOptions setActivityTimeout(final long activityTimeout) { 149 if (activityTimeout < 1000) { 150 throw new IllegalArgumentException( 151 "Activity timeout must be at least 1,000ms (and is recommended to be much higher)"); 152 } 153 154 this.activityTimeout = activityTimeout; 155 return this; 156 } 157 158 public long getActivityTimeout() { 159 return activityTimeout; 160 } 161 162 /** 163 * The number of milliseconds after a "ping" is sent that the client will 164 * wait to receive a "pong" response from the server before considering the 165 * connection broken and triggering a transition to the disconnected state. 166 * 167 * The default value is 30,000. 168 * 169 * @param pongTimeout 170 * time to wait for pong response, in milliseconds 171 * @return this, for chaining 172 */ 173 public PusherOptions setPongTimeout(final long pongTimeout) { 174 if (pongTimeout < 1000) { 175 throw new IllegalArgumentException( 176 "Pong timeout must be at least 1,000ms (and is recommended to be much higher)"); 177 } 178 179 this.pongTimeout = pongTimeout; 180 return this; 181 } 182 183 public long getPongTimeout() { 184 return pongTimeout; 185 } 186 187 /** 188 * Construct the URL for the WebSocket connection based on the options 189 * previous set on this object and the provided API key 190 * 191 * @param apiKey The API key 192 * @return the WebSocket URL 193 */ 194 public String buildUrl(final String apiKey) { 195 return String.format("%s://%s:%s/app/%s%s", encrypted ? WSS_SCHEME : WS_SCHEME, host, encrypted ? wssPort 196 : wsPort, apiKey, URI_SUFFIX); 197 } 198 199 private static String readVersionFromProperties() { 200 InputStream inStream = null; 201 try { 202 final Properties p = new Properties(); 203 inStream = PusherOptions.class.getResourceAsStream("/pusher.properties"); 204 p.load(inStream); 205 String version = (String)p.get("version"); 206 207 // If the properties file contents indicates the version is being run 208 // from source then replace with a dev indicator. Otherwise the Pusher 209 // Socket API will reject the connection. 210 if(version.equals(SRC_LIB_DEV_VERSION)) { 211 version = LIB_DEV_VERSION; 212 } 213 214 if (version != null && version.length() > 0) { 215 return version; 216 } 217 } 218 catch (final Exception e) { 219 // Fall back to fixed value 220 } 221 finally { 222 try { 223 if (inStream != null) { 224 inStream.close(); 225 } 226 } 227 catch (final IOException e) { 228 // Ignore problem closing stream 229 } 230 } 231 return "0.0.0"; 232 } 233}