| // |
| // ======================================================================== |
| // Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| |
| package org.eclipse.jetty.server.session; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| import javax.servlet.ServletContext; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpSessionActivationListener; |
| import javax.servlet.http.HttpSessionBindingEvent; |
| import javax.servlet.http.HttpSessionBindingListener; |
| import javax.servlet.http.HttpSessionContext; |
| import javax.servlet.http.HttpSessionEvent; |
| |
| import org.eclipse.jetty.util.log.Log; |
| import org.eclipse.jetty.util.log.Logger; |
| import org.eclipse.jetty.util.thread.Locker; |
| import org.eclipse.jetty.util.thread.Locker.Lock; |
| |
| |
| |
| |
| /** |
| * Session |
| * |
| * |
| */ |
| public class Session implements SessionManager.SessionIf |
| { |
| private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); |
| |
| |
| public final static String SESSION_CREATED_SECURE="org.eclipse.jetty.security.sessionCreatedSecure"; |
| |
| |
| public enum State {VALID, INVALID, INVALIDATING}; |
| |
| |
| protected SessionData _sessionData; |
| protected SessionManager _manager; |
| protected String _extendedId; //the _id plus the worker name |
| protected long _requests; |
| private boolean _idChanged; |
| private boolean _newSession; |
| private State _state = State.VALID; //state of the session:valid,invalid or being invalidated |
| private Locker _lock = new Locker(); |
| |
| public Session (HttpServletRequest request, SessionData data) |
| { |
| _sessionData = data; |
| _newSession = true; |
| _requests = 1; |
| } |
| |
| |
| public Session (SessionData data) |
| { |
| _sessionData = data; |
| _requests = 1; |
| } |
| |
| |
| public void setSessionManager (SessionManager manager) |
| { |
| _manager = manager; |
| } |
| |
| |
| public void setExtendedId (String extendedId) |
| { |
| _extendedId = extendedId; |
| } |
| |
| /* ------------------------------------------------------------- */ |
| protected void cookieSet() |
| { |
| try (Lock lock = lock()) |
| { |
| _sessionData.setCookieSet(_sessionData.getAccessed()); |
| } |
| } |
| /* ------------------------------------------------------------ */ |
| protected boolean access(long time) |
| { |
| try (Lock lock=lock()) |
| { |
| if (!isValid()) |
| return false; |
| _newSession=false; |
| long lastAccessed = _sessionData.getAccessed(); |
| _sessionData.setAccessed(time); |
| _sessionData.setLastAccessed(lastAccessed); |
| int maxInterval=getMaxInactiveInterval(); |
| _sessionData.setExpiry(maxInterval <= 0 ? 0 : (time + maxInterval*1000L)); |
| if (isExpiredAt(time)) |
| { |
| invalidate(); |
| return false; |
| } |
| _requests++; |
| return true; |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| protected void complete() |
| { |
| try (Lock lock = lock()) |
| { |
| _requests--; |
| } |
| } |
| |
| |
| |
| /* ------------------------------------------------------------- */ |
| /** Check to see if session has expired as at the time given. |
| * @param time the time in milliseconds |
| * @return true if expired |
| */ |
| protected boolean isExpiredAt(long time) |
| { |
| try (Lock lock = _lock.lockIfNotHeld()) |
| { |
| return _sessionData.isExpiredAt(time); |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param nodename |
| */ |
| public void setLastNode (String nodename) |
| { |
| _sessionData.setLastNode(nodename); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return |
| */ |
| public String getLastNode () |
| { |
| return _sessionData.getLastNode(); |
| } |
| |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Call binding and attribute listeners based on the new and old |
| * values of the attribute. |
| * |
| * @param name name of the attribute |
| * @param newValue new value of the attribute |
| * @param oldValue previous value of the attribute |
| */ |
| protected void callSessionAttributeListeners (String name, Object newValue, Object oldValue) |
| { |
| if (newValue==null || !newValue.equals(oldValue)) |
| { |
| if (oldValue!=null) |
| unbindValue(name,oldValue); |
| if (newValue!=null) |
| bindValue(name,newValue); |
| |
| if (_manager == null) |
| throw new IllegalStateException ("No session manager for session "+ _sessionData.getId()); |
| _manager.doSessionAttributeListeners(this,name,oldValue,newValue); |
| } |
| } |
| |
| |
| |
| /* ------------------------------------------------------------- */ |
| /** |
| * Unbind value if value implements {@link HttpSessionBindingListener} (calls {@link HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)}) |
| * @param name the name with which the object is bound or unbound |
| * @param value the bound value |
| */ |
| public void unbindValue(java.lang.String name, Object value) |
| { |
| if (value!=null&&value instanceof HttpSessionBindingListener) |
| ((HttpSessionBindingListener)value).valueUnbound(new HttpSessionBindingEvent(this,name)); |
| } |
| |
| |
| /* ------------------------------------------------------------- */ |
| /** |
| * Bind value if value implements {@link HttpSessionBindingListener} (calls {@link HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)}) |
| * @param name the name with which the object is bound or unbound |
| * @param value the bound value |
| */ |
| public void bindValue(java.lang.String name, Object value) |
| { |
| if (value!=null&&value instanceof HttpSessionBindingListener) |
| ((HttpSessionBindingListener)value).valueBound(new HttpSessionBindingEvent(this,name)); |
| } |
| |
| |
| /* ------------------------------------------------------------- */ |
| /** |
| * Call the activation listeners. This must be called holding the |
| * _lock. |
| */ |
| public void didActivate() |
| { |
| HttpSessionEvent event = new HttpSessionEvent(this); |
| for (Iterator<String> iter = _sessionData.getKeys().iterator(); iter.hasNext();) |
| { |
| Object value = _sessionData.getAttribute(iter.next()); |
| if (value instanceof HttpSessionActivationListener) |
| { |
| HttpSessionActivationListener listener = (HttpSessionActivationListener) value; |
| listener.sessionDidActivate(event); |
| } |
| } |
| } |
| |
| |
| /* ------------------------------------------------------------- */ |
| /** |
| * Call the passivation listeners. This must be called holding the |
| * _lock |
| */ |
| public void willPassivate() |
| { |
| HttpSessionEvent event = new HttpSessionEvent(this); |
| for (Iterator<String> iter = _sessionData.getKeys().iterator(); iter.hasNext();) |
| { |
| Object value = _sessionData.getAttribute(iter.next()); |
| if (value instanceof HttpSessionActivationListener) |
| { |
| HttpSessionActivationListener listener = (HttpSessionActivationListener) value; |
| listener.sessionWillPassivate(event); |
| } |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public boolean isValid() |
| { |
| try (Lock lock = _lock.lockIfNotHeld()) |
| { |
| return _state==State.VALID; |
| } |
| } |
| |
| |
| /* ------------------------------------------------------------- */ |
| public long getCookieSetTime() |
| { |
| try (Lock lock = _lock.lockIfNotHeld()) |
| { |
| return _sessionData.getCookieSet(); |
| } |
| } |
| |
| |
| /* ------------------------------------------------------------- */ |
| @Override |
| public long getCreationTime() throws IllegalStateException |
| { |
| try (Lock lock = _lock.lockIfNotHeld()) |
| { |
| checkValidForRead(); |
| return _sessionData.getCreated(); |
| } |
| } |
| |
| |
| |
| /** |
| * @see javax.servlet.http.HttpSession#getId() |
| */ |
| @Override |
| public String getId() |
| { |
| try (Lock lock = _lock.lockIfNotHeld()) |
| { |
| return _sessionData.getId(); |
| } |
| } |
| |
| |
| public String getExtendedId() |
| { |
| return _extendedId; |
| } |
| |
| public String getContextPath() |
| { |
| return _sessionData.getContextPath(); |
| } |
| |
| |
| public String getVHost () |
| { |
| return _sessionData.getVhost(); |
| } |
| |
| |
| /** |
| * @see javax.servlet.http.HttpSession#getLastAccessedTime() |
| */ |
| @Override |
| public long getLastAccessedTime() |
| { |
| try (Lock lock = _lock.lockIfNotHeld()) |
| { |
| return _sessionData.getLastAccessed(); |
| } |
| } |
| |
| /** |
| * @see javax.servlet.http.HttpSession#getServletContext() |
| */ |
| @Override |
| public ServletContext getServletContext() |
| { |
| if (_manager == null) |
| throw new IllegalStateException ("No session manager for session "+ _sessionData.getId()); |
| return _manager._context; |
| } |
| |
| /** |
| * @see javax.servlet.http.HttpSession#setMaxInactiveInterval(int) |
| */ |
| @Override |
| public void setMaxInactiveInterval(int secs) |
| { |
| try (Lock lock = lock()) |
| { |
| _sessionData.setMaxInactiveMs((long)secs*1000L); |
| _sessionData.setExpiry(_sessionData.getMaxInactiveMs() <= 0 ? 0 : (System.currentTimeMillis() + _sessionData.getMaxInactiveMs()*1000L)); |
| _sessionData.setDirty(true); |
| } |
| } |
| |
| /** |
| * @see javax.servlet.http.HttpSession#getMaxInactiveInterval() |
| */ |
| @Override |
| public int getMaxInactiveInterval() |
| { |
| try (Lock lock = _lock.lockIfNotHeld()) |
| { |
| return (int)(_sessionData.getMaxInactiveMs()/1000); |
| } |
| } |
| |
| /** |
| * @see javax.servlet.http.HttpSession#getSessionContext() |
| */ |
| @Override |
| public HttpSessionContext getSessionContext() |
| { |
| checkValidForRead(); |
| return SessionManager.__nullSessionContext; |
| } |
| |
| |
| public SessionManager getSessionManager() |
| { |
| return _manager; |
| } |
| |
| |
| /* ------------------------------------------------------------- */ |
| /** |
| * asserts that the session is valid |
| * @throws IllegalStateException if the session is invalid |
| */ |
| protected void checkValidForWrite() throws IllegalStateException |
| { |
| if (!_lock.isLocked()) |
| throw new IllegalStateException(); |
| |
| if (_state != State.VALID) |
| throw new IllegalStateException(); |
| } |
| |
| |
| /* ------------------------------------------------------------- */ |
| /** |
| * asserts that the session is valid |
| * @throws IllegalStateException if the session is invalid |
| */ |
| protected void checkValidForRead () throws IllegalStateException |
| { |
| if (!_lock.isLocked()) |
| throw new IllegalStateException(); |
| if (_state == State.INVALID) |
| throw new IllegalStateException(); |
| } |
| |
| |
| |
| |
| /** |
| * @see javax.servlet.http.HttpSession#getAttribute(java.lang.String) |
| */ |
| @Override |
| public Object getAttribute(String name) |
| { |
| try (Lock lock = _lock.lockIfNotHeld()) |
| { |
| checkValidForRead(); |
| return _sessionData.getAttribute(name); |
| } |
| } |
| |
| /** |
| * @see javax.servlet.http.HttpSession#getValue(java.lang.String) |
| */ |
| @Override |
| public Object getValue(String name) |
| { |
| try (Lock lock = _lock.lockIfNotHeld()) |
| { |
| return _sessionData.getAttribute(name); |
| } |
| } |
| |
| /** |
| * @see javax.servlet.http.HttpSession#getAttributeNames() |
| */ |
| @Override |
| public Enumeration<String> getAttributeNames() |
| { |
| try (Lock lock = _lock.lockIfNotHeld()) |
| { |
| checkValidForRead(); |
| final Iterator<String> itor = _sessionData.getKeys().iterator(); |
| return new Enumeration<String> () |
| { |
| |
| @Override |
| public boolean hasMoreElements() |
| { |
| return itor.hasNext(); |
| } |
| |
| @Override |
| public String nextElement() |
| { |
| return itor.next(); |
| } |
| |
| }; |
| } |
| } |
| |
| |
| |
| |
| /* ------------------------------------------------------------ */ |
| public int getAttributes() |
| { |
| return _sessionData.getKeys().size(); |
| } |
| |
| |
| |
| |
| /* ------------------------------------------------------------ */ |
| public Set<String> getNames() |
| { |
| return Collections.unmodifiableSet(_sessionData.getKeys()); |
| } |
| |
| |
| /* ------------------------------------------------------------- */ |
| /** |
| * @deprecated As of Version 2.2, this method is replaced by |
| * {@link #getAttributeNames} |
| */ |
| @Deprecated |
| @Override |
| public String[] getValueNames() throws IllegalStateException |
| { |
| try (Lock lock = _lock.lockIfNotHeld()) |
| { |
| checkValidForRead(); |
| Iterator<String> itor = _sessionData.getKeys().iterator(); |
| if (!itor.hasNext()) |
| return new String[0]; |
| ArrayList<String> names = new ArrayList<String>(); |
| while (itor.hasNext()) |
| names.add(itor.next()); |
| return names.toArray(new String[names.size()]); |
| } |
| } |
| |
| /* ------------------------------------------------------------- */ |
| /** |
| * @see javax.servlet.http.HttpSession#setAttribute(java.lang.String, java.lang.Object) |
| */ |
| @Override |
| public void setAttribute(String name, Object value) |
| { |
| Object old=null; |
| try (Lock lock = lock()) |
| { |
| //if session is not valid, don't accept the set |
| checkValidForWrite(); |
| old=_sessionData.setAttribute(name,value); |
| } |
| if (value == null && old == null) |
| return; //if same as remove attribute but attribute was already removed, no change |
| callSessionAttributeListeners(name, value, old); |
| } |
| |
| |
| |
| /* ------------------------------------------------------------- */ |
| /** |
| * @see javax.servlet.http.HttpSession#putValue(java.lang.String, java.lang.Object) |
| */ |
| @Override |
| public void putValue(String name, Object value) |
| { |
| setAttribute(name,value); |
| } |
| |
| |
| |
| /* ------------------------------------------------------------- */ |
| /** |
| * @see javax.servlet.http.HttpSession#removeAttribute(java.lang.String) |
| */ |
| @Override |
| public void removeAttribute(String name) |
| { |
| setAttribute(name, null); |
| } |
| |
| |
| |
| /* ------------------------------------------------------------- */ |
| /** |
| * @see javax.servlet.http.HttpSession#removeValue(java.lang.String) |
| */ |
| @Override |
| public void removeValue(String name) |
| { |
| setAttribute(name, null); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param request |
| */ |
| public void renewId(HttpServletRequest request) |
| { |
| if (_manager == null) |
| throw new IllegalStateException ("No session manager for session "+ _sessionData.getId()); |
| |
| String id = null; |
| String extendedId = null; |
| try (Lock lock = lock()) |
| { |
| checkValidForWrite(); //don't renew id on a session that is not valid |
| id = _sessionData.getId(); //grab the values as they are now |
| extendedId = getExtendedId(); |
| } |
| |
| _manager._sessionIdManager.renewSessionId(id, extendedId, request); |
| setIdChanged(true); |
| } |
| |
| |
| /* ------------------------------------------------------------- */ |
| /** Swap the id on a session from old to new, keeping the object |
| * the same. |
| * |
| * @param oldId |
| * @param oldExtendedId |
| * @param newId |
| * @param newExtendedId |
| */ |
| public void renewId (String oldId, String oldExtendedId, String newId, String newExtendedId) |
| { |
| try (Lock lock = lock()) |
| { |
| checkValidForWrite(); //can't change id on invalid session |
| |
| if (!oldId.equals(getId())) |
| throw new IllegalStateException("Id clash detected on renewal: was "+oldId+" but is "+ getId()); |
| |
| //save session with new id |
| _sessionData.setId(newId); |
| setExtendedId(newExtendedId); |
| _sessionData.setLastSaved(0); //forces an insert |
| _sessionData.setDirty(true); //forces an insert |
| } |
| } |
| |
| /* ------------------------------------------------------------- */ |
| /** Called by users to invalidate a session, or called by the |
| * access method as a request enters the session if the session |
| * has expired, or called by manager as a result of scavenger |
| * expiring session |
| * |
| * @see javax.servlet.http.HttpSession#invalidate() |
| */ |
| @Override |
| public void invalidate() |
| { |
| if (_manager == null) |
| throw new IllegalStateException ("No session manager for session "+ _sessionData.getId()); |
| |
| boolean result = false; |
| |
| try (Lock lock = lock()) |
| { |
| switch (_state) |
| { |
| case INVALID: |
| { |
| throw new IllegalStateException(); //spec does not allow invalidate of already invalid session |
| } |
| case VALID: |
| { |
| //only first change from valid to invalidating should be actionable |
| result = true; |
| _state = State.INVALIDATING; |
| break; |
| } |
| default: |
| { |
| LOG.info("Session {} already being invalidated", _sessionData.getId()); |
| } |
| } |
| } |
| |
| try |
| { |
| //if the session was not already invalid, or in process of being invalidated, do invalidate |
| if (result) |
| { |
| //tell id mgr to remove session from all other contexts |
| ((AbstractSessionIdManager)_manager.getSessionIdManager()).invalidateAll(_sessionData.getId()); |
| } |
| } |
| catch (Exception e) |
| { |
| LOG.warn(e); |
| } |
| } |
| |
| |
| |
| /* ------------------------------------------------------------- */ |
| /** Grab the lock on the session |
| * @return |
| */ |
| public Lock lock () |
| { |
| return _lock.lock(); |
| } |
| |
| |
| |
| |
| |
| /* ------------------------------------------------------------- */ |
| protected void doInvalidate() throws IllegalStateException |
| { |
| try (Lock lock = lock()) |
| { |
| try |
| { |
| if (LOG.isDebugEnabled()) |
| LOG.debug("invalidate {}",_sessionData.getId()); |
| if (isValid()) |
| { |
| Set<String> keys = null; |
| |
| do |
| { |
| keys = _sessionData.getKeys(); |
| for (String key:keys) |
| { |
| Object old=_sessionData.setAttribute(key,null); |
| if (old == null) |
| return; //if same as remove attribute but attribute was already removed, no change |
| callSessionAttributeListeners(key, null, old); |
| } |
| |
| } |
| while (!keys.isEmpty()); |
| } |
| } |
| finally |
| { |
| // mark as invalid |
| _state = State.INVALID; |
| } |
| } |
| } |
| |
| /* ------------------------------------------------------------- */ |
| @Override |
| public boolean isNew() throws IllegalStateException |
| { |
| try (Lock lock = _lock.lockIfNotHeld()) |
| { |
| checkValidForRead(); |
| return _newSession; |
| } |
| } |
| |
| |
| |
| /* ------------------------------------------------------------- */ |
| public void setIdChanged(boolean changed) |
| { |
| try (Lock lock = lock()) |
| { |
| _idChanged=changed; |
| } |
| } |
| |
| |
| /* ------------------------------------------------------------- */ |
| public boolean isIdChanged () |
| { |
| try (Lock lock = _lock.lockIfNotHeld()) |
| { |
| return _idChanged; |
| } |
| } |
| |
| |
| /* ------------------------------------------------------------- */ |
| /** |
| * @see org.eclipse.jetty.server.session.AbstractSessionManager.SessionIf#getSession() |
| */ |
| @Override |
| public Session getSession() |
| { |
| // TODO why is this used |
| return this; |
| } |
| |
| /* ------------------------------------------------------------- */ |
| protected SessionData getSessionData() |
| { |
| return _sessionData; |
| } |
| } |