1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4//  ------------------------------------------------------------------------
5//  All rights reserved. This program and the accompanying materials
6//  are made available under the terms of the Eclipse Public License v1.0
7//  and Apache License v2.0 which accompanies this distribution.
8//
9//      The Eclipse Public License is available at
10//      http://www.eclipse.org/legal/epl-v10.html
11//
12//      The Apache License v2.0 is available at
13//      http://www.opensource.org/licenses/apache2.0.php
14//
15//  You may elect to redistribute this code under either of these licenses.
16//  ========================================================================
17//
18
19package org.eclipse.jetty.server.session;
20
21import java.util.ArrayList;
22import java.util.Collections;
23import java.util.Enumeration;
24import java.util.HashMap;
25import java.util.HashSet;
26import java.util.Iterator;
27import java.util.List;
28import java.util.Map;
29import java.util.Set;
30
31import javax.servlet.ServletContext;
32import javax.servlet.http.HttpServletRequest;
33import javax.servlet.http.HttpSessionActivationListener;
34import javax.servlet.http.HttpSessionBindingEvent;
35import javax.servlet.http.HttpSessionBindingListener;
36import javax.servlet.http.HttpSessionContext;
37import javax.servlet.http.HttpSessionEvent;
38
39import org.eclipse.jetty.util.log.Logger;
40
41/**
42 *
43 * <p>
44 * Implements {@link javax.servlet.http.HttpSession} from the <code>javax.servlet</code> package.
45 * </p>
46 *
47 */
48@SuppressWarnings("deprecation")
49public abstract class AbstractSession implements AbstractSessionManager.SessionIf
50{
51    final static Logger LOG = SessionHandler.LOG;
52
53    private final AbstractSessionManager _manager;
54    private final String _clusterId; // ID unique within cluster
55    private final String _nodeId;    // ID unique within node
56    private final Map<String,Object> _attributes=new HashMap<String, Object>();
57    private boolean _idChanged;
58    private final long _created;
59    private long _cookieSet;
60    private long _accessed;         // the time of the last access
61    private long _lastAccessed;     // the time of the last access excluding this one
62    private boolean _invalid;
63    private boolean _doInvalidate;
64    private long _maxIdleMs;
65    private boolean _newSession;
66    private int _requests;
67
68
69
70    /* ------------------------------------------------------------- */
71    protected AbstractSession(AbstractSessionManager abstractSessionManager, HttpServletRequest request)
72    {
73        _manager = abstractSessionManager;
74
75        _newSession=true;
76        _created=System.currentTimeMillis();
77        _clusterId=_manager._sessionIdManager.newSessionId(request,_created);
78        _nodeId=_manager._sessionIdManager.getNodeId(_clusterId,request);
79        _accessed=_created;
80        _lastAccessed=_created;
81        _requests=1;
82        _maxIdleMs=_manager._dftMaxIdleSecs>0?_manager._dftMaxIdleSecs*1000L:-1;
83        if (LOG.isDebugEnabled())
84            LOG.debug("new session & id "+_nodeId+" "+_clusterId);
85    }
86
87    /* ------------------------------------------------------------- */
88    protected AbstractSession(AbstractSessionManager abstractSessionManager, long created, long accessed, String clusterId)
89    {
90        _manager = abstractSessionManager;
91        _created=created;
92        _clusterId=clusterId;
93        _nodeId=_manager._sessionIdManager.getNodeId(_clusterId,null);
94        _accessed=accessed;
95        _lastAccessed=accessed;
96        _requests=1;
97        _maxIdleMs=_manager._dftMaxIdleSecs>0?_manager._dftMaxIdleSecs*1000L:-1;
98        if (LOG.isDebugEnabled())
99            LOG.debug("new session "+_nodeId+" "+_clusterId);
100    }
101
102    /* ------------------------------------------------------------- */
103    /**
104     * asserts that the session is valid
105     */
106    protected void checkValid() throws IllegalStateException
107    {
108        if (_invalid)
109            throw new IllegalStateException();
110    }
111
112    /* ------------------------------------------------------------- */
113    public AbstractSession getSession()
114    {
115        return this;
116    }
117
118    /* ------------------------------------------------------------- */
119    public long getAccessed()
120    {
121        synchronized (this)
122        {
123            return _accessed;
124        }
125    }
126
127    /* ------------------------------------------------------------ */
128    public Object getAttribute(String name)
129    {
130        synchronized (this)
131        {
132            checkValid();
133            return _attributes.get(name);
134        }
135    }
136
137    /* ------------------------------------------------------------ */
138    public int getAttributes()
139    {
140        synchronized (this)
141        {
142            checkValid();
143            return _attributes.size();
144        }
145    }
146
147    /* ------------------------------------------------------------ */
148    @SuppressWarnings({ "unchecked" })
149    public Enumeration<String> getAttributeNames()
150    {
151        synchronized (this)
152        {
153            checkValid();
154            List<String> names=_attributes==null?Collections.EMPTY_LIST:new ArrayList<String>(_attributes.keySet());
155            return Collections.enumeration(names);
156        }
157    }
158
159    /* ------------------------------------------------------------ */
160    public Set<String> getNames()
161    {
162        synchronized (this)
163        {
164            return new HashSet<String>(_attributes.keySet());
165        }
166    }
167
168    /* ------------------------------------------------------------- */
169    public long getCookieSetTime()
170    {
171        return _cookieSet;
172    }
173
174    /* ------------------------------------------------------------- */
175    public long getCreationTime() throws IllegalStateException
176    {
177        return _created;
178    }
179
180    /* ------------------------------------------------------------ */
181    public String getId() throws IllegalStateException
182    {
183        return _manager._nodeIdInSessionId?_nodeId:_clusterId;
184    }
185
186    /* ------------------------------------------------------------- */
187    public String getNodeId()
188    {
189        return _nodeId;
190    }
191
192    /* ------------------------------------------------------------- */
193    public String getClusterId()
194    {
195        return _clusterId;
196    }
197
198    /* ------------------------------------------------------------- */
199    public long getLastAccessedTime() throws IllegalStateException
200    {
201        checkValid();
202        return _lastAccessed;
203    }
204
205    /* ------------------------------------------------------------- */
206    public void setLastAccessedTime(long time)
207    {
208        _lastAccessed = time;
209    }
210
211    /* ------------------------------------------------------------- */
212    public int getMaxInactiveInterval()
213    {
214        checkValid();
215        return (int)(_maxIdleMs/1000);
216    }
217
218    /* ------------------------------------------------------------ */
219    /*
220     * @see javax.servlet.http.HttpSession#getServletContext()
221     */
222    public ServletContext getServletContext()
223    {
224        return _manager._context;
225    }
226
227    /* ------------------------------------------------------------- */
228    @Deprecated
229    public HttpSessionContext getSessionContext() throws IllegalStateException
230    {
231        checkValid();
232        return AbstractSessionManager.__nullSessionContext;
233    }
234
235    /* ------------------------------------------------------------- */
236    /**
237     * @deprecated As of Version 2.2, this method is replaced by
238     *             {@link #getAttribute}
239     */
240    @Deprecated
241    public Object getValue(String name) throws IllegalStateException
242    {
243        return getAttribute(name);
244    }
245
246    /* ------------------------------------------------------------- */
247    /**
248     * @deprecated As of Version 2.2, this method is replaced by
249     *             {@link #getAttributeNames}
250     */
251    @Deprecated
252    public String[] getValueNames() throws IllegalStateException
253    {
254        synchronized(this)
255        {
256            checkValid();
257            if (_attributes==null)
258                return new String[0];
259            String[] a=new String[_attributes.size()];
260            return (String[])_attributes.keySet().toArray(a);
261        }
262    }
263
264    /* ------------------------------------------------------------ */
265    protected  Map<String,Object> getAttributeMap ()
266    {
267        return _attributes;
268    }
269
270    /* ------------------------------------------------------------ */
271    protected void addAttributes(Map<String,Object> map)
272    {
273        _attributes.putAll(map);
274    }
275
276    /* ------------------------------------------------------------ */
277    protected boolean access(long time)
278    {
279        synchronized(this)
280        {
281            if (_invalid)
282                return false;
283            _newSession=false;
284            _lastAccessed=_accessed;
285            _accessed=time;
286
287            if (_maxIdleMs>0 && _lastAccessed>0 && _lastAccessed + _maxIdleMs < time)
288            {
289                invalidate();
290                return false;
291            }
292            _requests++;
293            return true;
294        }
295    }
296
297    /* ------------------------------------------------------------ */
298    protected void complete()
299    {
300        synchronized(this)
301        {
302            _requests--;
303            if (_doInvalidate && _requests<=0  )
304                doInvalidate();
305        }
306    }
307
308
309    /* ------------------------------------------------------------- */
310    protected void timeout() throws IllegalStateException
311    {
312        // remove session from context and invalidate other sessions with same ID.
313        _manager.removeSession(this,true);
314
315        // Notify listeners and unbind values
316        boolean do_invalidate=false;
317        synchronized (this)
318        {
319            if (!_invalid)
320            {
321                if (_requests<=0)
322                    do_invalidate=true;
323                else
324                    _doInvalidate=true;
325            }
326        }
327        if (do_invalidate)
328            doInvalidate();
329    }
330
331    /* ------------------------------------------------------------- */
332    public void invalidate() throws IllegalStateException
333    {
334        // remove session from context and invalidate other sessions with same ID.
335        _manager.removeSession(this,true);
336        doInvalidate();
337    }
338
339    /* ------------------------------------------------------------- */
340    protected void doInvalidate() throws IllegalStateException
341    {
342        try
343        {
344            LOG.debug("invalidate {}",_clusterId);
345            if (isValid())
346                clearAttributes();
347        }
348        finally
349        {
350            synchronized (this)
351            {
352                // mark as invalid
353                _invalid=true;
354            }
355        }
356    }
357
358    /* ------------------------------------------------------------- */
359    public void clearAttributes()
360    {
361        while (_attributes!=null && _attributes.size()>0)
362        {
363            ArrayList<String> keys;
364            synchronized(this)
365            {
366                keys=new ArrayList<String>(_attributes.keySet());
367            }
368
369            Iterator<String> iter=keys.iterator();
370            while (iter.hasNext())
371            {
372                String key=(String)iter.next();
373
374                Object value;
375                synchronized(this)
376                {
377                    value=doPutOrRemove(key,null);
378                }
379                unbindValue(key,value);
380
381                _manager.doSessionAttributeListeners(this,key,value,null);
382            }
383        }
384        if (_attributes!=null)
385            _attributes.clear();
386    }
387
388    /* ------------------------------------------------------------- */
389    public boolean isIdChanged()
390    {
391        return _idChanged;
392    }
393
394    /* ------------------------------------------------------------- */
395    public boolean isNew() throws IllegalStateException
396    {
397        checkValid();
398        return _newSession;
399    }
400
401    /* ------------------------------------------------------------- */
402    /**
403     * @deprecated As of Version 2.2, this method is replaced by
404     *             {@link #setAttribute}
405     */
406    @Deprecated
407    public void putValue(java.lang.String name, java.lang.Object value) throws IllegalStateException
408    {
409        setAttribute(name,value);
410    }
411
412    /* ------------------------------------------------------------ */
413    public void removeAttribute(String name)
414    {
415        setAttribute(name,null);
416    }
417
418    /* ------------------------------------------------------------- */
419    /**
420     * @deprecated As of Version 2.2, this method is replaced by
421     *             {@link #removeAttribute}
422     */
423    @Deprecated
424    public void removeValue(java.lang.String name) throws IllegalStateException
425    {
426        removeAttribute(name);
427    }
428
429    /* ------------------------------------------------------------ */
430    protected Object doPutOrRemove(String name, Object value)
431    {
432        return value==null?_attributes.remove(name):_attributes.put(name,value);
433    }
434
435    /* ------------------------------------------------------------ */
436    protected Object doGet(String name)
437    {
438        return _attributes.get(name);
439    }
440
441    /* ------------------------------------------------------------ */
442    public void setAttribute(String name, Object value)
443    {
444        Object old=null;
445        synchronized (this)
446        {
447            checkValid();
448            old=doPutOrRemove(name,value);
449        }
450
451        if (value==null || !value.equals(old))
452        {
453            if (old!=null)
454                unbindValue(name,old);
455            if (value!=null)
456                bindValue(name,value);
457
458            _manager.doSessionAttributeListeners(this,name,old,value);
459
460        }
461    }
462
463    /* ------------------------------------------------------------- */
464    public void setIdChanged(boolean changed)
465    {
466        _idChanged=changed;
467    }
468
469    /* ------------------------------------------------------------- */
470    public void setMaxInactiveInterval(int secs)
471    {
472        _maxIdleMs=(long)secs*1000L;
473    }
474
475    /* ------------------------------------------------------------- */
476    @Override
477    public String toString()
478    {
479        return this.getClass().getName()+":"+getId()+"@"+hashCode();
480    }
481
482    /* ------------------------------------------------------------- */
483    /** If value implements HttpSessionBindingListener, call valueBound() */
484    public void bindValue(java.lang.String name, Object value)
485    {
486        if (value!=null&&value instanceof HttpSessionBindingListener)
487            ((HttpSessionBindingListener)value).valueBound(new HttpSessionBindingEvent(this,name));
488    }
489
490    /* ------------------------------------------------------------ */
491    public boolean isValid()
492    {
493        return !_invalid;
494    }
495
496    /* ------------------------------------------------------------- */
497    protected void cookieSet()
498    {
499        synchronized (this)
500        {
501            _cookieSet=_accessed;
502        }
503    }
504
505    /* ------------------------------------------------------------ */
506    public int getRequests()
507    {
508        synchronized (this)
509        {
510            return _requests;
511        }
512    }
513
514    /* ------------------------------------------------------------ */
515    public void setRequests(int requests)
516    {
517        synchronized (this)
518        {
519            _requests=requests;
520        }
521    }
522
523    /* ------------------------------------------------------------- */
524    /** If value implements HttpSessionBindingListener, call valueUnbound() */
525    public void unbindValue(java.lang.String name, Object value)
526    {
527        if (value!=null&&value instanceof HttpSessionBindingListener)
528            ((HttpSessionBindingListener)value).valueUnbound(new HttpSessionBindingEvent(this,name));
529    }
530
531    /* ------------------------------------------------------------- */
532    public void willPassivate()
533    {
534        synchronized(this)
535        {
536            HttpSessionEvent event = new HttpSessionEvent(this);
537            for (Iterator<Object> iter = _attributes.values().iterator(); iter.hasNext();)
538            {
539                Object value = iter.next();
540                if (value instanceof HttpSessionActivationListener)
541                {
542                    HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
543                    listener.sessionWillPassivate(event);
544                }
545            }
546        }
547    }
548
549    /* ------------------------------------------------------------- */
550    public void didActivate()
551    {
552        synchronized(this)
553        {
554            HttpSessionEvent event = new HttpSessionEvent(this);
555            for (Iterator<Object> iter = _attributes.values().iterator(); iter.hasNext();)
556            {
557                Object value = iter.next();
558                if (value instanceof HttpSessionActivationListener)
559                {
560                    HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
561                    listener.sessionDidActivate(event);
562                }
563            }
564        }
565    }
566
567
568}
569