View Javadoc

1   // ========================================================================
2   // Copyright (c) 1999-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.server.session;
15  
16  import static java.lang.Math.round;
17  
18  import java.io.Serializable;
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.Enumeration;
22  import java.util.EventListener;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  import javax.servlet.ServletContext;
28  import javax.servlet.http.HttpServletRequest;
29  import javax.servlet.http.HttpSession;
30  import javax.servlet.http.HttpSessionActivationListener;
31  import javax.servlet.http.HttpSessionAttributeListener;
32  import javax.servlet.http.HttpSessionBindingEvent;
33  import javax.servlet.http.HttpSessionBindingListener;
34  import javax.servlet.http.HttpSessionContext;
35  import javax.servlet.http.HttpSessionEvent;
36  import javax.servlet.http.HttpSessionListener;
37  
38  import org.eclipse.jetty.http.HttpCookie;
39  import org.eclipse.jetty.server.Server;
40  import org.eclipse.jetty.server.SessionIdManager;
41  import org.eclipse.jetty.server.SessionManager;
42  import org.eclipse.jetty.server.handler.ContextHandler;
43  import org.eclipse.jetty.util.LazyList;
44  import org.eclipse.jetty.util.component.AbstractLifeCycle;
45  import org.eclipse.jetty.util.statistic.CounterStatistic;
46  import org.eclipse.jetty.util.statistic.SampleStatistic;
47  
48  /* ------------------------------------------------------------ */
49  /**
50   * An Abstract implementation of SessionManager. The partial implementation of
51   * SessionManager interface provides the majority of the handling required to
52   * implement a SessionManager. Concrete implementations of SessionManager based
53   * on AbstractSessionManager need only implement the newSession method to return
54   * a specialised version of the Session inner class that provides an attribute
55   * Map.
56   * <p>
57   *
58   * 
59   */
60  public abstract class AbstractSessionManager extends AbstractLifeCycle implements SessionManager
61  {
62      /* ------------------------------------------------------------ */
63      public final static int __distantFuture=60*60*24*7*52*20;
64  
65      private static final HttpSessionContext __nullSessionContext=new NullSessionContext();
66  
67      private boolean _usingCookies=true;
68  
69      /* ------------------------------------------------------------ */
70      // Setting of max inactive interval for new sessions
71      // -1 means no timeout
72      protected int _dftMaxIdleSecs=-1;
73      protected SessionHandler _sessionHandler;
74      protected boolean _httpOnly=false;
75      protected SessionIdManager _sessionIdManager;
76      protected boolean _secureCookies=false;
77      protected Object _sessionAttributeListeners;
78      protected Object _sessionListeners;
79  
80      protected ClassLoader _loader;
81      protected ContextHandler.Context _context;
82      protected String _sessionCookie=__DefaultSessionCookie;
83      protected String _sessionIdPathParameterName = __DefaultSessionIdPathParameterName;
84      protected String _sessionIdPathParameterNamePrefix =";"+ _sessionIdPathParameterName +"=";
85      protected String _sessionDomain;
86      protected String _sessionPath;
87      protected int _maxCookieAge=-1;
88      protected int _refreshCookieAge;
89      protected boolean _nodeIdInSessionId;
90      protected boolean _checkingRemoteSessionIdEncoding;
91  
92      protected final CounterStatistic _sessionsStats = new CounterStatistic();
93      protected final SampleStatistic _sessionTimeStats = new SampleStatistic();
94      
95      /* ------------------------------------------------------------ */
96      public AbstractSessionManager()
97      {
98      }
99  
100     /* ------------------------------------------------------------ */
101     public HttpCookie access(HttpSession session,boolean secure)
102     {
103         long now=System.currentTimeMillis();
104 
105         Session s = ((SessionIf)session).getSession();
106         s.access(now);
107 
108         // Do we need to refresh the cookie?
109         if (isUsingCookies() &&
110             (s.isIdChanged() ||
111              (getMaxCookieAge()>0 && getRefreshCookieAge()>0 && ((now-s.getCookieSetTime())/1000>getRefreshCookieAge()))
112             )
113            )
114         {
115             HttpCookie cookie=getSessionCookie(session,_context==null?"/":(_context.getContextPath()),secure);
116             s.cookieSet();
117             s.setIdChanged(false);
118             return cookie;
119         }
120 
121         return null;
122     }
123 
124     /* ------------------------------------------------------------ */
125     public void addEventListener(EventListener listener)
126     {
127         if (listener instanceof HttpSessionAttributeListener)
128             _sessionAttributeListeners=LazyList.add(_sessionAttributeListeners,listener);
129         if (listener instanceof HttpSessionListener)
130             _sessionListeners=LazyList.add(_sessionListeners,listener);
131     }
132 
133     /* ------------------------------------------------------------ */
134     public void clearEventListeners()
135     {
136         _sessionAttributeListeners=null;
137         _sessionListeners=null;
138     }
139 
140     /* ------------------------------------------------------------ */
141     public void complete(HttpSession session)
142     {
143         Session s = ((SessionIf)session).getSession();
144         s.complete();
145     }
146 
147     /* ------------------------------------------------------------ */
148     @Override
149     public void doStart() throws Exception
150     {
151         _context=ContextHandler.getCurrentContext();
152         _loader=Thread.currentThread().getContextClassLoader();
153 
154         if (_sessionIdManager==null)
155         {
156             final Server server=getSessionHandler().getServer();
157             synchronized (server)
158             {
159                 _sessionIdManager=server.getSessionIdManager();
160                 if (_sessionIdManager==null)
161                 {
162                     _sessionIdManager=new HashSessionIdManager();
163                     server.setSessionIdManager(_sessionIdManager);
164                 }
165             }
166         }
167         if (!_sessionIdManager.isStarted())
168             _sessionIdManager.start();
169 
170         // Look for a session cookie name
171         if (_context!=null)
172         {
173             String tmp=_context.getInitParameter(SessionManager.__SessionCookieProperty);
174             if (tmp!=null)
175                 _sessionCookie=tmp;
176 
177             tmp=_context.getInitParameter(SessionManager.__SessionIdPathParameterNameProperty);
178             if (tmp!=null)
179                 setSessionIdPathParameterName(tmp);
180 
181             // set up the max session cookie age if it isn't already
182             if (_maxCookieAge==-1)
183             {
184                 tmp=_context.getInitParameter(SessionManager.__MaxAgeProperty);
185                 if (tmp!=null)
186                     _maxCookieAge=Integer.parseInt(tmp.trim());
187             }
188 
189             // set up the session domain if it isn't already
190             if (_sessionDomain==null)
191                 _sessionDomain=_context.getInitParameter(SessionManager.__SessionDomainProperty);
192 
193             // set up the sessionPath if it isn't already
194             if (_sessionPath==null)
195                 _sessionPath=_context.getInitParameter(SessionManager.__SessionPathProperty);
196             
197             tmp=_context.getInitParameter(SessionManager.__CheckRemoteSessionEncoding);
198             if (tmp!=null)
199                 _checkingRemoteSessionIdEncoding=Boolean.parseBoolean(tmp);
200         }
201 
202         super.doStart();
203     }
204 
205     /* ------------------------------------------------------------ */
206     @Override
207     public void doStop() throws Exception
208     {
209         super.doStop();
210 
211         invalidateSessions();
212 
213         _loader=null;
214     }
215 
216     /* ------------------------------------------------------------ */
217     /**
218      * @return Returns the httpOnly.
219      */
220     public boolean getHttpOnly()
221     {
222         return _httpOnly;
223     }
224 
225     /* ------------------------------------------------------------ */
226     public HttpSession getHttpSession(String nodeId)
227     {
228         String cluster_id = getIdManager().getClusterId(nodeId);
229 
230         synchronized (this)
231         {
232             Session session = getSession(cluster_id);
233 
234             if (session!=null && !session.getNodeId().equals(nodeId))
235                 session.setIdChanged(true);
236             return session;
237         }
238     }
239 
240     /* ------------------------------------------------------------ */
241     /* ------------------------------------------------------------ */
242     /**
243      * @return Returns the metaManager used for cross context session management
244      */
245     public SessionIdManager getIdManager()
246     {
247         return _sessionIdManager;
248     }
249 
250     /* ------------------------------------------------------------ */
251     public int getMaxCookieAge()
252     {
253         return _maxCookieAge;
254     }
255 
256     /* ------------------------------------------------------------ */
257     /**
258      * @return seconds
259      */
260     public int getMaxInactiveInterval()
261     {
262         return _dftMaxIdleSecs;
263     }
264     
265     /* ------------------------------------------------------------ */
266     /**
267      * @see #getSessionsMax()
268      */
269     @Deprecated
270     public int getMaxSessions()
271     {
272         return getSessionsMax();
273     }
274 
275     /* ------------------------------------------------------------ */
276     /**
277      * @return maximum number of sessions
278      */
279     public int getSessionsMax()
280     {
281         return (int)_sessionsStats.getMax();
282     }
283 
284     /* ------------------------------------------------------------ */
285     /**
286      * @return total number of sessions
287      */
288     public int getSessionsTotal()
289     {
290         return (int)_sessionsStats.getTotal();
291     }
292 
293     /* ------------------------------------------------------------ */
294     /**
295      * @deprecated use {@link #getIdManager()}
296      */
297     @Deprecated
298     public SessionIdManager getMetaManager()
299     {
300         return getIdManager();
301     }
302 
303     /* ------------------------------------------------------------ */
304     /**
305      * @deprecated always returns 0. no replacement available.
306      */
307     @Deprecated
308     public int getMinSessions()
309     {
310         return 0;
311     }
312 
313     /* ------------------------------------------------------------ */
314     public int getRefreshCookieAge()
315     {
316         return _refreshCookieAge;
317     }
318 
319 
320     /* ------------------------------------------------------------ */
321     /**
322      * @return Returns the secureCookies.
323      */
324     public boolean getSecureCookies()
325     {
326         return _secureCookies;
327     }
328 
329     /* ------------------------------------------------------------ */
330     public String getSessionCookie()
331     {
332         return _sessionCookie;
333     }
334 
335     /* ------------------------------------------------------------ */
336     public HttpCookie getSessionCookie(HttpSession session, String contextPath, boolean requestIsSecure)
337     {
338         if (isUsingCookies())
339         {
340             String sessionPath = (_sessionPath==null) ? contextPath : _sessionPath;
341             sessionPath = (sessionPath==null||sessionPath.length()==0) ? "/" : sessionPath;
342             String id = getNodeId(session);
343             HttpCookie cookie=new HttpCookie(
344                     _sessionCookie,
345                     id,
346                     _sessionDomain,
347                     sessionPath,
348                     getMaxCookieAge(),
349                     getHttpOnly(),
350                     requestIsSecure&&getSecureCookies());      
351                     
352             return cookie;
353         }
354         return null;
355     }
356 
357     public String getSessionDomain()
358     {
359         return _sessionDomain;
360     }
361 
362     /* ------------------------------------------------------------ */
363     /**
364      * @return Returns the sessionHandler.
365      */
366     public SessionHandler getSessionHandler()
367     {
368         return _sessionHandler;
369     }
370 
371     /* ------------------------------------------------------------ */
372     /**
373      * @deprecated  Need to review if it is needed.
374      */
375     public abstract Map getSessionMap();
376 
377     /* ------------------------------------------------------------ */
378     public String getSessionPath()
379     {
380         return _sessionPath;
381     }
382 
383     /* ------------------------------------------------------------ */
384     public int getSessions()
385     {
386         return (int)_sessionsStats.getCurrent();
387     }
388 
389     /* ------------------------------------------------------------ */
390     public String getSessionIdPathParameterName()
391     {
392         return _sessionIdPathParameterName;
393     }
394 
395     /* ------------------------------------------------------------ */
396     public String getSessionIdPathParameterNamePrefix()
397     {
398         return _sessionIdPathParameterNamePrefix;
399     }
400 
401     /* ------------------------------------------------------------ */
402     /**
403      * @return Returns the usingCookies.
404      */
405     public boolean isUsingCookies()
406     {
407         return _usingCookies;
408     }
409 
410     /* ------------------------------------------------------------ */
411     public boolean isValid(HttpSession session)
412     {
413         Session s = ((SessionIf)session).getSession();
414         return s.isValid();
415     }
416 
417     /* ------------------------------------------------------------ */
418     public String getClusterId(HttpSession session)
419     {
420         Session s = ((SessionIf)session).getSession();
421         return s.getClusterId();
422     }
423 
424     /* ------------------------------------------------------------ */
425     public String getNodeId(HttpSession session)
426     {
427         Session s = ((SessionIf)session).getSession();
428         return s.getNodeId();
429     }
430 
431     /* ------------------------------------------------------------ */
432     /**
433      * Create a new HttpSession for a request
434      */
435     public HttpSession newHttpSession(HttpServletRequest request)
436     {
437         Session session=newSession(request);
438         session.setMaxInactiveInterval(_dftMaxIdleSecs);
439         addSession(session,true);
440         return session;
441     }
442 
443     /* ------------------------------------------------------------ */
444     public void removeEventListener(EventListener listener)
445     {
446         if (listener instanceof HttpSessionAttributeListener)
447             _sessionAttributeListeners=LazyList.remove(_sessionAttributeListeners,listener);
448         if (listener instanceof HttpSessionListener)
449             _sessionListeners=LazyList.remove(_sessionListeners,listener);
450     }
451     
452     /* ------------------------------------------------------------ */
453     /**
454      * @see #statsReset()
455      */
456     @Deprecated
457     public void resetStats()
458     {
459         statsReset();
460     }
461 
462     /* ------------------------------------------------------------ */
463     /**
464      * Reset statistics values
465      */
466     public void statsReset()
467     {
468         _sessionsStats.reset(getSessions());
469         _sessionTimeStats.reset();
470     }
471 
472     /* ------------------------------------------------------------ */
473     /**
474      * @param httpOnly
475      *            The httpOnly to set.
476      */
477     public void setHttpOnly(boolean httpOnly)
478     {
479         _httpOnly=httpOnly;
480     }
481 
482 
483     /* ------------------------------------------------------------ */
484     /**
485      * @param metaManager The metaManager used for cross context session management.
486      */
487     public void setIdManager(SessionIdManager metaManager)
488     {
489         _sessionIdManager=metaManager;
490     }
491 
492     /* ------------------------------------------------------------ */
493     public void setMaxCookieAge(int maxCookieAgeInSeconds)
494     {
495         _maxCookieAge=maxCookieAgeInSeconds;
496 
497         if (_maxCookieAge>0 && _refreshCookieAge==0)
498             _refreshCookieAge=_maxCookieAge/3;
499 
500     }
501 
502     /* ------------------------------------------------------------ */
503     /**
504      * @param seconds
505      */
506     public void setMaxInactiveInterval(int seconds)
507     {
508         _dftMaxIdleSecs=seconds;
509     }
510 
511     /* ------------------------------------------------------------ */
512     /**
513      * @deprecated use {@link #setIdManager(SessionIdManager)}
514      */
515     @Deprecated
516     public void setMetaManager(SessionIdManager metaManager)
517     {
518         setIdManager(metaManager);
519     }
520 
521     /* ------------------------------------------------------------ */
522     public void setRefreshCookieAge(int ageInSeconds)
523     {
524         _refreshCookieAge=ageInSeconds;
525     }
526 
527 
528     /* ------------------------------------------------------------ */
529     /**
530      * @param secureCookies
531      *            The secureCookies to set.
532      */
533     public void setSecureCookies(boolean secureCookies)
534     {
535         _secureCookies=secureCookies;
536     }
537 
538     public void setSessionCookie(String cookieName)
539     {
540         _sessionCookie=cookieName;
541     }
542 
543     public void setSessionDomain(String domain)
544     {
545         _sessionDomain=domain;
546     }
547 
548     /* ------------------------------------------------------------ */
549     /**
550      * @param sessionHandler
551      *            The sessionHandler to set.
552      */
553     public void setSessionHandler(SessionHandler sessionHandler)
554     {
555         _sessionHandler=sessionHandler;
556     }
557 
558     /* ------------------------------------------------------------ */
559     public void setSessionPath(String path)
560     {
561         _sessionPath=path;
562     }
563 
564     /* ------------------------------------------------------------ */
565     public void setSessionIdPathParameterName(String param)
566     {
567         _sessionIdPathParameterName =(param==null||"none".equals(param))?null:param;
568         _sessionIdPathParameterNamePrefix =(param==null||"none".equals(param))?null:(";"+ _sessionIdPathParameterName +"=");
569     }
570     /* ------------------------------------------------------------ */
571     /**
572      * @param usingCookies
573      *            The usingCookies to set.
574      */
575     public void setUsingCookies(boolean usingCookies)
576     {
577         _usingCookies=usingCookies;
578     }
579 
580 
581     protected abstract void addSession(Session session);
582 
583     /* ------------------------------------------------------------ */
584     /**
585      * Add the session Registers the session with this manager and registers the
586      * session ID with the sessionIDManager;
587      */
588     protected void addSession(Session session, boolean created)
589     {
590         //noinspection SynchronizeOnNonFinalField
591         synchronized (_sessionIdManager)
592         {
593             _sessionIdManager.addSession(session);
594             synchronized (this)
595             {
596                 addSession(session);
597             }
598         }
599 
600         if (created)
601         {
602             _sessionsStats.increment();
603             if (_sessionListeners!=null)
604             {
605                 HttpSessionEvent event=new HttpSessionEvent(session);
606                 for (int i=0; i<LazyList.size(_sessionListeners); i++)
607                     ((HttpSessionListener)LazyList.get(_sessionListeners,i)).sessionCreated(event);
608             }
609         }
610         else
611         {
612             session.didActivate();
613         }
614     }
615 
616     /* ------------------------------------------------------------ */
617     /**
618      * Get a known existing session
619      * @param idInCluster The session ID in the cluster, stripped of any worker name.
620      * @return A Session or null if none exists.
621      */
622     public abstract Session getSession(String idInCluster);
623 
624     protected abstract void invalidateSessions();
625 
626 
627     /* ------------------------------------------------------------ */
628     /**
629      * Create a new session instance
630      * @param request
631      * @return the new session
632      */
633     protected abstract Session newSession(HttpServletRequest request);
634 
635 
636 
637     /* ------------------------------------------------------------ */
638     /**
639      * @return true if the cluster node id (worker id) is returned as part of the session id by {@link HttpSession#getId()}. Default is false.
640      */
641     public boolean isNodeIdInSessionId()
642     {
643         return _nodeIdInSessionId;
644     }
645 
646     /* ------------------------------------------------------------ */
647     /**
648      * @param nodeIdInSessionId true if the cluster node id (worker id) will be returned as part of the session id by {@link HttpSession#getId()}. Default is false.
649      */
650     public void setNodeIdInSessionId(boolean nodeIdInSessionId)
651     {
652         _nodeIdInSessionId=nodeIdInSessionId;
653     }
654 
655     /* ------------------------------------------------------------ */
656     /** Remove session from manager
657      * @param session The session to remove
658      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
659      * {@link SessionIdManager#invalidateAll(String)} should be called.
660      */
661     public void removeSession(HttpSession session, boolean invalidate)
662     {
663         Session s = ((SessionIf)session).getSession();
664         removeSession(s,invalidate);
665     }
666 
667     /* ------------------------------------------------------------ */
668     /** Remove session from manager
669      * @param session The session to remove
670      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
671      * {@link SessionIdManager#invalidateAll(String)} should be called.
672      */
673     public void removeSession(Session session, boolean invalidate)
674     {
675         // Remove session from context and global maps
676         boolean removed = false;
677         
678         synchronized (this)
679         {
680             //take this session out of the map of sessions for this context
681             if (getSession(session.getClusterId()) != null)
682             {
683                 removed = true;
684                 removeSession(session.getClusterId());
685             }
686         }
687         
688         if (removed)
689         {
690             _sessionsStats.decrement();
691             _sessionTimeStats.set(round((System.currentTimeMillis() - session.getCreationTime())/1000.0));
692             
693             // Remove session from all context and global id maps
694             _sessionIdManager.removeSession(session);
695             if (invalidate)
696                 _sessionIdManager.invalidateAll(session.getClusterId());
697             
698             if (invalidate && _sessionListeners!=null)
699             {
700                 HttpSessionEvent event=new HttpSessionEvent(session);
701                 for (int i=LazyList.size(_sessionListeners); i-->0;)
702                     ((HttpSessionListener)LazyList.get(_sessionListeners,i)).sessionDestroyed(event);
703             }
704             
705             if (!invalidate)
706             {
707                 session.willPassivate();
708             }
709         }
710     }
711 
712     /* ------------------------------------------------------------ */
713     protected abstract void removeSession(String idInCluster);
714     
715     /* ------------------------------------------------------------ */
716     /**
717      * @return maximum amount of time session remained valid
718      */
719     public long getSessionTimeMax()
720     {
721         return _sessionTimeStats.getMax();
722     }
723 
724     /* ------------------------------------------------------------ */
725     /**
726      * @return total amount of time all sessions remained valid
727      */
728     public long getSessionTimeTotal()
729     {
730         return _sessionTimeStats.getTotal();
731     }
732     
733     /* ------------------------------------------------------------ */
734     /**
735      * @return mean amount of time session remained valid
736      */
737     public double getSessionTimeMean()
738     {
739         return _sessionTimeStats.getMean();
740     }
741     
742     /* ------------------------------------------------------------ */
743     /**
744      * @return standard deviation of amount of time session remained valid
745      */
746     public double getSessionTimeStdDev()
747     {
748         return _sessionTimeStats.getStdDev();
749     }
750 
751     /* ------------------------------------------------------------ */
752     /**
753      * @see org.eclipse.jetty.server.SessionManager#isCheckingRemoteSessionIdEncoding()
754      */
755     public boolean isCheckingRemoteSessionIdEncoding()
756     {
757         return _checkingRemoteSessionIdEncoding;
758     }
759 
760     /* ------------------------------------------------------------ */
761     /**
762      * @see org.eclipse.jetty.server.SessionManager#setCheckingRemoteSessionIdEncoding(boolean)
763      */
764     public void setCheckingRemoteSessionIdEncoding(boolean remote)
765     {
766         _checkingRemoteSessionIdEncoding=remote;
767     }
768 
769     /* ------------------------------------------------------------ */
770     /**
771      * Null returning implementation of HttpSessionContext
772      *
773      * 
774      */
775     public static class NullSessionContext implements HttpSessionContext
776     {
777         /* ------------------------------------------------------------ */
778         private NullSessionContext()
779         {
780         }
781 
782         /* ------------------------------------------------------------ */
783         /**
784          * @deprecated From HttpSessionContext
785          */
786         @Deprecated
787         public Enumeration getIds()
788         {
789             return Collections.enumeration(Collections.EMPTY_LIST);
790         }
791 
792         /* ------------------------------------------------------------ */
793         /**
794          * @deprecated From HttpSessionContext
795          */
796         @Deprecated
797         public HttpSession getSession(String id)
798         {
799             return null;
800         }
801     }
802 
803     /* ------------------------------------------------------------ */
804     /* ------------------------------------------------------------ */
805     /* ------------------------------------------------------------ */
806     /**
807      * Interface that any session wrapper should implement so that
808      * SessionManager may access the Jetty session implementation.
809      *
810      */
811     public interface SessionIf extends HttpSession
812     {
813         public Session getSession();
814     }
815 
816     /* ------------------------------------------------------------ */
817     /**
818      *
819      * <p>
820      * Implements {@link javax.servlet.http.HttpSession} from the <code>javax.servlet</code> package.
821      * </p>
822      * 
823      *
824      */
825     public abstract class Session implements SessionIf, Serializable
826     {
827         protected final String _clusterId; // ID unique within cluster
828         protected final String _nodeId;    // ID unique within node
829         protected boolean _idChanged;
830         protected final long _created;
831         protected long _cookieSet;
832         protected long _accessed;
833         protected long _lastAccessed;
834         protected boolean _invalid;
835         protected boolean _doInvalidate;
836         protected long _maxIdleMs=_dftMaxIdleSecs*1000;
837         protected boolean _newSession;
838         protected Map _values;
839         protected int _requests;
840 
841         /* ------------------------------------------------------------- */
842         protected Session(HttpServletRequest request)
843         {
844             _newSession=true;
845             _created=System.currentTimeMillis();
846             _clusterId=_sessionIdManager.newSessionId(request,_created);
847             _nodeId=_sessionIdManager.getNodeId(_clusterId,request);
848             _accessed=_created;
849             _requests=1;
850         }
851 
852         /* ------------------------------------------------------------- */
853         protected Session(long created, String clusterId)
854         {
855             _created=created;
856             _clusterId=clusterId;
857             _nodeId=_sessionIdManager.getNodeId(_clusterId,null);
858             _accessed=_created;
859         }
860 
861         /* ------------------------------------------------------------- */
862         public Session getSession()
863         {
864             return this;
865         }
866 
867         /* ------------------------------------------------------------- */
868         protected void initValues()
869         {
870             _values=newAttributeMap();
871         }
872 
873         /* ------------------------------------------------------------ */
874         public Object getAttribute(String name)
875         {
876             synchronized (this)
877             {
878                 if (_invalid)
879                     throw new IllegalStateException();
880 
881                 if (null == _values)
882                     return null;
883 
884                 return _values.get(name);
885             }
886         }
887 
888         /* ------------------------------------------------------------ */
889         public Enumeration getAttributeNames()
890         {
891             synchronized (this)
892             {
893                 if (_invalid)
894                     throw new IllegalStateException();
895                 List names=_values==null?Collections.EMPTY_LIST:new ArrayList(_values.keySet());
896                 return Collections.enumeration(names);
897             }
898         }
899 
900         /* ------------------------------------------------------------- */
901         public long getCookieSetTime()
902         {
903             return _cookieSet;
904         }
905 
906         /* ------------------------------------------------------------- */
907         public long getCreationTime() throws IllegalStateException
908         {
909             if (_invalid)
910                 throw new IllegalStateException();
911             return _created;
912         }
913 
914         /* ------------------------------------------------------------ */
915         public String getId() throws IllegalStateException
916         {
917             return _nodeIdInSessionId?_nodeId:_clusterId;
918         }
919 
920         /* ------------------------------------------------------------- */
921         protected String getNodeId()
922         {
923             return _nodeId;
924         }
925 
926         /* ------------------------------------------------------------- */
927         protected String getClusterId()
928         {
929             return _clusterId;
930         }
931 
932         /* ------------------------------------------------------------- */
933         public long getLastAccessedTime() throws IllegalStateException
934         {
935             if (_invalid)
936                 throw new IllegalStateException();
937             return _lastAccessed;
938         }
939 
940         /* ------------------------------------------------------------- */
941         public int getMaxInactiveInterval()
942         {
943             if (_invalid)
944                 throw new IllegalStateException();
945             return (int)(_maxIdleMs/1000);
946         }
947 
948         /* ------------------------------------------------------------ */
949         /*
950          * @see javax.servlet.http.HttpSession#getServletContext()
951          */
952         public ServletContext getServletContext()
953         {
954             return _context;
955         }
956 
957         /* ------------------------------------------------------------- */
958         /**
959          * @deprecated
960          */
961         @Deprecated
962         public HttpSessionContext getSessionContext() throws IllegalStateException
963         {
964             if (_invalid)
965                 throw new IllegalStateException();
966             return __nullSessionContext;
967         }
968 
969         /* ------------------------------------------------------------- */
970         /**
971          * @deprecated As of Version 2.2, this method is replaced by
972          *             {@link #getAttribute}
973          */
974         @Deprecated
975         public Object getValue(String name) throws IllegalStateException
976         {
977             return getAttribute(name);
978         }
979 
980         /* ------------------------------------------------------------- */
981         /**
982          * @deprecated As of Version 2.2, this method is replaced by
983          *             {@link #getAttributeNames}
984          */
985         @Deprecated
986         public String[] getValueNames() throws IllegalStateException
987         {
988             synchronized(this)
989             {
990                 if (_invalid)
991                     throw new IllegalStateException();
992                 if (_values==null)
993                     return new String[0];
994                 String[] a=new String[_values.size()];
995                 return (String[])_values.keySet().toArray(a);
996             }
997         }
998 
999         /* ------------------------------------------------------------ */
1000         protected void access(long time)
1001         {
1002             synchronized(this)
1003             {
1004                 _newSession=false;
1005                 _lastAccessed=_accessed;
1006                 _accessed=time;
1007                 _requests++;
1008             }
1009         }
1010 
1011         /* ------------------------------------------------------------ */
1012         protected void complete()
1013         {
1014             synchronized(this)
1015             {
1016                 _requests--;
1017                 if (_doInvalidate && _requests<=0  )
1018                     doInvalidate();
1019             }
1020         }
1021 
1022 
1023         /* ------------------------------------------------------------- */
1024         protected void timeout() throws IllegalStateException
1025         {
1026             // remove session from context and invalidate other sessions with same ID.
1027             removeSession(this,true);
1028 
1029             // Notify listeners and unbind values
1030             synchronized (this)
1031             {
1032                 if (!_invalid)
1033                 {
1034                     if (_requests<=0)
1035                         doInvalidate();
1036                     else
1037                         _doInvalidate=true;
1038                 }
1039             }
1040         }
1041 
1042         /* ------------------------------------------------------------- */
1043         public void invalidate() throws IllegalStateException
1044         {
1045             // remove session from context and invalidate other sessions with same ID.
1046             removeSession(this,true);
1047             doInvalidate();
1048         }
1049 
1050         /* ------------------------------------------------------------- */
1051         protected void doInvalidate() throws IllegalStateException
1052         {
1053             try
1054             {
1055                 // Notify listeners and unbind values
1056                 if (_invalid)
1057                     throw new IllegalStateException();
1058 
1059                 while (_values!=null && _values.size()>0)
1060                 {
1061                     ArrayList keys;
1062                     synchronized (this)
1063                     {
1064                         keys=new ArrayList(_values.keySet());
1065                     }
1066 
1067                     Iterator iter=keys.iterator();
1068                     while (iter.hasNext())
1069                     {
1070                         String key=(String)iter.next();
1071 
1072                         Object value;
1073                         synchronized (this)
1074                         {
1075                             value=_values.remove(key);
1076                         }
1077                         unbindValue(key,value);
1078 
1079                         if (_sessionAttributeListeners!=null)
1080                         {
1081                             HttpSessionBindingEvent event=new HttpSessionBindingEvent(this,key,value);
1082 
1083                             for (int i=0; i<LazyList.size(_sessionAttributeListeners); i++)
1084                                 ((HttpSessionAttributeListener)LazyList.get(_sessionAttributeListeners,i)).attributeRemoved(event);
1085                         }
1086                     }
1087                 }
1088             }
1089             finally
1090             {
1091                 // mark as invalid
1092                 _invalid=true;
1093             }
1094         }
1095 
1096         /* ------------------------------------------------------------- */
1097         public boolean isIdChanged()
1098         {
1099             return _idChanged;
1100         }
1101 
1102         /* ------------------------------------------------------------- */
1103         public boolean isNew() throws IllegalStateException
1104         {
1105             if (_invalid)
1106                 throw new IllegalStateException();
1107             return _newSession;
1108         }
1109 
1110         /* ------------------------------------------------------------- */
1111         /**
1112          * @deprecated As of Version 2.2, this method is replaced by
1113          *             {@link #setAttribute}
1114          */
1115         @Deprecated
1116         public void putValue(java.lang.String name, java.lang.Object value) throws IllegalStateException
1117         {
1118             setAttribute(name,value);
1119         }
1120 
1121         /* ------------------------------------------------------------ */
1122         public void removeAttribute(String name)
1123         {
1124             Object old;
1125             synchronized(this)
1126             {
1127                 if (_invalid)
1128                     throw new IllegalStateException();
1129                 if (_values==null)
1130                     return;
1131 
1132                 old=_values.remove(name);
1133             }
1134 
1135             if (old!=null)
1136             {
1137                 unbindValue(name,old);
1138                 if (_sessionAttributeListeners!=null)
1139                 {
1140                     HttpSessionBindingEvent event=new HttpSessionBindingEvent(this,name,old);
1141 
1142                     for (int i=0; i<LazyList.size(_sessionAttributeListeners); i++)
1143                         ((HttpSessionAttributeListener)LazyList.get(_sessionAttributeListeners,i)).attributeRemoved(event);
1144                 }
1145             }
1146 
1147         }
1148 
1149         /* ------------------------------------------------------------- */
1150         /**
1151          * @deprecated As of Version 2.2, this method is replaced by
1152          *             {@link #removeAttribute}
1153          */
1154         @Deprecated
1155         public void removeValue(java.lang.String name) throws IllegalStateException
1156         {
1157             removeAttribute(name);
1158         }
1159 
1160         /* ------------------------------------------------------------ */
1161         public void setAttribute(String name, Object value)
1162         {
1163             Object old_value;
1164             if (value==null)
1165             {
1166                 removeAttribute(name);
1167                 return;
1168             }
1169 
1170             synchronized(this)
1171             {
1172                 if (_invalid)
1173                     throw new IllegalStateException();
1174                 if (_values==null)
1175                     _values=newAttributeMap();
1176                 old_value=_values.put(name,value);
1177             }
1178 
1179             if (old_value==null || !value.equals(old_value))
1180             {
1181                 unbindValue(name,old_value);
1182                 bindValue(name,value);
1183 
1184                 if (_sessionAttributeListeners!=null)
1185                 {
1186                     HttpSessionBindingEvent event=new HttpSessionBindingEvent(this,name,old_value==null?value:old_value);
1187 
1188                     for (int i=0; i<LazyList.size(_sessionAttributeListeners); i++)
1189                     {
1190                         HttpSessionAttributeListener l=(HttpSessionAttributeListener)LazyList.get(_sessionAttributeListeners,i);
1191 
1192                         if (old_value==null)
1193                             l.attributeAdded(event);
1194                         else
1195                             l.attributeReplaced(event);
1196                     }
1197                 }
1198             }
1199         }
1200 
1201         /* ------------------------------------------------------------- */
1202         public void setIdChanged(boolean changed)
1203         {
1204             _idChanged=changed;
1205         }
1206 
1207         /* ------------------------------------------------------------- */
1208         public void setMaxInactiveInterval(int secs)
1209         {
1210             _maxIdleMs=(long)secs*1000;
1211         }
1212 
1213         /* ------------------------------------------------------------- */
1214         @Override
1215         public String toString()
1216         {
1217             return this.getClass().getName()+":"+getId()+"@"+hashCode();
1218         }
1219 
1220         /* ------------------------------------------------------------- */
1221         /** If value implements HttpSessionBindingListener, call valueBound() */
1222         protected void bindValue(java.lang.String name, Object value)
1223         {
1224             if (value!=null&&value instanceof HttpSessionBindingListener)
1225                 ((HttpSessionBindingListener)value).valueBound(new HttpSessionBindingEvent(this,name));
1226         }
1227 
1228         /* ------------------------------------------------------------ */
1229         protected boolean isValid()
1230         {
1231             return !_invalid;
1232         }
1233 
1234         /* ------------------------------------------------------------ */
1235         protected abstract Map newAttributeMap();
1236 
1237         /* ------------------------------------------------------------- */
1238         protected void cookieSet()
1239         {
1240             _cookieSet=_accessed;
1241         }
1242 
1243         /* ------------------------------------------------------------- */
1244         /** If value implements HttpSessionBindingListener, call valueUnbound() */
1245         protected void unbindValue(java.lang.String name, Object value)
1246         {
1247             if (value!=null&&value instanceof HttpSessionBindingListener)
1248                 ((HttpSessionBindingListener)value).valueUnbound(new HttpSessionBindingEvent(this,name));
1249         }
1250 
1251         /* ------------------------------------------------------------- */
1252         protected void willPassivate()
1253         {
1254             synchronized(this)
1255             {
1256                 HttpSessionEvent event = new HttpSessionEvent(this);
1257                 for (Iterator iter = _values.values().iterator(); iter.hasNext();)
1258                 {
1259                     Object value = iter.next();
1260                     if (value instanceof HttpSessionActivationListener)
1261                     {
1262                         HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
1263                         listener.sessionWillPassivate(event);
1264                     }
1265                 }
1266             }
1267         }
1268 
1269         /* ------------------------------------------------------------- */
1270         protected void didActivate()
1271         {
1272             synchronized(this)
1273             {
1274                 HttpSessionEvent event = new HttpSessionEvent(this);
1275                 for (Iterator iter = _values.values().iterator(); iter.hasNext();)
1276                 {
1277                     Object value = iter.next();
1278                     if (value instanceof HttpSessionActivationListener)
1279                     {
1280                         HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
1281                         listener.sessionDidActivate(event);
1282                     }
1283                 }
1284             }
1285         }
1286     }
1287 }