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..0d7c85e8f4 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) { @@ -144,19 +148,19 @@ public void setStartTimestamp(Date startTimestamp) { * active. */ public Date getStopTimestamp() { - return stopTimestamp; + return stopTimestamp.get(); } public void setStopTimestamp(Date stopTimestamp) { - this.stopTimestamp = stopTimestamp; + this.stopTimestamp.set(stopTimestamp); } public Date getLastAccessTime() { - return lastAccessTime; + return lastAccessTime.get(); } public void setLastAccessTime(Date lastAccessTime) { - this.lastAccessTime = lastAccessTime; + this.lastAccessTime.set(lastAccessTime); } /** @@ -166,19 +170,19 @@ 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); } public long getTimeout() { - return timeout; + return timeout.get(); } public void setTimeout(long timeout) { - this.timeout = timeout; + this.timeout.set(timeout); } public String getHost() { @@ -194,17 +198,15 @@ public Map getAttributes() { } public void setAttributes(Map attributes) { - this.attributes = attributes; + this.attributes = attributes == null ? null : new ConcurrentHashMap<>(attributes); } public void touch() { - this.lastAccessTime = new Date(); + this.lastAccessTime.set(new Date()); } public void stop() { - if (this.stopTimestamp == null) { - this.stopTimestamp = new Date(); - } + stopTimestamp.compareAndSet(null, new Date()); } protected boolean isStopped() { @@ -213,7 +215,7 @@ protected boolean isStopped() { protected void expire() { stop(); - this.expired = true; + this.expired.set(true); } /** @@ -301,12 +303,17 @@ 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; } public Collection getAttributeKeys() throws InvalidSessionException { @@ -451,21 +458,31 @@ private void writeObject(ObjectOutputStream out) throws IOException { if (startTimestamp != null) { out.writeObject(startTimestamp); } + + var stopTimestamp = getStopTimestamp(); if (stopTimestamp != null) { out.writeObject(stopTimestamp); } + + var lastAccessTime = getLastAccessTime(); if (lastAccessTime != null) { out.writeObject(lastAccessTime); } + + var timeout = getTimeout(); if (timeout != 0L) { out.writeLong(timeout); } + + var expired = isExpired(); if (expired) { out.writeBoolean(expired); } if (host != null) { out.writeUTF(host); } + + var attributes = getAttributes(); if (!CollectionUtils.isEmpty(attributes)) { out.writeObject(attributes); } @@ -491,16 +508,24 @@ 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(); @@ -523,10 +548,10 @@ private short getAlteredFieldsBitMask() { int bitMask = 0; bitMask = id != null ? bitMask | ID_BIT_MASK : bitMask; bitMask = startTimestamp != null ? bitMask | START_TIMESTAMP_BIT_MASK : bitMask; - bitMask = stopTimestamp != null ? bitMask | STOP_TIMESTAMP_BIT_MASK : bitMask; - bitMask = lastAccessTime != null ? bitMask | LAST_ACCESS_TIME_BIT_MASK : bitMask; - bitMask = timeout != 0L ? bitMask | TIMEOUT_BIT_MASK : bitMask; - bitMask = expired ? bitMask | EXPIRED_BIT_MASK : bitMask; + bitMask = stopTimestamp.get() != null ? bitMask | STOP_TIMESTAMP_BIT_MASK : bitMask; + bitMask = lastAccessTime.get() != null ? bitMask | LAST_ACCESS_TIME_BIT_MASK : bitMask; + bitMask = timeout.get() != 0L ? bitMask | TIMEOUT_BIT_MASK : bitMask; + bitMask = expired.get() ? bitMask | EXPIRED_BIT_MASK : bitMask; bitMask = host != null ? bitMask | HOST_BIT_MASK : bitMask; bitMask = !CollectionUtils.isEmpty(attributes) ? bitMask | ATTRIBUTES_BIT_MASK : bitMask; return (short) bitMask;