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