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.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 id = getNodeId(session);
341             HttpCookie cookie=new HttpCookie(
342                     _sessionCookie,
343                     id,
344                     _sessionDomain,
345                     (contextPath==null||contextPath.length()==0)?"/":contextPath,
346                     getMaxCookieAge(),
347                     getHttpOnly(),
348                     requestIsSecure&&getSecureCookies());      
349                     
350             return cookie;
351         }
352         return null;
353     }
354 
355     public String getSessionDomain()
356     {
357         return _sessionDomain;
358     }
359 
360     /* ------------------------------------------------------------ */
361     /**
362      * @return Returns the sessionHandler.
363      */
364     public SessionHandler getSessionHandler()
365     {
366         return _sessionHandler;
367     }
368 
369     /* ------------------------------------------------------------ */
370     /**
371      * @deprecated  Need to review if it is needed.
372      */
373     public abstract Map getSessionMap();
374 
375     /* ------------------------------------------------------------ */
376     public String getSessionPath()
377     {
378         return _sessionPath;
379     }
380 
381     /* ------------------------------------------------------------ */
382     public int getSessions()
383     {
384         return (int)_sessionsStats.getCurrent();
385     }
386 
387     /* ------------------------------------------------------------ */
388     public String getSessionIdPathParameterName()
389     {
390         return _sessionIdPathParameterName;
391     }
392 
393     /* ------------------------------------------------------------ */
394     public String getSessionIdPathParameterNamePrefix()
395     {
396         return _sessionIdPathParameterNamePrefix;
397     }
398 
399     /* ------------------------------------------------------------ */
400     /**
401      * @return Returns the usingCookies.
402      */
403     public boolean isUsingCookies()
404     {
405         return _usingCookies;
406     }
407 
408     /* ------------------------------------------------------------ */
409     public boolean isValid(HttpSession session)
410     {
411         Session s = ((SessionIf)session).getSession();
412         return s.isValid();
413     }
414 
415     /* ------------------------------------------------------------ */
416     public String getClusterId(HttpSession session)
417     {
418         Session s = ((SessionIf)session).getSession();
419         return s.getClusterId();
420     }
421 
422     /* ------------------------------------------------------------ */
423     public String getNodeId(HttpSession session)
424     {
425         Session s = ((SessionIf)session).getSession();
426         return s.getNodeId();
427     }
428 
429     /* ------------------------------------------------------------ */
430     /**
431      * Create a new HttpSession for a request
432      */
433     public HttpSession newHttpSession(HttpServletRequest request)
434     {
435         Session session=newSession(request);
436         session.setMaxInactiveInterval(_dftMaxIdleSecs);
437         addSession(session,true);
438         return session;
439     }
440 
441     /* ------------------------------------------------------------ */
442     public void removeEventListener(EventListener listener)
443     {
444         if (listener instanceof HttpSessionAttributeListener)
445             _sessionAttributeListeners=LazyList.remove(_sessionAttributeListeners,listener);
446         if (listener instanceof HttpSessionListener)
447             _sessionListeners=LazyList.remove(_sessionListeners,listener);
448     }
449     
450     /* ------------------------------------------------------------ */
451     /**
452      * @see #statsReset()
453      */
454     @Deprecated
455     public void resetStats()
456     {
457         statsReset();
458     }
459 
460     /* ------------------------------------------------------------ */
461     /**
462      * Reset statistics values
463      */
464     public void statsReset()
465     {
466         _sessionsStats.reset(getSessions());
467         _sessionTimeStats.reset();
468     }
469 
470     /* ------------------------------------------------------------ */
471     /**
472      * @param httpOnly
473      *            The httpOnly to set.
474      */
475     public void setHttpOnly(boolean httpOnly)
476     {
477         _httpOnly=httpOnly;
478     }
479 
480 
481     /* ------------------------------------------------------------ */
482     /**
483      * @param metaManager The metaManager used for cross context session management.
484      */
485     public void setIdManager(SessionIdManager metaManager)
486     {
487         _sessionIdManager=metaManager;
488     }
489 
490     /* ------------------------------------------------------------ */
491     public void setMaxCookieAge(int maxCookieAgeInSeconds)
492     {
493         _maxCookieAge=maxCookieAgeInSeconds;
494 
495         if (_maxCookieAge>0 && _refreshCookieAge==0)
496             _refreshCookieAge=_maxCookieAge/3;
497 
498     }
499 
500     /* ------------------------------------------------------------ */
501     /**
502      * @param seconds
503      */
504     public void setMaxInactiveInterval(int seconds)
505     {
506         _dftMaxIdleSecs=seconds;
507     }
508 
509     /* ------------------------------------------------------------ */
510     /**
511      * @deprecated use {@link #setIdManager(SessionIdManager)}
512      */
513     @Deprecated
514     public void setMetaManager(SessionIdManager metaManager)
515     {
516         setIdManager(metaManager);
517     }
518 
519     /* ------------------------------------------------------------ */
520     public void setRefreshCookieAge(int ageInSeconds)
521     {
522         _refreshCookieAge=ageInSeconds;
523     }
524 
525 
526     /* ------------------------------------------------------------ */
527     /**
528      * @param secureCookies
529      *            The secureCookies to set.
530      */
531     public void setSecureCookies(boolean secureCookies)
532     {
533         _secureCookies=secureCookies;
534     }
535 
536     public void setSessionCookie(String cookieName)
537     {
538         _sessionCookie=cookieName;
539     }
540 
541     public void setSessionDomain(String domain)
542     {
543         _sessionDomain=domain;
544     }
545 
546     /* ------------------------------------------------------------ */
547     /**
548      * @param sessionHandler
549      *            The sessionHandler to set.
550      */
551     public void setSessionHandler(SessionHandler sessionHandler)
552     {
553         _sessionHandler=sessionHandler;
554     }
555 
556     /* ------------------------------------------------------------ */
557     public void setSessionPath(String path)
558     {
559         _sessionPath=path;
560     }
561 
562     /* ------------------------------------------------------------ */
563     public void setSessionIdPathParameterName(String param)
564     {
565         _sessionIdPathParameterName =(param==null||"none".equals(param))?null:param;
566         _sessionIdPathParameterNamePrefix =(param==null||"none".equals(param))?null:(";"+ _sessionIdPathParameterName +"=");
567     }
568     /* ------------------------------------------------------------ */
569     /**
570      * @param usingCookies
571      *            The usingCookies to set.
572      */
573     public void setUsingCookies(boolean usingCookies)
574     {
575         _usingCookies=usingCookies;
576     }
577 
578 
579     protected abstract void addSession(Session session);
580 
581     /* ------------------------------------------------------------ */
582     /**
583      * Add the session Registers the session with this manager and registers the
584      * session ID with the sessionIDManager;
585      */
586     protected void addSession(Session session, boolean created)
587     {
588         //noinspection SynchronizeOnNonFinalField
589         synchronized (_sessionIdManager)
590         {
591             _sessionIdManager.addSession(session);
592             synchronized (this)
593             {
594                 addSession(session);
595             }
596         }
597 
598         if (created)
599         {
600             _sessionsStats.increment();
601             if (_sessionListeners!=null)
602             {
603                 HttpSessionEvent event=new HttpSessionEvent(session);
604                 for (int i=0; i<LazyList.size(_sessionListeners); i++)
605                     ((HttpSessionListener)LazyList.get(_sessionListeners,i)).sessionCreated(event);
606             }
607         }
608         else
609         {
610             session.didActivate();
611         }
612     }
613 
614     /* ------------------------------------------------------------ */
615     /**
616      * Get a known existing session
617      * @param idInCluster The session ID in the cluster, stripped of any worker name.
618      * @return A Session or null if none exists.
619      */
620     public abstract Session getSession(String idInCluster);
621 
622     protected abstract void invalidateSessions();
623 
624 
625     /* ------------------------------------------------------------ */
626     /**
627      * Create a new session instance
628      * @param request
629      * @return the new session
630      */
631     protected abstract Session newSession(HttpServletRequest request);
632 
633 
634 
635     /* ------------------------------------------------------------ */
636     /**
637      * @return true if the cluster node id (worker id) is returned as part of the session id by {@link HttpSession#getId()}. Default is false.
638      */
639     public boolean isNodeIdInSessionId()
640     {
641         return _nodeIdInSessionId;
642     }
643 
644     /* ------------------------------------------------------------ */
645     /**
646      * @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.
647      */
648     public void setNodeIdInSessionId(boolean nodeIdInSessionId)
649     {
650         _nodeIdInSessionId=nodeIdInSessionId;
651     }
652 
653     /* ------------------------------------------------------------ */
654     /** Remove session from manager
655      * @param session The session to remove
656      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
657      * {@link SessionIdManager#invalidateAll(String)} should be called.
658      */
659     public void removeSession(HttpSession session, boolean invalidate)
660     {
661         Session s = ((SessionIf)session).getSession();
662         removeSession(s,invalidate);
663     }
664 
665     /* ------------------------------------------------------------ */
666     /** Remove session from manager
667      * @param session The session to remove
668      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
669      * {@link SessionIdManager#invalidateAll(String)} should be called.
670      */
671     public void removeSession(Session session, boolean invalidate)
672     {
673         // Remove session from context and global maps
674         boolean removed = false;
675         
676         synchronized (this)
677         {
678             //take this session out of the map of sessions for this context
679             if (getSession(session.getClusterId()) != null)
680             {
681                 removed = true;
682                 removeSession(session.getClusterId());
683             }
684         }
685         
686         if (removed)
687         {
688             _sessionsStats.decrement();
689             _sessionTimeStats.set(round((System.currentTimeMillis() - session.getCreationTime())/1000.0));
690             
691             // Remove session from all context and global id maps
692             _sessionIdManager.removeSession(session);
693             if (invalidate)
694                 _sessionIdManager.invalidateAll(session.getClusterId());
695             
696             if (invalidate && _sessionListeners!=null)
697             {
698                 HttpSessionEvent event=new HttpSessionEvent(session);
699                 for (int i=LazyList.size(_sessionListeners); i-->0;)
700                     ((HttpSessionListener)LazyList.get(_sessionListeners,i)).sessionDestroyed(event);
701             }
702             
703             if (!invalidate)
704             {
705                 session.willPassivate();
706             }
707         }
708     }
709 
710     /* ------------------------------------------------------------ */
711     protected abstract void removeSession(String idInCluster);
712     
713     /* ------------------------------------------------------------ */
714     /**
715      * @return maximum amount of time session remained valid
716      */
717     public long getSessionTimeMax()
718     {
719         return _sessionTimeStats.getMax();
720     }
721 
722     /* ------------------------------------------------------------ */
723     /**
724      * @return total amount of time all sessions remained valid
725      */
726     public long getSessionTimeTotal()
727     {
728         return _sessionTimeStats.getTotal();
729     }
730     
731     /* ------------------------------------------------------------ */
732     /**
733      * @return mean amount of time session remained valid
734      */
735     public double getSessionTimeMean()
736     {
737         return _sessionTimeStats.getMean();
738     }
739     
740     /* ------------------------------------------------------------ */
741     /**
742      * @return standard deviation of amount of time session remained valid
743      */
744     public double getSessionTimeStdDev()
745     {
746         return _sessionTimeStats.getStdDev();
747     }
748 
749     /* ------------------------------------------------------------ */
750     /**
751      * @see org.eclipse.jetty.server.SessionManager#isCheckingRemoteSessionIdEncoding()
752      */
753     public boolean isCheckingRemoteSessionIdEncoding()
754     {
755         return _checkingRemoteSessionIdEncoding;
756     }
757 
758     /* ------------------------------------------------------------ */
759     /**
760      * @see org.eclipse.jetty.server.SessionManager#setCheckingRemoteSessionIdEncoding(boolean)
761      */
762     public void setCheckingRemoteSessionIdEncoding(boolean remote)
763     {
764         _checkingRemoteSessionIdEncoding=remote;
765     }
766 
767     /* ------------------------------------------------------------ */
768     /**
769      * Null returning implementation of HttpSessionContext
770      *
771      * 
772      */
773     public static class NullSessionContext implements HttpSessionContext
774     {
775         /* ------------------------------------------------------------ */
776         private NullSessionContext()
777         {
778         }
779 
780         /* ------------------------------------------------------------ */
781         /**
782          * @deprecated From HttpSessionContext
783          */
784         @Deprecated
785         public Enumeration getIds()
786         {
787             return Collections.enumeration(Collections.EMPTY_LIST);
788         }
789 
790         /* ------------------------------------------------------------ */
791         /**
792          * @deprecated From HttpSessionContext
793          */
794         @Deprecated
795         public HttpSession getSession(String id)
796         {
797             return null;
798         }
799     }
800 
801     /* ------------------------------------------------------------ */
802     /* ------------------------------------------------------------ */
803     /* ------------------------------------------------------------ */
804     /**
805      * Interface that any session wrapper should implement so that
806      * SessionManager may access the Jetty session implementation.
807      *
808      */
809     public interface SessionIf extends HttpSession
810     {
811         public Session getSession();
812     }
813 
814     /* ------------------------------------------------------------ */
815     /**
816      *
817      * <p>
818      * Implements {@link javax.servlet.http.HttpSession} from the <code>javax.servlet</code> package.
819      * </p>
820      * 
821      *
822      */
823     public abstract class Session implements SessionIf, Serializable
824     {
825         protected final String _clusterId; // ID unique within cluster
826         protected final String _nodeId;    // ID unique within node
827         protected boolean _idChanged;
828         protected final long _created;
829         protected long _cookieSet;
830         protected long _accessed;
831         protected long _lastAccessed;
832         protected boolean _invalid;
833         protected boolean _doInvalidate;
834         protected long _maxIdleMs=_dftMaxIdleSecs*1000;
835         protected boolean _newSession;
836         protected Map _values;
837         protected int _requests;
838 
839         /* ------------------------------------------------------------- */
840         protected Session(HttpServletRequest request)
841         {
842             _newSession=true;
843             _created=System.currentTimeMillis();
844             _clusterId=_sessionIdManager.newSessionId(request,_created);
845             _nodeId=_sessionIdManager.getNodeId(_clusterId,request);
846             _accessed=_created;
847             _requests=1;
848         }
849 
850         /* ------------------------------------------------------------- */
851         protected Session(long created, String clusterId)
852         {
853             _created=created;
854             _clusterId=clusterId;
855             _nodeId=_sessionIdManager.getNodeId(_clusterId,null);
856             _accessed=_created;
857         }
858 
859         /* ------------------------------------------------------------- */
860         public Session getSession()
861         {
862             return this;
863         }
864 
865         /* ------------------------------------------------------------- */
866         protected void initValues()
867         {
868             _values=newAttributeMap();
869         }
870 
871         /* ------------------------------------------------------------ */
872         public Object getAttribute(String name)
873         {
874             synchronized (this)
875             {
876                 if (_invalid)
877                     throw new IllegalStateException();
878 
879                 if (null == _values)
880                     return null;
881 
882                 return _values.get(name);
883             }
884         }
885 
886         /* ------------------------------------------------------------ */
887         public Enumeration getAttributeNames()
888         {
889             synchronized (this)
890             {
891                 if (_invalid)
892                     throw new IllegalStateException();
893                 List names=_values==null?Collections.EMPTY_LIST:new ArrayList(_values.keySet());
894                 return Collections.enumeration(names);
895             }
896         }
897 
898         /* ------------------------------------------------------------- */
899         public long getCookieSetTime()
900         {
901             return _cookieSet;
902         }
903 
904         /* ------------------------------------------------------------- */
905         public long getCreationTime() throws IllegalStateException
906         {
907             if (_invalid)
908                 throw new IllegalStateException();
909             return _created;
910         }
911 
912         /* ------------------------------------------------------------ */
913         public String getId() throws IllegalStateException
914         {
915             return _nodeIdInSessionId?_nodeId:_clusterId;
916         }
917 
918         /* ------------------------------------------------------------- */
919         protected String getNodeId()
920         {
921             return _nodeId;
922         }
923 
924         /* ------------------------------------------------------------- */
925         protected String getClusterId()
926         {
927             return _clusterId;
928         }
929 
930         /* ------------------------------------------------------------- */
931         public long getLastAccessedTime() throws IllegalStateException
932         {
933             if (_invalid)
934                 throw new IllegalStateException();
935             return _lastAccessed;
936         }
937 
938         /* ------------------------------------------------------------- */
939         public int getMaxInactiveInterval()
940         {
941             if (_invalid)
942                 throw new IllegalStateException();
943             return (int)(_maxIdleMs/1000);
944         }
945 
946         /* ------------------------------------------------------------ */
947         /*
948          * @see javax.servlet.http.HttpSession#getServletContext()
949          */
950         public ServletContext getServletContext()
951         {
952             return _context;
953         }
954 
955         /* ------------------------------------------------------------- */
956         /**
957          * @deprecated
958          */
959         @Deprecated
960         public HttpSessionContext getSessionContext() throws IllegalStateException
961         {
962             if (_invalid)
963                 throw new IllegalStateException();
964             return __nullSessionContext;
965         }
966 
967         /* ------------------------------------------------------------- */
968         /**
969          * @deprecated As of Version 2.2, this method is replaced by
970          *             {@link #getAttribute}
971          */
972         @Deprecated
973         public Object getValue(String name) throws IllegalStateException
974         {
975             return getAttribute(name);
976         }
977 
978         /* ------------------------------------------------------------- */
979         /**
980          * @deprecated As of Version 2.2, this method is replaced by
981          *             {@link #getAttributeNames}
982          */
983         @Deprecated
984         public String[] getValueNames() throws IllegalStateException
985         {
986             synchronized(this)
987             {
988                 if (_invalid)
989                     throw new IllegalStateException();
990                 if (_values==null)
991                     return new String[0];
992                 String[] a=new String[_values.size()];
993                 return (String[])_values.keySet().toArray(a);
994             }
995         }
996 
997         /* ------------------------------------------------------------ */
998         protected void access(long time)
999         {
1000             synchronized(this)
1001             {
1002                 _newSession=false;
1003                 _lastAccessed=_accessed;
1004                 _accessed=time;
1005                 _requests++;
1006             }
1007         }
1008 
1009         /* ------------------------------------------------------------ */
1010         protected void complete()
1011         {
1012             synchronized(this)
1013             {
1014                 _requests--;
1015                 if (_doInvalidate && _requests<=0  )
1016                     doInvalidate();
1017             }
1018         }
1019 
1020 
1021         /* ------------------------------------------------------------- */
1022         protected void timeout() throws IllegalStateException
1023         {
1024             // remove session from context and invalidate other sessions with same ID.
1025             removeSession(this,true);
1026 
1027             // Notify listeners and unbind values
1028             synchronized (this)
1029             {
1030                 if (!_invalid)
1031                 {
1032                     if (_requests<=0)
1033                         doInvalidate();
1034                     else
1035                         _doInvalidate=true;
1036                 }
1037             }
1038         }
1039 
1040         /* ------------------------------------------------------------- */
1041         public void invalidate() throws IllegalStateException
1042         {
1043             // remove session from context and invalidate other sessions with same ID.
1044             removeSession(this,true);
1045             doInvalidate();
1046         }
1047 
1048         /* ------------------------------------------------------------- */
1049         protected void doInvalidate() throws IllegalStateException
1050         {
1051             try
1052             {
1053                 // Notify listeners and unbind values
1054                 if (_invalid)
1055                     throw new IllegalStateException();
1056 
1057                 while (_values!=null && _values.size()>0)
1058                 {
1059                     ArrayList keys;
1060                     synchronized (this)
1061                     {
1062                         keys=new ArrayList(_values.keySet());
1063                     }
1064 
1065                     Iterator iter=keys.iterator();
1066                     while (iter.hasNext())
1067                     {
1068                         String key=(String)iter.next();
1069 
1070                         Object value;
1071                         synchronized (this)
1072                         {
1073                             value=_values.remove(key);
1074                         }
1075                         unbindValue(key,value);
1076 
1077                         if (_sessionAttributeListeners!=null)
1078                         {
1079                             HttpSessionBindingEvent event=new HttpSessionBindingEvent(this,key,value);
1080 
1081                             for (int i=0; i<LazyList.size(_sessionAttributeListeners); i++)
1082                                 ((HttpSessionAttributeListener)LazyList.get(_sessionAttributeListeners,i)).attributeRemoved(event);
1083                         }
1084                     }
1085                 }
1086             }
1087             finally
1088             {
1089                 // mark as invalid
1090                 _invalid=true;
1091             }
1092         }
1093 
1094         /* ------------------------------------------------------------- */
1095         public boolean isIdChanged()
1096         {
1097             return _idChanged;
1098         }
1099 
1100         /* ------------------------------------------------------------- */
1101         public boolean isNew() throws IllegalStateException
1102         {
1103             if (_invalid)
1104                 throw new IllegalStateException();
1105             return _newSession;
1106         }
1107 
1108         /* ------------------------------------------------------------- */
1109         /**
1110          * @deprecated As of Version 2.2, this method is replaced by
1111          *             {@link #setAttribute}
1112          */
1113         @Deprecated
1114         public void putValue(java.lang.String name, java.lang.Object value) throws IllegalStateException
1115         {
1116             setAttribute(name,value);
1117         }
1118 
1119         /* ------------------------------------------------------------ */
1120         public void removeAttribute(String name)
1121         {
1122             Object old;
1123             synchronized(this)
1124             {
1125                 if (_invalid)
1126                     throw new IllegalStateException();
1127                 if (_values==null)
1128                     return;
1129 
1130                 old=_values.remove(name);
1131             }
1132 
1133             if (old!=null)
1134             {
1135                 unbindValue(name,old);
1136                 if (_sessionAttributeListeners!=null)
1137                 {
1138                     HttpSessionBindingEvent event=new HttpSessionBindingEvent(this,name,old);
1139 
1140                     for (int i=0; i<LazyList.size(_sessionAttributeListeners); i++)
1141                         ((HttpSessionAttributeListener)LazyList.get(_sessionAttributeListeners,i)).attributeRemoved(event);
1142                 }
1143             }
1144 
1145         }
1146 
1147         /* ------------------------------------------------------------- */
1148         /**
1149          * @deprecated As of Version 2.2, this method is replaced by
1150          *             {@link #removeAttribute}
1151          */
1152         @Deprecated
1153         public void removeValue(java.lang.String name) throws IllegalStateException
1154         {
1155             removeAttribute(name);
1156         }
1157 
1158         /* ------------------------------------------------------------ */
1159         public void setAttribute(String name, Object value)
1160         {
1161             Object old_value;
1162             if (value==null)
1163             {
1164                 removeAttribute(name);
1165                 return;
1166             }
1167 
1168             synchronized(this)
1169             {
1170                 if (_invalid)
1171                     throw new IllegalStateException();
1172                 if (_values==null)
1173                     _values=newAttributeMap();
1174                 old_value=_values.put(name,value);
1175             }
1176 
1177             if (old_value==null || !value.equals(old_value))
1178             {
1179                 unbindValue(name,old_value);
1180                 bindValue(name,value);
1181 
1182                 if (_sessionAttributeListeners!=null)
1183                 {
1184                     HttpSessionBindingEvent event=new HttpSessionBindingEvent(this,name,old_value==null?value:old_value);
1185 
1186                     for (int i=0; i<LazyList.size(_sessionAttributeListeners); i++)
1187                     {
1188                         HttpSessionAttributeListener l=(HttpSessionAttributeListener)LazyList.get(_sessionAttributeListeners,i);
1189 
1190                         if (old_value==null)
1191                             l.attributeAdded(event);
1192                         else
1193                             l.attributeReplaced(event);
1194                     }
1195                 }
1196             }
1197         }
1198 
1199         /* ------------------------------------------------------------- */
1200         public void setIdChanged(boolean changed)
1201         {
1202             _idChanged=changed;
1203         }
1204 
1205         /* ------------------------------------------------------------- */
1206         public void setMaxInactiveInterval(int secs)
1207         {
1208             _maxIdleMs=(long)secs*1000;
1209         }
1210 
1211         /* ------------------------------------------------------------- */
1212         @Override
1213         public String toString()
1214         {
1215             return this.getClass().getName()+":"+getId()+"@"+hashCode();
1216         }
1217 
1218         /* ------------------------------------------------------------- */
1219         /** If value implements HttpSessionBindingListener, call valueBound() */
1220         protected void bindValue(java.lang.String name, Object value)
1221         {
1222             if (value!=null&&value instanceof HttpSessionBindingListener)
1223                 ((HttpSessionBindingListener)value).valueBound(new HttpSessionBindingEvent(this,name));
1224         }
1225 
1226         /* ------------------------------------------------------------ */
1227         protected boolean isValid()
1228         {
1229             return !_invalid;
1230         }
1231 
1232         /* ------------------------------------------------------------ */
1233         protected abstract Map newAttributeMap();
1234 
1235         /* ------------------------------------------------------------- */
1236         protected void cookieSet()
1237         {
1238             _cookieSet=_accessed;
1239         }
1240 
1241         /* ------------------------------------------------------------- */
1242         /** If value implements HttpSessionBindingListener, call valueUnbound() */
1243         protected void unbindValue(java.lang.String name, Object value)
1244         {
1245             if (value!=null&&value instanceof HttpSessionBindingListener)
1246                 ((HttpSessionBindingListener)value).valueUnbound(new HttpSessionBindingEvent(this,name));
1247         }
1248 
1249         /* ------------------------------------------------------------- */
1250         protected void willPassivate()
1251         {
1252             synchronized(this)
1253             {
1254                 HttpSessionEvent event = new HttpSessionEvent(this);
1255                 for (Iterator iter = _values.values().iterator(); iter.hasNext();)
1256                 {
1257                     Object value = iter.next();
1258                     if (value instanceof HttpSessionActivationListener)
1259                     {
1260                         HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
1261                         listener.sessionWillPassivate(event);
1262                     }
1263                 }
1264             }
1265         }
1266 
1267         /* ------------------------------------------------------------- */
1268         protected void didActivate()
1269         {
1270             synchronized(this)
1271             {
1272                 HttpSessionEvent event = new HttpSessionEvent(this);
1273                 for (Iterator iter = _values.values().iterator(); iter.hasNext();)
1274                 {
1275                     Object value = iter.next();
1276                     if (value instanceof HttpSessionActivationListener)
1277                     {
1278                         HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
1279                         listener.sessionDidActivate(event);
1280                     }
1281                 }
1282             }
1283         }
1284     }
1285 }