View Javadoc

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