com.pubnub.api.PubnubCore Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pubnub-android-debug Show documentation
Show all versions of pubnub-android-debug Show documentation
PubNub is a cross-platform client-to-client (1:1 and 1:many) push service in the cloud, capable of broadcasting real-time messages to millions of web and mobile clients simultaneously, in less than a quarter second!
package com.pubnub.api;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Random;
/**
* Pubnub object facilitates querying channels for messages and listening on
* channels for presence/message events
*
* @author Pubnub
*
*/
abstract class PubnubCore {
private String HOSTNAME = "pubsub";
private int HOSTNAME_SUFFIX = 1;
private String DOMAIN = "pubnub.com";
private String ORIGIN_STR = null;
protected String PUBLISH_KEY = "";
protected String SUBSCRIBE_KEY = "";
protected String SECRET_KEY = "";
private String CIPHER_KEY = "";
private String IV = null;
private volatile String AUTH_STR = null;
private volatile boolean CACHE_BUSTING = true;
protected Hashtable params;
private volatile boolean resumeOnReconnect;
private boolean SSL = true;
protected String UUID = null;
private Subscriptions channelSubscriptions;
private Subscriptions channelGroupSubscriptions;
protected SubscribeManager subscribeManager;
protected NonSubscribeManager nonSubscribeManager;
protected TimedTaskManager timedTaskManager;
private volatile String _timetoken = "0";
private volatile String _saved_timetoken = "0";
private String PRESENCE_SUFFIX = "-pnpres";
protected static String VERSION = "";
private Random generator = new Random();
private static Logger log = new Logger(PubnubCore.class);
protected abstract String getUserAgent();
private int PRESENCE_HEARTBEAT_TASK = 0;
private int HEARTBEAT = 320;
private volatile int PRESENCE_HB_INTERVAL = 0;
/**
* This method when called stops Pubnub threads
*/
public void shutdown() {
nonSubscribeManager.stop();
subscribeManager.stop();
timedTaskManager.stop();
}
/**
* This method returns the state of Resume on Reconnect setting
*
* @return Current state of Resume On Reconnect Setting
*/
public boolean isResumeOnReconnect() {
return resumeOnReconnect;
}
/**
* This method sets retry interval for subscribe. Pubnub API will make
* maxRetries attempts to connect to pubnub servers. These attemtps will be
* made at an interval of retryInterval milliseconds.
*
* @param retryInterval
* Retry Interval in milliseconds
*/
public void setRetryInterval(int retryInterval) {
subscribeManager.setRetryInterval(retryInterval);
}
/**
* This method sets window interval for subscribe.
*
* @param windowInterval
* Window Interval in milliseconds
*/
public void setWindowInterval(int windowInterval) {
subscribeManager.setWindowInterval(windowInterval);
}
/**
* Returns current retry interval for subscribe
* @return Current Retry Interval in milliseconds
*/
public int getRetryInterval() {
return subscribeManager.retryInterval;
}
/**
* Returns current window interval for subscribe
* @return Current Window Interval in milliseconds
*/
public int getWindowInterval() {
return subscribeManager.windowInterval;
}
String[] getPresenceHeartbeatUrl() {
String channelString = channelSubscriptions.getItemStringNoPresence();
if (channelString.length() <= 0) {
return null;
}
return new String[]{ getPubnubUrl(), "v2", "presence", "sub-key",
this.SUBSCRIBE_KEY, "channel",
PubnubUtil.urlEncode(channelString), "heartbeat"
};
}
private String getState() {
return (channelSubscriptions.state.length() > 0) ? channelSubscriptions.state.toString() : null;
}
class PresenceHeartbeatTask extends TimedTask {
private Callback callback;
PresenceHeartbeatTask(int interval, Callback callback) {
super(interval);
this.callback = callback;
}
public void run() {
String[] urlComponents = getPresenceHeartbeatUrl();
if (urlComponents == null)
return;
//String[] urlComponents = { getPubnubUrl(), "time", "0"};
Hashtable parameters = PubnubUtil.hashtableClone(params);
if (parameters.get("uuid") == null)
parameters.put("uuid", UUID);
String st = getState();
if (st != null) parameters.put("state", st);
if (HEARTBEAT > 0 && HEARTBEAT < 320 ) parameters.put("heartbeat", String.valueOf(HEARTBEAT));
HttpRequest hreq = new HttpRequest(urlComponents, parameters,
new ResponseHandler() {
public void handleResponse(HttpRequest hreq, String response) {
JSONObject jso;
try {
jso = new JSONObject(response);
response = jso.getString("message");
} catch (JSONException e) {
handleError(
hreq,
PubnubError.getErrorObject(
PubnubError.PNERROBJ_INVALID_JSON, 1, response
)
);
return;
}
callback.successCallback(
channelSubscriptions.getItemStringNoPresence(),
response
);
}
public void handleError(HttpRequest hreq, PubnubError error) {
callback.errorCallback(channelSubscriptions.getItemStringNoPresence(), error);
}
});
_request(hreq, nonSubscribeManager);
}
}
/**
* This method sets presence expiry timeout.
*
* @param pnexpires
* Presence Expiry timeout in seconds
*/
public void setPnExpires(int pnexpires, Callback callback) {
setHeartbeat(pnexpires,callback);
}
/**
* This method sets presence expiry timeout.
*
* @param heartbeat
* Presence Heartbeat value in seconds
*/
public void setHeartbeat(int heartbeat, Callback callback) {
Callback cb = getWrappedCallback(callback);
HEARTBEAT = (heartbeat > 0 && heartbeat < 5)?5:heartbeat;
if (PRESENCE_HB_INTERVAL == 0) {
PRESENCE_HB_INTERVAL = (HEARTBEAT - 3 >= 1)?HEARTBEAT - 3:1;
}
if (PRESENCE_HEARTBEAT_TASK == 0) {
PRESENCE_HEARTBEAT_TASK = timedTaskManager.addTask("Presence-Heartbeat",
new PresenceHeartbeatTask(PRESENCE_HB_INTERVAL, cb));
} else if (PRESENCE_HB_INTERVAL == 0 || PRESENCE_HB_INTERVAL > 320) {
timedTaskManager.removeTask(PRESENCE_HEARTBEAT_TASK);
} else {
timedTaskManager.updateTask(PRESENCE_HEARTBEAT_TASK, PRESENCE_HB_INTERVAL);
}
disconnectAndResubscribe();
}
public void setPnExpires(int pnexpires) {
setPnExpires(pnexpires, null);
}
public void setHeartbeat(int heartbeat) {
setHeartbeat(heartbeat, null);
}
public void setHeartbeatInterval(int heartbeatInterval) {
setHeartbeatInterval(heartbeatInterval, null);
}
public void setHeartbeatInterval(int heartbeatInterval, Callback callback) {
Callback cb = getWrappedCallback(callback);
PRESENCE_HB_INTERVAL = heartbeatInterval;
if (PRESENCE_HEARTBEAT_TASK == 0) {
PRESENCE_HEARTBEAT_TASK = timedTaskManager.addTask("Presence-Heartbeat",
new PresenceHeartbeatTask(PRESENCE_HB_INTERVAL, cb));
} else if (PRESENCE_HB_INTERVAL == 0 || PRESENCE_HB_INTERVAL > 320) {
timedTaskManager.removeTask(PRESENCE_HEARTBEAT_TASK);
} else {
timedTaskManager.updateTask(PRESENCE_HEARTBEAT_TASK, PRESENCE_HB_INTERVAL);
}
}
public int getHeartbeatInterval() {
return PRESENCE_HB_INTERVAL;
}
/**
* Returns presence expiry timeout value
* @return Current presence expiry timeout value
*/
public int getPnExpires() {
return getHeartbeat();
}
/**
* Returns presence heartbeat value
* @return Current presence heartbeat value
*/
public int getHeartbeat() {
return HEARTBEAT;
}
/**
* This methods sets maximum number of retries for subscribe. Pubnub API
* will make maxRetries attempts to connect to pubnub servers before timing
* out.
*
* @param maxRetries
* Max number of retries
*/
public void setMaxRetries(int maxRetries) {
subscribeManager.setMaxRetries(maxRetries);
}
/**
* Returns current max retries for Subscribe
* @return Current max retries
*/
public int getMaxRetries() {
return subscribeManager.maxRetries;
}
protected String getPubnubUrl() {
if (ORIGIN_STR == null) {
// SSL On?
if (this.SSL) {
ORIGIN_STR = "https://";
} else {
ORIGIN_STR = "http://";
}
ORIGIN_STR += HOSTNAME;
ORIGIN_STR += ((!this.CACHE_BUSTING)?"":"-" + String.valueOf(HOSTNAME_SUFFIX));
ORIGIN_STR += "." + DOMAIN;
}
return ORIGIN_STR;
}
private Callback voidCallback = new Callback()
{public void successCallback(String channel, Object message) {}};
protected Callback getWrappedCallback(Callback callback){
if (callback == null) {
return voidCallback;
} else
return callback;
}
private boolean validateInput(String name, Object input, Callback callback) {
if (input == null) {
callback.errorCallback("", PubnubError.getErrorObject(
PubnubError.PNERROBJ_INVALID_ARGUMENTS, 1, name + " cannot be null"));
return false;
}
if (input instanceof String && ((String)input).length() == 0) {
callback.errorCallback("", PubnubError.getErrorObject(
PubnubError.PNERROBJ_INVALID_ARGUMENTS, 2, name + " cannot be zero length"));
return false;
}
return true;
}
/**
* Enable/Disable Cache Busting
*
* @param cacheBusting
*/
public void setCacheBusting(boolean cacheBusting) {
this.CACHE_BUSTING = cacheBusting;
}
/**
* Get Cache Busting value
* @return current cache busting setting
*/
public boolean getCacheBusting() {
return this.CACHE_BUSTING;
}
/**
* This method returns all channel names currently subscribed to in form of
* a comma separated String
*
* @return Comma separated string with all channel names currently
* subscribed
*/
public String getCurrentlySubscribedChannelNames() {
String currentChannels = channelSubscriptions.getItemString();
return currentChannels.equals("") ? "no channels." : currentChannels;
}
/**
* If Resume on Reconnect is set to true, then Pubnub catches up on
* reconnection after disconnection. If false, then messages sent on the
* channel between disconnection and reconnection are not received.
*
* @param resumeOnReconnect
* True or False setting for Resume on Reconnect
*/
public void setResumeOnReconnect(boolean resumeOnReconnect) {
this.resumeOnReconnect = resumeOnReconnect;
}
/**
* Returns Resume on Reconnect current setting
* @return Resume on Reconnect setting
*/
public boolean getResumeOnReconnect() {
return this.resumeOnReconnect;
}
/**
* This method returns unique identifier.
* @return Unique Identifier .
*/
abstract String uuid();
/**
* Sets value for UUID
*
* @param uuid
* UUID value for Pubnub client
*/
public void setUUID(String uuid) {
this.UUID = uuid;
}
/**
* Gets current UUID
*
* @return uuid
* current UUID value for Pubnub client
*/
public String getUUID() {
return this.UUID;
}
/**
*
* Constructor for Pubnub Class
*
* @param publish_key
* Publish Key
* @param subscribe_key
* Subscribe Key
* @param secret_key
* Secret Key
* @param cipher_key
* Cipher Key
* @param ssl_on
* SSL enabled ?
* @param initialization_vector
* Initialization vector
*/
public PubnubCore(String publish_key, String subscribe_key,
String secret_key, String cipher_key, boolean ssl_on, String initialization_vector) {
this.init(publish_key, subscribe_key, secret_key, cipher_key, ssl_on, initialization_vector);
}
/**
*
* Constructor for Pubnub Class
*
* @param publish_key
* Publish Key
* @param subscribe_key
* Subscribe Key
* @param secret_key
* Secret Key
* @param cipher_key
* Cipher Key
* @param ssl_on
* SSL enabled ?
*/
public PubnubCore(String publish_key, String subscribe_key,
String secret_key, String cipher_key, boolean ssl_on) {
this.init(publish_key, subscribe_key, secret_key, cipher_key, ssl_on);
}
/**
*
* Constructor for Pubnub Class
*
* @param publish_key
* Publish Key
* @param subscribe_key
* Subscribe Key
* @param secret_key
* Secret Key
* @param ssl_on
* SSL enabled ?
*/
public PubnubCore(String publish_key, String subscribe_key,
String secret_key, boolean ssl_on) {
this.init(publish_key, subscribe_key, secret_key, "", ssl_on);
}
/**
*
* Constructor for Pubnub Class
*
* @param publish_key
* Publish Key
* @param subscribe_key
* Subscribe Key
*/
public PubnubCore(String publish_key, String subscribe_key) {
this.init(publish_key, subscribe_key, "", "", false);
}
/**
*
* Constructor for Pubnub Class
*
* @param publish_key
* Publish Key
* @param subscribe_key
* Subscribe Key
*/
public PubnubCore(String publish_key, String subscribe_key, boolean ssl) {
this.init(publish_key, subscribe_key, "", "", ssl);
}
/**
*
* Constructor for Pubnub Class
*
* @param publish_key
* Publish Key
* @param subscribe_key
* Subscribe Key
* @param secret_key
* Secret Key
*/
public PubnubCore(String publish_key, String subscribe_key,
String secret_key) {
this.init(publish_key, subscribe_key, secret_key, "", false);
}
/**
*
* Initialize PubNub Object State.
*
* @param publish_key
* @param subscribe_key
* @param secret_key
* @param cipher_key
* @param ssl_on
*/
private void init(String publish_key, String subscribe_key,
String secret_key, String cipher_key, boolean ssl_on) {
this.init(publish_key, subscribe_key, secret_key, cipher_key, ssl_on, null);
}
/**
*
* Initialize PubNub Object State.
*
* @param publish_key
* @param subscribe_key
* @param secret_key
* @param cipher_key
* @param ssl_on
*/
private void init(String publish_key, String subscribe_key,
String secret_key, String cipher_key, boolean ssl_on, String initialization_vector) {
this.PUBLISH_KEY = publish_key;
this.SUBSCRIBE_KEY = subscribe_key;
this.SECRET_KEY = secret_key;
this.CIPHER_KEY = cipher_key;
this.SSL = ssl_on;
if (UUID == null)
UUID = uuid();
if (channelSubscriptions == null)
channelSubscriptions = new Subscriptions();
if (channelGroupSubscriptions == null)
channelGroupSubscriptions = new Subscriptions();
if (subscribeManager == null)
subscribeManager = new SubscribeManager("Subscribe-Manager-"
+ System.identityHashCode(this), 10000, 310000);
if (nonSubscribeManager == null)
nonSubscribeManager = new NonSubscribeManager(
"Non-Subscribe-Manager-" + System.identityHashCode(this),
10000, 15000);
if (timedTaskManager == null)
timedTaskManager = new TimedTaskManager("TimedTaskManager");
if (params == null)
params = new Hashtable();
params.put("pnsdk", getUserAgent());
subscribeManager.setHeader("V", VERSION);
subscribeManager.setHeader("Accept-Encoding", "gzip");
subscribeManager.setHeader("User-Agent", getUserAgent());
nonSubscribeManager.setHeader("V", VERSION);
nonSubscribeManager.setHeader("Accept-Encoding", "gzip");
nonSubscribeManager.setHeader("User-Agent", getUserAgent());
}
/**
* This method sets timeout value for subscribe/presence. Default value is
* 310000 milliseconds i.e. 310 seconds
*
* @param timeout
* Timeout value in milliseconds for subscribe/presence
*/
protected void setSubscribeTimeout(int timeout) {
subscribeManager.setRequestTimeout(timeout);
this.disconnectAndResubscribe();
}
protected int getSubscribeTimeout() {
return subscribeManager.requestTimeout;
}
/**
* This method set timeout value for non subscribe operations like publish,
* history, hereNow. Default value is 15000 milliseconds i.e. 15 seconds.
*
* @param timeout
* Timeout value in milliseconds for Non subscribe operations
* like publish, history, hereNow
*/
protected void setNonSubscribeTimeout(int timeout) {
nonSubscribeManager.setRequestTimeout(timeout);
}
protected int getNonSubscribeTimeout() {
return nonSubscribeManager.requestTimeout;
}
/**
* Send a message to a channel.
*
* @param channel
* Channel name
* @param message
* JSONObject to be published
* @param callback
* object of sub class of Callback class
*/
public void publish(String channel, JSONObject message, boolean storeInHistory, Callback callback) {
Hashtable args = new Hashtable();
args.put("channel", channel);
args.put("message", message);
args.put("callback", callback);
args.put("storeInHistory", (storeInHistory)?"":"0");
publish(args);
}
/**
* Send a message to a channel.
*
* @param channel
* Channel name
* @param message
* JSONOArray to be published
* @param callback
* object of sub class of Callback class
*/
public void publish(String channel, JSONArray message, boolean storeInHistory, Callback callback) {
Hashtable args = new Hashtable();
args.put("channel", channel);
args.put("message", message);
args.put("callback", callback);
args.put("storeInHistory", (storeInHistory)?"":"0");
publish(args);
}
/**
* Send a message to a channel.
*
* @param channel
* Channel name
* @param message
* String to be published
* @param callback
* object of sub class of Callback class
*/
public void publish(String channel, String message, boolean storeInHistory, Callback callback) {
Hashtable args = new Hashtable();
args.put("channel", channel);
args.put("message", message);
args.put("callback", callback);
args.put("storeInHistory", (storeInHistory)?"":"0");
publish(args);
}
/**
* Send a message to a channel.
*
* @param channel
* Channel name
* @param message
* Integer to be published
* @param callback
* object of sub class of Callback class
*/
public void publish(String channel, Integer message, boolean storeInHistory, Callback callback) {
Hashtable args = new Hashtable();
args.put("channel", channel);
args.put("message", message);
args.put("callback", callback);
args.put("storeInHistory", (storeInHistory)?"":"0");
publish(args);
}
/**
* Send a message to a channel.
*
* @param channel
* Channel name
* @param message
* Double to be published
* @param callback
* object of sub class of Callback class
*/
public void publish(String channel, Double message, boolean storeInHistory, Callback callback) {
Hashtable args = new Hashtable();
args.put("channel", channel);
args.put("message", message);
args.put("callback", callback);
args.put("storeInHistory", (storeInHistory)?"":"0");
publish(args);
}
/**
* Send a message to a channel.
*
* @param channel
* Channel name
* @param message
* JSONObject to be published
* @param callback
* object of sub class of Callback class
*/
public void publish(String channel, JSONObject message, Callback callback) {
Hashtable args = new Hashtable();
args.put("channel", channel);
args.put("message", message);
args.put("callback", callback);
publish(args);
}
/**
* Send a message to a channel.
*
* @param channel
* Channel name
* @param message
* JSONOArray to be published
* @param callback
* object of sub class of Callback class
*/
public void publish(String channel, JSONArray message, Callback callback) {
Hashtable args = new Hashtable();
args.put("channel", channel);
args.put("message", message);
args.put("callback", callback);
publish(args);
}
/**
* Send a message to a channel.
*
* @param channel
* Channel name
* @param message
* String to be published
* @param callback
* object of sub class of Callback class
*/
public void publish(String channel, String message, Callback callback) {
Hashtable args = new Hashtable();
args.put("channel", channel);
args.put("message", message);
args.put("callback", callback);
publish(args);
}
/**
* Send a message to a channel.
*
* @param channel
* Channel name
* @param message
* Integer to be published
* @param callback
* object of sub class of Callback class
*/
public void publish(String channel, Integer message, Callback callback) {
Hashtable args = new Hashtable();
args.put("channel", channel);
args.put("message", message);
args.put("callback", callback);
publish(args);
}
/**
* Send a message to a channel.
*
* @param channel
* Channel name
* @param message
* Double to be published
* @param callback
* object of sub class of Callback class
*/
public void publish(String channel, Double message, Callback callback) {
Hashtable args = new Hashtable();
args.put("channel", channel);
args.put("message", message);
args.put("callback", callback);
publish(args);
}
/**
* Send a message to a channel.
*
* @param args
* Hashtable containing channel name, message.
* @param callback
* object of sub class of Callback class
*/
protected void publish(Hashtable args, Callback callback) {
args.put("callback", callback);
publish(args);
}
/**
* Send a message to a channel.
*
* @param args
* Hashtable containing channel name, message, callback
*/
protected void publish(Hashtable args) {
final String channel = (String) args.get("channel");
final Object message = args.get("message");
final Callback callback = getWrappedCallback((Callback) args.get("callback"));
String storeInHistory = (String) args.get("storeInHistory");
String msgStr = message.toString();
Hashtable parameters = PubnubUtil.hashtableClone(params);
if (storeInHistory != null && storeInHistory.length() > 0) parameters.put("store", storeInHistory);
if (this.CIPHER_KEY.length() > 0) {
// Encrypt Message
PubnubCrypto pc = new PubnubCrypto(this.CIPHER_KEY, this.IV);
try {
if (message instanceof String) {
msgStr = "\"" + msgStr + "\"";
}
msgStr = "\"" + pc.encrypt(msgStr) + "\"";
} catch (PubnubException e) {
callback.errorCallback(channel, getPubnubError(e, PubnubError.PNERROBJ_ENCRYPTION_ERROR, 4, msgStr + " : " + e.toString()));
return;
}
} else {
if (message instanceof String) {
msgStr = "\"" + msgStr + "\"";
}
}
// Generate String to Sign
String signature = "0";
if (this.SECRET_KEY.length() > 0) {
StringBuffer string_to_sign = new StringBuffer();
string_to_sign.append(this.PUBLISH_KEY).append('/')
.append(this.SUBSCRIBE_KEY).append('/')
.append(this.SECRET_KEY).append('/').append(channel)
.append('/').append(msgStr);
// Sign Message
try {
signature = new String(PubnubCrypto.hexEncode(PubnubCrypto
.md5(string_to_sign.toString())), "UTF-8");
} catch (UnsupportedEncodingException e) {
PubnubError pe = PubnubError.getErrorObject(PubnubError.PNERROBJ_ENCRYPTION_ERROR, 6, msgStr + " : " + e.toString());
callback.errorCallback(channel, pe);
} catch (PubnubException e) {
callback.errorCallback(channel, getPubnubError(e, PubnubError.PNERROBJ_ENCRYPTION_ERROR, 5, msgStr + " : " + e.toString()));
}
}
String[] urlComponents = { getPubnubUrl(), "publish", this.PUBLISH_KEY,
this.SUBSCRIBE_KEY, PubnubUtil.urlEncode(signature),
PubnubUtil.urlEncode(channel), "0",
PubnubUtil.urlEncode(msgStr)
};
class PublishResponseHandler extends ResponseHandler {
public void handleResponse(HttpRequest hreq, String response) {
JSONArray jsarr;
try {
jsarr = new JSONArray(response);
} catch (JSONException e) {
handleError(hreq,
PubnubError.getErrorObject(PubnubError.PNERROBJ_INVALID_JSON, 1, response));
return;
}
callback.successCallback(channel, jsarr);
}
public void handleError(HttpRequest hreq, PubnubError error) {
callback.errorCallback(channel, error);
return;
}
}
HttpRequest hreq = new HttpRequest(urlComponents, parameters, new PublishResponseHandler());
_request(hreq, nonSubscribeManager);
}
/**
*
* Listen for presence of subscribers on a channel
*
* @param channel
* Name of the channel on which to listen for join/leave i.e.
* presence events
* @param callback
* object of sub class of Callback class
* @exception PubnubException
* Throws PubnubException if Callback is null
*/
public void presence(String channel, Callback callback)
throws PubnubException {
Hashtable args = new Hashtable(2);
args.put("channels", new String[] {channel + PRESENCE_SUFFIX});
args.put("callback", callback);
subscribe(args);
}
/**
* Read presence information for uuid
*
* @param uuid
* UUID
* @param callback
* object of sub class of Callback class
*/
public void whereNow(final String uuid, Callback callback) {
final Callback cb = getWrappedCallback(callback);
String[] urlargs = { getPubnubUrl(), "v2", "presence", "sub_key",
this.SUBSCRIBE_KEY, "uuid", PubnubUtil.urlEncode(uuid)
};
HttpRequest hreq = new HttpRequest(urlargs, params,
new ResponseHandler() {
public void handleResponse(HttpRequest hreq, String response) {
invokeCallback("", response, "payload", cb, 4);
}
public void handleError(HttpRequest hreq, PubnubError error) {
cb.errorCallback("", error);
return;
}
});
_request(hreq, nonSubscribeManager);
}
public void whereNow(Callback callback) {
whereNow(this.UUID, callback);
}
public void setState(String channel, String uuid, JSONObject state, Callback callback) {
_setState(channelSubscriptions, PubnubUtil.urlEncode(channel), null, uuid, state, callback);
}
public void channelGroupSetState(String group, String uuid, JSONObject state, Callback callback) {
_setState(channelSubscriptions, ".", group, uuid, state, callback);
}
protected void _setState(Subscriptions sub, String channel, String group, String uuid, JSONObject state, Callback callback) {
SubscriptionItem item = sub.getItem(channel);
final Callback cb = getWrappedCallback(callback);
Hashtable parameters = PubnubUtil.hashtableClone(params);
String[] urlArgs = { getPubnubUrl(), "v2", "presence", "sub-key",
this.SUBSCRIBE_KEY, "channel", channel, "uuid", PubnubUtil.urlEncode(uuid),
"data"
};
if (state != null) parameters.put("state", state.toString());
if (group != null) parameters.put("channel-group", group);
if (item != null) {
try {
sub.state.put(channel, state);
} catch (JSONException e) {
}
}
HttpRequest hreq = new HttpRequest(urlArgs, parameters,
new ResponseHandler() {
public void handleResponse(HttpRequest hreq, String response) {
invokeCallback("", response, "payload", cb, 2 );
}
public void handleError(HttpRequest hreq, PubnubError error) {
cb.errorCallback("", error);
}
});
_request(hreq, nonSubscribeManager);
}
public void getState(String channel, String uuid, Callback callback) {
final Callback cb = getWrappedCallback(callback);
Hashtable parameters = PubnubUtil.hashtableClone(params);
String[] urlArgs = { getPubnubUrl(), "v2", "presence", "sub-key",
this.SUBSCRIBE_KEY, "channel", PubnubUtil.urlEncode(channel),
"uuid", PubnubUtil.urlEncode(uuid)
};
HttpRequest hreq = new HttpRequest(urlArgs, parameters,
new ResponseHandler() {
public void handleResponse(HttpRequest hreq, String response) {
invokeCallback("", response, "payload", cb, 1 );
}
public void handleError(HttpRequest hreq, PubnubError error) {
cb.errorCallback("", error);
}
});
_request(hreq, nonSubscribeManager);
}
protected void invokeCallback(String channel, String response, String key,
Callback callback, int extendedErrorCode) {
invokeCallback(channel, response, key, callback, extendedErrorCode, false);
}
protected void invokeCallback(String channel, String response, String key,
Callback callback, int extendedErrorCode, boolean key_strict) {
JSONObject responseJso = null;
try {
responseJso = new JSONObject(response);
} catch (JSONException e) {
callback.errorCallback(channel,
PubnubError.getErrorObject(PubnubError.PNERROBJ_JSON_ERROR, extendedErrorCode, response));
return;
}
JSONObject payloadJso = null;
if (key != null && key.length() > 0) {
try {
payloadJso = (JSONObject) responseJso.get(key);
} catch (JSONException e) {
if (!key_strict) {
callback.successCallback(channel, responseJso);
} else {
callback.errorCallback(channel,
PubnubError.getErrorObject(PubnubError.PNERROBJ_JSON_ERROR, extendedErrorCode, response));
}
return;
}
callback.successCallback(channel, payloadJso);
return;
}
}
protected void invokeJSONStringCallback(String response, String key, Callback callback) {
String responseJSON;
try {
responseJSON = (new JSONObject(response)).getString(key);
callback.successCallback(null, responseJSON);
} catch (JSONException e) {
callback.errorCallback(
null,
PubnubError.getErrorObject(PubnubError.PNERROBJ_JSON_ERROR, 0, response)
);
}
}
/**
* Get all namespaces
*
* @param callback to invoke
*/
public void channelGroupListNamespaces(Callback callback) {
final Callback cb = getWrappedCallback(callback);
String[] url = new String[]{getPubnubUrl(), "v1", "channel-registration", "sub-key",
this.SUBSCRIBE_KEY, "namespace"};
Hashtable parameters = PubnubUtil.hashtableClone(params);
HttpRequest hreq = new HttpRequest(url, parameters, new ResponseHandler() {
public void handleResponse(HttpRequest hreq, String response) {
invokeCallback("", response, "payload", cb, 0);
}
public void handleError(HttpRequest hreq, PubnubError error) {
cb.errorCallback(null, error);
}
});
_request(hreq, nonSubscribeManager);
}
/**
* Remove namespace
*
* @param namespace to remove
* @param callback to invoke
*/
public void channelGroupRemoveNamespace(String namespace, Callback callback) {
final Callback cb = getWrappedCallback(callback);
String[] url = new String[]{getPubnubUrl(), "v1", "channel-registration", "sub-key",
this.SUBSCRIBE_KEY, "namespace", namespace, "remove"};
Hashtable parameters = PubnubUtil.hashtableClone(params);
HttpRequest hreq = new HttpRequest(url, parameters, new ResponseHandler() {
public void handleResponse(HttpRequest hreq, String response) {
invokeJSONStringCallback(response, "message", cb);
}
public void handleError(HttpRequest hreq, PubnubError error) {
cb.errorCallback(null, error);
}
});
_request(hreq, nonSubscribeManager);
}
/**
* Get the list of groups in the global namespace
*
* @param callback to invoke
*/
public void channelGroupListGroups(Callback callback) {
channelGroupListGroups(null, callback);
}
/**
* Get the list of groups in the namespace
*
* @param namespace name
* @param callback to invoke
*/
public void channelGroupListGroups(String namespace, Callback callback) {
final Callback cb = getWrappedCallback(callback);
String[] url;
if (namespace != null) {
url = new String[]{getPubnubUrl(), "v1", "channel-registration", "sub-key",
this.SUBSCRIBE_KEY, "namespace", namespace, "channel-group"};
} else {
url = new String[]{getPubnubUrl(), "v1", "channel-registration", "sub-key",
this.SUBSCRIBE_KEY, "channel-group"};
}
Hashtable parameters = PubnubUtil.hashtableClone(params);
HttpRequest hreq = new HttpRequest(url, parameters, new ResponseHandler() {
public void handleResponse(HttpRequest hreq, String response) {
invokeCallback("", response, "payload", cb, 0);
}
public void handleError(HttpRequest hreq, PubnubError error) {
cb.errorCallback(null, error);
}
});
_request(hreq, nonSubscribeManager);
}
/**
* Get the list of channels in the namespaced group
*
* @param group name
* @param callback to invoke
*/
public void channelGroupListChannels(String group, Callback callback) {
final Callback cb = getWrappedCallback(callback);
ChannelGroup channelGroup;
String[] url;
try {
channelGroup = new ChannelGroup(group);
} catch (PubnubException e) {
cb.errorCallback(null, PubnubError.PNERROBJ_CHANNEL_GROUP_PARSING_ERROR);
return;
}
if (channelGroup.namespace != null) {
url = new String[]{getPubnubUrl(), "v1", "channel-registration", "sub-key",
this.SUBSCRIBE_KEY, "namespace", channelGroup.namespace, "channel-group", channelGroup.group};
} else {
url = new String[]{getPubnubUrl(), "v1", "channel-registration", "sub-key",
this.SUBSCRIBE_KEY, "channel-group", channelGroup.group};
}
Hashtable parameters = PubnubUtil.hashtableClone(params);
HttpRequest hreq = new HttpRequest(url, parameters, new ResponseHandler() {
public void handleResponse(HttpRequest hreq, String response) {
invokeCallback("", response, "payload", cb, 0);
}
public void handleError(HttpRequest hreq, PubnubError error) {
cb.errorCallback(null, error);
}
});
_request(hreq, nonSubscribeManager);
}
public void channelGroupAddChannel(String group, String channel, Callback callback) {
channelGroupUpdate("add", group, new String[]{channel}, callback);
}
public void channelGroupAddChannel(String group, String[] channels, Callback callback) {
channelGroupUpdate("add", group, channels, callback);
}
public void channelGroupRemoveChannel(String group, String channel, Callback callback) {
channelGroupUpdate("remove", group, new String[]{channel}, callback);
}
public void channelGroupRemoveChannel(String group, String[] channels, Callback callback) {
channelGroupUpdate("remove", group, channels, callback);
}
private void channelGroupUpdate(String action, String group, String[] channels, final Callback callback) {
final Callback cb = getWrappedCallback(callback);
ChannelGroup channelGroup;
String[] url;
try {
channelGroup = new ChannelGroup(group);
} catch (PubnubException e) {
cb.errorCallback(null, PubnubError.PNERROBJ_CHANNEL_GROUP_PARSING_ERROR);
return;
}
if (channelGroup.namespace != null) {
url = new String[]{getPubnubUrl(), "v1", "channel-registration", "sub-key",
this.SUBSCRIBE_KEY, "namespace", channelGroup.namespace, "channel-group", channelGroup.group};
} else {
url = new String[]{getPubnubUrl(), "v1", "channel-registration", "sub-key",
this.SUBSCRIBE_KEY, "channel-group", channelGroup.group};
}
Hashtable parameters = PubnubUtil.hashtableClone(params);
if (channels.length > 0) {
parameters.put(action, PubnubUtil.joinString(channels, ","));
}
HttpRequest hreq = new HttpRequest(url, parameters, new ResponseHandler() {
public void handleResponse(HttpRequest hreq, String response) {
invokeJSONStringCallback(response, "message", cb);
}
public void handleError(HttpRequest hreq, PubnubError error) {
cb.errorCallback(null, error);
}
});
_request(hreq, nonSubscribeManager);
}
public void channelGroupRemoveGroup(String group, Callback callback) {
final Callback cb = getWrappedCallback(callback);
ChannelGroup channelGroup;
String[] url;
try {
channelGroup = new ChannelGroup(group);
} catch (PubnubException e) {
cb.errorCallback(null, PubnubError.PNERROBJ_CHANNEL_GROUP_PARSING_ERROR);
return;
}
if (channelGroup.namespace != null) {
url = new String[]{getPubnubUrl(), "v1", "channel-registration", "sub-key",
this.SUBSCRIBE_KEY, "namespace",
channelGroup.namespace, "channel-group", channelGroup.group, "remove"};
} else {
url = new String[]{getPubnubUrl(), "v1", "channel-registration", "sub-key",
this.SUBSCRIBE_KEY, "channel-group", channelGroup.group, "remove"};
}
Hashtable parameters = PubnubUtil.hashtableClone(params);
HttpRequest hreq = new HttpRequest(url, parameters, new ResponseHandler() {
public void handleResponse(HttpRequest hreq, String response) {
invokeJSONStringCallback(response, "message", cb);
}
public void handleError(HttpRequest hreq, PubnubError error) {
cb.errorCallback(null, error);
}
});
_request(hreq, nonSubscribeManager);
}
/**
* Read presence information from a channel
*
* @param channel
* Channel name
* @param callback
* object of sub class of Callback class
*/
public void hereNow(final String channel, Callback callback) {
hereNow(new String[]{channel}, null, false, true, callback);
}
public void hereNow(boolean state, boolean uuids, Callback callback) {
hereNow(null, null, state, uuids, callback);
}
public void hereNow(final String channel, boolean state, boolean uuids, Callback callback) {
hereNow(new String[]{channel}, null, state, uuids, callback);
}
public void channelGroupHereNow(String group, Callback callback) {
channelGroupHereNow(group, false, true, callback);
}
public void channelGroupHereNow(String group, boolean state, boolean uuids, Callback callback) {
channelGroupHereNow(new String[]{group}, state, uuids, callback);
}
public void channelGroupHereNow(String[] groups, boolean state, boolean uuids, Callback callback) {
hereNow(null, groups, state, uuids, callback);
}
/**
* Read presence information from a channel or a channel group
*
* @param channels array
* @param channelGroups array
* @param state state enabled ?
* @param uuids enable / disable returning uuids in response ?
* @param callback object of sub class of Callback class
*/
public void hereNow(String[] channels, String[] channelGroups, boolean state,
boolean uuids, Callback callback) {
final Callback cb = getWrappedCallback(callback);
Hashtable parameters = PubnubUtil.hashtableClone(params);
ArrayList urlArgs = new ArrayList();
urlArgs.add(getPubnubUrl());
urlArgs.add("v2");
urlArgs.add("presence");
urlArgs.add("sub_key");
urlArgs.add(this.SUBSCRIBE_KEY);
if (channels != null || channelGroups != null) {
String channelsString = PubnubUtil.joinString(channels, ",");
if ("".equals(channelsString)) {
channelsString = ",";
} else {
channelsString = PubnubUtil.urlEncode(channelsString);
}
urlArgs.add("channel");
urlArgs.add(channelsString);
}
if (state) parameters.put("state", "1");
if (!uuids) parameters.put("disable_uuids", "1");
if (channelGroups != null && channelGroups.length > 0) {
parameters.put("channel-group", PubnubUtil.joinString(channelGroups, ","));
}
String[] path = (String[]) urlArgs.toArray(new String[urlArgs.size()]);
HttpRequest hreq = new HttpRequest(path, parameters,
new ResponseHandler() {
public void handleResponse(HttpRequest hreq, String response) {
invokeCallback(null, response, "payload", cb, 1);
}
public void handleError(HttpRequest hreq, PubnubError error) {
cb.errorCallback(null, error);
}
});
_request(hreq, nonSubscribeManager);
}
/**
*
* Read History for a channel.
*
* @param channel
* Channel name for which history is required
* @param start
* Start time
* @param end
* End time
* @param count
* Upper limit on number of messages to be returned
* @param reverse
* True if messages need to be in reverse order
* @param callback
* Callback
*/
public void history(final String channel, long start, long end,
int count, boolean reverse, Callback callback) {
history(channel, start, end, count, reverse, false, callback);
}
private PubnubError getPubnubError(PubnubException px, PubnubError error, int code, String message) {
PubnubError pe = px.getPubnubError();
if (pe == null) {
pe = PubnubError.getErrorObject(error, code, message);
}
return pe;
}
/**
*
* Read History for a channel.
*
* @param channel
* Channel name for which history is required
* @param start
* Start time
* @param end
* End time
* @param count
* Upper limit on number of messages to be returned
* @param reverse
* True if messages need to be in reverse order
* @param includeTimetoken
* True/False whether to include timetokens in response
* @param callback
* Callback
*/
public void history(final String channel, long start, long end,
int count, boolean reverse, boolean includeTimetoken, Callback callback) {
final Callback cb = getWrappedCallback(callback);
Hashtable parameters = PubnubUtil.hashtableClone(params);
if (count == -1)
count = 100;
parameters.put("count", String.valueOf(count));
parameters.put("reverse", String.valueOf(reverse));
parameters.put("include_token", String.valueOf(includeTimetoken));
if (start != -1)
parameters.put("start", Long.toString(start).toLowerCase());
if (end != -1)
parameters.put("end", Long.toString(end).toLowerCase());
String[] urlargs = { getPubnubUrl(), "v2", "history", "sub-key",
this.SUBSCRIBE_KEY, "channel", PubnubUtil.urlEncode(channel)
};
class HistoryResponseHandler extends ResponseHandler {
public void handleResponse(HttpRequest hreq, String response) {
JSONArray respArr;
try {
respArr = new JSONArray(response);
decryptJSONArray((JSONArray) respArr.get(0));
cb.successCallback(channel, respArr);
} catch (JSONException e) {
cb.errorCallback(channel,
PubnubError.getErrorObject(PubnubError.PNERROBJ_JSON_ERROR, 3));
} catch (IOException e) {
cb.errorCallback(channel,
PubnubError.getErrorObject(PubnubError.PNERROBJ_DECRYPTION_ERROR, 9, response));
} catch (PubnubException e) {
cb.errorCallback(channel, getPubnubError(e, PubnubError.PNERROBJ_DECRYPTION_ERROR, 10, response + " : " + e.toString()));
} catch (Exception e) {
cb.errorCallback(channel,
PubnubError.getErrorObject(PubnubError.PNERROBJ_DECRYPTION_ERROR, 11, response + " : " + e.toString()));
}
}
public void handleError(HttpRequest hreq, PubnubError error) {
cb.errorCallback(channel, error);
return;
}
}
HttpRequest hreq = new HttpRequest(urlargs, parameters, new HistoryResponseHandler());
_request(hreq, nonSubscribeManager);
}
/**
*
* Read History for a channel.
*
* @param channel
* Channel name for which history is required
* @param start
* Start time
* @param end
* End time
* @param reverse
* True if messages need to be in reverse order
*
* @param callback
* Callback
*/
public void history(String channel, long start, long end,
boolean reverse, Callback callback) {
history(channel, start, end, -1, reverse, callback);
}
/**
*
* Read History for a channel.
*
* @param channel
* Channel name for which history is required
* @param count
* Maximum number of messages
* @param callback
* Callback object
*/
public void history(String channel, int count, Callback callback) {
history(channel, -1, -1, count, false, callback);
}
/**
*
* Read History for a channel.
*
* @param channel
* Channel name for which history is required
* @param includeTimetoken
* True/False whether to include timetokens in response
* @param count
* Maximum number of messages
* @param callback
* Callback object
*/
public void history(String channel, boolean includeTimetoken, int count, Callback callback) {
history(channel, -1, -1, count, false, includeTimetoken, callback);
}
/**
*
* Read History for a channel.
*
* @param channel
* Channel name for which history is required
* @param start
* Start time
* @param reverse
* True if messages need to be in reverse order
* @param callback
* Callback
*/
public void history(String channel, long start, boolean reverse,
Callback callback) {
history(channel, start, -1, -1, reverse, callback);
}
/**
*
* Read History for a channel.
*
* @param channel
* Channel name for which history is required
* @param start
* Start time
* @param end
* End time
* @param callback
* Callback
*/
public void history(String channel, long start, long end,
Callback callback) {
history(channel, start, end, -1, false, callback);
}
/**
*
* Read History for a channel.
*
* @param channel
* Channel name for which history is required
* @param start
* Start time
* @param end
* End time
* @param count
* Upper limit on number of messages to be returned
* @param callback
* Callback
*/
public void history(String channel, long start, long end, int count,
Callback callback) {
history(channel, start, end, count, false, callback);
}
/**
*
* Read History for a channel.
*
* @param channel
* Channel name for which history is required
* @param start
* Start time
* @param count
* Upper limit on number of messages to be returned
* @param reverse
* True if messages need to be in reverse order
*
* @param callback
* Callback
*/
public void history(String channel, long start, int count,
boolean reverse, Callback callback) {
history(channel, start, -1, count, reverse, callback);
}
/**
*
* Read History for a channel.
*
* @param channel
* Channel name for which history is required
* @param start
* Start time
* @param count
* Upper limit on number of messages to be returned
* @param callback
* Callback
*/
public void history(String channel, long start, int count,
Callback callback) {
history(channel, start, -1, count, false, callback);
}
/**
*
* Read History for a channel.
*
* @param channel
* Channel name for which history is required
* @param count
* Upper limit on number of messages to be returned
* @param reverse
* True if messages need to be in reverse order
* @param callback
* Callback
*/
public void history(String channel, int count, boolean reverse,
Callback callback) {
history(channel, -1, -1, count, reverse, callback);
}
/**
*
* Read History for a channel.
*
* @param channel
* Channel name for which history is required
* @param reverse
* True if messages need to be in reverse order
* @param callback
* Callback
*/
public void history(String channel, boolean reverse,
Callback callback) {
history(channel, -1, -1, -1, reverse, callback);
}
/**
* Read current time from PubNub Cloud.
*
* @param callback
* Callback object
*/
public void time(Callback callback) {
final Callback cb = getWrappedCallback(callback);
String[] url = { getPubnubUrl(), "time", "0" };
HttpRequest hreq = new HttpRequest(url, params, new ResponseHandler() {
public void handleResponse(HttpRequest hreq, String response) {
cb.successCallback(null, response);
}
public void handleError(HttpRequest hreq, PubnubError error) {
cb.errorCallback(null, error);
}
});
_request(hreq, nonSubscribeManager);
}
private void keepOnlyPluralSubscriptionItems(Hashtable args) {
String _channel = (String) args.get("channel");
String _group = (String) args.get("group");
if (_channel != null && !(_channel.equals(""))) {
args.put("channels", new String[]{_channel});
args.remove("channel");
}
if (_group != null && !(_group.equals(""))) {
args.put("groups", new String[]{_group});
args.remove("group");
}
}
private boolean inputsValid(Hashtable args) throws PubnubException {
boolean channelsOk;
boolean groupsOk;
if (!(args.get("callback") instanceof Callback) || args.get("callback") == null) {
throw new PubnubException("Invalid Callback");
}
String[] _channels = (String[]) args.get("channels");
String[] _groups = (String[]) args.get("groups");
channelsOk = (_channels != null && _channels.length > 0);
groupsOk = (_groups != null && _groups.length > 0);
if (!channelsOk && !groupsOk) {
throw new PubnubException("Channel or Channel Group Missing");
}
return true;
}
private void leave(final String channel) {
_leave(PubnubUtil.urlEncode(channel), PubnubUtil.hashtableClone(this.params));
}
private void channelGroupLeave(String group) {
Hashtable params = PubnubUtil.hashtableClone(this.params);
params.put("channel-group", group);
_leave(",", params);
}
private void _leave(String channel, Hashtable params) {
String[] urlArgs = {getPubnubUrl(), "v2/presence/sub_key",
this.SUBSCRIBE_KEY, "channel", channel, "leave"
};
params.put("uuid", UUID);
HttpRequest hreq = new HttpRequest(urlArgs, params,
new ResponseHandler() {
public void handleResponse(HttpRequest hreq, String response) {
}
public void handleError(HttpRequest hreq, PubnubError error) {
}
});
_request(hreq, nonSubscribeManager);
}
/**
* Unsubscribe from channels.
*
* @param channels String array containing channel names
*/
public void unsubscribe(String[] channels) {
for (int i = 0; i < channels.length; i++) {
String channel = channels[i];
channelSubscriptions.removeItem(channel);
channelSubscriptions.state.remove(channel);
leave(channel);
}
resubscribe();
}
/**
* Unsubscribe/Disconnect from channel.
*
* @param channel channel name as String.
*/
public void unsubscribe(String channel) {
unsubscribe(new String[]{channel});
}
/**
* Unsubscribe/Disconnect from channel.
*
* @param args Hashtable containing channel name.
*/
protected void unsubscribe(Hashtable args) {
String[] channelList = (String[]) args.get("channels");
if (channelList == null) {
channelList = new String[]{(String) args.get("channel")};
}
unsubscribe(channelList);
}
/**
* Unsubscribe from channel group
*
* @param group to unsubscribe
*/
public void channelGroupUnsubscribe(String group) {
channelGroupUnsubscribe(new String[]{group});
}
/**
* Unsubscribe from multiple channel groups
*
* @param groups to unsubscribe
*/
public void channelGroupUnsubscribe(String[] groups) {
for (int i = 0; i < groups.length; i++) {
String group = groups[i];
channelGroupSubscriptions.removeItem(group);
channelGroupLeave(group);
}
resubscribe();
}
/**
* Unsubscribe from presence channel.
*
* @param channel channel name as String.
*/
public void unsubscribePresence(String channel) {
unsubscribe(new String[]{channel + PRESENCE_SUFFIX});
}
/**
* Unsubscribe from all channel and channel groups.
*/
public void unsubscribeAll() {
String[] channels = channelSubscriptions.getItemNames();
String[] groups = channelGroupSubscriptions.getItemNames();
for (int i = 0; i < channels.length; i++) {
String channel = channels[i];
channelSubscriptions.removeItem(channel);
leave(channel);
}
for (int i = 0; i < groups.length; i++) {
String group = groups[i];
channelGroupSubscriptions.removeItem(group);
channelGroupLeave(group);
}
disconnectAndResubscribe();
}
/**
* Unsubscribe from all channel.
*/
public void unsubscribeAllChannels() {
String[] channels = channelSubscriptions.getItemNames();
for (int i = 0; i < channels.length; i++) {
String channel = channels[i];
channelSubscriptions.removeItem(channel);
leave(channel);
}
disconnectAndResubscribe();
}
/**
* Unsubscribe from all channel groups.
*/
public void channelGroupUnsubscribeAllGroups() {
String[] groups = channelGroupSubscriptions.getItemNames();
for (int i = 0; i < groups.length; i++) {
String group = groups[i];
channelGroupSubscriptions.removeItem(group);
channelGroupLeave(group);
}
disconnectAndResubscribe();
}
/**
* Listen for a message on a channel.
*
* @param args hashtable
* @param callback to call
* @throws PubnubException
*/
protected void subscribe(Hashtable args, Callback callback)
throws PubnubException {
args.put("callback", callback);
subscribe(args);
}
/**
* Listen for a message on a channel.
*
* @param args hashtable
* @throws PubnubException
*/
protected void subscribe(Hashtable args) throws PubnubException {
keepOnlyPluralSubscriptionItems(args);
if (!inputsValid(args)) {
return;
}
_subscribe(args);
}
/**
* Listen for a message on a channel.
*
* @param channels array to listen on
* @param callback to call
* @throws PubnubException
*/
public void subscribe(String[] channels, Callback callback)
throws PubnubException {
subscribe(channels, callback, "0");
}
/**
* Listen for a message on a channel.
*
* @param channels array to listen on
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException
*/
public void subscribe(String[] channels, Callback callback, String timetoken)
throws PubnubException {
Hashtable args = new Hashtable();
args.put("channels", channels);
args.put("callback", callback);
args.put("timetoken", timetoken);
subscribe(args);
}
/**
* Listen for a message on a channel.
*
* @param channels array to listen on
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException Throws PubnubException if Callback is null
*/
public void subscribe(String[] channels, Callback callback, long timetoken)
throws PubnubException {
subscribe(channels, callback, String.valueOf(timetoken));
}
/**
* Listen for a message on a channel.
*
* @param channel name
* @param callback to call
* @throws PubnubException
*/
public void subscribe(String channel, Callback callback)
throws PubnubException {
subscribe(channel, callback, "0");
}
/**
* Listen for a message on a channel.
*
* @param channel name
* @param callback to call
* @throws PubnubException
*/
public void subscribe(String channel, Callback callback, String timetoken)
throws PubnubException {
subscribe(new String[]{channel}, callback, timetoken);
}
/**
* Listen for a message on a channel.
*
* @param channel name
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException
*/
public void subscribe(String channel, Callback callback, long timetoken)
throws PubnubException {
subscribe(channel, callback, String.valueOf(timetoken));
}
/**
* Listen for a message on a channel and on a channel group.
*
* @param channel name
* @param group name
* @param callback to call
* @throws PubnubException
*/
public void subscribe(String channel, String group, Callback callback)
throws PubnubException {
subscribe(channel, group, callback, "0");
}
/**
* Listen for a message on a channel and on a channel group.
*
* @param channel name
* @param group name
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException
*/
public void subscribe(String channel, String group, Callback callback, long timetoken)
throws PubnubException {
subscribe(channel, group, callback, String.valueOf(timetoken));
}
/**
* Listen for a message on a channel and on a channel group.
*
* @param channel name
* @param group name
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException
*/
public void subscribe(String channel, String group, Callback callback, String timetoken)
throws PubnubException {
subscribe(new String[]{channel}, new String[]{group}, callback, timetoken);
}
/**
* Listen for a message on a multiple channels and a single channel group.
*
* @param channels array to listen on
* @param group name
* @param callback to call
* @throws PubnubException
*/
public void subscribe(String[] channels, String group, Callback callback)
throws PubnubException {
subscribe(channels, group, callback, "0");
}
/**
* Listen for a message on a multiple channels and a single channel group.
*
* @param channels array to listen on
* @param group name
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException
*/
public void subscribe(String[] channels, String group, Callback callback, long timetoken)
throws PubnubException {
subscribe(channels, group, callback, String.valueOf(timetoken));
}
/**
* Listen for a message on a multiple channels and a single channel group.
*
* @param channels array to listen on
* @param group name
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException
*/
public void subscribe(String[] channels, String group, Callback callback, String timetoken)
throws PubnubException {
subscribe(channels, new String[]{group}, callback, timetoken);
}
/**
* Listen for a message on a channel and a multiple channel groups.
*
* @param channel name
* @param groups array to listen on
* @param callback to call
* @throws PubnubException
*/
public void subscribe(String channel, String[] groups, Callback callback)
throws PubnubException {
subscribe(channel, groups, callback, "0");
}
/**
* Listen for a message on a channel and a multiple channel groups.
*
* @param channel name
* @param groups array to listen on
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException
*/
public void subscribe(String channel, String[] groups, Callback callback, long timetoken)
throws PubnubException {
subscribe(channel, groups, callback, String.valueOf(timetoken));
}
/**
* Listen for a message on a channel and a multiple channel groups.
*
* @param channel name
* @param groups array to listen on
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException
*/
public void subscribe(String channel, String[] groups, Callback callback, String timetoken)
throws PubnubException {
subscribe(new String[]{channel}, groups, callback, timetoken);
}
/**
* Listen for a message on a multiple channels and a multiple channel groups
*
* @param channels array to listen on
* @param groups array to listen on
* @param callback to call
* @throws PubnubException
*/
public void subscribe(String[] channels, String[] groups, Callback callback)
throws PubnubException {
subscribe(channels, groups, callback, "0");
}
/**
* Listen for a message on a multiple channels and a multiple channel groups
*
* @param channels array to listen on
* @param groups array to listen on
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException
*/
public void subscribe(String[] channels, String[] groups, Callback callback, long timetoken)
throws PubnubException {
subscribe(channels, groups, callback, String.valueOf(timetoken));
}
/**
* Listen for a message on a multiple channels and a multiple channel groups
*
* @param channels array to listen on
* @param groups array to listen on
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException
*/
public void subscribe(String[] channels, String[] groups, Callback callback, String timetoken)
throws PubnubException {
Hashtable args = new Hashtable();
args.put("channels", channels);
args.put("groups", groups);
args.put("callback", callback);
args.put("timetoken", timetoken);
subscribe(args);
}
/**
* Listen for a message on a channel group.
*
* @param group name to subscribe
* @param callback to call
* @throws PubnubException
*/
public void channelGroupSubscribe(String group, Callback callback)
throws PubnubException {
channelGroupSubscribe(group, callback, "0");
}
/**
* Listen for a message on multiple channel groups.
*
* @param groups to subscribe
* @param callback to call
* @throws PubnubException if Callback is null
*/
public void channelGroupSubscribe(String[] groups, Callback callback)
throws PubnubException {
channelGroupSubscribe(groups, callback, "0");
}
/**
* Listen for a message on a channel group.
*
* @param group name to subscribe
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException
*/
public void channelGroupSubscribe(String group, Callback callback, long timetoken)
throws PubnubException {
channelGroupSubscribe(group, callback, String.valueOf(timetoken));
}
/**
* Listen for a message on a channel group.
*
* @param group name to subscribe
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException
*/
public void channelGroupSubscribe(String group, Callback callback, String timetoken)
throws PubnubException {
channelGroupSubscribe(new String[]{group}, callback, timetoken);
}
/**
* Listen for a message on multiple channel group.
*
* @param groups to subscribe
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException
*/
public void channelGroupSubscribe(String[] groups, Callback callback, long timetoken)
throws PubnubException {
channelGroupSubscribe(groups, callback, String.valueOf(timetoken));
}
/**
* Listen for a message on multiple channel group.
*
* @param groups to subscribe
* @param callback to call
* @param timetoken to use for subscribing
* @throws PubnubException
*/
public void channelGroupSubscribe(String[] groups, Callback callback, String timetoken)
throws PubnubException {
Hashtable args = new Hashtable();
args.put("groups", groups);
args.put("callback", callback);
args.put("timetoken", timetoken);
subscribe(args);
}
private void callErrorCallbacks(String[] channelList, PubnubError error) {
for (int i = 0; i < channelList.length; i++) {
String channel = channelList[i];
Callback cb = channelSubscriptions.getItem(channel).callback;
cb.errorCallback(channel, error);
}
}
private void decryptJSONArray(JSONArray messages) throws JSONException, IllegalStateException, IOException, PubnubException {
if (CIPHER_KEY.length() > 0) {
for (int i = 0; i < messages.length(); i++) {
PubnubCrypto pc = new PubnubCrypto(CIPHER_KEY, IV);
String message;
message = pc.decrypt(messages.get(i).toString());
messages.put(i, PubnubUtil.stringToJSON(message));
}
}
}
/**
* @param args
* Hashtable
*/
private void _subscribe(Hashtable args) {
String[] channelList = (String[]) args.get("channels");
String[] groupList = (String[]) args.get("groups");
if (channelList == null) {
channelList = new String[0];
}
if (groupList == null) {
groupList = new String[0];
}
Callback callback = (Callback) args.get("callback");
String timetoken = (String) args.get("timetoken");
if (!_timetoken.equals("0"))
_saved_timetoken = _timetoken;
_timetoken = (timetoken == null) ? "0" : timetoken;
/*
* Scan through the channels array. If a channel does not exist in
* hashtable create a new item with default values. If already exists
* and connected, then return
*/
for (int i = 0; i < channelList.length; i++) {
String channel = channelList[i];
SubscriptionItem channelObj = (SubscriptionItem) channelSubscriptions.getItem(channel);
if (channelObj == null) {
SubscriptionItem ch = new SubscriptionItem(channel, callback);
channelSubscriptions.addItem(ch);
}
}
for (int i = 0; i < groupList.length; i++) {
String group = groupList[i];
SubscriptionItem channelGroupObj = (SubscriptionItem) channelGroupSubscriptions.getItem(group);
if (channelGroupObj == null) {
SubscriptionItem chg = new SubscriptionItem(group, callback);
channelGroupSubscriptions.addItem(chg);
}
}
_subscribe_base(true);
}
private void _subscribe_base(boolean fresh) {
_subscribe_base(fresh, false, null);
}
private void _subscribe_base(boolean fresh, boolean dar) {
_subscribe_base(fresh, dar, null);
}
private void _subscribe_base(Worker worker) {
_subscribe_base(false, false, worker);
}
private void _subscribe_base(boolean fresh, Worker worker) {
_subscribe_base(fresh, false, worker);
}
private boolean isWorkerDead(HttpRequest hreq) {
return (hreq == null || hreq.getWorker() == null)?false:hreq.getWorker()._die;
}
private void _subscribe_base(boolean fresh, boolean dar, Worker worker) {
String channelString = channelSubscriptions.getItemString();
String groupString = channelGroupSubscriptions.getItemString();
String[] channelsArray = channelSubscriptions.getItemNames();
String[] groupsArray = channelGroupSubscriptions.getItemNames();
if (channelsArray.length <= 0 && groupsArray.length <= 0) {
subscribeManager.resetHttpManager();
return;
}
if (channelString == null) {
callErrorCallbacks(channelsArray,
PubnubError.PNERROBJ_PARSING_ERROR);
return;
}
if (channelString.equals("")) {
channelString = ",";
} else {
channelString = PubnubUtil.urlEncode(channelString);
}
String[] urlComponents = { getPubnubUrl(), "subscribe",
PubnubCore.this.SUBSCRIBE_KEY,
channelString, "0", _timetoken
};
Hashtable params = PubnubUtil.hashtableClone(this.params);
params.put("uuid", UUID);
if (groupsArray.length > 0) {
params.put("channel-group", groupString);
}
String st = getState();
if (st != null) params.put("state", st);
if (HEARTBEAT > 5 && HEARTBEAT < 320) params.put("heartbeat", String.valueOf(HEARTBEAT));
log.verbose("Subscribing with timetoken : " + _timetoken);
HttpRequest hreq = new HttpRequest(urlComponents, params,
new ResponseHandler() {
public void handleResponse(HttpRequest hreq, String response) {
/*
* Check if response has channel names. A JSON response
* with more than 2 items means the response contains
* the channel names as well. The channel names are in a
* comma delimted string. Call success callback on all
* he channels passing the corresponding response
* message.
*/
JSONArray jsa;
try {
jsa = new JSONArray(response);
_timetoken = (!_saved_timetoken.equals("0") && isResumeOnReconnect()) ? _saved_timetoken
: jsa.get(1).toString();
log.verbose("Resume On Reconnect is "
+ isResumeOnReconnect());
log.verbose("Saved Timetoken : " + _saved_timetoken);
log.verbose("In Response Timetoken : "
+ jsa.get(1).toString());
log.verbose("Timetoken value set to " + _timetoken);
_saved_timetoken = "0";
log.verbose("Saved Timetoken reset to 0");
if (!hreq.isDar()) {
channelSubscriptions.invokeConnectCallbackOnItems(_timetoken);
channelGroupSubscriptions.invokeConnectCallbackOnItems(_timetoken);
} else {
channelSubscriptions.invokeReconnectCallbackOnItems(_timetoken);
channelGroupSubscriptions.invokeReconnectCallbackOnItems(_timetoken);
}
JSONArray messages = new JSONArray(jsa.get(0).toString());
if (jsa.length() == 4) {
/*
* Response has multiple channels or/and groups
*/
String[] _groups = PubnubUtil.splitString(jsa.getString(2), ",");
String[] _channels = PubnubUtil.splitString(jsa.getString(3), ",");
for (int i = 0; i < _channels.length; i++) {
String _groupName = _groups[i];
String _channelName = _channels[i];
Object message = messages.get(i);
SubscriptionItem _group = channelGroupSubscriptions.getItem(_groups[i]);
SubscriptionItem _channel = channelSubscriptions.getItem(_groups[i]);
if (_groupName.equals(_channelName)
&& _channel != null
&& !isWorkerDead(hreq)
) {
invokeSubscribeCallback(_channelName, _channel.callback,
message, _timetoken, hreq);
} else if (!_groupName.equals(_channelName)
&& _group != null
&& !isWorkerDead(hreq)
) {
invokeSubscribeCallback(_channelName, _group.callback,
message, _timetoken, hreq);
}
}
} else if(jsa.length() == 3) {
/*
* Response has multiple channels
*/
String[] _channels = PubnubUtil.splitString(jsa.getString(2), ",");
for (int i = 0; i < _channels.length; i++) {
SubscriptionItem _channel = channelSubscriptions.getItem(_channels[i]);
Object message = messages.get(i);
if (_channel != null) {
invokeSubscribeCallback(_channel.name, _channel.callback,
message, _timetoken, hreq);
}
}
} else if(jsa.length() < 3) {
/*
* Response for single channel Callback on
* single channel
*/
SubscriptionItem _channel = channelSubscriptions.getFirstItem();
if (_channel != null) {
for (int i = 0; i < messages.length(); i++) {
Object message = messages.get(i);
invokeSubscribeCallback(_channel.name, _channel.callback,
message, _timetoken, hreq);
}
}
}
if (hreq.isSubzero()) {
log.verbose("Response of subscribe 0 request. Need to do dAr process again");
_subscribe_base(false, hreq.isDar(), hreq.getWorker());
} else
_subscribe_base(false);
} catch (JSONException e) {
if (hreq.isSubzero()) {
log.verbose("Response of subscribe 0 request. Need to do dAr process again");
_subscribe_base(false, hreq.isDar(), hreq.getWorker());
} else
_subscribe_base(false, hreq.getWorker());
}
}
public void handleBackFromDar(HttpRequest hreq) {
_subscribe_base(false, hreq.getWorker());
}
public void handleError(HttpRequest hreq, PubnubError error) {
disconnectAndResubscribe(error);
}
public void handleTimeout(HttpRequest hreq) {
log.verbose("Timeout Occurred, Calling disconnect callbacks on the channels");
String timeoutTimetoken = (isResumeOnReconnect()) ? (_timetoken
.equals("0")) ? _saved_timetoken : _timetoken
: "0";
log.verbose("Timeout Timetoken : " + timeoutTimetoken);
channelSubscriptions.invokeDisconnectCallbackOnItems(timeoutTimetoken);
channelGroupSubscriptions.invokeDisconnectCallbackOnItems(timeoutTimetoken);
channelSubscriptions.invokeErrorCallbackOnItems(
PubnubError.getErrorObject(PubnubError.PNERROBJ_TIMEOUT, 1)
);
channelGroupSubscriptions.invokeErrorCallbackOnItems(
PubnubError.getErrorObject(PubnubError.PNERROBJ_TIMEOUT, 1)
);
// disconnectAndResubscribe();
// channelSubscriptions.removeAllItems();
}
public String getTimetoken() {
return _timetoken;
}
});
if (_timetoken.equals("0")) {
hreq.setSubzero(true);
log.verbose("This is a subscribe 0 request");
}
hreq.setDar(dar);
if ( worker != null && worker instanceof Worker )
hreq.setWorker(worker);
_request(hreq, subscribeManager, fresh);
}
private void invokeSubscribeCallback(String channel, Callback callback, Object message, String timetoken,
HttpRequest hreq) throws JSONException {
if (CIPHER_KEY.length() > 0
&& !channel
.endsWith(PRESENCE_SUFFIX)) {
PubnubCrypto pc = new PubnubCrypto(
CIPHER_KEY, IV);
try {
message = pc.decrypt(message.toString());
if (!isWorkerDead(hreq)) callback
.successWrapperCallback(
channel,
PubnubUtil.parseJSON(PubnubUtil.stringToJSON(message.toString())), timetoken);
} catch (IllegalStateException e) {
if (!isWorkerDead(hreq)) callback
.errorCallback(
channel,
PubnubError.getErrorObject(
PubnubError.PNERROBJ_DECRYPTION_ERROR, 12,
message.toString()));
} catch (PubnubException e) {
if (!isWorkerDead(hreq)) callback
.errorCallback(
channel,
getPubnubError(e, PubnubError.PNERROBJ_DECRYPTION_ERROR, 16,
message.toString() + " : " + e.toString()));
} catch (Exception e) {
if (!isWorkerDead(hreq)) callback
.errorCallback(
channel,
PubnubError.getErrorObject(
PubnubError.PNERROBJ_DECRYPTION_ERROR, 15,
message.toString() + " : " + e.toString()));
}
} else {
if (!isWorkerDead(hreq)) callback.successWrapperCallback(
channel,
PubnubUtil.parseJSON(message), timetoken);
}
}
/**
* @param hreq
* @param connManager
* @param abortExisting
*/
private void _request(final HttpRequest hreq, RequestManager connManager,
boolean abortExisting) {
if (abortExisting) {
connManager.resetHttpManager();
}
connManager.queue(hreq);
}
/**
* @param hreq
* @param simpleConnManager
*/
protected void _request(final HttpRequest hreq,
RequestManager simpleConnManager) {
_request(hreq, simpleConnManager, false);
}
private int getRandom() {
return Math.abs(this.generator.nextInt());
}
private void changeOrigin() {
this.ORIGIN_STR = null;
this.HOSTNAME_SUFFIX = getRandom();
}
private void resubscribe() {
changeOrigin();
if (!_timetoken.equals("0"))
_saved_timetoken = _timetoken;
_timetoken = "0";
log.verbose("Before Resubscribe Timetoken : " + _timetoken);
log.verbose("Before Resubscribe Saved Timetoken : " + _saved_timetoken);
_subscribe_base(true, true);
}
private void resubscribe(String timetoken) {
changeOrigin();
if (!timetoken.equals("0"))
_saved_timetoken = timetoken;
_timetoken = "0";
log.verbose("Before Resubscribe Timetoken : " + _timetoken);
log.verbose("Before Resubscribe Saved Timetoken : " + _saved_timetoken);
_subscribe_base(true, true);
}
/**
* Disconnect from all channels, and resubscribe
*
*/
public void disconnectAndResubscribeWithTimetoken(String timetoken) {
disconnectAndResubscribeWithTimetoken(timetoken,
PubnubError.PNERROBJ_DISCONN_AND_RESUB);
}
/**
* Disconnect from all channels, and resubscribe
*
*/
public void disconnectAndResubscribeWithTimetoken(String timetoken,
PubnubError error) {
log.verbose("Received disconnectAndResubscribeWithTimetoken");
channelSubscriptions.invokeErrorCallbackOnItems(error);
channelGroupSubscriptions.invokeErrorCallbackOnItems(error);
resubscribe(timetoken);
}
/**
* Disconnect from all channels, and resubscribe
*
*/
public void disconnectAndResubscribe() {
disconnectAndResubscribe(PubnubError.PNERROBJ_DISCONNECT);
}
/**
* Disconnect from all channels, and resubscribe
*
*/
public void disconnectAndResubscribe(PubnubError error) {
log.verbose("Received disconnectAndResubscribe");
channelSubscriptions.invokeErrorCallbackOnItems(error);
channelGroupSubscriptions.invokeErrorCallbackOnItems(error);
resubscribe();
}
/**
* This method returns array of channel names, currently subscribed to
*
* @return Array of channel names
*/
public String[] getSubscribedChannelsArray() {
return channelSubscriptions.getItemNames();
}
/**
* Sets origin value, default is "pubsub"
*
* @param origin
* Origin value
*/
public void setOrigin(String origin) {
this.HOSTNAME = origin;
}
/**
* Returns origin
* @return origin
*/
public String getOrigin() {
return this.HOSTNAME;
}
/**
* Sets domain value, default is "pubnub.com"
*
* @param domain
* Domain value
*/
public void setDomain(String domain) {
this.DOMAIN = domain;
}
/**
* Returns domain
* @return domain
*/
public String getDomain() {
return this.DOMAIN;
}
/**
* This method returns auth key. Return null if not set
*
* @return Auth Key. null if auth key not set
*/
public String getAuthKey() {
return this.AUTH_STR;
}
/**
* This method sets auth key.
*
* @param authKey
* . 0 length string or null unsets auth key
*/
public void setAuthKey(String authKey) {
this.AUTH_STR = authKey;
if (authKey == null || authKey.length() == 0) {
params.remove("auth");
} else {
params.put("auth", this.AUTH_STR);
}
resubscribe();
}
/**
* This method unsets auth key.
*
*/
public void unsetAuthKey() {
this.AUTH_STR = null;
params.remove("auth");
resubscribe();
}
}