package com.github.ompc.greys.core.server;
import com.github.ompc.greys.core.GlobalOptions;
import com.github.ompc.greys.core.advisor.AdviceWeaver;
import com.github.ompc.greys.core.util.LogUtil;
import org.slf4j.Logger;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import static com.github.ompc.greys.core.util.GaStringUtils.DEFAULT_PROMPT;
import static java.lang.System.currentTimeMillis;
import static org.apache.commons.io.IOUtils.closeQuietly;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/**
* 服务端会话
* Created by oldmanpushcart@gmail.com on 15/5/2.
*/
public class Session {
private final Logger logger = LogUtil.getLogger();
// 会话锁ID序列
private final static AtomicInteger lockTxSeq = new AtomicInteger();
// 空锁
private final static int LOCK_TX_EMPTY = -1;
private final int javaPid;
private final int sessionId;
private final long sessionDuration;
private final SocketChannel socketChannel;
private Charset charset;
// 是否会话静默,静默的会话不输出提示符,LOGO,同时也会影响一些命令的输出
private boolean silent = true;
// 提示符
private String prompt = DEFAULT_PROMPT;
// 会话最后一次交互时间(触摸时间)
private volatile long gmtLastTouch;
// 是否被销毁
private volatile boolean isDestroy = false;
// 会话锁ID
private final AtomicInteger lockTx = new AtomicInteger(LOCK_TX_EMPTY);
// 会话输出阻塞队列
private final BlockingQueue<String> writeQueue = new LinkedBlockingQueue<String>(GlobalOptions.sessionWriteQueueCapacity);
/**
* 构建Session
*
* @param javaPid Java进程ID
* @param sessionId 会话ID
* @param sessionDuration 会话持续时间(单位毫秒)
* @param socketChannel socket
* @param charset 会话字符集
*/
Session(int javaPid, int sessionId, long sessionDuration, SocketChannel socketChannel, Charset charset) {
this.javaPid = javaPid;
this.sessionId = sessionId;
this.sessionDuration = sessionDuration;
this.socketChannel = socketChannel;
this.charset = charset;
this.gmtLastTouch = currentTimeMillis();
}
/**
* 销毁会话
*/
public void destroy() {
isDestroy = true;
closeQuietly(socketChannel);
logger.info("session[{}] destroyed.", sessionId);
}
/**
* 触摸会话<br/>
* 超时触摸的会话有可能会被超时
*/
public void touch() {
gmtLastTouch = currentTimeMillis();
}
/**
* 会话是否到期
*
* @return true:会话到期 / false:会话尚未到期
*/
public boolean isExpired() {
return sessionDuration <= currentTimeMillis() - gmtLastTouch;
}
/**
* 锁定会话
*
* @return true : 锁定成功 / false : 锁定失败
*/
public boolean tryLock() {
return lockTx.compareAndSet(LOCK_TX_EMPTY, lockTxSeq.getAndIncrement());
}
/**
* 解锁会话
*/
public void unLock() {
final int currentLockTx = lockTx.get();
// 如果当前锁已经是LOCK_TX_EMPTY,则没有必要继续执行
if (LOCK_TX_EMPTY == currentLockTx) {
return;
}
if (!lockTx.compareAndSet(currentLockTx, LOCK_TX_EMPTY)) {
// 能到这一步说明是lock()/unLock()编写出错,需要开发排查
throw new IllegalStateException();
}
// 解锁的时候需要清理输出队列
writeQueue.clear();
// 取消监听注册
AdviceWeaver.unReg(currentLockTx);
}
/**
* 当前会话是否已经被锁定
*
* @return true : 锁定 / false : 未锁定
*/
public boolean isLocked() {
return lockTx.get() != LOCK_TX_EMPTY;
}
/**
* 获取锁
*
* @return 返回锁ID
*/
public int getLock() {
return lockTx.get();
}
/**
* 当前会话是否已经被销毁
*
* @return true : 销毁 / false : 违背销毁
*/
public boolean isDestroy() {
return isDestroy;
}
/**
* 获取提示符<br/>
* 这里主要是要求增加上\r
*
* @return 可以正常绘制的提示符
*/
public String prompt() {
return isNotBlank(getPrompt())
? "\r" + getPrompt()
: EMPTY;
}
/**
* 获取会话提示符
*
* @return 会话提示符
*/
public String getPrompt() {
return prompt;
}
/**
* 设置会话提示符
*
* @param prompt 会话提示符
*/
public void setPrompt(String prompt) {
this.prompt = prompt;
}
/**
* 获取会话写入队列
*
* @return 写入队列
*/
public BlockingQueue<String> getWriteQueue() {
return writeQueue;
}
public int getSessionId() {
return sessionId;
}
public Charset getCharset() {
return charset;
}
public SocketChannel getSocketChannel() {
return socketChannel;
}
public void setCharset(Charset charset) {
this.charset = charset;
}
public long getSessionDuration() {
return sessionDuration;
}
public int getJavaPid() {
return javaPid;
}
public boolean isSilent() {
return silent;
}
public void setSilent(boolean silent) {
this.silent = silent;
}
}