/* * Copyright (c) 1998-2012 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package org.ireland.jnetty.server.session; import javax.servlet.ServletContext; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import javax.servlet.http.HttpSessionContext; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.ireland.jnetty.webapp.WebApp; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.Map; import java.util.TreeMap; /** * Implements a HTTP session. */ public class HttpSessionImpl implements HttpSession { private static final Log log = LogFactory.getLog(HttpSessionImpl.class.getName()); private static final boolean debug = log.isDebugEnabled(); // the session's identifier private String _id; // the owning session manager protected SessionManager _manager; // the session objectStore // Map containing the actual values. protected Map<String, Object> _values; // time the session was created private long _creationTime; // time the session was last accessed private long _accessTime; // maximum time the session may stay alive. private long _idleTimeout; private long _lastUseTime; // true if the session is new private boolean _isNew = true; // true if the session is still valid, i.e. not invalidated private boolean _isValid = true; /** * Create a new session object. * * @param manager * the owning session manager. * @param id * the session identifier. * @param creationTime * the time in milliseconds when the session was created. */ public HttpSessionImpl(SessionManager manager, String id, long creationTime) { _manager = manager; _creationTime = creationTime; _accessTime = _creationTime; _lastUseTime = _accessTime; _idleTimeout = manager.getSessionTimeout(); _id = id; _values = createValueMap(); if (debug) log.debug(this + " new"); } /** * Create the map used to objectStore values. */ protected Map<String, Object> createValueMap() { return new TreeMap<String, Object>(); } /** * Returns the time the session was created. */ @Override public long getCreationTime() { // this test forced by TCK if (!_isValid) throw new IllegalStateException(this+": can't call getCreationTime() when session is no longer valid."); return _creationTime; } /** * Returns the session identifier. */ @Override public String getId() { return _id; } /** * Returns the last objectAccess time. */ @Override public long getLastAccessedTime() { // this test forced by TCK if (!_isValid) throw new IllegalStateException(this+": can't call getLastAccessedTime() when session is no longer valid."); return _accessTime; } /** * Returns the time the session is allowed to be alive. * * @return time allowed to live in seconds */ @Override public int getMaxInactiveInterval() { if (Long.MAX_VALUE / 2 <= _idleTimeout) return -1; else return (int) (_idleTimeout / 1000); } /** * Sets the maximum time a session is allowed to be alive. * * @param value * time allowed to live in seconds */ @Override public void setMaxInactiveInterval(int value) { if (value < 0) _idleTimeout = Long.MAX_VALUE / 2; else _idleTimeout = ((long) value) * 1000; } /** * Returns the session context. * * @deprecated */ @Override public HttpSessionContext getSessionContext() { return null; } /** * Returns the servlet context. */ @Override public ServletContext getServletContext() { return _manager.getWebApp(); } /** * Returns the session manager. */ public SessionManager getManager() { return _manager; } /** * Returns true if the session is new. */ @Override public boolean isNew() { if (!_isValid) throw new IllegalStateException(this+" can't call isNew() when session is no longer valid."); return _isNew; } /** * Returns true if the session is valid. */ public boolean isValid() { return _isValid; } public boolean isTimeout() { return isTimeout(System.currentTimeMillis()); } boolean isTimeout(long now) { long maxIdleTime = _idleTimeout; long lastUseTime = getLastUseTime(); return lastUseTime + maxIdleTime < now; } private long getLastUseTime() { return _lastUseTime; } /** * Returns true if the session is empty. */ public boolean isEmpty() { return _values == null || _values.size() == 0; } // // Attribute API // /** * Returns the named attribute from the session. */ @Override public Object getAttribute(String name) { if (!_isValid) throw new IllegalStateException(this+": can't call getAttribute() when session is no longer valid."); synchronized (_values) { Object value = _values.get(name); return value; } } /** * Sets a session attribute. If the value is a listener, notify it of the objectModified. If the value has changed * mark the session as changed for persistent sessions. * * @param name * the name of the attribute * @param value * the value of the attribute */ @Override public void setAttribute(String name, Object value) { if (!_isValid) throw new IllegalStateException(this+": can't call setAttribute(String, Object) when session is no longer valid."); Object oldValue; if (value != null && !(value instanceof Serializable) && debug) { log.debug(this+" attribute '"+name+"' value is non-serializable type '"+value.getClass().getName()+"'"); } synchronized (_values) { if (value != null) oldValue = _values.put(name, value); else oldValue = _values.remove(name); } if (oldValue instanceof HttpSessionBindingListener) { HttpSessionBindingListener listener; listener = (HttpSessionBindingListener) oldValue; listener.valueUnbound(new HttpSessionBindingEvent(HttpSessionImpl.this, name, oldValue)); } if (value instanceof HttpSessionBindingListener) { HttpSessionBindingListener listener; listener = (HttpSessionBindingListener) value; listener.valueBound(new HttpSessionBindingEvent(HttpSessionImpl.this, name, value)); } // Notify the attribute listeners ArrayList listeners = _manager.getAttributeListeners(); if (listeners != null && listeners.size() > 0) { HttpSessionBindingEvent event; if (oldValue != null) event = new HttpSessionBindingEvent(this, name, oldValue); else event = new HttpSessionBindingEvent(this, name, value); for (int i = 0; i < listeners.size(); i++) { HttpSessionAttributeListener listener; listener = (HttpSessionAttributeListener) listeners.get(i); if (oldValue != null) listener.attributeReplaced(event); else listener.attributeAdded(event); } } } /** * Remove a session attribute. If the value is a listener, notify it of the objectModified. * * @param name * the name of the attribute to objectRemove */ @Override public void removeAttribute(String name) { if (!_isValid) throw new IllegalStateException(this+": can't call removeAttribute(String) when session is no longer valid."); Object oldValue; synchronized (_values) { oldValue = _values.remove(name); } notifyAttributeRemoved(name, oldValue); } /** * Notify any Attribute unbound listeners. */ private void notifyAttributeRemoved(String name, Object oldValue) { if (oldValue == null) return; if (oldValue instanceof HttpSessionBindingListener) { HttpSessionBindingListener listener; listener = (HttpSessionBindingListener) oldValue; listener.valueUnbound(new HttpSessionBindingEvent(this, name, oldValue)); } // Notify the attributes listeners ArrayList listeners = _manager.getAttributeListeners(); if (listeners != null) { HttpSessionBindingEvent event; event = new HttpSessionBindingEvent(this, name, oldValue); for (int i = 0; i < listeners.size(); i++) { HttpSessionAttributeListener listener; listener = (HttpSessionAttributeListener) listeners.get(i); listener.attributeRemoved(event); } } } /** * Return an enumeration of all the sessions' attribute names. * * @return enumeration of the attribute names. */ @Override public Enumeration<String> getAttributeNames() { synchronized (_values) { if (!_isValid) throw new IllegalStateException(this+" can't call getAttributeNames() when session is no longer valid."); return Collections.enumeration(_values.keySet()); } } /** * @deprecated */ public Object getValue(String name) { return getAttribute(name); } /** * @deprecated */ public void putValue(String name, Object value) { setAttribute(name, value); } /** * @deprecated */ public void removeValue(String name) { removeAttribute(name); } /** * @deprecated */ public String[] getValueNames() { synchronized (_values) { if (!_isValid) throw new IllegalStateException(this+" can't call getValueNames() when session is no longer valid."); if (_values == null) return new String[0]; String[] s = new String[_values.size()]; Enumeration<String> e = getAttributeNames(); int count = 0; while (e.hasMoreElements()) s[count++] = (String) e.nextElement(); return s; } } // // lifecycle: creation // /** * Creates a new session. */ void create(long now, boolean isCreate) { if (debug) { log.debug(this + " create session"); } if (_isValid) { clearAllAttributes(); } // TCK now cares about exact time now = System.currentTimeMillis(); _isValid = true; _isNew = true; _creationTime = now; } // // invalidation, lru, timeout // /** * Invalidates the session, called by user code. * */ @Override public void invalidate() { if (debug) log.debug(this + " invalidate"); clearAllAttributes(); _isValid = false; _manager.removeSession(this); } /** * Cleans up the session. */ void clearAllAttributes() { if (_values.size() == 0) return; // ClusterObject clusterObject = _clusterObject; ArrayList<String> names = new ArrayList<String>(); ArrayList<Object> values = new ArrayList<Object>(); synchronized (_values) { for (Map.Entry<String, Object> entry : _values.entrySet()) { names.add(entry.getKey()); values.add(entry.getValue()); } _values.clear(); } // server/015a for (int i = 0; i < names.size(); i++) { String name = names.get(i); Object value = values.get(i); notifyValueUnbound(name, value); } } /** * Notify any value unbound listeners. */ private void notifyValueUnbound(String name, Object oldValue) { if (oldValue == null) return; if (oldValue instanceof HttpSessionBindingListener) { HttpSessionBindingListener listener; listener = (HttpSessionBindingListener) oldValue; listener.valueUnbound(new HttpSessionBindingEvent(this, name, oldValue)); } // Notify the attributes listeners ArrayList listeners = _manager.getAttributeListeners(); if (listeners != null) { HttpSessionBindingEvent event; event = new HttpSessionBindingEvent(this, name, oldValue); for (int i = 0; i < listeners.size(); i++) { HttpSessionAttributeListener listener; listener = (HttpSessionAttributeListener) listeners.get(i); listener.attributeRemoved(event); } } } /** * Invalidates a session based on a timeout */ void timeout() { clearAllAttributes(); } /** * Callback when the session is removed from the session cache, generally because the session cache is full. */ public void removeEvent() { if (debug) log.debug(this + " remove"); long now = System.currentTimeMillis(); // server/015k, server/10g2 if (_accessTime + getMaxInactiveInterval() < now) { publishSessionDestroyed(); } } /** * 触发Session Destroyed事件 */ private void publishSessionDestroyed() { ArrayList<HttpSessionListener> listeners = _manager.getListeners(); if (listeners != null) { HttpSessionEvent event = new HttpSessionEvent(this); for (int i = listeners.size() - 1; i >= 0; i--) { HttpSessionListener listener; listener = (HttpSessionListener) listeners.get(i); listener.sessionDestroyed(event); } } } @Override public String toString() { String contextPath = ""; SessionManager manager = _manager; if (manager != null) { WebApp webApp = manager.getWebApp(); if (webApp != null) contextPath = "," + webApp.getContextPath(); } return getClass().getSimpleName() + "[" + getId() + contextPath + "]"; } }