抄了apache tomcat的源代码里关于Session, StandardSession, Manager, StandardManager中关于Session管理部分的代码。master
@@ -0,0 +1,31 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package me.chanjar.weixin.common.session; | |||
/** | |||
* Manifest constants for the <code>org.apache.catalina.session</code> | |||
* package. | |||
* | |||
* @author Craig R. McClanahan | |||
*/ | |||
public class Constants { | |||
public static final String Package = "me.chanjar.weixin.common.session"; | |||
} |
@@ -0,0 +1,73 @@ | |||
package me.chanjar.weixin.common.session; | |||
/** | |||
* Created by qianjia on 15/1/21. | |||
*/ | |||
public interface InternalSession { | |||
/** | |||
* Return the <code>HttpSession</code> for which this object | |||
* is the facade. | |||
*/ | |||
WxSession getSession(); | |||
/** | |||
* Set the <code>isValid</code> flag for this session. | |||
* | |||
* @param isValid The new value for the <code>isValid</code> flag | |||
*/ | |||
public void setValid(boolean isValid); | |||
/** | |||
* Return the <code>isValid</code> flag for this session. | |||
*/ | |||
boolean isValid(); | |||
/** | |||
* Return the session identifier for this session. | |||
*/ | |||
String getIdInternal(); | |||
/** | |||
* Perform the internal processing required to invalidate this session, | |||
* without triggering an exception if the session has already expired. | |||
*/ | |||
void expire(); | |||
/** | |||
* Update the accessed time information for this session. This method | |||
* should be called by the context when a request comes in for a particular | |||
* session, even if the application does not reference it. | |||
*/ | |||
void access(); | |||
/** | |||
* Set the <code>isNew</code> flag for this session. | |||
* | |||
* @param isNew The new value for the <code>isNew</code> flag | |||
*/ | |||
void setNew(boolean isNew); | |||
/** | |||
* Set the creation time for this session. This method is called by the | |||
* Manager when an existing Session instance is reused. | |||
* | |||
* @param time The new creation time | |||
*/ | |||
void setCreationTime(long time); | |||
/** | |||
* Set the default maximum inactive interval (in seconds) | |||
* for Sessions created by this Manager. | |||
* | |||
* @param interval The new default value | |||
*/ | |||
void setMaxInactiveInterval(int interval); | |||
/** | |||
* Set the session identifier for this session. | |||
* | |||
* @param id The new session identifier | |||
*/ | |||
void setId(String id); | |||
} |
@@ -0,0 +1,43 @@ | |||
package me.chanjar.weixin.common.session; | |||
import java.util.Enumeration; | |||
/** | |||
* Created by qianjia on 15/1/21. | |||
*/ | |||
public class InternalSessionFacade implements WxSession { | |||
/** | |||
* Wrapped session object. | |||
*/ | |||
private WxSession session = null; | |||
public InternalSessionFacade(WxSession session) { | |||
session = session; | |||
} | |||
@Override | |||
public Object getAttribute(String name) { | |||
return session.getAttribute(name); | |||
} | |||
@Override | |||
public Enumeration<String> getAttributeNames() { | |||
return session.getAttributeNames(); | |||
} | |||
@Override | |||
public void setAttribute(String name, Object value) { | |||
session.setAttribute(name, value); | |||
} | |||
@Override | |||
public void removeAttribute(String name) { | |||
session.removeAttribute(name); | |||
} | |||
@Override | |||
public void invalidate() { | |||
session.invalidate(); | |||
} | |||
} |
@@ -0,0 +1,66 @@ | |||
package me.chanjar.weixin.common.session; | |||
/** | |||
* Created by qianjia on 15/1/21. | |||
*/ | |||
public interface InternalSessionManager { | |||
/** | |||
* Construct and return a new session object, based on the default | |||
* settings specified by this Manager's properties. The session | |||
* id specified will be used as the session id. | |||
* If a new session cannot be created for any reason, return | |||
* <code>null</code>. | |||
* | |||
* @param sessionId The session id which should be used to create the | |||
* new session; if <code>null</code>, a new session id will be | |||
* generated | |||
* @exception IllegalStateException if a new session cannot be | |||
* instantiated for any reason | |||
*/ | |||
public InternalSession createSession(String sessionId); | |||
/** | |||
* Remove this Session from the active Sessions for this Manager. | |||
* | |||
* @param session Session to be removed | |||
*/ | |||
public void remove(InternalSession session); | |||
/** | |||
* Remove this Session from the active Sessions for this Manager. | |||
* | |||
* @param session Session to be removed | |||
* @param update Should the expiration statistics be updated | |||
*/ | |||
public void remove(InternalSession session, boolean update); | |||
/** | |||
* Add this Session to the set of active Sessions for this Manager. | |||
* | |||
* @param session Session to be added | |||
*/ | |||
void add(InternalSession session); | |||
/** | |||
* Returns the number of active sessions | |||
* | |||
* @return number of sessions active | |||
*/ | |||
int getActiveSessions(); | |||
/** | |||
* Get a session from the recycled ones or create a new empty one. | |||
* The PersistentManager manager does not need to create session data | |||
* because it reads it from the Store. | |||
*/ | |||
InternalSession createEmptySession(); | |||
InternalSession[] findSessions(); | |||
/** | |||
* Implements the Manager interface, direct call to processExpires | |||
*/ | |||
public void backgroundProcess(); | |||
} |
@@ -0,0 +1,80 @@ | |||
# Licensed to the Apache Software Foundation (ASF) under one or more | |||
# contributor license agreements. See the NOTICE file distributed with | |||
# this work for additional information regarding copyright ownership. | |||
# The ASF licenses this file to You under the Apache License, Version 2.0 | |||
# (the "License"); you may not use this file except in compliance with | |||
# the License. You may obtain a copy of the License at | |||
# | |||
# http://www.apache.org/licenses/LICENSE-2.0 | |||
# | |||
# Unless required by applicable law or agreed to in writing, software | |||
# distributed under the License is distributed on an "AS IS" BASIS, | |||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
# See the License for the specific language governing permissions and | |||
# limitations under the License. | |||
applicationSession.session.ise=invalid session state | |||
applicationSession.value.iae=null value | |||
fileStore.saving=Saving Session {0} to file {1} | |||
fileStore.loading=Loading Session {0} from file {1} | |||
fileStore.removing=Removing Session {0} at file {1} | |||
fileStore.deleteFailed=Unable to delete file [{0}] which is preventing the creation of the session storage location | |||
fileStore.createFailed=Unable to create directory [{0}] for the storage of session data | |||
JDBCStore.close=Exception closing database connection {0} | |||
JDBCStore.saving=Saving Session {0} to database {1} | |||
JDBCStore.loading=Loading Session {0} from database {1} | |||
JDBCStore.removing=Removing Session {0} at database {1} | |||
JDBCStore.SQLException=SQL Error {0} | |||
serverSession.value.iae=null value | |||
sessionManagerImpl.createRandom=Created random number generator for session ID generation in {0}ms. | |||
sessionManagerImpl.createSession.tmase=createSession: Too many active sessions | |||
sessionManagerImpl.sessionTimeout=Invalid session timeout setting {0} | |||
sessionManagerImpl.getSession.ise=getSession: Session id cannot be null | |||
sessionManagerImpl.expireException=processsExpire: Exception during session expiration | |||
sessionManagerImpl.loading=Loading persisted sessions from {0} | |||
sessionManagerImpl.loading.cnfe=ClassNotFoundException while loading persisted sessions: {0} | |||
sessionManagerImpl.loading.ioe=IOException while loading persisted sessions: {0} | |||
sessionManagerImpl.unloading=Saving persisted sessions to {0} | |||
sessionManagerImpl.unloading.debug=Unloading persisted sessions | |||
sessionManagerImpl.unloading.ioe=IOException while saving persisted sessions: {0} | |||
sessionManagerImpl.unloading.nosessions=No persisted sessions to unload | |||
sessionManagerImpl.managerLoad=Exception loading sessions from persistent storage | |||
sessionManagerImpl.managerUnload=Exception unloading sessions to persistent storage | |||
sessionManagerImpl.createSession.ise=createSession: Session id cannot be null | |||
sessionImpl.attributeEvent=Session attribute event listener threw exception | |||
sessionImpl.bindingEvent=Session binding event listener threw exception | |||
sessionImpl.invalidate.ise=invalidate: Session already invalidated | |||
sessionImpl.isNew.ise=isNew: Session already invalidated | |||
sessionImpl.getAttribute.ise=getAttribute: Session already invalidated | |||
sessionImpl.getAttributeNames.ise=getAttributeNames: Session already invalidated | |||
sessionImpl.getCreationTime.ise=getCreationTime: Session already invalidated | |||
sessionImpl.getThisAccessedTime.ise=getThisAccessedTime: Session already invalidated | |||
sessionImpl.getLastAccessedTime.ise=getLastAccessedTime: Session already invalidated | |||
sessionImpl.getId.ise=getId: Session already invalidated | |||
sessionImpl.getMaxInactiveInterval.ise=getMaxInactiveInterval: Session already invalidated | |||
sessionImpl.getValueNames.ise=getValueNames: Session already invalidated | |||
sessionImpl.logoutfail=Exception logging out user when expiring session | |||
sessionImpl.notSerializable=Cannot serialize session attribute {0} for session {1} | |||
sessionImpl.removeAttribute.ise=removeAttribute: Session already invalidated | |||
sessionImpl.sessionEvent=Session event listener threw exception | |||
sessionImpl.setAttribute.iae=setAttribute: Non-serializable attribute {0} | |||
sessionImpl.setAttribute.ise=setAttribute: Session [{0}] has already been invalidated | |||
sessionImpl.setAttribute.namenull=setAttribute: name parameter cannot be null | |||
sessionImpl.sessionCreated=Created Session id = {0} | |||
persistentManager.loading=Loading {0} persisted sessions | |||
persistentManager.unloading=Saving {0} persisted sessions | |||
persistentManager.expiring=Expiring {0} sessions before saving them | |||
persistentManager.deserializeError=Error deserializing Session {0}: {1} | |||
persistentManager.serializeError=Error serializing Session {0}: {1} | |||
persistentManager.swapMaxIdle=Swapping session {0} to Store, idle for {1} seconds | |||
persistentManager.backupMaxIdle=Backing up session {0} to Store, idle for {1} seconds | |||
persistentManager.backupException=Exception occurred when backing up Session {0}: {1} | |||
persistentManager.tooManyActive=Too many active sessions, {0}, looking for idle sessions to swap out | |||
persistentManager.swapTooManyActive=Swapping out session {0}, idle for {1} seconds too many sessions active | |||
persistentManager.processSwaps=Checking for sessions to swap out, {0} active sessions in memory | |||
persistentManager.activeSession=Session {0} has been idle for {1} seconds | |||
persistentManager.swapIn=Swapping session {0} in from Store | |||
persistentManager.swapInException=Exception in the Store during swapIn: {0} | |||
persistentManager.swapInInvalid=Swapped session {0} is invalid | |||
persistentManager.storeKeysException=Unable to determine the list of session IDs for sessions in the session store, assuming that the store is empty | |||
persistentManager.storeSizeException=Unable to determine the number of sessions in the session store, assuming that the store is empty |
@@ -0,0 +1,294 @@ | |||
package me.chanjar.weixin.common.session; | |||
import me.chanjar.weixin.common.util.res.StringManager; | |||
import java.util.*; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
/** | |||
* Created by qianjia on 15/1/21. | |||
*/ | |||
public class SessionImpl implements WxSession, InternalSession { | |||
/** | |||
* The string manager for this package. | |||
*/ | |||
protected static final StringManager sm = | |||
StringManager.getManager(Constants.Package); | |||
// ------------------------------ WxSession | |||
protected Map<String, Object> attributes = new ConcurrentHashMap<String, Object>(); | |||
@Override | |||
public Object getAttribute(String name) { | |||
if (!isValidInternal()) | |||
throw new IllegalStateException | |||
(sm.getString("sessionImpl.getAttribute.ise")); | |||
if (name == null) return null; | |||
return (attributes.get(name)); | |||
} | |||
@Override | |||
public Enumeration<String> getAttributeNames() { | |||
if (!isValidInternal()) | |||
throw new IllegalStateException | |||
(sm.getString("sessionImpl.getAttributeNames.ise")); | |||
Set<String> names = new HashSet<String>(); | |||
names.addAll(attributes.keySet()); | |||
return Collections.enumeration(names); | |||
} | |||
@Override | |||
public void setAttribute(String name, Object value) { | |||
// Name cannot be null | |||
if (name == null) | |||
throw new IllegalArgumentException | |||
(sm.getString("sessionImpl.setAttribute.namenull")); | |||
// Null value is the same as removeAttribute() | |||
if (value == null) { | |||
removeAttribute(name); | |||
return; | |||
} | |||
// Validate our current state | |||
if (!isValidInternal()) | |||
throw new IllegalStateException(sm.getString( | |||
"sessionImpl.setAttribute.ise", getIdInternal())); | |||
attributes.put(name, value); | |||
} | |||
@Override | |||
public void removeAttribute(String name) { | |||
removeAttributeInternal(name); | |||
} | |||
@Override | |||
public void invalidate() { | |||
if (!isValidInternal()) | |||
throw new IllegalStateException | |||
(sm.getString("sessionImpl.invalidate.ise")); | |||
// Cause this session to expire | |||
expire(); | |||
} | |||
// ------------------------------ InternalSession | |||
/** | |||
* The session identifier of this Session. | |||
*/ | |||
protected String id = null; | |||
/** | |||
* Flag indicating whether this session is valid or not. | |||
*/ | |||
protected volatile boolean isValid = false; | |||
/** | |||
* Flag indicating whether this session is new or not. | |||
*/ | |||
protected boolean isNew = false; | |||
/** | |||
* We are currently processing a session expiration, so bypass | |||
* certain IllegalStateException tests. NOTE: This value is not | |||
* included in the serialized version of this object. | |||
*/ | |||
protected transient volatile boolean expiring = false; | |||
/** | |||
* The Manager with which this Session is associated. | |||
*/ | |||
protected transient InternalSessionManager manager = null; | |||
/** | |||
* Type array. | |||
*/ | |||
protected static final String EMPTY_ARRAY[] = new String[0]; | |||
/** | |||
* The time this session was created, in milliseconds since midnight, | |||
* January 1, 1970 GMT. | |||
*/ | |||
protected long creationTime = 0L; | |||
/** | |||
* The current accessed time for this session. | |||
*/ | |||
protected volatile long thisAccessedTime = creationTime; | |||
/** | |||
* The last accessed time for this Session. | |||
*/ | |||
protected volatile long lastAccessedTime = creationTime; | |||
/** | |||
* The default maximum inactive interval for Sessions created by | |||
* this Manager. | |||
*/ | |||
protected int maxInactiveInterval = 30 * 60; | |||
/** | |||
* The facade associated with this session. NOTE: This value is not | |||
* included in the serialized version of this object. | |||
*/ | |||
protected transient InternalSessionFacade facade = null; | |||
public SessionImpl(InternalSessionManager manager) { | |||
this.manager = manager; | |||
} | |||
@Override | |||
public WxSession getSession() { | |||
if (facade == null){ | |||
facade = new InternalSessionFacade(this); | |||
} | |||
return (facade); | |||
} | |||
/** | |||
* Return the <code>isValid</code> flag for this session without any expiration | |||
* check. | |||
*/ | |||
protected boolean isValidInternal() { | |||
return this.isValid; | |||
} | |||
/** | |||
* Set the <code>isValid</code> flag for this session. | |||
* | |||
* @param isValid The new value for the <code>isValid</code> flag | |||
*/ | |||
@Override | |||
public void setValid(boolean isValid) { | |||
this.isValid = isValid; | |||
} | |||
@Override | |||
public boolean isValid() { | |||
return isValid; | |||
} | |||
@Override | |||
public String getIdInternal() { | |||
return (this.id); | |||
} | |||
protected void removeAttributeInternal(String name) { | |||
// Avoid NPE | |||
if (name == null) return; | |||
// Remove this attribute from our collection | |||
attributes.remove(name); | |||
} | |||
@Override | |||
public void expire() { | |||
// Check to see if session has already been invalidated. | |||
// Do not check expiring at this point as expire should not return until | |||
// isValid is false | |||
if (!isValid) | |||
return; | |||
synchronized (this) { | |||
// Check again, now we are inside the sync so this code only runs once | |||
// Double check locking - isValid needs to be volatile | |||
// The check of expiring is to ensure that an infinite loop is not | |||
// entered as per bug 56339 | |||
if (expiring || !isValid) | |||
return; | |||
if (manager == null) | |||
return; | |||
// Mark this session as "being expired" | |||
expiring = true; | |||
// Remove this session from our manager's active sessions | |||
manager.remove(this, true); | |||
// We have completed expire of this session | |||
setValid(false); | |||
expiring = false; | |||
// Unbind any objects associated with this session | |||
String keys[] = keys(); | |||
for (int i = 0; i < keys.length; i++) { | |||
removeAttributeInternal(keys[i]); | |||
} | |||
} | |||
} | |||
@Override | |||
public void access() { | |||
this.thisAccessedTime = System.currentTimeMillis(); | |||
} | |||
@Override | |||
public void setNew(boolean isNew) { | |||
this.isNew = isNew; | |||
} | |||
@Override | |||
public void setCreationTime(long time) { | |||
this.creationTime = time; | |||
this.lastAccessedTime = time; | |||
this.thisAccessedTime = time; | |||
} | |||
@Override | |||
public void setMaxInactiveInterval(int interval) { | |||
int oldMaxInactiveInterval = this.maxInactiveInterval; | |||
this.maxInactiveInterval = interval; | |||
} | |||
@Override | |||
public void setId(String id) { | |||
if ((this.id != null) && (manager != null)) | |||
manager.remove(this); | |||
this.id = id; | |||
if (manager != null) | |||
manager.add(this); | |||
} | |||
/** | |||
* Return the names of all currently defined session attributes | |||
* as an array of Strings. If there are no defined attributes, a | |||
* zero-length array is returned. | |||
*/ | |||
protected String[] keys() { | |||
return attributes.keySet().toArray(EMPTY_ARRAY); | |||
} | |||
} |
@@ -0,0 +1,252 @@ | |||
package me.chanjar.weixin.common.session; | |||
import me.chanjar.weixin.common.util.res.StringManager; | |||
import java.util.Map; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
public class SessionManagerImpl implements WxSessionManager, InternalSessionManager { | |||
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; | |||
@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()); | |||
} | |||
} | |||
/** | |||
* Return the active Session, associated with this Manager, with the | |||
* specified session id (if any); otherwise return <code>null</code>. | |||
* | |||
* @param id The session id for the session to be returned | |||
* | |||
* @exception IllegalStateException if a new session cannot be | |||
* instantiated for any reason | |||
* @exception java.io.IOException if an input/output error occurs while | |||
* processing this request | |||
*/ | |||
protected 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.setNew(true); | |||
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 SessionImpl(this); | |||
} | |||
@Override | |||
public void add(InternalSession session) { | |||
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 " + getName() + " at " + timeNow + " sessioncount " + 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 " + getName() + " processingTime " + (timeEnd - timeNow) + " expired sessions: " + expireHere); | |||
processingTime += ( timeEnd - timeNow ); | |||
} | |||
/** | |||
* Return the descriptive short name of this Manager implementation. | |||
*/ | |||
public String getName() { | |||
return (name); | |||
} | |||
} |
@@ -0,0 +1,57 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package me.chanjar.weixin.common.session; | |||
/** | |||
* An exception that indicates the maximum number of active sessions has been | |||
* reached and the server is refusing to create any new sessions. | |||
*/ | |||
public class TooManyActiveSessionsException | |||
extends IllegalStateException | |||
{ | |||
private static final long serialVersionUID = 1L; | |||
/** | |||
* The maximum number of active sessions the server will tolerate. | |||
*/ | |||
private final int maxActiveSessions; | |||
/** | |||
* Creates a new TooManyActiveSessionsException. | |||
* | |||
* @param message A description for the exception. | |||
* @param maxActive The maximum number of active sessions allowed by the | |||
* session manager. | |||
*/ | |||
public TooManyActiveSessionsException(String message, | |||
int maxActive) | |||
{ | |||
super(message); | |||
maxActiveSessions = maxActive; | |||
} | |||
/** | |||
* Gets the maximum number of sessions allowed by the session manager. | |||
* | |||
* @return The maximum number of sessions allowed by the session manager. | |||
*/ | |||
public int getMaxActiveSessions() | |||
{ | |||
return maxActiveSessions; | |||
} | |||
} |
@@ -0,0 +1,20 @@ | |||
package me.chanjar.weixin.common.session; | |||
import java.util.Enumeration; | |||
/** | |||
* Created by qianjia on 15/1/21. | |||
*/ | |||
public interface WxSession { | |||
public Object getAttribute(String name); | |||
public Enumeration<String> getAttributeNames(); | |||
public void setAttribute(String name, Object value); | |||
public void removeAttribute(String name); | |||
public void invalidate(); | |||
} |
@@ -0,0 +1,11 @@ | |||
package me.chanjar.weixin.common.session; | |||
public interface WxSessionManager { | |||
public WxSession getSession(String sessionId); | |||
public WxSession getSession(String sessionId, boolean create); | |||
} |
@@ -0,0 +1,251 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package me.chanjar.weixin.common.util.res; | |||
import java.text.MessageFormat; | |||
import java.util.Enumeration; | |||
import java.util.Hashtable; | |||
import java.util.LinkedHashMap; | |||
import java.util.Locale; | |||
import java.util.Map; | |||
import java.util.MissingResourceException; | |||
import java.util.ResourceBundle; | |||
/** | |||
* An internationalization / localization helper class which reduces | |||
* the bother of handling ResourceBundles and takes care of the | |||
* common cases of message formating which otherwise require the | |||
* creation of Object arrays and such. | |||
* | |||
* <p>The StringManager operates on a package basis. One StringManager | |||
* per package can be created and accessed via the getManager method | |||
* call. | |||
* | |||
* <p>The StringManager will look for a ResourceBundle named by | |||
* the package name given plus the suffix of "LocalStrings". In | |||
* practice, this means that the localized information will be contained | |||
* in a LocalStrings.properties file located in the package | |||
* directory of the classpath. | |||
* | |||
* <p>Please see the documentation for java.util.ResourceBundle for | |||
* more information. | |||
* | |||
* @author James Duncan Davidson [duncan@eng.sun.com] | |||
* @author James Todd [gonzo@eng.sun.com] | |||
* @author Mel Martinez [mmartinez@g1440.com] | |||
* @see java.util.ResourceBundle | |||
*/ | |||
public class StringManager { | |||
private static int LOCALE_CACHE_SIZE = 10; | |||
/** | |||
* The ResourceBundle for this StringManager. | |||
*/ | |||
private final ResourceBundle bundle; | |||
private final Locale locale; | |||
/** | |||
* Creates a new StringManager for a given package. This is a | |||
* private method and all access to it is arbitrated by the | |||
* static getManager method call so that only one StringManager | |||
* per package will be created. | |||
* | |||
* @param packageName Name of package to create StringManager for. | |||
*/ | |||
private StringManager(String packageName, Locale locale) { | |||
String bundleName = packageName + ".LocalStrings"; | |||
ResourceBundle bnd = null; | |||
try { | |||
bnd = ResourceBundle.getBundle(bundleName, locale); | |||
} catch( MissingResourceException ex ) { | |||
// Try from the current loader (that's the case for trusted apps) | |||
// Should only be required if using a TC5 style classloader structure | |||
// where common != shared != server | |||
ClassLoader cl = Thread.currentThread().getContextClassLoader(); | |||
if( cl != null ) { | |||
try { | |||
bnd = ResourceBundle.getBundle(bundleName, locale, cl); | |||
} catch(MissingResourceException ex2) { | |||
// Ignore | |||
} | |||
} | |||
} | |||
bundle = bnd; | |||
// Get the actual locale, which may be different from the requested one | |||
if (bundle != null) { | |||
Locale bundleLocale = bundle.getLocale(); | |||
if (bundleLocale.equals(Locale.ROOT)) { | |||
this.locale = Locale.ENGLISH; | |||
} else { | |||
this.locale = bundleLocale; | |||
} | |||
} else { | |||
this.locale = null; | |||
} | |||
} | |||
/** | |||
Get a string from the underlying resource bundle or return | |||
null if the String is not found. | |||
@param key to desired resource String | |||
@return resource String matching <i>key</i> from underlying | |||
bundle or null if not found. | |||
@throws IllegalArgumentException if <i>key</i> is null. | |||
*/ | |||
public String getString(String key) { | |||
if(key == null){ | |||
String msg = "key may not have a null value"; | |||
throw new IllegalArgumentException(msg); | |||
} | |||
String str = null; | |||
try { | |||
// Avoid NPE if bundle is null and treat it like an MRE | |||
if (bundle != null) { | |||
str = bundle.getString(key); | |||
} | |||
} catch(MissingResourceException mre) { | |||
//bad: shouldn't mask an exception the following way: | |||
// str = "[cannot find message associated with key '" + key + | |||
// "' due to " + mre + "]"; | |||
// because it hides the fact that the String was missing | |||
// from the calling code. | |||
//good: could just throw the exception (or wrap it in another) | |||
// but that would probably cause much havoc on existing | |||
// code. | |||
//better: consistent with container pattern to | |||
// simply return null. Calling code can then do | |||
// a null check. | |||
str = null; | |||
} | |||
return str; | |||
} | |||
/** | |||
* Get a string from the underlying resource bundle and format | |||
* it with the given set of arguments. | |||
* | |||
* @param key | |||
* @param args | |||
*/ | |||
public String getString(final String key, final Object... args) { | |||
String value = getString(key); | |||
if (value == null) { | |||
value = key; | |||
} | |||
MessageFormat mf = new MessageFormat(value); | |||
mf.setLocale(locale); | |||
return mf.format(args, new StringBuffer(), null).toString(); | |||
} | |||
/** | |||
* Identify the Locale this StringManager is associated with | |||
*/ | |||
public Locale getLocale() { | |||
return locale; | |||
} | |||
// -------------------------------------------------------------- | |||
// STATIC SUPPORT METHODS | |||
// -------------------------------------------------------------- | |||
private static final Map<String, Map<Locale,StringManager>> managers = | |||
new Hashtable<String, Map<Locale,StringManager>>(); | |||
/** | |||
* Get the StringManager for a particular package. If a manager for | |||
* a package already exists, it will be reused, else a new | |||
* StringManager will be created and returned. | |||
* | |||
* @param packageName The package name | |||
*/ | |||
public static final synchronized StringManager getManager( | |||
String packageName) { | |||
return getManager(packageName, Locale.getDefault()); | |||
} | |||
/** | |||
* Get the StringManager for a particular package and Locale. If a manager | |||
* for a package/Locale combination already exists, it will be reused, else | |||
* a new StringManager will be created and returned. | |||
* | |||
* @param packageName The package name | |||
* @param locale The Locale | |||
*/ | |||
public static final synchronized StringManager getManager( | |||
String packageName, Locale locale) { | |||
Map<Locale,StringManager> map = managers.get(packageName); | |||
if (map == null) { | |||
/* | |||
* Don't want the HashMap to be expanded beyond LOCALE_CACHE_SIZE. | |||
* Expansion occurs when size() exceeds capacity. Therefore keep | |||
* size at or below capacity. | |||
* removeEldestEntry() executes after insertion therefore the test | |||
* for removal needs to use one less than the maximum desired size | |||
* | |||
*/ | |||
map = new LinkedHashMap<Locale,StringManager>(LOCALE_CACHE_SIZE, 1, true) { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
protected boolean removeEldestEntry( | |||
Map.Entry<Locale,StringManager> eldest) { | |||
if (size() > (LOCALE_CACHE_SIZE - 1)) { | |||
return true; | |||
} | |||
return false; | |||
} | |||
}; | |||
managers.put(packageName, map); | |||
} | |||
StringManager mgr = map.get(locale); | |||
if (mgr == null) { | |||
mgr = new StringManager(packageName, locale); | |||
map.put(locale, mgr); | |||
} | |||
return mgr; | |||
} | |||
/** | |||
* Retrieve the StringManager for a list of Locales. The first StringManager | |||
* found will be returned. | |||
* | |||
* @param requestedLocales the list of Locales | |||
* | |||
* @return the found StringManager or the default StringManager | |||
*/ | |||
public static StringManager getManager(String packageName, | |||
Enumeration<Locale> requestedLocales) { | |||
while (requestedLocales.hasMoreElements()) { | |||
Locale locale = requestedLocales.nextElement(); | |||
StringManager result = getManager(packageName, locale); | |||
if (result.getLocale().equals(locale)) { | |||
return result; | |||
} | |||
} | |||
// Return the default | |||
return getManager(packageName); | |||
} | |||
} |