Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
cn.leancloud.session.AVSession Maven / Gradle / Ivy
package cn.leancloud.session;
import cn.leancloud.AVException;
import cn.leancloud.AVLogger;
import cn.leancloud.command.*;
import cn.leancloud.core.AppConfiguration;
import cn.leancloud.im.*;
import cn.leancloud.im.v2.AVIMClient;
import cn.leancloud.im.v2.AVIMMessage;
import cn.leancloud.im.v2.Conversation;
import cn.leancloud.im.v2.Conversation.AVIMOperation;
import cn.leancloud.session.AVIMOperationQueue.Operation;
import cn.leancloud.utils.LogUtil;
import cn.leancloud.utils.StringUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
public class AVSession {
private static final AVLogger LOGGER = LogUtil.getLogger(AVSession.class);
public static final int REALTIME_TOKEN_WINDOW_INSECONDS = 300;
static final int OPERATION_OPEN_SESSION = 10004;
static final int OPERATION_CLOSE_SESSION = 10005;
static final int OPERATION_UNKNOW = -1;
private static final String ERROR_INVALID_SESSION_ID = "Null id in session id list.";
/**
* 用于 read 的多端同步
*/
private final String LAST_NOTIFY_TIME = "lastNotifyTime";
/**
* 用于 patch 的多端同步
*/
private final String LAST_PATCH_TIME = "lastPatchTime";
/**
* 用于存储相关联的 AVUser 的 sessionToken
*/
private final String AVUSER_SESSION_TOKEN = "avuserSessionToken";
/**
* client id
*/
private final String selfId;
/**
* client tag(optional)
*/
private String tag;
/**
* AVUser session token(only for AVIMClient.open with AVUser)
*/
private String userSessionToken = null;
/**
* RTM sessionToken(only available after AVIMClient.open)
*/
private String realtimeSessionToken = null;
/**
* RTM sessionToken expired timestamp.
*/
private long realtimeSessionTokenExpired = 0l;
/**
* last notified time.
*/
private long lastNotifyTime = 0;
/**
* last patch time.
*/
private long lastPatchTime = 0;
public enum Status{
Opened, Closed, Resuming
}
private volatile Status currentStatus = Status.Closed;
// 标识是否需要从缓存恢复
private final AtomicBoolean sessionResume = new AtomicBoolean(false);
private final AtomicLong lastServerAckReceived = new AtomicLong(0);
PendingMessageCache pendingMessages;
AVIMOperationQueue conversationOperationCache;
private final ConcurrentMap conversationHolderCache =
new ConcurrentHashMap();
final AVSessionListener sessionListener;
private final AVConnectionListener websocketListener;
public AVConnectionListener getWebSocketListener() {
return websocketListener;
}
public AVSession(String selfId, AVSessionListener sessionListener) {
this.selfId = selfId;
this.sessionListener = sessionListener;
pendingMessages = new PendingMessageCache(selfId, PendingMessageCache.Message.class);
conversationOperationCache = new AVIMOperationQueue(selfId);
this.websocketListener = new AVDefaultConnectionListener(this);
}
public boolean setSessionResume(boolean flag) {
return this.sessionResume.getAndSet(flag);
}
public void setSessionStatue(Status curStatus) {
this.currentStatus = curStatus;
}
public boolean isResume() {
return this.sessionResume.get();
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public void open(final String clientTag, final String sessionToken, boolean isReconnection, final int requestId) {
this.tag = clientTag;
updateUserSessionToken(sessionToken);
try {
boolean connectionEstablished = AVConnectionManager.getInstance().isConnectionEstablished();
if (!connectionEstablished) {
sessionListener.onError(AVSession.this, new IllegalStateException(
"Connection Lost"), OPERATION_OPEN_SESSION, requestId);
return;
}
if (Status.Opened == currentStatus) {
sessionListener.onSessionOpen(AVSession.this, requestId);
return;
}
openWithSignature(requestId, isReconnection, true);
} catch (Exception ex) {
sessionListener.onError(AVSession.this, ex, OPERATION_OPEN_SESSION, requestId);
}
}
void reopen() {
String rtmSessionToken = AVSessionCacheHelper.IMSessionTokenCache.getIMSessionToken(getSelfPeerId());
if (!StringUtil.isEmpty(rtmSessionToken)) {
openWithSessionToken(rtmSessionToken);
} else {
int requestId = WindTalker.getNextIMRequestId();
openWithSignature(requestId, true, false);
}
}
public void renewRealtimeSesionToken(final int requestId) {
final SignatureCallback callback = new SignatureCallback() {
@Override
public void onSignatureReady(Signature sig, AVException exception) {
if (null != exception) {
LOGGER.d("failed to generate signaure. cause:", exception);
} else {
SessionControlPacket scp = SessionControlPacket.genSessionCommand(
getSelfPeerId(), null,
SessionControlPacket.SessionControlOp.RENEW_RTMTOKEN, sig,
getLastNotifyTime(), getLastPatchTime(), requestId);
scp.setTag(tag);
scp.setSessionToken(realtimeSessionToken);
AVConnectionManager.getInstance().sendPacket(scp);
}
}
@Override
public Signature computeSignature() throws SignatureFactory.SignatureException {
SignatureFactory signatureFactory = AVIMOptions.getGlobalOptions().getSignatureFactory();
if (null == signatureFactory && !StringUtil.isEmpty(getUserSessionToken())) {
signatureFactory = new AVUserSignatureFactory(getUserSessionToken());
}
if (null != signatureFactory) {
return signatureFactory.createSignature(getSelfPeerId(), new ArrayList());
}
return null;
}
};
new SignatureTask(callback, getSelfPeerId()).start();
}
void updateRealtimeSessionToken(String sessionToken, int expireInSec) {
this.realtimeSessionToken = sessionToken;
this.realtimeSessionTokenExpired = System.currentTimeMillis() + expireInSec * 1000;
AVIMClient.getInstance(this.getSelfPeerId()).updateRealtimeSessionToken(sessionToken, this.realtimeSessionTokenExpired/1000);
if (StringUtil.isEmpty(sessionToken)) {
AVSessionCacheHelper.IMSessionTokenCache.removeIMSessionToken(getSelfPeerId());
} else {
AVSessionCacheHelper.IMSessionTokenCache.addIMSessionToken(getSelfPeerId(), sessionToken,
realtimeSessionTokenExpired);
}
}
public boolean realtimeSessionTokenExpired() {
long now = System.currentTimeMillis()/1000;
return (now + REALTIME_TOKEN_WINDOW_INSECONDS) >= this.realtimeSessionTokenExpired;
};
private void openWithSessionToken(String rtmSessionToken) {
SessionControlPacket scp = SessionControlPacket.genSessionCommand(
this.getSelfPeerId(), null, SessionControlPacket.SessionControlOp.OPEN,
null, this.getLastNotifyTime(), this.getLastPatchTime(), null);
scp.setSessionToken(rtmSessionToken);
scp.setReconnectionRequest(true);
AVConnectionManager.getInstance().sendPacket(scp);
}
private void openWithSignature(final int requestId, final boolean reconnectionFlag,
final boolean notifyListener) {
final SignatureCallback callback = new SignatureCallback() {
@Override
public void onSignatureReady(Signature sig, AVException exception) {
if (null != exception) {
if (notifyListener) {
sessionListener.onError(AVSession.this, exception,
OPERATION_OPEN_SESSION, requestId);
}
LOGGER.d("failed to generate signaure. cause:", exception);
} else {
conversationOperationCache.offer(AVIMOperationQueue.Operation.getOperation(
Conversation.AVIMOperation.CLIENT_OPEN.getCode(), getSelfPeerId(), null, requestId));
CommandPacket scp = WindTalker.getInstance().assembleSessionOpenPacket(getSelfPeerId(), tag, sig, getLastNotifyTime(),
getLastPatchTime(), reconnectionFlag, requestId);
AVConnectionManager.getInstance().sendPacket(scp);
}
}
@Override
public Signature computeSignature() throws SignatureFactory.SignatureException {
SignatureFactory signatureFactory = AVIMOptions.getGlobalOptions().getSignatureFactory();
if (null == signatureFactory && !StringUtil.isEmpty(getUserSessionToken())) {
signatureFactory = new AVUserSignatureFactory(getUserSessionToken());
}
if (null != signatureFactory) {
return signatureFactory.createSignature(getSelfPeerId(), new ArrayList());
}
return null;
}
};
new SignatureTask(callback, getSelfPeerId()).start();
}
public void close() {
close(CommandPacket.UNSUPPORTED_OPERATION);
}
public void cleanUp() {
updateRealtimeSessionToken("", 0);
if (pendingMessages != null) {
pendingMessages.clear();
}
if (conversationOperationCache != null) {
this.conversationOperationCache.clear();
}
this.conversationHolderCache.clear();
MessageReceiptCache.clean(this.getSelfPeerId());
}
public void close(int requestId) {
try {
// 都关掉了,我们需要去除Session记录
AVSessionCacheHelper.getTagCacheInstance().removeSession(getSelfPeerId());
AVSessionCacheHelper.IMSessionTokenCache.removeIMSessionToken(getSelfPeerId());
// 如果session都已不在,缓存消息静静地等到桑田沧海
this.cleanUp();
if (Status.Closed == currentStatus) {
this.sessionListener.onSessionClose(this, requestId);
return;
}
if (AVConnectionManager.getInstance().isConnectionEstablished()) {
conversationOperationCache.offer(Operation.getOperation(
AVIMOperation.CLIENT_DISCONNECT.getCode(), selfId, null, requestId));
CommandPacket scp = WindTalker.getInstance().assembleSessionPacket(this.selfId, null,
SessionControlPacket.SessionControlOp.CLOSE, null, requestId);
AVConnectionManager.getInstance().sendPacket(scp);
} else {
// 如果网络已经断开的时候,我们就不要管它了,直接强制关闭吧
this.sessionListener.onSessionClose(this, requestId);
}
} catch (Exception e) {
sessionListener.onError(this, e,
OPERATION_CLOSE_SESSION, requestId);
}
}
protected void storeMessage(PendingMessageCache.Message cacheMessage, int requestId) {
pendingMessages.offer(cacheMessage);
conversationOperationCache.offer(Operation.getOperation(
AVIMOperation.CONVERSATION_SEND_MESSAGE.getCode(), getSelfPeerId(), cacheMessage.cid,
requestId));
}
public String getSelfPeerId() {
return this.selfId;
}
protected void setServerAckReceived(long lastAckReceivedTimestamp) {
lastServerAckReceived.set(lastAckReceivedTimestamp);
}
public void queryOnlinePeers(List peerIds, int requestId) {
SessionControlPacket scp =
SessionControlPacket.genSessionCommand(this.selfId, peerIds,
SessionControlPacket.SessionControlOp.QUERY, null, requestId);
AVConnectionManager.getInstance().sendPacket(scp);
}
public void queryConversations(Map params, int requestId) {
if (Status.Closed == currentStatus) {
RuntimeException se = new RuntimeException("Connection Lost");
InternalConfiguration.getOperationTube().onOperationCompleted(getSelfPeerId(), null, requestId,
AVIMOperation.CONVERSATION_QUERY, se);
return;
}
conversationOperationCache.offer(Operation.getOperation(
AVIMOperation.CONVERSATION_QUERY.getCode(), selfId, null, requestId));
LOGGER.d("offer operation with requestId=" + requestId + ", selfId=" + selfId);
ConversationQueryPacket packet = ConversationQueryPacket.getConversationQueryPacket(getSelfPeerId(),
params, requestId);
AVConnectionManager.getInstance().sendPacket(packet);
}
public AVException checkSessionStatus() {
if (Status.Closed == currentStatus) {
return new AVException(AVException.OPERATION_FORBIDDEN,
"Please call AVIMClient.open() first");
} else if (Status.Resuming == currentStatus) {
return new AVException(new RuntimeException("Connecting to server"));
} else if (!AVConnectionManager.getInstance().isConnectionEstablished()) {
return new AVException(new RuntimeException("Connection Lost"));
} else {
return null;
}
}
public Status getCurrentStatus() {
return this.currentStatus;
}
public AVConversationHolder getConversationHolder(String conversationId, int convType) {
AVConversationHolder conversation = conversationHolderCache.get(conversationId);
if (conversation != null) {
return conversation;
} else {
conversation = new AVConversationHolder(conversationId, this, convType);
AVConversationHolder elderObject =
conversationHolderCache.putIfAbsent(conversationId, conversation);
return elderObject == null ? conversation : elderObject;
}
}
public void removeConversation(String conversationId) {
conversationHolderCache.remove(conversationId);
}
public void createConversation(final List members,
final Map attributes,
final boolean isTransient, final boolean isUnique, final boolean isTemp, final int tempTTL,
final boolean isSystem, final int requestId) {
if (!AVConnectionManager.getInstance().isConnectionEstablished()) {
RuntimeException se = new RuntimeException("Connection Lost");
sessionListener.onError(this, se, Conversation.AVIMOperation.CONVERSATION_CREATION.getCode(),
requestId);
return;
}
SignatureCallback callback = new SignatureCallback() {
@Override
public Signature computeSignature() throws SignatureFactory.SignatureException {
SignatureFactory signatureFactory = AVIMOptions.getGlobalOptions().getSignatureFactory();
if (signatureFactory != null) {
return signatureFactory.createSignature(selfId, members);
}
return null;
}
@Override
public void onSignatureReady(Signature sig, AVException e) {
if (e == null) {
conversationOperationCache.offer(Operation.getOperation(
AVIMOperation.CONVERSATION_CREATION.getCode(), getSelfPeerId(), null, requestId));
AVConnectionManager.getInstance().sendPacket(ConversationControlPacket.genConversationCommand(selfId, null,
members, ConversationControlPacket.ConversationControlOp.START, attributes, sig,
isTransient, isUnique, isTemp, tempTTL, isSystem, requestId));
} else {
InternalConfiguration.getOperationTube().onOperationCompleted(getSelfPeerId(), null, requestId,
AVIMOperation.CONVERSATION_CREATION, e);
}
}
};
new SignatureTask(callback, getSelfPeerId()).start();
}
long getLastNotifyTime() {
if (lastNotifyTime <= 0) {
lastNotifyTime = AppConfiguration.getDefaultSetting().getLong(selfId,
LAST_NOTIFY_TIME, 0L);
}
return lastNotifyTime;
}
void updateLastNotifyTime(long notifyTime) {
long currentTime = getLastNotifyTime();
if (notifyTime > currentTime) {
lastNotifyTime = notifyTime;
AppConfiguration.getDefaultSetting().saveLong(selfId, LAST_NOTIFY_TIME, notifyTime);
}
}
/**
* 获取最后接收到 server patch 的时间
* 按照业务需求,当本地没有缓存此数据时,返回最初始的客户端值
* @return
*/
long getLastPatchTime() {
if (lastPatchTime <= 0) {
lastPatchTime = AppConfiguration.getDefaultSetting().getLong(selfId,
LAST_PATCH_TIME, 0L);
}
if (lastPatchTime <= 0) {
lastPatchTime = System.currentTimeMillis();
AppConfiguration.getDefaultSetting().saveLong(selfId, LAST_PATCH_TIME, lastPatchTime);
}
return lastPatchTime;
}
void updateLastPatchTime(long patchTime) {
long currentTime = getLastPatchTime();
if (patchTime > currentTime) {
lastPatchTime = patchTime;
AppConfiguration.getDefaultSetting().saveLong(selfId, LAST_PATCH_TIME, patchTime);
}
}
String getUserSessionToken() {
if (StringUtil.isEmpty(userSessionToken)) {
userSessionToken = AppConfiguration.getDefaultSetting().getString(selfId,
AVUSER_SESSION_TOKEN, "");
}
return userSessionToken;
}
void updateUserSessionToken(String token) {
userSessionToken = token;
if (!StringUtil.isEmpty(userSessionToken)) {
AppConfiguration.getDefaultSetting().saveString(selfId, AVUSER_SESSION_TOKEN,
userSessionToken);
}
}
/**
* 确认客户端已经拉取到未推送到本地的离线消息
* 因为没有办法判断哪些消息是离线消息,所以对所有拉取到的消息都发送 ack
* @param messages
* @param conversationId
*/
public void sendUnreadMessagesAck(ArrayList messages, String conversationId) {
if (AVIMOptions.getGlobalOptions().isOnlyPushCount() && null != messages && messages.size() > 0) {
Long largestTimeStamp = 0L;
for (AVIMMessage message : messages) {
if (largestTimeStamp < message.getTimestamp()) {
largestTimeStamp = message.getTimestamp();
}
}
AVConnectionManager.getInstance().sendPacket(ConversationAckPacket.getConversationAckPacket(getSelfPeerId(),
conversationId, largestTimeStamp));
}
}
}