package me.chanjar.weixin.common.session;
import me.chanjar.weixin.common.util.res.StringManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 基于内存的session manager
*/
public class StandardSessionManager implements WxSessionManager, InternalSessionManager {
protected final Logger log = LoggerFactory.getLogger(StandardSessionManager.class);
protected static final StringManager sm =
StringManager.getManager(Constants.Package);
/**
* The set of currently active Sessions for this Manager, keyed by
* session identifier.
*/
protected Map<String, InternalSession> sessions = new ConcurrentHashMap<String, InternalSession>();
@Override
public WxSession getSession(String sessionId) {
return getSession(sessionId, true);
}
@Override
public WxSession getSession(String sessionId, boolean create) {
if (sessionId == null) {
throw new IllegalStateException
(sm.getString("sessionManagerImpl.getSession.ise"));
}
InternalSession session = findSession(sessionId);
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
session.access();
return session.getSession();
}
// Create a new session if requested and the response is not committed
if (!create) {
return (null);
}
session = createSession(sessionId);
if (session == null) {
return null;
}
session.access();
return session.getSession();
}
// -------------------------------------- InternalSessionManager
/**
* The descriptive name of this Manager implementation (for logging).
*/
private static final String name = "SessionManagerImpl";
/**
* The maximum number of active Sessions allowed, or -1 for no limit.
*/
protected int maxActiveSessions = -1;
/**
* Number of session creations that failed due to maxActiveSessions.
*/
protected int rejectedSessions = 0;
/**
* The default maximum inactive interval for Sessions created by
* this Manager.
*/
protected int maxInactiveInterval = 30 * 60;
// Number of sessions created by this manager
protected long sessionCounter=0;
protected volatile int maxActive=0;
private final Object maxActiveUpdateLock = new Object();
/**
* Processing time during session expiration.
*/
protected long processingTime = 0;
/**
* Iteration count for background processing.
*/
private int count = 0;
/**
* Frequency of the session expiration, and related manager operations.
* Manager operations will be done once for the specified amount of
* backgrondProcess calls (ie, the lower the amount, the most often the
* checks will occur).
*/
protected int processExpiresFrequency = 6;
/**
* background processor delay in seconds
*/
protected int backgroundProcessorDelay = 10;
/**
* 后台清理线程是否已经开启
*/
private final AtomicBoolean backgroundProcessStarted = new AtomicBoolean(false);
@Override
public void remove(InternalSession session) {
remove(session, false);
}
@Override
public void remove(InternalSession session, boolean update) {
if (session.getIdInternal() != null) {
sessions.remove(session.getIdInternal());
}
}
@Override
public InternalSession findSession(String id) {
if (id == null)
return (null);
return sessions.get(id);
}
@Override
public InternalSession createSession(String sessionId) {
if (sessionId == null) {
throw new IllegalStateException
(sm.getString("sessionManagerImpl.createSession.ise"));
}
if ((maxActiveSessions >= 0) &&
(getActiveSessions() >= maxActiveSessions)) {
rejectedSessions++;
throw new TooManyActiveSessionsException(
sm.getString("sessionManagerImpl.createSession.tmase"),
maxActiveSessions);
}
// Recycle or create a Session instance
InternalSession session = createEmptySession();
// Initialize the properties of the new session and return it
session.setValid(true);
session.setCreationTime(System.currentTimeMillis());
session.setMaxInactiveInterval(this.maxInactiveInterval);
String id = sessionId;
session.setId(id);
sessionCounter++;
return (session);
}
@Override
public int getActiveSessions() {
return sessions.size();
}
@Override
public InternalSession createEmptySession() {
return (getNewSession());
}
/**
* Get new session class to be used in the doLoad() method.
*/
protected InternalSession getNewSession() {
return new StandardSession(this);
}
@Override
public void add(InternalSession session) {
// 当第一次有session创建的时候,开启session清理线程
if (!backgroundProcessStarted.getAndSet(true)) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
// 每秒清理一次
Thread.sleep(backgroundProcessorDelay * 1000l);
backgroundProcess();
} catch (InterruptedException e) {
log.error("SessionManagerImpl.backgroundProcess error", e);
}
}
}
});
t.setDaemon(true);
t.start();
}
sessions.put(session.getIdInternal(), session);
int size = getActiveSessions();
if( size > maxActive ) {
synchronized(maxActiveUpdateLock) {
if( size > maxActive ) {
maxActive = size;
}
}
}
}
/**
* Return the set of active Sessions associated with this Manager.
* If this Manager has no active Sessions, a zero-length array is returned.
*/
@Override
public InternalSession[] findSessions() {
return sessions.values().toArray(new InternalSession[0]);
}
@Override
public void backgroundProcess() {
count = (count + 1) % processExpiresFrequency;
if (count == 0)
processExpires();
}
/**
* Invalidate all sessions that have expired.
*/
public void processExpires() {
long timeNow = System.currentTimeMillis();
InternalSession sessions[] = findSessions();
int expireHere = 0 ;
if(log.isDebugEnabled())
log.debug("Start expire sessions {} at {} sessioncount {}", getName(), timeNow, sessions.length);
for (int i = 0; i < sessions.length; i++) {
if (sessions[i]!=null && !sessions[i].isValid()) {
expireHere++;
}
}
long timeEnd = System.currentTimeMillis();
if(log.isDebugEnabled())
log.debug("End expire sessions {} processingTime {} expired sessions: {}", getName(), timeEnd - timeNow, expireHere);
processingTime += ( timeEnd - timeNow );
}
@Override
public void setMaxInactiveInterval(int interval) {
this.maxInactiveInterval = interval;
}
/**
* Set the manager checks frequency.
*
* @param processExpiresFrequency the new manager checks frequency
*/
@Override
public void setProcessExpiresFrequency(int processExpiresFrequency) {
if (processExpiresFrequency <= 0) {
return;
}
this.processExpiresFrequency = processExpiresFrequency;
}
@Override
public void setBackgroundProcessorDelay(int backgroundProcessorDelay) {
this.backgroundProcessorDelay = backgroundProcessorDelay;
}
/**
* Return the descriptive short name of this Manager implementation.
*/
public String getName() {
return (name);
}
/**
* Set the maximum number of active Sessions allowed, or -1 for
* no limit.
*
* @param max The new maximum number of sessions
*/
@Override
public void setMaxActiveSessions(int max) {
this.maxActiveSessions = max;
}
}