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     /* ------------------------------------------------------------ */
501     public void setSessionCookie(String cookieName)
502     {
503         _sessionCookie=cookieName;
504     }
505 
506     /* ------------------------------------------------------------ */
507     public void setSessionDomain(String domain)
508     {
509         _sessionDomain=domain;
510     }
511 
512     /* ------------------------------------------------------------ */
513     /**
514      * @param sessionHandler
515      *            The sessionHandler to set.
516      */
517     public void setSessionHandler(SessionHandler sessionHandler)
518     {
519         _sessionHandler=sessionHandler;
520     }
521 
522     /* ------------------------------------------------------------ */
523     public void setSessionPath(String path)
524     {
525         _sessionPath=path;
526     }
527 
528     /* ------------------------------------------------------------ */
529     /** Set the session ID URL parameter name
530      * @param param The parameter name for session id URL rewriting (null or "none" for no rewriting).
531      */
532     public void setSessionURL(String param)
533     {
534         _sessionURL=(param==null||"none".equals(param))?null:param;
535         _sessionURLPrefix=(param==null||"none".equals(param))?null:(";"+_sessionURL+"=");
536     }
537     /* ------------------------------------------------------------ */
538     /**
539      * @param usingCookies
540      *            The usingCookies to set.
541      */
542     public void setUsingCookies(boolean usingCookies)
543     {
544         _usingCookies=usingCookies;
545     }
546 
547 
548     /* ------------------------------------------------------------ */
549     protected abstract void addSession(Session session);
550 
551     /* ------------------------------------------------------------ */
552     /**
553      * Add the session Registers the session with this manager and registers the
554      * session ID with the sessionIDManager;
555      */
556     protected void addSession(Session session, boolean created)
557     {
558         synchronized (_sessionIdManager)
559         {
560             _sessionIdManager.addSession(session);
561             synchronized (this)
562             {
563                 addSession(session);
564                 if (getSessions()>this._maxSessions)
565                     this._maxSessions=getSessions();
566             }
567         }
568 
569         if (!created)
570         {
571             session.didActivate();
572         }
573         else if (_sessionListeners!=null)
574         {
575             HttpSessionEvent event=new HttpSessionEvent(session);
576             for (int i=0; i<LazyList.size(_sessionListeners); i++)
577                 ((HttpSessionListener)LazyList.get(_sessionListeners,i)).sessionCreated(event);
578         }
579     }
580     
581     /* ------------------------------------------------------------ */
582     /**
583      * Get a known existingsession
584      * @param idInCluster The session ID in the cluster, stripped of any worker name.
585      * @return A Session or null if none exists.
586      */
587     public abstract Session getSession(String idInCluster);
588 
589     /* ------------------------------------------------------------ */
590     protected abstract void invalidateSessions();
591 
592     
593     /* ------------------------------------------------------------ */
594     /**
595      * Create a new session instance
596      * @param request
597      * @return
598      */
599     protected abstract Session newSession(HttpServletRequest request);
600     
601 
602 
603     /* ------------------------------------------------------------ */
604     /**
605      * @return true if the cluster node id (worker id) is returned as part of the session id by {@link HttpSession#getId()}. Default is false. 
606      */
607     public boolean isNodeIdInSessionId()
608     {
609         return _nodeIdInSessionId;
610     }
611 
612     /* ------------------------------------------------------------ */
613     /**
614      * @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.
615      */
616     public void setNodeIdInSessionId(boolean nodeIdInSessionId)
617     {
618         _nodeIdInSessionId=nodeIdInSessionId;
619     }
620 
621     /* ------------------------------------------------------------ */
622     /** Remove session from manager 
623      * @param session The session to remove
624      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
625      * {@link SessionIdManager#invalidateAll(String)} should be called.
626      */
627     public void removeSession(HttpSession session, boolean invalidate)
628     {
629         Session s = ((SessionIf)session).getSession();
630         removeSession(s,invalidate);
631     }
632 
633     /* ------------------------------------------------------------ */
634     /** Remove session from manager 
635      * @param session The session to remove
636      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
637      * {@link SessionIdManager#invalidateAll(String)} should be called.
638      */
639     public void removeSession(Session session, boolean invalidate)
640     {
641         // Remove session from context and global maps
642         synchronized (_sessionIdManager)
643         {
644             boolean removed = false;
645             
646             synchronized (this)
647             {
648                 //take this session out of the map of sessions for this context
649                 if (getSession(session.getClusterId()) != null)
650                 {
651                     removed = true;
652                     removeSession(session.getClusterId());
653                 }
654             }   
655             
656             if (removed)
657             {
658                 // Remove session from all context and global id maps
659                 _sessionIdManager.removeSession(session);
660                 if (invalidate)
661                     _sessionIdManager.invalidateAll(session.getClusterId());
662             }
663         }
664         
665         if (invalidate && _sessionListeners!=null)
666         {
667             HttpSessionEvent event=new HttpSessionEvent(session);
668             for (int i=LazyList.size(_sessionListeners); i-->0;)
669                 ((HttpSessionListener)LazyList.get(_sessionListeners,i)).sessionDestroyed(event);
670         }
671         if (!invalidate)
672         {
673             session.willPassivate();
674         }
675     }
676 
677     /* ------------------------------------------------------------ */
678     protected abstract void removeSession(String idInCluster);
679     
680     /* ------------------------------------------------------------ */
681     /**
682      * Null returning implementation of HttpSessionContext
683      * 
684      * @author Greg Wilkins (gregw)
685      */
686     public static class NullSessionContext implements HttpSessionContext
687     {
688         /* ------------------------------------------------------------ */
689         private NullSessionContext()
690         {
691         }
692 
693         /* ------------------------------------------------------------ */
694         /**
695          * @deprecated From HttpSessionContext
696          */
697         public Enumeration getIds()
698         {
699             return Collections.enumeration(Collections.EMPTY_LIST);
700         }
701 
702         /* ------------------------------------------------------------ */
703         /**
704          * @deprecated From HttpSessionContext
705          */
706         public HttpSession getSession(String id)
707         {
708             return null;
709         }
710     }
711 
712     /* ------------------------------------------------------------ */
713     /* ------------------------------------------------------------ */
714     /* ------------------------------------------------------------ */
715     /**
716      * Interface that any session wrapper should implement so that
717      * SessionManager may access the Jetty session implementation.
718      *
719      */
720     public interface SessionIf extends HttpSession
721     {
722         public Session getSession();
723     }
724     
725     /* ------------------------------------------------------------ */
726     /* ------------------------------------------------------------ */
727     /* ------------------------------------------------------------ */
728     /**
729      * 
730      * <p>
731      * Implements {@link javax.servlet.HttpSession} from the {@link javax.servlet} package.   
732      * </p>
733      * @author gregw
734      *
735      */
736     public abstract class Session implements SessionIf, Serializable
737     {
738         protected final String _clusterId;
739         protected final String _nodeId;
740         protected boolean _idChanged;
741         protected final long _created;
742         protected long _cookieSet;
743         protected long _accessed;
744         protected long _lastAccessed;
745         protected boolean _invalid;
746         protected boolean _doInvalidate;
747         protected long _maxIdleMs=_dftMaxIdleSecs*1000;
748         protected boolean _newSession;
749         protected Map _values;
750         protected int _requests;
751 
752         /* ------------------------------------------------------------- */
753         protected Session(HttpServletRequest request)
754         {
755             _newSession=true;
756             _created=System.currentTimeMillis();
757             _clusterId=_sessionIdManager.newSessionId(request,_created);
758             _nodeId=_sessionIdManager.getNodeId(_clusterId,request);
759             _accessed=_created;
760             _requests=1;
761         }
762 
763         /* ------------------------------------------------------------- */
764         protected Session(long created, String clusterId)
765         {
766             _created=created;
767             _clusterId=clusterId;
768             _nodeId=_sessionIdManager.getNodeId(_clusterId,null);
769             _accessed=_created;
770         }
771 
772         /* ------------------------------------------------------------- */
773         public Session getSession()
774         {
775             return this;
776         }
777         
778         /* ------------------------------------------------------------- */
779         protected void initValues() 
780         {
781             _values=newAttributeMap();
782         }
783 
784         /* ------------------------------------------------------------ */
785         public synchronized Object getAttribute(String name)
786         {
787             if (_invalid)
788                 throw new IllegalStateException();
789 
790             if (null == _values)
791                 return null;
792             
793             return _values.get(name);
794         }
795 
796         /* ------------------------------------------------------------ */
797         public synchronized Enumeration getAttributeNames()
798         {
799             if (_invalid)
800                 throw new IllegalStateException();
801             List names=_values==null?Collections.EMPTY_LIST:new ArrayList(_values.keySet());
802             return Collections.enumeration(names);
803         }
804         
805         /* ------------------------------------------------------------- */
806         public long getCookieSetTime() 
807         {
808             return _cookieSet;
809         }
810 
811         /* ------------------------------------------------------------- */
812         public long getCreationTime() throws IllegalStateException
813         {
814             if (_invalid)
815                 throw new IllegalStateException();
816             return _created;
817         }
818 
819         /* ------------------------------------------------------------ */
820         public String getId() throws IllegalStateException
821         {
822             return _nodeIdInSessionId?_nodeId:_clusterId;
823         }
824 
825         /* ------------------------------------------------------------- */
826         protected String getNodeId()
827         {
828             return _nodeId;
829         }
830         
831         /* ------------------------------------------------------------- */
832         protected String getClusterId()
833         {
834             return _clusterId;
835         }
836 
837         /* ------------------------------------------------------------- */
838         public long getLastAccessedTime() throws IllegalStateException
839         {
840             if (_invalid)
841                 throw new IllegalStateException();
842             return _lastAccessed;
843         }
844 
845         /* ------------------------------------------------------------- */
846         public int getMaxInactiveInterval()
847         {
848             if (_invalid)
849                 throw new IllegalStateException();
850             return (int)(_maxIdleMs/1000);
851         }
852 
853         /* ------------------------------------------------------------ */
854         /*
855          * @see javax.servlet.http.HttpSession#getServletContext()
856          */
857         public ServletContext getServletContext()
858         {
859             return _context;
860         }
861 
862         /* ------------------------------------------------------------- */
863         /**
864          * @deprecated
865          */
866         public HttpSessionContext getSessionContext() throws IllegalStateException
867         {
868             if (_invalid)
869                 throw new IllegalStateException();
870             return __nullSessionContext;
871         }
872 
873         /* ------------------------------------------------------------- */
874         /**
875          * @deprecated As of Version 2.2, this method is replaced by
876          *             {@link #getAttribute}
877          */
878         public Object getValue(String name) throws IllegalStateException
879         {
880             return getAttribute(name);
881         }
882 
883         /* ------------------------------------------------------------- */
884         /**
885          * @deprecated As of Version 2.2, this method is replaced by
886          *             {@link #getAttributeNames}
887          */
888         public synchronized String[] getValueNames() throws IllegalStateException
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         protected void access(long time)
900         {
901             synchronized(this)
902             {
903                 _newSession=false;
904                 _lastAccessed=_accessed;
905                 _accessed=time;
906                 _requests++;
907             }
908         }
909 
910         /* ------------------------------------------------------------ */
911         protected void complete()
912         {
913             synchronized(this)
914             {
915                 _requests--;
916                 if (_doInvalidate && _requests<=0  )
917                     doInvalidate();
918             }
919         }
920         
921         
922         /* ------------------------------------------------------------- */
923         protected void timeout() throws IllegalStateException
924         {
925             // remove session from context and invalidate other sessions with same ID.
926             removeSession(this,true);
927 
928             // Notify listeners and unbind values
929             synchronized (this)
930             {
931                 if (_requests<=0)
932                     doInvalidate();
933                 else
934                     _doInvalidate=true;
935             }
936         }
937         
938         /* ------------------------------------------------------------- */
939         public void invalidate() throws IllegalStateException
940         {
941             // remove session from context and invalidate other sessions with same ID.
942             removeSession(this,true);
943             doInvalidate();
944         }
945         
946         /* ------------------------------------------------------------- */
947         protected void doInvalidate() throws IllegalStateException
948         {
949             try
950             {
951                 // Notify listeners and unbind values
952                 if (_invalid)
953                     throw new IllegalStateException();
954 
955                 while (_values!=null && _values.size()>0)
956                 {
957                     ArrayList keys;
958                     synchronized (this)
959                     {
960                         keys=new ArrayList(_values.keySet());
961                     }
962 
963                     Iterator iter=keys.iterator();
964                     while (iter.hasNext())
965                     {
966                         String key=(String)iter.next();
967 
968                         Object value;
969                         synchronized (this)
970                         {
971                             value=_values.remove(key);
972                         }
973                         unbindValue(key,value);
974 
975                         if (_sessionAttributeListeners!=null)
976                         {
977                             HttpSessionBindingEvent event=new HttpSessionBindingEvent(this,key,value);
978 
979                             for (int i=0; i<LazyList.size(_sessionAttributeListeners); i++)
980                                 ((HttpSessionAttributeListener)LazyList.get(_sessionAttributeListeners,i)).attributeRemoved(event);
981                         }
982                     }
983                 }
984             }
985             finally
986             {
987                 // mark as invalid
988                 _invalid=true;
989             }
990         }
991 
992         /* ------------------------------------------------------------- */
993         public boolean isIdChanged()
994         {
995             return _idChanged;
996         }
997 
998         /* ------------------------------------------------------------- */
999         public boolean isNew() throws IllegalStateException
1000         {
1001             if (_invalid)
1002                 throw new IllegalStateException();
1003             return _newSession;
1004         }
1005 
1006         /* ------------------------------------------------------------- */
1007         /**
1008          * @deprecated As of Version 2.2, this method is replaced by
1009          *             {@link #setAttribute}
1010          */
1011         public void putValue(java.lang.String name, java.lang.Object value) throws IllegalStateException
1012         {
1013             setAttribute(name,value);
1014         }
1015 
1016         /* ------------------------------------------------------------ */
1017         public synchronized void removeAttribute(String name)
1018         {
1019             if (_invalid)
1020                 throw new IllegalStateException();
1021             if (_values==null)
1022                 return;
1023 
1024             Object old=_values.remove(name);
1025             if (old!=null)
1026             {
1027                 unbindValue(name,old);
1028                 if (_sessionAttributeListeners!=null)
1029                 {
1030                     HttpSessionBindingEvent event=new HttpSessionBindingEvent(this,name,old);
1031 
1032                     for (int i=0; i<LazyList.size(_sessionAttributeListeners); i++)
1033                         ((HttpSessionAttributeListener)LazyList.get(_sessionAttributeListeners,i)).attributeRemoved(event);
1034                 }
1035             }
1036         }
1037 
1038         /* ------------------------------------------------------------- */
1039         /**
1040          * @deprecated As of Version 2.2, this method is replaced by
1041          *             {@link #removeAttribute}
1042          */
1043         public void removeValue(java.lang.String name) throws IllegalStateException
1044         {
1045             removeAttribute(name);
1046         }
1047 
1048         /* ------------------------------------------------------------ */
1049         public synchronized void setAttribute(String name, Object value)
1050         {
1051             if (value==null)
1052             {
1053                 removeAttribute(name);
1054                 return;
1055             }
1056 
1057             if (_invalid)
1058                 throw new IllegalStateException();
1059             if (_values==null)
1060                 _values=newAttributeMap();
1061             Object oldValue=_values.put(name,value);
1062 
1063             if (oldValue==null || !value.equals(oldValue)) 
1064             {
1065                 unbindValue(name,oldValue);
1066                 bindValue(name,value);
1067 
1068                 if (_sessionAttributeListeners!=null)
1069                 {
1070                     HttpSessionBindingEvent event=new HttpSessionBindingEvent(this,name,oldValue==null?value:oldValue);
1071 
1072                     for (int i=0; i<LazyList.size(_sessionAttributeListeners); i++)
1073                     {
1074                         HttpSessionAttributeListener l=(HttpSessionAttributeListener)LazyList.get(_sessionAttributeListeners,i);
1075 
1076                         if (oldValue==null)
1077                             l.attributeAdded(event);
1078                         else if (value==null)
1079                             l.attributeRemoved(event);
1080                         else
1081                             l.attributeReplaced(event);
1082                     }
1083                 }
1084             }
1085         }
1086 
1087         /* ------------------------------------------------------------- */
1088         public void setIdChanged(boolean changed)
1089         {
1090             _idChanged=changed;
1091         }
1092 
1093         /* ------------------------------------------------------------- */
1094         public void setMaxInactiveInterval(int secs)
1095         {
1096             _maxIdleMs=(long)secs*1000;
1097         }
1098 
1099         /* ------------------------------------------------------------- */
1100         public String toString()
1101         {
1102             return this.getClass().getName()+":"+getId()+"@"+hashCode();
1103         }
1104 
1105         /* ------------------------------------------------------------- */
1106         /** If value implements HttpSessionBindingListener, call valueBound() */
1107         protected void bindValue(java.lang.String name, Object value)
1108         {
1109             if (value!=null&&value instanceof HttpSessionBindingListener)
1110                 ((HttpSessionBindingListener)value).valueBound(new HttpSessionBindingEvent(this,name));
1111         }
1112 
1113         /* ------------------------------------------------------------ */
1114         protected boolean isValid()
1115         {
1116             return !_invalid;
1117         }
1118 
1119         /* ------------------------------------------------------------ */
1120         protected abstract Map newAttributeMap();
1121 
1122         /* ------------------------------------------------------------- */
1123         protected void cookieSet()
1124         {
1125             _cookieSet=_accessed;
1126         }
1127 
1128         /* ------------------------------------------------------------- */
1129         /** If value implements HttpSessionBindingListener, call valueUnbound() */
1130         protected void unbindValue(java.lang.String name, Object value)
1131         {
1132             if (value!=null&&value instanceof HttpSessionBindingListener)
1133                 ((HttpSessionBindingListener)value).valueUnbound(new HttpSessionBindingEvent(this,name));
1134         }
1135 
1136         /* ------------------------------------------------------------- */
1137         protected synchronized void willPassivate() 
1138         {
1139             HttpSessionEvent event = new HttpSessionEvent(this);
1140             for (Iterator iter = _values.values().iterator(); iter.hasNext();) 
1141             {
1142                 Object value = iter.next();
1143                 if (value instanceof HttpSessionActivationListener) 
1144                 {
1145                     HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
1146                     listener.sessionWillPassivate(event);
1147                 }
1148             }
1149         }
1150 
1151         /* ------------------------------------------------------------- */
1152         protected synchronized void didActivate() 
1153         {
1154             HttpSessionEvent event = new HttpSessionEvent(this);
1155             for (Iterator iter = _values.values().iterator(); iter.hasNext();) 
1156             {
1157                 Object value = iter.next();
1158                 if (value instanceof HttpSessionActivationListener) 
1159                 {
1160                     HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
1161                     listener.sessionDidActivate(event);
1162                 }
1163             }
1164         }
1165     }
1166 }