diff --git a/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionContext.java b/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionContext.java index 702148dbc8..2ded81bfb7 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionContext.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionContext.java @@ -47,20 +47,24 @@ public DefaultSessionContext(Map map) { super(map); } + @Override public String getHost() { return getTypedValue(HOST, String.class); } + @Override public void setHost(String host) { if (StringUtils.hasText(host)) { put(HOST, host); } } + @Override public Serializable getSessionId() { return getTypedValue(SESSION_ID, Serializable.class); } + @Override public void setSessionId(Serializable sessionId) { nullSafePut(SESSION_ID, sessionId); } diff --git a/core/src/main/java/org/apache/shiro/session/mgt/SimpleSession.java b/core/src/main/java/org/apache/shiro/session/mgt/SimpleSession.java index fbe8df02a1..71664758d6 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/SimpleSession.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/SimpleSession.java @@ -34,8 +34,11 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; /** @@ -91,18 +94,19 @@ public class SimpleSession implements ValidatingSession, Serializable { // ============================================================== private transient Serializable id; private transient Date startTimestamp; - private transient Date stopTimestamp; - private transient Date lastAccessTime; - private transient long timeout; - private transient boolean expired; + private transient AtomicReference stopTimestamp; + private transient AtomicReference lastAccessTime; + private transient AtomicLong timeout; + private transient AtomicBoolean expired = new AtomicBoolean(); private transient String host; - private transient Map attributes; + private transient volatile Map attributes; public SimpleSession() { //TODO - remove concrete reference to DefaultSessionManager - this.timeout = DefaultSessionManager.DEFAULT_GLOBAL_SESSION_TIMEOUT; + this.timeout = new AtomicLong(DefaultSessionManager.DEFAULT_GLOBAL_SESSION_TIMEOUT); this.startTimestamp = new Date(); - this.lastAccessTime = this.startTimestamp; + this.stopTimestamp = new AtomicReference<>(); + this.lastAccessTime = new AtomicReference<>(this.startTimestamp); } public SimpleSession(String host) { @@ -110,6 +114,7 @@ public SimpleSession(String host) { this.host = host; } + @Override public Serializable getId() { return this.id; } @@ -118,14 +123,11 @@ public void setId(Serializable id) { this.id = id; } + @Override public Date getStartTimestamp() { return startTimestamp; } - public void setStartTimestamp(Date startTimestamp) { - this.startTimestamp = startTimestamp; - } - /** * Returns the time the session was stopped, or null if the session is still active. *

@@ -144,19 +146,16 @@ public void setStartTimestamp(Date startTimestamp) { * active. */ public Date getStopTimestamp() { - return stopTimestamp; - } - - public void setStopTimestamp(Date stopTimestamp) { - this.stopTimestamp = stopTimestamp; + return stopTimestamp.get(); } + @Override public Date getLastAccessTime() { - return lastAccessTime; + return lastAccessTime.get(); } public void setLastAccessTime(Date lastAccessTime) { - this.lastAccessTime = lastAccessTime; + this.lastAccessTime.set(lastAccessTime); } /** @@ -166,45 +165,45 @@ public void setLastAccessTime(Date lastAccessTime) { * @return true if this session has expired, false otherwise. */ public boolean isExpired() { - return expired; + return expired.get(); } public void setExpired(boolean expired) { - this.expired = expired; + this.expired.set(expired); } + @Override public long getTimeout() { - return timeout; + return timeout.get(); } + @Override public void setTimeout(long timeout) { - this.timeout = timeout; + this.timeout.set(timeout); } + @Override public String getHost() { return host; } - public void setHost(String host) { - this.host = host; - } - public Map getAttributes() { return attributes; } public void setAttributes(Map attributes) { - this.attributes = attributes; + this.attributes = attributes == null ? null : attributes instanceof ConcurrentHashMap ? attributes + : new ConcurrentHashMap<>(attributes); } + @Override public void touch() { - this.lastAccessTime = new Date(); + this.lastAccessTime.set(new Date()); } + @Override public void stop() { - if (this.stopTimestamp == null) { - this.stopTimestamp = new Date(); - } + stopTimestamp.compareAndSet(null, new Date()); } protected boolean isStopped() { @@ -213,12 +212,13 @@ protected boolean isStopped() { protected void expire() { stop(); - this.expired = true; + this.expired.set(true); } /** * @since 0.9 */ + @Override public boolean isValid() { return !isStopped() && !isExpired(); } @@ -267,6 +267,7 @@ protected boolean isTimedOut() { return false; } + @Override public void validate() throws InvalidSessionException { //check for stopped: if (isStopped()) { @@ -301,14 +302,20 @@ public void validate() throws InvalidSessionException { } private Map getAttributesLazy() { - Map attributes = getAttributes(); - if (attributes == null) { - attributes = new HashMap(); - setAttributes(attributes); + Map local = attributes; + if (local == null) { + synchronized (this) { + local = attributes; + if (local == null) { + local = new ConcurrentHashMap<>(); + attributes = local; + } + } } - return attributes; + return local; } + @Override public Collection getAttributeKeys() throws InvalidSessionException { Map attributes = getAttributes(); if (attributes == null) { @@ -317,6 +324,7 @@ public Collection getAttributeKeys() throws InvalidSessionException { return attributes.keySet(); } + @Override public Object getAttribute(Object key) { Map attributes = getAttributes(); if (attributes == null) { @@ -325,6 +333,7 @@ public Object getAttribute(Object key) { return attributes.get(key); } + @Override public void setAttribute(Object key, Object value) { if (value == null) { removeAttribute(key); @@ -333,6 +342,7 @@ public void setAttribute(Object key, Object value) { } } + @Override public Object removeAttribute(Object key) { Map attributes = getAttributes(); if (attributes == null) { @@ -433,6 +443,10 @@ public String toString() { return sb.toString(); } + void setStartTimestamp(Date startTimestamp) { + this.startTimestamp = startTimestamp; + } + /** * Serializes this object to the specified output stream for JDK Serialization. * @@ -443,7 +457,14 @@ public String toString() { @SuppressWarnings("checkstyle:NPathComplexity") private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); - short alteredFieldsBitMask = getAlteredFieldsBitMask(); + + var stopTimestamp = getStopTimestamp(); + var lastAccessTime = getLastAccessTime(); + var timeout = getTimeout(); + var expired = isExpired(); + var attributes = getAttributes(); + + short alteredFieldsBitMask = getAlteredFieldsBitMask(stopTimestamp, lastAccessTime, timeout, expired, attributes); out.writeShort(alteredFieldsBitMask); if (id != null) { out.writeObject(id); @@ -451,21 +472,26 @@ private void writeObject(ObjectOutputStream out) throws IOException { if (startTimestamp != null) { out.writeObject(startTimestamp); } + if (stopTimestamp != null) { out.writeObject(stopTimestamp); } + if (lastAccessTime != null) { out.writeObject(lastAccessTime); } + if (timeout != 0L) { out.writeLong(timeout); } + if (expired) { out.writeBoolean(expired); } if (host != null) { out.writeUTF(host); } + if (!CollectionUtils.isEmpty(attributes)) { out.writeObject(attributes); } @@ -491,22 +517,35 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE this.startTimestamp = (Date) in.readObject(); } if (isFieldPresent(bitMask, STOP_TIMESTAMP_BIT_MASK)) { - this.stopTimestamp = (Date) in.readObject(); + this.stopTimestamp = new AtomicReference<>((Date) in.readObject()); + } else { + this.stopTimestamp = new AtomicReference<>(); } if (isFieldPresent(bitMask, LAST_ACCESS_TIME_BIT_MASK)) { - this.lastAccessTime = (Date) in.readObject(); + this.lastAccessTime = new AtomicReference<>((Date) in.readObject()); + } else { + this.lastAccessTime = new AtomicReference<>(); } if (isFieldPresent(bitMask, TIMEOUT_BIT_MASK)) { - this.timeout = in.readLong(); + this.timeout = new AtomicLong(in.readLong()); + } else { + this.timeout = new AtomicLong(); } if (isFieldPresent(bitMask, EXPIRED_BIT_MASK)) { - this.expired = in.readBoolean(); + this.expired = new AtomicBoolean(in.readBoolean()); + } else { + this.expired = new AtomicBoolean(); } if (isFieldPresent(bitMask, HOST_BIT_MASK)) { this.host = in.readUTF(); } if (isFieldPresent(bitMask, ATTRIBUTES_BIT_MASK)) { - this.attributes = (Map) in.readObject(); + var attributes = (Map) in.readObject(); + if (attributes instanceof ConcurrentHashMap) { + this.attributes = attributes; + } else { + this.attributes = new ConcurrentHashMap<>(attributes); + } } } @@ -519,7 +558,8 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE * @since 1.0 */ @SuppressWarnings("checkstyle:NPathComplexity") - private short getAlteredFieldsBitMask() { + private short getAlteredFieldsBitMask(Date stopTimestamp, Date lastAccessTime, long timeout, boolean expired, + Map attributes) { int bitMask = 0; bitMask = id != null ? bitMask | ID_BIT_MASK : bitMask; bitMask = startTimestamp != null ? bitMask | START_TIMESTAMP_BIT_MASK : bitMask; @@ -547,5 +587,4 @@ private short getAlteredFieldsBitMask() { private static boolean isFieldPresent(short bitMask, int fieldBitMask) { return (bitMask & fieldBitMask) != 0; } - } diff --git a/core/src/main/java/org/apache/shiro/session/mgt/SimpleSessionFactory.java b/core/src/main/java/org/apache/shiro/session/mgt/SimpleSessionFactory.java index 56b8c2c59d..a09ecb63bc 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/SimpleSessionFactory.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/SimpleSessionFactory.java @@ -35,12 +35,6 @@ public class SimpleSessionFactory implements SessionFactory { * @return a new {@link SimpleSession SimpleSession} instance */ public Session createSession(SessionContext initData) { - if (initData != null) { - String host = initData.getHost(); - if (host != null) { - return new SimpleSession(host); - } - } - return new SimpleSession(); + return initData != null ? new SimpleSession(initData.getHost()) : new SimpleSession(); } } diff --git a/core/src/main/java/org/apache/shiro/session/mgt/eis/CachingSessionDAO.java b/core/src/main/java/org/apache/shiro/session/mgt/eis/CachingSessionDAO.java index 211d0f6e94..1af752d94e 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/eis/CachingSessionDAO.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/eis/CachingSessionDAO.java @@ -28,6 +28,7 @@ import java.io.Serializable; import java.util.Collection; import java.util.Collections; +import java.util.concurrent.atomic.AtomicReference; /** * An CachingSessionDAO is a SessionDAO that provides a transparent caching layer between the components that @@ -46,7 +47,6 @@ * @since 0.2 */ public abstract class CachingSessionDAO extends AbstractSessionDAO implements CacheManagerAware { - /** * The default active sessions cache name, equal to {@code shiro-activeSessionCache}. */ @@ -60,7 +60,7 @@ public abstract class CachingSessionDAO extends AbstractSessionDAO implements Ca /** * The Cache instance responsible for caching Sessions. */ - private Cache activeSessions; + private AtomicReference> activeSessions = new AtomicReference<>(); /** * The name of the session cache, defaults to {@link #ACTIVE_SESSION_CACHE_NAME}. @@ -123,7 +123,7 @@ public void setActiveSessionsCacheName(String activeSessionsCacheName) { * should be retrieved from the */ public Cache getActiveSessionsCache() { - return this.activeSessions; + return this.activeSessions.get(); } /** @@ -135,7 +135,7 @@ public Cache getActiveSessionsCache() { * acquired from the {@link #setCacheManager configured} {@code CacheManager}. */ public void setActiveSessionsCache(Cache cache) { - this.activeSessions = cache; + this.activeSessions.set(cache); } /** @@ -148,10 +148,8 @@ public void setActiveSessionsCache(Cache cache) { * @return the active sessions cache instance. */ private Cache getActiveSessionsCacheLazy() { - if (this.activeSessions == null) { - this.activeSessions = createActiveSessionsCache(); - } - return activeSessions; + activeSessions.compareAndSet(null, createActiveSessionsCache()); + return activeSessions.get(); } /**