
ibt.ortc.extensibility.OrtcClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of messaging Show documentation
Show all versions of messaging Show documentation
Realtime Cloud Messaging (ORTC) SDK for Java
/**
* @fileoverview This file contains the ortc client abstract class
* @author ORTC team members ([email protected])
*/
package ibt.ortc.extensibility;
import ibt.ortc.api.Balancer;
import ibt.ortc.api.InvalidBalancerServerException;
import ibt.ortc.api.OnDisablePresence;
import ibt.ortc.api.OnEnablePresence;
import ibt.ortc.api.OnPresence;
import ibt.ortc.api.Ortc;
import ibt.ortc.api.Pair;
import ibt.ortc.api.Proxy;
import ibt.ortc.api.Strings;
import ibt.ortc.extensibility.exception.OrtcAlreadyConnectedException;
import ibt.ortc.extensibility.exception.OrtcDoesNotHavePermissionException;
import ibt.ortc.extensibility.exception.OrtcEmptyFieldException;
import ibt.ortc.extensibility.exception.OrtcInvalidCharactersException;
import ibt.ortc.extensibility.exception.OrtcMaxLengthException;
import ibt.ortc.extensibility.exception.OrtcNotConnectedException;
import ibt.ortc.extensibility.exception.OrtcNotSubscribedException;
import ibt.ortc.extensibility.exception.OrtcSubscribedException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.Set;
/**
* Abstract class representing an Ortc Client
*
*
* How to use in android:
*
*
* try {
* Ortc ortc = new Ortc();
*
* OrtcFactory factory;
*
* factory = ortc.loadOrtcFactory("IbtRealtimeSJ");
*
* client = factory.createClient();
*
* HashMap<String, ChannelPermissions> permissions = new HashMap<String, ChannelPermissions>();
* permissions.put("channel1:*", ChannelPermissions.Write);
* permissions.put("channel1", ChannelPermissions.Write);
*
* if (!Ortc.saveAuthentication(
* "http://ortc-developers.realtime.co/server/2.1/", true,
* "SessionId", false, "APPKEY", 1800, "PVTKEY", permissions)) {
* throw new Exception("Was not possible to authenticate");
* }
*
* client.setClusterUrl(defaultServerUrl);
* client.setConnectionMetadata("DroidApp");
*
* client.onConnected = new OnConnected() {
* @Override
* public void run(final Object sender) {
* runOnUiThread(new Runnable() {
* @Override
* public void run() {
* TextView t = ((TextView) findViewById(R.id.TextViewTitle));
* t.setText("Client connected to: "
* + ((OrtcClient) sender).getUrl());
* }
* });
* }
* };
*
* client.onDisconnected = new OnDisconnected() {
* @Override
* public void run(Object arg0) {
* runOnUiThread(new Runnable() {
* @Override
* public void run() {
* TextView t = ((TextView) findViewById(R.id.TextViewTitle));
* t.setText("Client disconnected");
* }
* });
* }
* };
*
* client.onSubscribed = new OnSubscribed() {
* @Override
* public void run(Object sender, String channel) {
* final String subscribedChannel = channel;
* runOnUiThread(new Runnable() {
* @Override
* public void run() {
* TextView textViewLog = (TextView) findViewById(R.id.TextViewLog);
* textViewLog.append(String.format("Channel subscribed %s\n",
* subscribedChannel));
* }
* });
* }
* };
*
* client.onUnsubscribed = new OnUnsubscribed() {
* @Override
* public void run(Object sender, String channel) {
* final String subscribedChannel = channel;
* runOnUiThread(new Runnable() {
* @Override
* public void run() {
* TextView textViewLog = (TextView) findViewById(R.id.TextViewLog);
* textViewLog.append(String.format(
* "Channel unsubscribed %s\n", subscribedChannel));
* }
* });
* }
* };
*
* client.onException = new OnException() {
* @Override
* public void run(Object send, Exception ex) {
* final Exception exception = ex;
* runOnUiThread(new Runnable() {
* @Override
* public void run() {
* TextView textViewLog = (TextView) findViewById(R.id.TextViewLog);
* textViewLog.append(String.format("Ortc Error: %s\n",
* exception.getMessage()));
* }
* });
* }
* };
*
* client.onReconnected = new OnReconnected() {
* @Override
* public void run(final Object sender) {
* runOnUiThread(new Runnable() {
* @Override
* public void run() {
* reconnectingTries = 0;
* TextView textViewLog = (TextView) findViewById(R.id.TextViewTitle);
* textViewLog.setText("Client reconnected to: "
* + ((OrtcClient) sender).getUrl());
* }
* });
* }
* };
*
* client.onReconnecting = new OnReconnecting() {
* @Override
* public void run(Object sender) {
* runOnUiThread(new Runnable() {
* @Override
* public void run() {
* reconnectingTries++;
* TextView textViewLog = (TextView) findViewById(R.id.TextViewTitle);
* textViewLog.setText(String.format("Client reconnecting %s",
* reconnectingTries));
* }
* });
* }
* };
*
* client.connect(defaultApplicationKey, defaultAuthenticationToken);
*
* } catch (Exception e) {
* System.out.println("ORTC ERROR: " + e.toString());
* }
*
*
*
* How to use in java:
*
*
* try {
* boolean isBalancer = true;
*
* Ortc api = new Ortc();
*
* OrtcFactory factory = api.loadOrtcFactory("IbtRealtimeSJ");
*
* final OrtcClient client = factory.createClient();
*
* if (isBalancer) {
* client.setClusterUrl(serverUrl);
* } else {
* client.setUrl(serverUrl);
* }
*
* System.out.println(String.format("Connecting to server %s", serverUrl));
*
* client.onConnected = new OnConnected() {
* @Override
* public void run(Object sender) {
* System.out
* .println(String.format("Connected to %s", client.getUrl()));
*
* client.subscribe("channel1", true, new OnMessage() {
* @Override
* public void run(Object sender, String channel, String message) {
* System.out.println(String.format(
* "Message received on channel %s: '%s'", channel,
* message));
*
* ((OrtcClient) sender).send(channel, "Echo " + message);
* }
* });
* }
* };
*
* client.onException = new OnException() {
* @Override
* public void run(Object send, Exception ex) {
* System.out.println(String.format("Error: '%s'", ex.toString()));
* }
* };
*
* client.onDisconnected = new OnDisconnected() {
* @Override
* public void run(Object sender) {
* System.out.println("Disconnected");
* }
* };
*
* client.onReconnected = new OnReconnected() {
* @Override
* public void run(Object sender) {
* System.out.println(String.format("Reconnected to %s",
* client.getUrl()));
* }
* };
*
* client.onReconnecting = new OnReconnecting() {
* @Override
* public void run(Object sender) {
* System.out.println(String.format("Reconnecting to %s",
* client.getUrl()));
* }
* };
*
* client.onSubscribed = new OnSubscribed() {
* @Override
* public void run(Object sender, String channel) {
* System.out.println(String.format("Subscribed to channel %s",
* channel));
* }
* };
*
* client.onUnsubscribed = new OnUnsubscribed() {
* @Override
* public void run(Object sender, String channel) {
* System.out.println(String.format("Unsubscribed from channel %s",
* channel));
* }
* };
*
* System.out.println("Connecting...");
* client.connect("APPLICATION_KEY", "AUTHENTICATION_TOKEN");
*
* } catch (Exception e) {
* System.out.println("ORTC ERROR: " + e.toString());
* }
*
*
* @version 2.1.0 23 Mar 2013
* @author IBT
*
*/
public abstract class OrtcClient {
// ========== Constants ==========
protected static final int MAX_MESSAGE_SIZE = 800;
protected static final int MAX_CHANNEL_SIZE = 100;
public static final int MAX_CONNECTION_METADATA_SIZE = 256;
protected static final int CONNECTION_TIMEOUT_DEFAULT_VALUE = 5000;
// ========== Constants ==========
// ========== Enumerators ==========
private enum ChannelPermission {
Read, Write
}
// ========== Enumerators ==========
// ========== Private Classes ==========
public static class BufferedMessage implements Comparable {
private int messagePart;
private String content;
// CAUSE: Constructor is declared public in non-public class
private BufferedMessage(int messagePart, String content) {
this.messagePart = messagePart;
this.content = content;
}
public String getContent() {
return content;
}
@Override
// CAUSE: Class defines compareTo(...) and uses Object.equals()
public int compareTo(BufferedMessage o) {
int result = o.messagePart == this.messagePart ? 0
: o.messagePart > this.messagePart ? -1 : 1;
return result;
}
@Override
// CAUSE: Class defines compareTo(...) and uses Object.equals()
public boolean equals(Object o) {
return o instanceof BufferedMessage && super.equals(o);
}
@Override
// CAUSE: Class defines compareTo(...) and uses Object.equals()
public int hashCode() {
return super.hashCode();
}
}
// ========== Private Classes ==========
// ========== Pseudo Delegates ==========
/**
* Event fired when a connection is established
*/
public OnConnected onConnected;
/**
* Event fired when a connection is closed
*/
public OnDisconnected onDisconnected;
/**
* Event fired when a exception occurs
*/
public OnException onException;
/**
* Event fired when a connection is reestablished after being closed
* unexpectedly
*/
public OnReconnected onReconnected;
/**
* Event fired when a connection is trying to be reestablished after being
* closed unexpectedly
*/
public OnReconnecting onReconnecting;
/**
* Event fired when a channel is subscribed
*/
public OnSubscribed onSubscribed;
/**
* Event fired when a channel is unsubscribed
*/
public OnUnsubscribed onUnsubscribed;
// ========== Pseudo Delegates ==========
// ========== Properties ==========
protected String clusterUrl;
protected String url;
protected String connectionMetadata;
protected String announcementSubChannel;
protected String applicationKey;
protected String authenticationToken;
protected URI uri;
protected int connectionTimeout;
protected int id;
protected ConnectionProtocol protocol;
protected Hashtable subscribedChannels;
protected Hashtable channelsPermissions;
private Hashtable> multiPartMessagesBuffer;
private boolean isCluster;
protected boolean isConnected;
protected boolean isDisconnecting;
protected boolean isReconnecting;
protected boolean isConnecting;
protected Proxy proxy;
protected static final int heartbeatMaxTime = 60;
protected static final int heartbeatMinTime = 10;
protected static final int heartbeatMaxFails = 6;
protected static final int heartbeatMinFails = 1;
protected boolean heartbeatActive = false;
protected int heartbeatFails = 3;
protected int heartbeatTime = 15;
protected HeartbeatSender heartbeatSender = null;
// ========== Properties ==========
// ========== Construtctor ==========
/**
* Creates an instance of Ortc Client
*/
public OrtcClient() {
this.connectionTimeout = CONNECTION_TIMEOUT_DEFAULT_VALUE;
this.isConnected = false;
this.isCluster = false;
this.isDisconnecting = false;
this.isReconnecting = false;
this.isConnecting = false;
this.proxy = null;
this.subscribedChannels = new Hashtable(11);
this.channelsPermissions = new Hashtable(11);
this.multiPartMessagesBuffer = new Hashtable>(
11);
}
// ========== Construtctor ==========
/**
* Connects the ortc client to the url previously specified
*
*
* client.setClusterUrl(defaultServerUrl);
* client.connect("APPKEY", "PVTKEY");
*
*
* @param applicationKey
* Application Key provided by the Ortc Services
* @param authenticationToken
* Authentication Token representing the connection. This should
* be generated by the application, such as session id for
* example.
*/
public void connect(final String applicationKey, final String authenticationToken) {
/*
* Sanity checks
*/
if (isConnected) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcAlreadyConnectedException());
} else if (Strings.isNullOrEmpty(clusterUrl)
&& Strings.isNullOrEmpty(url)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcEmptyFieldException("URL"));
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcEmptyFieldException("Cluster URL"));
} else if (Strings.isNullOrEmpty(applicationKey)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcEmptyFieldException("Application Key"));
} else if (Strings.isNullOrEmpty(authenticationToken)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcEmptyFieldException("Authentication Token"));
} else if (!isCluster && !Strings.ortcIsValidUrl(url)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcInvalidCharactersException("URL"));
} else if (isCluster && !Strings.ortcIsValidUrl(clusterUrl)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcInvalidCharactersException("Cluster URL"));
} else if (!Strings.ortcIsValidInput(applicationKey)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcInvalidCharactersException("Application Key"));
} else if (!Strings.ortcIsValidInput(authenticationToken)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcInvalidCharactersException("Authentication Token"));
} else if (!Strings.isNullOrEmpty(announcementSubChannel)
&& !Strings.ortcIsValidInput(announcementSubChannel)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcInvalidCharactersException(
"Announcement Subchannel"));
} else if (!Strings.isNullOrEmpty(connectionMetadata)
&& connectionMetadata.length() > MAX_CONNECTION_METADATA_SIZE) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcMaxLengthException("Connection metadata",
MAX_CONNECTION_METADATA_SIZE));
} else if (isConnecting && !isReconnecting) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcNotConnectedException("Already trying to connect"));
} else {
final OrtcClient self = this;
new Thread(new Runnable() {
@Override
public void run() {
Exception e = null;
try {
if(!isReconnecting){
self.isConnecting = true;
}
self.applicationKey = applicationKey;
self.authenticationToken = authenticationToken;
if (self.isCluster) {
String clusterServer = Balancer.getServerFromBalancer(
self.clusterUrl, self.applicationKey, self.proxy);
self.setUrl(clusterServer);
self.isCluster = true;
}
self.uri = new URI(self.url);
self.protocol = "http".equals(uri.getScheme()) ? ConnectionProtocol.Unsecure
: ConnectionProtocol.Secure;
self.connect();
}
catch (IllegalArgumentException ex) {
e = ex;
}
catch (URISyntaxException ex) {
e = ex;
} catch (MalformedURLException ex) {
e = ex;
} catch (IOException ex) {
e = ex;
} catch (InvalidBalancerServerException ex) {
e = ex;
} finally {
if (e != null) {
self.isReconnecting = true;
self.raiseOrtcEvent(EventEnum.OnException, self, new Exception("Unable to connect"));
self.raiseOrtcEvent(EventEnum.OnReconnecting, self);
}
}
}
}).start();
}
}
protected abstract void connect();
/**
* Closes the current connection
*/
public void disconnect() {
if(!isConnected){
if(this.isReconnecting){
stopReconnecting();
this.isConnecting = false;
} else {
raiseOrtcEvent(EventEnum.OnException,this, new OrtcNotConnectedException());
}
}else{
stopHeartBeatInterval();
this.disconnectIntern();
}
}
protected abstract void disconnectIntern();
private Pair channelHasPermission(String channelName,
ChannelPermission permission) {
Pair result = new Pair(true, null);
if (channelsPermissions.size() > 0) {
int domainChannelCharacterIndex = channelName.indexOf(':');
String channelToValidate = channelName;
if (domainChannelCharacterIndex > 0) {
channelToValidate = String.format("%s%s", channelName
.substring(0, domainChannelCharacterIndex + 1), "*");
}
String hash = channelsPermissions.get(channelName);
if (Strings.isNullOrEmpty(hash)) {
hash = channelsPermissions.get(channelToValidate);
}
result = new Pair(!Strings.isNullOrEmpty(hash),
hash);
}
if (!result.first) {
String errorMessage = String
.format("No permission found to %s to the channel %s",
permission == ChannelPermission.Read ? "subscribe"
: "send", channelName);
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcDoesNotHavePermissionException(errorMessage));
}
return result;
}
private Pair isSendValid(String channelName, String message, String applicationKey, String privateKey) {
// NOTE: Sanity check for send method
Pair result = new Pair(true, null);
if (!isConnected) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcNotConnectedException());
result.first = false;
} else if (Strings.isNullOrEmpty(channelName)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcEmptyFieldException("Channel"));
result.first = false;
} else if (!Strings.ortcIsValidInput(channelName)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcInvalidCharactersException("Channel"));
result.first = false;
} else if (Strings.isNullOrEmpty(message)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcEmptyFieldException("Message"));
result.first = false;
} else if (channelName.length() > MAX_CHANNEL_SIZE) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcMaxLengthException("Channel", MAX_CHANNEL_SIZE));
result.first = false;
}
if (result.first && Strings.isNullOrEmpty(applicationKey) && Strings.isNullOrEmpty(privateKey)) {
Pair channelPermission = channelHasPermission(
channelName, ChannelPermission.Write);
result.first = channelPermission.first;
result.second = channelPermission.second;
}
return result;
}
/**
* Sends a message to the specified channel.
*
* @param channel
* Channel to wich the message should be sent
* @param message
* The content of the message to be sent
*/
public void send(String channel, String message) {
// CAUSE: Assignment to method parameter
Pair sendValidation = isSendValid(channel, message, null, null);
//String lMessage = message.replace("\n", "\\n");
if (sendValidation != null && sendValidation.first) {
try {
String messageId = Strings.randomString(8);
ArrayList> messagesToSend = multiPartMessage(
message, messageId);
for (Pair messageToSend : messagesToSend) {
send(channel, messageToSend.second, messageToSend.first,
sendValidation.second);
}
} catch (IOException e) {
raiseOrtcEvent(EventEnum.OnException, this, e);
}
}
}
public void sendProxy(String applicationKey, String privateKey, String channel, String message) {
// CAUSE: Assignment to method parameter
Pair sendValidation = isSendValid(channel, message, applicationKey,privateKey);
//String lMessage = message.replace("\n", "\\n");
if (sendValidation != null && sendValidation.first) {
try {
String messageId = Strings.randomString(8);
ArrayList> messagesToSend = multiPartMessage(
message, messageId);
for (Pair messageToSend : messagesToSend) {
send(applicationKey,privateKey, channel, messageToSend.second, messageToSend.first,
sendValidation.second);
}
} catch (IOException e) {
raiseOrtcEvent(EventEnum.OnException, this, e);
}
}
}
private ArrayList> multiPartMessage(String message,
String messageId) throws IOException {
// CAUSE: Reliance on default encoding
byte[] messageBytes = message.getBytes("UTF-8");
// CAUSE: Instantiating collection without specified initial capacity
ArrayList> messageParts = new ArrayList>(
10);
int totalParts = (messageBytes.length % MAX_MESSAGE_SIZE) == 0 ? (messageBytes.length / MAX_MESSAGE_SIZE)
: (messageBytes.length / MAX_MESSAGE_SIZE) + 1;
int messagePartIndex = 1;
int currentPosition = 0;
do {
int messagePartSize = messageBytes.length - currentPosition > MAX_MESSAGE_SIZE ? MAX_MESSAGE_SIZE
: messageBytes.length - currentPosition;
if (messagePartSize > 0) {
byte[] messagePartBytes = new byte[messagePartSize];
System.arraycopy(messageBytes, currentPosition,
messagePartBytes, 0, messagePartSize);
String messagePartIdentifier = String.format("%s_%s-%s",
messageId, messagePartIndex, totalParts);
messageParts.add(new Pair(
// CAUSE: Reliance on default encoding
messagePartIdentifier, new String(messagePartBytes,
"UTF-8")));
}
currentPosition += messagePartSize;
messagePartIndex++;
} while (currentPosition < messageBytes.length);
return messageParts;
}
protected abstract void send(String channel, String message,
String messagePartIdentifier, String permission);
protected abstract void send(String applicationKey, String privateKey, String channel, String message,
String messagePartIdentifier, String permission);
private Pair isSubscribeValid(String channelName,
ChannelSubscription channel) {
// NOTE: Sanity check for subscribe method
Pair result = new Pair(true, null);
// CAUSE: Unused assignment
Exception error;
if (!isConnected) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcNotConnectedException());
result.first = false;
} else if (Strings.isNullOrEmpty(channelName)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcEmptyFieldException("Channel"));
result.first = false;
} else if (!Strings.ortcIsValidInput(channelName)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcInvalidCharactersException("Channel"));
result.first = false;
} else if (channel != null && channel.isSubscribing()) {
raiseOrtcEvent(
EventEnum.OnException,
this,
new OrtcSubscribedException(String.format(
"Already subscribing to the channel %s",
channelName)));
result.first = false;
} else if (channel != null && channel.isSubscribed()) {
error = new OrtcSubscribedException(String.format(
"Already subscribed to the channel %s", channelName));
raiseOrtcEvent(EventEnum.OnException, this, error);
result.first = false;
} else if (channelName.length() > MAX_CHANNEL_SIZE) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcMaxLengthException("Channel", MAX_CHANNEL_SIZE));
result.first = false;
}
if (result.first) {
Pair channelPermission = channelHasPermission(
channelName, ChannelPermission.Read);
result.first = channelPermission.first;
result.second = channelPermission.second;
}
return result;
}
/**
* Subscribe the specified channel in order to receive messages in that
* channel
*
* @param channel
* Channel to be subscribed
* @param subscribeOnReconnect
* Indicates if the channel should be subscribe if the event on
* reconnected is fired
* @param onMessage
* Event handler that will be called when a message will be
* received on the subscribed channel
*/
public void subscribe(String channel, boolean subscribeOnReconnect,
OnMessage onMessage) {
ChannelSubscription subscribedChannel = subscribedChannels.get(channel);
Pair subscribeValidation = isSubscribeValid(channel,
subscribedChannel);
if (subscribeValidation != null && subscribeValidation.first) {
subscribedChannel = new ChannelSubscription(subscribeOnReconnect,
onMessage);
subscribedChannel.setSubscribing(true);
subscribedChannels.put(channel, subscribedChannel);
subscribe(channel, subscribeValidation.second);
}
}
protected abstract void subscribe(String channel, String permission);
private boolean isUnsubscribeValid(String channelName,
ChannelSubscription channel) {
boolean result = true;
if (!isConnected) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcNotConnectedException());
result = false;
} else if (channel == null) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcEmptyFieldException("Channel"));
result = false;
} else if (!Strings.ortcIsValidInput(channelName)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcInvalidCharactersException("Channel"));
result = false;
} else if (!channel.isSubscribed()) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcNotSubscribedException(channelName));
result = false;
} else if (channelName.length() > MAX_CHANNEL_SIZE) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcMaxLengthException("Channel", MAX_CHANNEL_SIZE));
result = false;
}
return result;
}
/**
* Stop receiving messages in the specified channel
*
* @param channel
* Channel to be unsubscribed
*/
public void unsubscribe(String channel) {
ChannelSubscription subscribedChannel = subscribedChannels.get(channel);
if (isUnsubscribeValid(channel, subscribedChannel)) {
subscribedChannel.setSubscribeOnReconnected(false);
unsubscribe(channel, true);
}
}
protected abstract void unsubscribe(String channel, boolean isValid);
protected void stopReconnecting() {
this.isDisconnecting = false;
this.isReconnecting = false;
}
protected void cancelSubscription(String channel) {
if (!Strings.isNullOrEmpty(channel)
&& subscribedChannels.containsKey(channel)) {
subscribedChannels.get(channel).setSubscribing(false);
}
}
/**
* Gets the subscriptions in the specified channel and if active the first
* 100 unique metadata.
*
*
* Ortc.presence("CHANNEL", new onPresence() {
*
* public void run(Exception error, Presence presence) {
* if (error != null) {
* System.out.println(error.getMessage());
* } else {
* System.out.println("Subscriptions - " + presence.getSubscriptions());
*
* Iterator<?> metadataIterator = presence.getMetadata().entrySet()
* .iterator();
* while (metadataIterator.hasNext()) {
* Map.Entry<String, Long> entry = (Map.Entry<String, Long>) metadataIterator
* .next();
* System.out.println(entry.getKey() + " - " + entry.getValue());
* }
* }
* }
* });
*
*
* @param channel
* Channel with presence data active.
* @param callback
* Callback with error and result.
* @throws ibt.ortc.extensibility.exception.OrtcNotConnectedException
*/
public void presence(String channel, OnPresence callback)
throws OrtcNotConnectedException {
if (!this.isConnected) {
throw new OrtcNotConnectedException();
} else {
String presenceUrl = this.isCluster ? this.clusterUrl : this.url;
Ortc.presence(presenceUrl, this.isCluster, this.applicationKey, this.authenticationToken, channel, this.proxy, callback);
}
}
/**
* Enables presence for the specified channel with first 100 unique metadata
* if metadata is set to true.
*
*
* Ortc.enablePresence("PRIVATE_KEY", "CHANNEL", true, new onEnablePresence() {
*
* public void run(Exception error, String result) {
* if (error != null) {
* System.out.println(error.getMessage());
* } else {
* System.out.println(result);
*
* }
* }
* });
*
*
* @param privateKey
* The private key provided when the ORTC service is purchased.
* @param channel
* Channel with presence data active.
* @param metadata
* Defines if to collect first 100 unique metadata.
* @param callback
* Callback with error and result.
* @throws ibt.ortc.extensibility.exception.OrtcNotConnectedException
*/
public void enablePresence(String privateKey, String channel,
Boolean metadata, OnEnablePresence callback)
throws OrtcNotConnectedException {
if (!this.isConnected) {
throw new OrtcNotConnectedException();
} else {
String presenceUrl = this.isCluster ? this.clusterUrl : this.url;
Ortc.enablePresence(presenceUrl, this.isCluster, this.applicationKey, privateKey, channel, metadata, this.proxy, callback);
}
}
/**
* Disables presence for the specified channel.
*
*
* Ortc.disablePresence("PRIVATE_KEY", "CHANNEL", new onDisablePresence() {
*
* public void run(Exception error, String result) {
* if (error != null) {
* System.out.println(error.getMessage());
* } else {
* System.out.println(result);
*
* }
* }
* });
*
*
* @param privateKey
* The private key provided when the ORTC service is purchased.
* @param channel
* Channel to disable presence
* @param callback
* Callback with error and result.
* @throws ibt.ortc.extensibility.exception.OrtcNotConnectedException
*/
public void disablePresence(String privateKey, String channel,
OnDisablePresence callback) throws OrtcNotConnectedException {
if (!this.isConnected) {
throw new OrtcNotConnectedException();
} else {
String presenceUrl = this.isCluster ? this.clusterUrl : this.url;
Ortc.disablePresence(presenceUrl, this.isCluster, this.applicationKey, privateKey, channel, this.proxy, callback);
}
}
// ========== Getters and Setters ==========
/**
* Indicates if the channel is subscribed
*
* @param channel
* Channel to check
* @return boolean True if the channel is subscribed otherwise false
*/
public Boolean isSubscribed(String channel) {
Boolean result = null;
/*
* Sanity checks
*/
if (!isConnected) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcNotConnectedException());
} else if (Strings.isNullOrEmpty(channel)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcEmptyFieldException("Channel"));
} else if (!Strings.ortcIsValidInput(channel)) {
raiseOrtcEvent(EventEnum.OnException, this,
new OrtcInvalidCharactersException("Channel"));
} else {
ChannelSubscription subscribedChannel = subscribedChannels
.get(channel);
result = subscribedChannel != null
&& subscribedChannel.isSubscribed();
}
return result;
}
/**
* Gets the Ortc client connection metadata
*
* @return String Connection metadata content
*/
public String getConnectionMetadata() {
return this.connectionMetadata;
}
/**
* Sets the client connection metadata
*
* @param connectionMetadata
* Connection metada content
*/
public void setConnectionMetadata(String connectionMetadata) {
this.connectionMetadata = connectionMetadata;
}
/**
* Gets the announcement sub channel to where the announcement messages are
* going to be sent
*
* @return String The announcement sub channel
*/
public String getAnnouncementSubChannel() {
return this.announcementSubChannel;
}
/**
* Sets the announcement sub channel to where the announcement messages are
* going to be sent
*
* @param channel
* Announcement sub channel
*/
public void setAnnouncementSubChannel(String channel) {
announcementSubChannel = channel;
}
/**
* Gets the cluster gateway url to wich the Ortc client is connecting
*
* @return String The cluster gateway url
*/
public String getClusterUrl() {
return this.clusterUrl;
}
/**
* Sets the cluster gateway url to wich the Ortc client should connect
*
* @param clusterUrl
* Ortc cluster gateway url
*/
public void setClusterUrl(String clusterUrl) {
this.isCluster = true;
this.clusterUrl = Strings.treatUrl(clusterUrl);
}
/**
* Gets the Ortc server url to wich the Ortc client is connecting
*
* @return String Ortc server url
*/
public String getUrl() {
return this.url;
}
/**
* Sets the Ortc server url to wich the Ortc client should connect
*
* @param url
* Ortc server url
*/
public void setUrl(String url) {
this.isCluster = false;
this.url = Strings.treatUrl(url);
}
/**
* Gets the connection timeout before trying a new reconnection attempt
*
* @return int Reconnect connection timeout
*/
public int getConnectionTimeout() {
return this.connectionTimeout;
}
/**
* Sets the connection timeout before trying a new reconnection attempt
*
* @param connectionTimeout
* Reconnect connection timeout
*/
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
/**
* Gets Ortc client unique identifier
*
* @return int Ortc client unique identifier
*/
public int getId() {
return this.id;
}
/**
* Sets Ortc client unique identifier
*
* @param id
* Ortc client unique identifier
*/
public void setId(int id) {
this.id = id;
}
/**
* Indicates if the ortc client is connected
*
* @return boolean True if is connected otherwise false
*/
public boolean getIsConnected() {
return this.isConnected;
}
/**
* Defines the proxy connection settings for the ortc client
*
* @param proxyHost Proxy host
* @param proxyPort Proxy port
*/
public void setProxy(String proxyHost, int proxyPort){
setProxy(proxyHost, proxyPort, null, null);
}
/**
* sets proxy, optionally using basic authentication
*
* @param host proxy host
* @param port proxy port
* @param user proxy user or null when no authentication is needed
* @param pwd proxy password or null when no authentication is needed
*/
public void setProxy(String host, int port, String user, String pwd) {
this.proxy = new Proxy(host, port, user, pwd);
}
public void setProxy(Proxy proxy) {
this.proxy = proxy;
}
// ========== Getters and Setters ==========
// ========== Raise of events ==========
protected void raiseOrtcEvent(EventEnum eventToRaise, Object... args) {
switch (eventToRaise) {
case OnConnected:
raiseOnConnected(args);
break;
case OnDisconnected:
raiseOnDisconnected(args);
break;
case OnException:
raiseOnException(args);
break;
case OnReconnected:
raiseOnReconnected(args);
break;
case OnReconnecting:
raiseOnReconnecting(args);
break;
case OnSubscribed:
raiseOnSubscribed(args);
break;
case OnUnsubscribed:
raiseOnUnsubscribed(args);
break;
case OnReceived:
raiseOnReceived(args);
break;
}
}
private void raiseOnConnected(Object... args) {
this.isConnected = true;
this.isDisconnecting = false;
if (isReconnecting && !isConnecting) {
raiseOrtcEvent(EventEnum.OnReconnected, args);
} else {
this.isConnecting = false;
if (onConnected != null) {
OrtcClient sender = args != null
&& args.length == 1 ? (OrtcClient) args[0] : null;
onConnected.run(sender);
startHeartBeatInterval();
}
}
}
private void raiseOnDisconnected(Object... args) {
stopHeartBeatInterval();
OrtcClient sender = args != null && args.length == 1 ? (OrtcClient) args[0]
: null;
this.channelsPermissions = new Hashtable(11);
if (isDisconnecting || isConnecting) {
this.isConnected = false;
this.isDisconnecting = false;
this.isConnecting = false;
this.subscribedChannels = new Hashtable(
11);
if (onDisconnected != null) {
onDisconnected.run(sender);
}
} else {
if(!isReconnecting){
if (onDisconnected != null) {
onDisconnected.run(sender);
}
}
raiseOrtcEvent(EventEnum.OnReconnecting, args);
}
}
private void raiseOnException(Object... args) {
if (onException != null) {
OrtcClient sender = args != null && args.length == 2 ? (OrtcClient) args[0]
: null;
Exception exception = args != null && args.length == 2 ? (Exception) args[1]
: null;
onException.run(sender, exception);
}
}
private void raiseOnReconnected(Object... args) {
this.isReconnecting = false;
LinkedList channelsToRemove = new LinkedList();
Set subscribedChannelsSet = this.subscribedChannels.keySet();
for (String channelName : subscribedChannelsSet) {
ChannelSubscription subscribedChannel = this.subscribedChannels
.get(channelName);
if (subscribedChannel.subscribeOnReconnected()) {
subscribedChannel.setSubscribing(true);
subscribedChannel.setSubscribed(false);
Pair channelPermission = channelHasPermission(
channelName, ChannelPermission.Read);
if (channelPermission != null && channelPermission.first) {
subscribe(channelName, channelPermission.second);
}
} else {
channelsToRemove.add(channelName);
}
}
for (String channelToRemove : channelsToRemove) {
this.subscribedChannels.remove(channelToRemove);
}
if (onReconnected != null) {
OrtcClient sender = args != null && args.length == 1 ? (OrtcClient) args[0]
: null;
onReconnected.run(sender);
startHeartBeatInterval();
}
}
private void raiseOnReconnecting(Object... args) {
stopHeartBeatInterval();
if (isReconnecting) {
try {
Thread.sleep(this.connectionTimeout);
} catch (InterruptedException e) {
raiseOrtcEvent(EventEnum.OnException, this, e);
}
}
this.isConnected = false;
this.isDisconnecting = false;
this.isReconnecting = true;
if (onReconnecting != null) {
OrtcClient sender = args != null && args.length == 1 ? (OrtcClient) args[0]
: null;
onReconnecting.run(sender);
}
this.connect(this.applicationKey, this.authenticationToken);
}
private void raiseOnSubscribed(Object... args) {
OrtcClient sender = args != null && args.length == 2 ? (OrtcClient) args[0]
: null;
String channel = args != null && args.length == 2 ? (String) args[1]
: null;
ChannelSubscription subscribeChannel = subscribedChannels.get(channel);
subscribeChannel.setSubscribed(true);
subscribeChannel.setSubscribing(false);
if (onSubscribed != null) {
onSubscribed.run(sender, channel);
}
}
private void raiseOnUnsubscribed(Object... args) {
OrtcClient sender = args != null && args.length == 2 ? (OrtcClient) args[0]
: null;
String channel = args != null && args.length == 2 ? (String) args[1]
: null;
ChannelSubscription subscribeChannel = subscribedChannels.get(channel);
subscribeChannel.setSubscribed(false);
subscribeChannel.setSubscribing(false);
if (onUnsubscribed != null) {
onUnsubscribed.run(sender, channel);
}
}
private void raiseOnReceived(Object... args) {
String channel = args != null && args.length == 5 ? (String) args[0]
: null;
String message = args != null && args.length == 5 ? (String) args[1]
: null;
String messageId = args != null && args.length == 5 ? (String) args[2]
: null;
// CAUSE: Possible null pointer dereference
Integer messagePart = args != null && args.length == 5 ? (Integer) args[3]
: null;
// CAUSE: Possible null pointer dereference
Integer messageTotalParts = args != null && args.length == 5 ? (Integer) args[4]
: null;
if (messagePart != null && messagePart == -1
|| (messagePart != null && messageTotalParts != null)
&& (messagePart == 1 && messageTotalParts == 1)) {
ChannelSubscription subscription = subscribedChannels.get(channel);
if (subscription != null) {
OnMessage onMessageEventHandler = subscription.getOnMessage();
if (onMessageEventHandler != null) {
message = CharEscaper.removeEsc(message);
onMessageEventHandler.run(this, channel, message);
try {
if (messageId != null
&& multiPartMessagesBuffer
.containsKey(messageId)) {
multiPartMessagesBuffer.remove(messageId);
}
} catch (Exception e) {
raiseOrtcEvent(EventEnum.OnException, this, e);
}
}
}
} else {
if (!multiPartMessagesBuffer.containsKey(messageId)) {
multiPartMessagesBuffer.put(messageId,
new LinkedList());
}
// CAUSE: Possible null pointer dereference
if (multiPartMessagesBuffer.get(messageId) != null) {
multiPartMessagesBuffer.get(messageId).add(
new BufferedMessage(messagePart, message));
}
LinkedList messageParts = multiPartMessagesBuffer
.get(messageId);
// CAUSE: Possible null pointer dereference
if (messageParts != null
&& messageParts.size() == messageTotalParts) {
Collections.sort(messageParts);
String fullMessage = "";
for (BufferedMessage part : messageParts) {
fullMessage = String.format("%s%s", fullMessage,
part.getContent());
}
raiseOnReceived(channel, fullMessage, messageId, -1, -1);
}
}
}
/**
* Get if heartbeat active.
*
* @return if heartbeat is active.
*/
public boolean getHeartbeatActive() {
return heartbeatActive;
}
/**
* Set heart beat active. Heart beat provides better accuracy for presence data.
*
* @param active
* true to activate heartbeat and false to deactivate.
*/
public void setHeartbeatActive(boolean active) {
this.heartbeatActive = active;
}
/**
* Get how many times can the client fail the heartbeat.
*
* @return amount of fails the heartbeat can have before disconnecting.
*/
public int getHeartbeatFails() {
return heartbeatFails;
}
/**
* Set heartbeat fails. Defines how many times can the client fail the heartbeat.
*
* @param newHeartbeatFails
*/
public void setHeartbeatFails(int newHeartbeatFails) {
if (newHeartbeatFails > 0) {
if (newHeartbeatFails > heartbeatMaxFails
|| newHeartbeatFails < heartbeatMinFails) {
raiseOrtcEvent(EventEnum.OnException, this, new Exception(
"Heartbeat fails is out of limits - Min: "
+ heartbeatMinFails + " | Max: "
+ heartbeatMaxFails));
} else {
heartbeatFails = newHeartbeatFails;
}
} else {
raiseOrtcEvent(EventEnum.OnException, this, new Exception(
"Invalid heartbeat fails " + newHeartbeatFails));
}
}
/**
* Get heartbeat interval.
*
* @return interval between heartbeats.
*/
public int getHeartbeatTime() {
return heartbeatTime;
}
/**
* Set heartbeat fails. Defines how many times can the client fail the heartbeat.
*
* @param newHeartbeatTime
*/
public void setHeartbeatTime(int newHeartbeatTime) {
if (newHeartbeatTime > 0) {
if (newHeartbeatTime > heartbeatMaxTime
|| newHeartbeatTime < heartbeatMinTime) {
raiseOrtcEvent(EventEnum.OnException, this, new Exception(
"Heartbeat time is out of limits - Min: "
+ heartbeatMinTime + " | Max: "
+ heartbeatMaxTime));
} else {
heartbeatTime = newHeartbeatTime;
}
} else {
raiseOrtcEvent(EventEnum.OnException, this, new Exception(
"Invalid heartbeat time " + newHeartbeatTime));
}
}
public void startHeartBeatInterval() {
if (heartbeatSender == null && heartbeatActive) {
heartbeatSender = new HeartbeatSender(this);
}
}
public void stopHeartBeatInterval() {
if (heartbeatSender != null) {
heartbeatSender.stop();
heartbeatSender = null;
}
}
protected abstract void sendHeartbeat();
// ========== Raise of events ==========
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy