blob: c809c6f2d24e34ec1e1946c65482b01110ca11be [file] [log] [blame]
/*
* 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 org.apache.ki.session.mgt;
import org.apache.ki.session.ExpiredSessionException;
import org.apache.ki.session.InvalidSessionException;
import org.apache.ki.session.StoppedSessionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.DateFormat;
import java.util.*;
/**
* Simple {@link org.apache.ki.session.Session} POJO implementation, intended to be used on the business/server tier.
*
* @author Les Hazlewood
* @since 0.1
*/
public class SimpleSession implements ValidatingSession, Serializable {
//TODO - complete JavaDoc
protected static final long MILLIS_PER_SECOND = 1000;
protected static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
protected static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
private transient static final Logger log = LoggerFactory.getLogger(SimpleSession.class);
private Serializable id = null;
private Date startTimestamp = null;
private Date stopTimestamp = null;
private Date lastAccessTime = null;
private long timeout = DefaultSessionManager.DEFAULT_GLOBAL_SESSION_TIMEOUT;
private boolean expired = false;
private InetAddress hostAddress = null;
private Map<Object, Object> attributes = null;
public SimpleSession() {
this(getLocalHost());
}
public SimpleSession(InetAddress hostAddress) {
this.startTimestamp = new Date();
this.lastAccessTime = startTimestamp;
this.hostAddress = hostAddress;
}
private static InetAddress getLocalHost() {
try {
return InetAddress.getLocalHost();
} catch (UnknownHostException e) {
throw new IllegalStateException(e);
}
}
public Serializable getId() {
return this.id;
}
public void setId(Serializable id) {
this.id = id;
}
public Date getStartTimestamp() {
return startTimestamp;
}
public void setStartTimestamp(Date startTimestamp) {
this.startTimestamp = startTimestamp;
}
/**
* Returns the time the session was stopped, or <tt>null</tt> if the session is still active.
* <p/>
* <p>A session may become stopped under a number of conditions:
* <ul>
* <li>If the user logs out of the system, their current session is terminated (released).</li>
* <li>If the session expires</li>
* <li>The application explicitly calls {@link #stop() destroy()}</li>
* <li>If there is an internal system error and the session state can no longer accurately
* reflect the user's behavior, such in the case of a system crash</li>
* </ul>
* </p>
* <p/>
* <p>Once stopped, a session may no longer be used. It is locked from all further activity.
*
* @return The time the session was stopped, or <tt>null</tt> if the session is still
* active.
*/
public Date getStopTimestamp() {
return stopTimestamp;
}
public void setStopTimestamp(Date stopTimestamp) {
this.stopTimestamp = stopTimestamp;
}
public Date getLastAccessTime() {
return lastAccessTime;
}
public void setLastAccessTime(Date lastAccessTime) {
this.lastAccessTime = lastAccessTime;
}
/**
* Returns true if this session has expired, false otherwise. If the session has
* expired, no further user interaction with the system may be done under this session.
*
* @return true if this session has expired, false otherwise.
*/
public boolean isExpired() {
return expired;
}
public void setExpired(boolean expired) {
this.expired = expired;
}
public long getTimeout() {
return timeout;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
public InetAddress getHostAddress() {
return hostAddress;
}
public void setHostAddress(InetAddress hostAddress) {
this.hostAddress = hostAddress;
}
public Map<Object, Object> getAttributes() {
touch();
return attributes;
}
public void setAttributes(Map<Object, Object> attributes) {
touch();
this.attributes = attributes;
}
public void touch() {
this.lastAccessTime = new Date();
}
public void stop() {
if (this.stopTimestamp == null) {
this.stopTimestamp = new Date();
}
}
protected boolean isStopped() {
return getStopTimestamp() != null;
}
protected void expire() {
stop();
if (!this.expired) {
this.expired = true;
}
}
/** @since 0.9 */
public boolean isValid() {
return !isStopped() && !isExpired();
}
/**
* Determines if this session is expired.
*
* @return true if the specified session has expired, false otherwise.
*/
protected boolean isTimedOut() {
if (isExpired()) {
return true;
}
long timeout = getTimeout();
if (timeout >= 0l) {
Date lastAccessTime = getLastAccessTime();
if (lastAccessTime == null) {
String msg = "session.lastAccessTime for session with id [" +
getId() + "] is null. This value must be set at " +
"least once, preferably at least upon instantiation. Please check the " +
getClass().getName() + " implementation and ensure " +
"this value will be set (perhaps in the constructor?)";
throw new IllegalStateException(msg);
}
// Calculate at what time a session would have been last accessed
// for it to be expired at this point. In other words, subtract
// from the current time the amount of time that a session can
// be inactive before expiring. If the session was last accessed
// before this time, it is expired.
long expireTimeMillis = System.currentTimeMillis() - timeout;
Date expireTime = new Date(expireTimeMillis);
return lastAccessTime.before(expireTime);
} else {
if (log.isTraceEnabled()) {
log.trace("No timeout for session with id [" + getId() +
"]. Session is not considered expired.");
}
}
return false;
}
public void validate() throws InvalidSessionException {
//check for stopped:
if (isStopped()) {
//timestamp is set, so the session is considered stopped:
String msg = "Session with id [" + getId() + "] has been " +
"explicitly stopped. No further interaction under this session is " +
"allowed.";
throw new StoppedSessionException(msg, getId());
}
//check for expiration
if (isTimedOut()) {
expire();
//throw an exception explaining details of why it expired:
Date lastAccessTime = getLastAccessTime();
long timeout = getTimeout();
Serializable sessionId = getId();
DateFormat df = DateFormat.getInstance();
String msg = "Session with id [" + sessionId + "] has expired. " +
"Last access time: " + df.format(lastAccessTime) +
". Current time: " + df.format(new Date()) +
". Session timeout is set to " + timeout / MILLIS_PER_SECOND + " seconds (" +
timeout / MILLIS_PER_MINUTE + " minutes)";
if (log.isTraceEnabled()) {
log.trace(msg);
}
throw new ExpiredSessionException(msg, sessionId);
}
}
private Map<Object, Object> getAttributesLazy() {
Map<Object, Object> attributes = getAttributes();
if (attributes == null) {
attributes = new HashMap<Object, Object>();
setAttributes(attributes);
}
return attributes;
}
public Collection<Object> getAttributeKeys() throws InvalidSessionException {
Map<Object, Object> attributes = getAttributes();
if (attributes == null) {
//noinspection unchecked
return Collections.EMPTY_SET;
}
return attributes.keySet();
}
public Object getAttribute(Object key) {
Map<Object, Object> attributes = getAttributes();
if (attributes == null) {
return null;
}
return attributes.get(key);
}
public void setAttribute(Object key, Object value) {
if (value == null) {
removeAttribute(key);
} else {
getAttributesLazy().put(key, value);
}
}
public Object removeAttribute(Object key) {
Map<Object, Object> attributes = getAttributes();
if (attributes == null) {
return null;
} else {
return attributes.remove(key);
}
}
}