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         if (_context != null)
171         {
172             // Look for a session cookie name
173             String tmp=_context.getInitParameter(SessionManager.__SessionCookieProperty);
174             if (tmp!=null)
175                 _sessionCookie=tmp;
176 
177             tmp=_context.getInitParameter(SessionManager.__SessionURLProperty);
178             if (tmp!=null)
179             {
180                 _sessionURL=(tmp==null||"none".equals(tmp))?null:tmp;
181                 _sessionURLPrefix=(tmp==null||"none".equals(tmp))?null:(";"+_sessionURL+"=");
182             }
183 
184             // set up the max session cookie age if it isn't already
185             if (_maxCookieAge==-1)
186             {
187                 tmp=_context.getInitParameter(SessionManager.__MaxAgeProperty);
188                 if (tmp!=null)
189                     _maxCookieAge=Integer.parseInt(tmp.trim());
190             }
191             // set up the session domain if it isn't already
192             if (_sessionDomain==null)
193             {
194                 // only try the context initParams
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                 _sessionPath=_context.getInitParameter(SessionManager.__SessionPathProperty);
203             }
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=getHttpOnly()?new HttpOnlyCookie(_sessionCookie,id):new Cookie(_sessionCookie,id);
318 
319             cookie.setPath((contextPath==null||contextPath.length()==0)?"/":contextPath);
320             cookie.setMaxAge(getMaxCookieAge());
321             cookie.setSecure(requestIsSecure&&getSecureCookies());
322 
323             // set up the overrides
324             if (_sessionDomain!=null)
325                 cookie.setDomain(_sessionDomain);
326             if (_sessionPath!=null)
327                 cookie.setPath(_sessionPath);
328 
329             return cookie;
330         }
331         return null;
332     }
333 
334     public String getSessionDomain()
335     {
336         return _sessionDomain;
337     }
338 
339     /* ------------------------------------------------------------ */
340     /**
341      * @return Returns the sessionHandler.
342      */
343     public SessionHandler getSessionHandler()
344     {
345         return _sessionHandler;
346     }
347 
348     /* ------------------------------------------------------------ */
349     /** 
350      * @deprecated.  Need to review if it is needed.
351      */
352     public abstract Map getSessionMap();
353     
354     /* ------------------------------------------------------------ */
355     public String getSessionPath()
356     {
357         return _sessionPath;
358     }
359 
360     /* ------------------------------------------------------------ */
361     public abstract int getSessions();
362 
363     /* ------------------------------------------------------------ */
364     public String getSessionURL()
365     {
366         return _sessionURL;
367     }
368 
369     /* ------------------------------------------------------------ */
370     public String getSessionURLPrefix()
371     {
372         return _sessionURLPrefix;
373     }
374 
375     /* ------------------------------------------------------------ */
376     /**
377      * @return Returns the usingCookies.
378      */
379     public boolean isUsingCookies()
380     {
381         return _usingCookies;
382     }
383 
384     /* ------------------------------------------------------------ */
385     public boolean isValid(HttpSession session)
386     {
387         Session s = ((SessionIf)session).getSession();
388         return s.isValid();
389     }
390     
391     /* ------------------------------------------------------------ */
392     public String getClusterId(HttpSession session)
393     {
394         Session s = ((SessionIf)session).getSession();
395         return s.getClusterId();
396     }
397     
398     /* ------------------------------------------------------------ */
399     public String getNodeId(HttpSession session)
400     {
401         Session s = ((SessionIf)session).getSession();
402         return s.getNodeId();
403     }
404 
405     /* ------------------------------------------------------------ */
406     /**
407      * Create a new HttpSession for a request
408      */
409     public HttpSession newHttpSession(HttpServletRequest request)
410     {
411         Session session=newSession(request);
412         session.setMaxInactiveInterval(_dftMaxIdleSecs);
413         addSession(session,true);
414         return session;
415     }
416 
417     /* ------------------------------------------------------------ */
418     public void removeEventListener(EventListener listener)
419     {
420         if (listener instanceof HttpSessionAttributeListener)
421             _sessionAttributeListeners=LazyList.remove(_sessionAttributeListeners,listener);
422         if (listener instanceof HttpSessionListener)
423             _sessionListeners=LazyList.remove(_sessionListeners,listener);
424     }
425 
426     /* ------------------------------------------------------------ */
427     public void resetStats()
428     {
429         _minSessions=getSessions();
430         _maxSessions=getSessions();
431     }
432 
433     /* ------------------------------------------------------------ */
434     /**
435      * @param httpOnly
436      *            The httpOnly to set.
437      */
438     public void setHttpOnly(boolean httpOnly)
439     {
440         _httpOnly=httpOnly;
441     }
442     
443 
444     /* ------------------------------------------------------------ */
445     /**
446      * @param metaManager The metaManager used for cross context session management.
447      */
448     public void setIdManager(SessionIdManager metaManager)
449     {
450         _sessionIdManager=metaManager;
451     }
452 
453     /* ------------------------------------------------------------ */
454     public void setMaxCookieAge(int maxCookieAgeInSeconds)
455     {
456         _maxCookieAge=maxCookieAgeInSeconds;
457         
458         if (_maxCookieAge>0 && _refreshCookieAge==0)
459             _refreshCookieAge=_maxCookieAge/3;
460             
461     }
462 
463     /* ------------------------------------------------------------ */
464     /**
465      * @param seconds
466      */
467     public void setMaxInactiveInterval(int seconds)
468     {
469         _dftMaxIdleSecs=seconds;
470     }
471 
472     /* ------------------------------------------------------------ */
473     /**
474      * @deprecated use {@link #setIdManager(SessionIdManager)}
475      */
476     public void setMetaManager(SessionIdManager metaManager)
477     {
478         setIdManager(metaManager);
479     }
480 
481     /* ------------------------------------------------------------ */
482     public void setRefreshCookieAge(int ageInSeconds)
483     {
484         _refreshCookieAge=ageInSeconds;
485     }
486 
487 
488     /* ------------------------------------------------------------ */
489     /**
490      * @param secureCookies
491      *            The secureCookies to set.
492      */
493     public void setSecureCookies(boolean secureCookies)
494     {
495         _secureCookies=secureCookies;
496     }
497 
498     /* ------------------------------------------------------------ */
499     public void setSessionCookie(String cookieName)
500     {
501         _sessionCookie=cookieName;
502     }
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     /* ------------------------------------------------------------ */
547     protected abstract void addSession(Session session);
548 
549     /* ------------------------------------------------------------ */
550     /**
551      * Add the session Registers the session with this manager and registers the
552      * session ID with the sessionIDManager;
553      */
554     protected void addSession(Session session, boolean created)
555     {
556         synchronized (_sessionIdManager)
557         {
558             _sessionIdManager.addSession(session);
559             synchronized (this)
560             {
561                 addSession(session);
562                 if (getSessions()>this._maxSessions)
563                     this._maxSessions=getSessions();
564             }
565         }
566 
567         if (!created)
568         {
569             session.didActivate();
570         }
571         else if (_sessionListeners!=null)
572         {
573             HttpSessionEvent event=new HttpSessionEvent(session);
574             for (int i=0; i<LazyList.size(_sessionListeners); i++)
575                 ((HttpSessionListener)LazyList.get(_sessionListeners,i)).sessionCreated(event);
576         }
577     }
578     
579     /* ------------------------------------------------------------ */
580     /**
581      * Get a known existingsession
582      * @param idInCluster The session ID in the cluster, stripped of any worker name.
583      * @return A Session or null if none exists.
584      */
585     public abstract Session getSession(String idInCluster);
586 
587     /* ------------------------------------------------------------ */
588     protected abstract void invalidateSessions();
589 
590     
591     /* ------------------------------------------------------------ */
592     /**
593      * Create a new session instance
594      * @param request
595      * @return
596      */
597     protected abstract Session newSession(HttpServletRequest request);
598     
599 
600 
601     /* ------------------------------------------------------------ */
602     /**
603      * @return true if the cluster node id (worker id) is returned as part of the session id by {@link HttpSession#getId()}. Default is false. 
604      */
605     public boolean isNodeIdInSessionId()
606     {
607         return _nodeIdInSessionId;
608     }
609 
610     /* ------------------------------------------------------------ */
611     /**
612      * @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.
613      */
614     public void setNodeIdInSessionId(boolean nodeIdInSessionId)
615     {
616         _nodeIdInSessionId=nodeIdInSessionId;
617     }
618 
619     /* ------------------------------------------------------------ */
620     /** Remove session from manager 
621      * @param session The session to remove
622      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
623      * {@link SessionIdManager#invalidateAll(String)} should be called.
624      */
625     public void removeSession(HttpSession session, boolean invalidate)
626     {
627         Session s = ((SessionIf)session).getSession();
628         removeSession(s,invalidate);
629     }
630 
631     /* ------------------------------------------------------------ */
632     /** Remove session from manager 
633      * @param session The session to remove
634      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
635      * {@link SessionIdManager#invalidateAll(String)} should be called.
636      */
637     public void removeSession(Session session, boolean invalidate)
638     {
639         // Remove session from context and global maps
640         boolean removed = false;
641         synchronized (this)
642         {
643             //take this session out of the map of sessions for this context
644             if (getSession(session.getClusterId()) != null)
645             {
646                 removed = true;
647                 removeSession(session.getClusterId());
648             }
649         }  
650 
651         if (removed && invalidate)
652         {
653             // Remove session from all context and global id maps
654             _sessionIdManager.removeSession(session);
655             _sessionIdManager.invalidateAll(session.getClusterId());
656         }
657         
658         if (invalidate && _sessionListeners!=null)
659         {
660             HttpSessionEvent event=new HttpSessionEvent(session);
661             for (int i=LazyList.size(_sessionListeners); i-->0;)
662                 ((HttpSessionListener)LazyList.get(_sessionListeners,i)).sessionDestroyed(event);
663         }
664         if (!invalidate)
665         {
666             session.willPassivate();
667         }
668     }
669 
670     /* ------------------------------------------------------------ */
671     protected abstract void removeSession(String idInCluster);
672     
673     /* ------------------------------------------------------------ */
674     /**
675      * Null returning implementation of HttpSessionContext
676      * 
677      * @author Greg Wilkins (gregw)
678      */
679     public static class NullSessionContext implements HttpSessionContext
680     {
681         /* ------------------------------------------------------------ */
682         private NullSessionContext()
683         {
684         }
685 
686         /* ------------------------------------------------------------ */
687         /**
688          * @deprecated From HttpSessionContext
689          */
690         public Enumeration getIds()
691         {
692             return Collections.enumeration(Collections.EMPTY_LIST);
693         }
694 
695         /* ------------------------------------------------------------ */
696         /**
697          * @deprecated From HttpSessionContext
698          */
699         public HttpSession getSession(String id)
700         {
701             return null;
702         }
703     }
704 
705     /* ------------------------------------------------------------ */
706     /* ------------------------------------------------------------ */
707     /* ------------------------------------------------------------ */
708     /**
709      * Interface that any session wrapper should implement so that
710      * SessionManager may access the Jetty session implementation.
711      *
712      */
713     public interface SessionIf extends HttpSession
714     {
715         public Session getSession();
716     }
717     
718     /* ------------------------------------------------------------ */
719     /* ------------------------------------------------------------ */
720     /* ------------------------------------------------------------ */
721     /**
722      * 
723      * <p>
724      * Implements {@link javax.servlet.HttpSession} from the {@link javax.servlet} package.   
725      * </p>
726      * @author gregw
727      *
728      */
729     public abstract class Session implements SessionIf, Serializable
730     {
731         protected final String _clusterId;
732         protected final String _nodeId;
733         protected boolean _idChanged;
734         protected final long _created;
735         protected long _cookieSet;
736         protected long _accessed;
737         protected long _lastAccessed;
738         protected boolean _invalid;
739         protected boolean _doInvalidate;
740         protected long _maxIdleMs=_dftMaxIdleSecs*1000;
741         protected boolean _newSession;
742         protected Map _values;
743         protected int _requests;
744 
745         /* ------------------------------------------------------------- */
746         protected Session(HttpServletRequest request)
747         {
748             _newSession=true;
749             _created=System.currentTimeMillis();
750             _clusterId=_sessionIdManager.newSessionId(request,_created);
751             _nodeId=_sessionIdManager.getNodeId(_clusterId,request);
752             _accessed=_created;
753             _requests=1;
754         }
755 
756         /* ------------------------------------------------------------- */
757         protected Session(long created, String clusterId)
758         {
759             _created=created;
760             _clusterId=clusterId;
761             _nodeId=_sessionIdManager.getNodeId(_clusterId,null);
762             _accessed=_created;
763         }
764 
765         /* ------------------------------------------------------------- */
766         public Session getSession()
767         {
768             return this;
769         }
770         
771         /* ------------------------------------------------------------- */
772         protected void initValues() 
773         {
774             _values=newAttributeMap();
775         }
776 
777         /* ------------------------------------------------------------ */
778         public synchronized Object getAttribute(String name)
779         {
780             if (_invalid)
781                 throw new IllegalStateException();
782 
783             if (null == _values)
784                 return null;
785             
786             return _values.get(name);
787         }
788 
789         /* ------------------------------------------------------------ */
790         public synchronized Enumeration getAttributeNames()
791         {
792             if (_invalid)
793                 throw new IllegalStateException();
794             List names=_values==null?Collections.EMPTY_LIST:new ArrayList(_values.keySet());
795             return Collections.enumeration(names);
796         }
797         
798         /* ------------------------------------------------------------- */
799         public long getCookieSetTime() 
800         {
801             return _cookieSet;
802         }
803 
804         /* ------------------------------------------------------------- */
805         public long getCreationTime() throws IllegalStateException
806         {
807             if (_invalid)
808                 throw new IllegalStateException();
809             return _created;
810         }
811 
812         /* ------------------------------------------------------------ */
813         public String getId() throws IllegalStateException
814         {
815             return _nodeIdInSessionId?_nodeId:_clusterId;
816         }
817 
818         /* ------------------------------------------------------------- */
819         protected String getNodeId()
820         {
821             return _nodeId;
822         }
823         
824         /* ------------------------------------------------------------- */
825         protected String getClusterId()
826         {
827             return _clusterId;
828         }
829 
830         /* ------------------------------------------------------------- */
831         public long getLastAccessedTime() throws IllegalStateException
832         {
833             if (_invalid)
834                 throw new IllegalStateException();
835             return _lastAccessed;
836         }
837 
838         /* ------------------------------------------------------------- */
839         public int getMaxInactiveInterval()
840         {
841             if (_invalid)
842                 throw new IllegalStateException();
843             return (int)(_maxIdleMs/1000);
844         }
845 
846         /* ------------------------------------------------------------ */
847         /*
848          * @see javax.servlet.http.HttpSession#getServletContext()
849          */
850         public ServletContext getServletContext()
851         {
852             return _context;
853         }
854 
855         /* ------------------------------------------------------------- */
856         /**
857          * @deprecated
858          */
859         public HttpSessionContext getSessionContext() throws IllegalStateException
860         {
861             if (_invalid)
862                 throw new IllegalStateException();
863             return __nullSessionContext;
864         }
865 
866         /* ------------------------------------------------------------- */
867         /**
868          * @deprecated As of Version 2.2, this method is replaced by
869          *             {@link #getAttribute}
870          */
871         public Object getValue(String name) throws IllegalStateException
872         {
873             return getAttribute(name);
874         }
875 
876         /* ------------------------------------------------------------- */
877         /**
878          * @deprecated As of Version 2.2, this method is replaced by
879          *             {@link #getAttributeNames}
880          */
881         public synchronized String[] getValueNames() throws IllegalStateException
882         {
883             if (_invalid)
884                 throw new IllegalStateException();
885             if (_values==null)
886                 return new String[0];
887             String[] a=new String[_values.size()];
888             return (String[])_values.keySet().toArray(a);
889         }
890 
891         /* ------------------------------------------------------------ */
892         protected void access(long time)
893         {
894             synchronized(this)
895             {
896                 _newSession=false;
897                 _lastAccessed=_accessed;
898                 _accessed=time;
899                 _requests++;
900             }
901         }
902 
903         /* ------------------------------------------------------------ */
904         protected void complete()
905         {
906             synchronized(this)
907             {
908                 _requests--;
909                 if (_doInvalidate && _requests<=0  )
910                     doInvalidate();
911             }
912         }
913         
914         
915         /* ------------------------------------------------------------- */
916         protected void timeout() throws IllegalStateException
917         {
918             // remove session from context and invalidate other sessions with same ID.
919             removeSession(this,true);
920 
921             // Notify listeners and unbind values
922             synchronized (this)
923             {
924                 if (!_invalid)
925                 {
926                     if (_requests<=0)
927                         doInvalidate();
928                     else
929                         _doInvalidate=true;
930                 }
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 }