]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/server/session/AbstractSessionManager.java
Update notes about password security
[gigi.git] / lib / jetty / org / eclipse / jetty / server / session / AbstractSessionManager.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18
19 package org.eclipse.jetty.server.session;
20
21 import static java.lang.Math.round;
22
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.Enumeration;
26 import java.util.EventListener;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.concurrent.CopyOnWriteArrayList;
31
32 import javax.servlet.SessionCookieConfig;
33 import javax.servlet.SessionTrackingMode;
34 import javax.servlet.http.HttpServletRequest;
35 import javax.servlet.http.HttpSession;
36 import javax.servlet.http.HttpSessionAttributeListener;
37 import javax.servlet.http.HttpSessionBindingEvent;
38 import javax.servlet.http.HttpSessionContext;
39 import javax.servlet.http.HttpSessionEvent;
40 import javax.servlet.http.HttpSessionIdListener;
41 import javax.servlet.http.HttpSessionListener;
42
43 import org.eclipse.jetty.http.HttpCookie;
44 import org.eclipse.jetty.server.Server;
45 import org.eclipse.jetty.server.SessionIdManager;
46 import org.eclipse.jetty.server.SessionManager;
47 import org.eclipse.jetty.server.handler.ContextHandler;
48 import org.eclipse.jetty.util.annotation.ManagedAttribute;
49 import org.eclipse.jetty.util.annotation.ManagedObject;
50 import org.eclipse.jetty.util.annotation.ManagedOperation;
51 import org.eclipse.jetty.util.component.ContainerLifeCycle;
52 import org.eclipse.jetty.util.log.Logger;
53 import org.eclipse.jetty.util.statistic.CounterStatistic;
54 import org.eclipse.jetty.util.statistic.SampleStatistic;
55
56 /* ------------------------------------------------------------ */
57 /**
58  * An Abstract implementation of SessionManager. The partial implementation of
59  * SessionManager interface provides the majority of the handling required to
60  * implement a SessionManager. Concrete implementations of SessionManager based
61  * on AbstractSessionManager need only implement the newSession method to return
62  * a specialised version of the Session inner class that provides an attribute
63  * Map.
64  * <p>
65  */
66 @SuppressWarnings("deprecation")
67 @ManagedObject("Abstract Session Manager")
68 public abstract class AbstractSessionManager extends ContainerLifeCycle implements SessionManager
69 {
70     final static Logger __log = SessionHandler.LOG;
71
72     public Set<SessionTrackingMode> __defaultSessionTrackingModes =
73         Collections.unmodifiableSet(
74             new HashSet<SessionTrackingMode>(
75                     Arrays.asList(new SessionTrackingMode[]{SessionTrackingMode.COOKIE,SessionTrackingMode.URL})));
76
77     
78
79     /* ------------------------------------------------------------ */
80     public final static int __distantFuture=60*60*24*7*52*20;
81
82     static final HttpSessionContext __nullSessionContext=new HttpSessionContext()
83     {
84         @Override
85         public HttpSession getSession(String sessionId)
86         {
87             return null;
88         }
89
90         @Override
91         @SuppressWarnings({ "rawtypes", "unchecked" })
92         public Enumeration getIds()
93         {
94             return Collections.enumeration(Collections.EMPTY_LIST);
95         }
96     };
97
98     private boolean _usingCookies=true;
99
100     /* ------------------------------------------------------------ */
101     // Setting of max inactive interval for new sessions
102     // -1 means no timeout
103     protected int _dftMaxIdleSecs=-1;
104     protected SessionHandler _sessionHandler;
105     protected boolean _httpOnly=false;
106     protected SessionIdManager _sessionIdManager;
107     protected boolean _secureCookies=false;
108     protected boolean _secureRequestOnly=true;
109
110     protected final List<HttpSessionAttributeListener> _sessionAttributeListeners = new CopyOnWriteArrayList<HttpSessionAttributeListener>();
111     protected final List<HttpSessionListener> _sessionListeners= new CopyOnWriteArrayList<HttpSessionListener>();
112     protected final List<HttpSessionIdListener> _sessionIdListeners = new CopyOnWriteArrayList<HttpSessionIdListener>();
113
114     protected ClassLoader _loader;
115     protected ContextHandler.Context _context;
116     protected String _sessionCookie=__DefaultSessionCookie;
117     protected String _sessionIdPathParameterName = __DefaultSessionIdPathParameterName;
118     protected String _sessionIdPathParameterNamePrefix =";"+ _sessionIdPathParameterName +"=";
119     protected String _sessionDomain;
120     protected String _sessionPath;
121     protected int _maxCookieAge=-1;
122     protected int _refreshCookieAge;
123     protected boolean _nodeIdInSessionId;
124     protected boolean _checkingRemoteSessionIdEncoding;
125     protected String _sessionComment;
126
127     public Set<SessionTrackingMode> _sessionTrackingModes;
128
129     private boolean _usingURLs;
130
131     protected final CounterStatistic _sessionsStats = new CounterStatistic();
132     protected final SampleStatistic _sessionTimeStats = new SampleStatistic();
133
134
135     /* ------------------------------------------------------------ */
136     public AbstractSessionManager()
137     {
138         setSessionTrackingModes(__defaultSessionTrackingModes);
139     }
140
141     /* ------------------------------------------------------------ */
142     public ContextHandler.Context getContext()
143     {
144         return _context;
145     }
146
147     /* ------------------------------------------------------------ */
148     public ContextHandler getContextHandler()
149     {
150         return _context.getContextHandler();
151     }
152
153     @ManagedAttribute("path of the session cookie, or null for default")
154     public String getSessionPath()
155     {
156         return _sessionPath;
157     }
158
159     @ManagedAttribute("if greater the zero, the time in seconds a session cookie will last for")
160     public int getMaxCookieAge()
161     {
162         return _maxCookieAge;
163     }
164
165     /* ------------------------------------------------------------ */
166     @Override
167     public HttpCookie access(HttpSession session,boolean secure)
168     {
169         long now=System.currentTimeMillis();
170
171         AbstractSession s = ((SessionIf)session).getSession();
172
173        if (s.access(now))
174        {
175             // Do we need to refresh the cookie?
176             if (isUsingCookies() &&
177                 (s.isIdChanged() ||
178                 (getSessionCookieConfig().getMaxAge()>0 && getRefreshCookieAge()>0 && ((now-s.getCookieSetTime())/1000>getRefreshCookieAge()))
179                 )
180                )
181             {
182                 HttpCookie cookie=getSessionCookie(session,_context==null?"/":(_context.getContextPath()),secure);
183                 s.cookieSet();
184                 s.setIdChanged(false);
185                 return cookie;
186             }
187         }
188         return null;
189     }
190
191     /* ------------------------------------------------------------ */
192     @Override
193     public void addEventListener(EventListener listener)
194     {
195         if (listener instanceof HttpSessionAttributeListener)
196             _sessionAttributeListeners.add((HttpSessionAttributeListener)listener);
197         if (listener instanceof HttpSessionListener)
198             _sessionListeners.add((HttpSessionListener)listener);
199         if (listener instanceof HttpSessionIdListener)
200             _sessionIdListeners.add((HttpSessionIdListener)listener);
201         addBean(listener,false);
202     }
203
204     /* ------------------------------------------------------------ */
205     @Override
206     public void clearEventListeners()
207     {
208         for (EventListener e :getBeans(EventListener.class))
209             removeBean(e);
210         _sessionAttributeListeners.clear();
211         _sessionListeners.clear();
212         _sessionIdListeners.clear();
213     }
214
215     /* ------------------------------------------------------------ */
216     @Override
217     public void complete(HttpSession session)
218     {
219         AbstractSession s = ((SessionIf)session).getSession();
220         s.complete();
221     }
222
223     /* ------------------------------------------------------------ */
224     @Override
225     public void doStart() throws Exception
226     {
227         _context=ContextHandler.getCurrentContext();
228         _loader=Thread.currentThread().getContextClassLoader();
229
230         if (_sessionIdManager==null)
231         {
232             final Server server=getSessionHandler().getServer();
233             synchronized (server)
234             {
235                 _sessionIdManager=server.getSessionIdManager();
236                 if (_sessionIdManager==null)
237                 {
238                     //create a default SessionIdManager and set it as the shared
239                     //SessionIdManager for the Server, being careful NOT to use
240                     //the webapp context's classloader, otherwise if the context
241                     //is stopped, the classloader is leaked.
242                     ClassLoader serverLoader = server.getClass().getClassLoader();
243                     try
244                     {
245                         Thread.currentThread().setContextClassLoader(serverLoader);
246                         _sessionIdManager=new HashSessionIdManager();
247                         server.setSessionIdManager(_sessionIdManager);
248                         server.manage(_sessionIdManager);
249                         _sessionIdManager.start();
250                     }
251                     finally
252                     {
253                         Thread.currentThread().setContextClassLoader(_loader);
254                     }
255                 }
256
257                 // server session id is never managed by this manager
258                 addBean(_sessionIdManager,false);
259             }
260         }
261         
262
263         // Look for a session cookie name
264         if (_context!=null)
265         {
266             String tmp=_context.getInitParameter(SessionManager.__SessionCookieProperty);
267             if (tmp!=null)
268                 _sessionCookie=tmp;
269
270             tmp=_context.getInitParameter(SessionManager.__SessionIdPathParameterNameProperty);
271             if (tmp!=null)
272                 setSessionIdPathParameterName(tmp);
273
274             // set up the max session cookie age if it isn't already
275             if (_maxCookieAge==-1)
276             {
277                 tmp=_context.getInitParameter(SessionManager.__MaxAgeProperty);
278                 if (tmp!=null)
279                     _maxCookieAge=Integer.parseInt(tmp.trim());
280             }
281
282             // set up the session domain if it isn't already
283             if (_sessionDomain==null)
284                 _sessionDomain=_context.getInitParameter(SessionManager.__SessionDomainProperty);
285
286             // set up the sessionPath if it isn't already
287             if (_sessionPath==null)
288                 _sessionPath=_context.getInitParameter(SessionManager.__SessionPathProperty);
289
290             tmp=_context.getInitParameter(SessionManager.__CheckRemoteSessionEncoding);
291             if (tmp!=null)
292                 _checkingRemoteSessionIdEncoding=Boolean.parseBoolean(tmp);
293         }
294
295         super.doStart();
296     }
297
298     /* ------------------------------------------------------------ */
299     @Override
300     public void doStop() throws Exception
301     {
302         super.doStop();
303
304         shutdownSessions();
305
306         _loader=null;
307     }
308
309     /* ------------------------------------------------------------ */
310     /**
311      * @return Returns the httpOnly.
312      */
313     @Override
314     @ManagedAttribute("true if cookies use the http only flag")
315     public boolean getHttpOnly()
316     {
317         return _httpOnly;
318     }
319
320     /* ------------------------------------------------------------ */
321     @Override
322     public HttpSession getHttpSession(String nodeId)
323     {
324         String cluster_id = getSessionIdManager().getClusterId(nodeId);
325
326         AbstractSession session = getSession(cluster_id);
327         if (session!=null && !session.getNodeId().equals(nodeId))
328             session.setIdChanged(true);
329         return session;
330     }
331
332     /* ------------------------------------------------------------ */
333     /**
334      * @return Returns the SessionIdManager used for cross context session management
335      */
336     @Override
337     @ManagedAttribute("Session ID Manager")
338     public SessionIdManager getSessionIdManager()
339     {
340         return _sessionIdManager;
341     }
342
343
344     /* ------------------------------------------------------------ */
345     /**
346      * @return seconds
347      */
348     @Override
349     @ManagedAttribute("defailt maximum time a session may be idle for (in s)")
350     public int getMaxInactiveInterval()
351     {
352         return _dftMaxIdleSecs;
353     }
354
355     /* ------------------------------------------------------------ */
356     /**
357      * @return maximum number of sessions
358      */
359     @ManagedAttribute("maximum number of simultaneous sessions")
360     public int getSessionsMax()
361     {
362         return (int)_sessionsStats.getMax();
363     }
364
365     /* ------------------------------------------------------------ */
366     /**
367      * @return total number of sessions
368      */
369     @ManagedAttribute("total number of sessions")
370     public int getSessionsTotal()
371     {
372         return (int)_sessionsStats.getTotal();
373     }
374
375     /* ------------------------------------------------------------ */
376     @ManagedAttribute("time before a session cookie is re-set (in s)")
377     public int getRefreshCookieAge()
378     {
379         return _refreshCookieAge;
380     }
381
382     /* ------------------------------------------------------------ */
383     /**
384      * @return same as SessionCookieConfig.getSecure(). If true, session
385      * cookies are ALWAYS marked as secure. If false, a session cookie is
386      * ONLY marked as secure if _secureRequestOnly == true and it is a HTTPS request.
387      */
388     @ManagedAttribute("if true, secure cookie flag is set on session cookies")
389     public boolean getSecureCookies()
390     {
391         return _secureCookies;
392     }
393
394     /* ------------------------------------------------------------ */
395     /**
396      * @return true if session cookie is to be marked as secure only on HTTPS requests
397      */
398     public boolean isSecureRequestOnly()
399     {
400         return _secureRequestOnly;
401     }
402
403
404     /* ------------------------------------------------------------ */
405     /**
406      * HTTPS request. Can be overridden by setting SessionCookieConfig.setSecure(true),
407      * in which case the session cookie will be marked as secure on both HTTPS and HTTP.
408      */
409     public void setSecureRequestOnly(boolean secureRequestOnly)
410     {
411         _secureRequestOnly = secureRequestOnly;
412     }
413
414     /* ------------------------------------------------------------ */
415     @ManagedAttribute("the set session cookie")
416     public String getSessionCookie()
417     {
418         return _sessionCookie;
419     }
420
421     /* ------------------------------------------------------------ */
422     /**
423      * A sessioncookie is marked as secure IFF any of the following conditions are true:
424      * <ol>
425      * <li>SessionCookieConfig.setSecure == true</li>
426      * <li>SessionCookieConfig.setSecure == false && _secureRequestOnly==true && request is HTTPS</li>
427      * </ol>
428      * According to SessionCookieConfig javadoc, case 1 can be used when:
429      * "... even though the request that initiated the session came over HTTP,
430      * is to support a topology where the web container is front-ended by an
431      * SSL offloading load balancer. In this case, the traffic between the client
432      * and the load balancer will be over HTTPS, whereas the traffic between the
433      * load balancer and the web container will be over HTTP."
434      *
435      * For case 2, you can use _secureRequestOnly to determine if you want the
436      * Servlet Spec 3.0  default behaviour when SessionCookieConfig.setSecure==false,
437      * which is:
438      * "they shall be marked as secure only if the request that initiated the
439      * corresponding session was also secure"
440      *
441      * The default for _secureRequestOnly is true, which gives the above behaviour. If
442      * you set it to false, then a session cookie is NEVER marked as secure, even if
443      * the initiating request was secure.
444      *
445      * @see org.eclipse.jetty.server.SessionManager#getSessionCookie(javax.servlet.http.HttpSession, java.lang.String, boolean)
446      */
447     @Override
448     public HttpCookie getSessionCookie(HttpSession session, String contextPath, boolean requestIsSecure)
449     {
450         if (isUsingCookies())
451         {
452             String sessionPath = (_cookieConfig.getPath()==null) ? contextPath : _cookieConfig.getPath();
453             sessionPath = (sessionPath==null||sessionPath.length()==0) ? "/" : sessionPath;
454             String id = getNodeId(session);
455             HttpCookie cookie = null;
456             if (_sessionComment == null)
457             {
458                 cookie = new HttpCookie(
459                                         _cookieConfig.getName(),
460                                         id,
461                                         _cookieConfig.getDomain(),
462                                         sessionPath,
463                                         _cookieConfig.getMaxAge(),
464                                         _cookieConfig.isHttpOnly(),
465                                         _cookieConfig.isSecure() || (isSecureRequestOnly() && requestIsSecure));
466             }
467             else
468             {
469                 cookie = new HttpCookie(
470                                         _cookieConfig.getName(),
471                                         id,
472                                         _cookieConfig.getDomain(),
473                                         sessionPath,
474                                         _cookieConfig.getMaxAge(),
475                                         _cookieConfig.isHttpOnly(),
476                                         _cookieConfig.isSecure() || (isSecureRequestOnly() && requestIsSecure),
477                                         _sessionComment,
478                                         1);
479             }
480
481             return cookie;
482         }
483         return null;
484     }
485
486     @ManagedAttribute("domain of the session cookie, or null for the default")
487     public String getSessionDomain()
488     {
489         return _sessionDomain;
490     }
491
492     /* ------------------------------------------------------------ */
493     /**
494      * @return Returns the sessionHandler.
495      */
496     public SessionHandler getSessionHandler()
497     {
498         return _sessionHandler;
499     }
500
501     /* ------------------------------------------------------------ */
502     @ManagedAttribute("number of currently active sessions")
503     public int getSessions()
504     {
505         return (int)_sessionsStats.getCurrent();
506     }
507
508     /* ------------------------------------------------------------ */
509     @Override
510     @ManagedAttribute("name of use for URL session tracking")
511     public String getSessionIdPathParameterName()
512     {
513         return _sessionIdPathParameterName;
514     }
515
516     /* ------------------------------------------------------------ */
517     @Override
518     public String getSessionIdPathParameterNamePrefix()
519     {
520         return _sessionIdPathParameterNamePrefix;
521     }
522
523     /* ------------------------------------------------------------ */
524     /**
525      * @return Returns the usingCookies.
526      */
527     @Override
528     public boolean isUsingCookies()
529     {
530         return _usingCookies;
531     }
532
533     /* ------------------------------------------------------------ */
534     @Override
535     public boolean isValid(HttpSession session)
536     {
537         AbstractSession s = ((SessionIf)session).getSession();
538         return s.isValid();
539     }
540
541     /* ------------------------------------------------------------ */
542     @Override
543     public String getClusterId(HttpSession session)
544     {
545         AbstractSession s = ((SessionIf)session).getSession();
546         return s.getClusterId();
547     }
548
549     /* ------------------------------------------------------------ */
550     @Override
551     public String getNodeId(HttpSession session)
552     {
553         AbstractSession s = ((SessionIf)session).getSession();
554         return s.getNodeId();
555     }
556
557     /* ------------------------------------------------------------ */
558     /**
559      * Create a new HttpSession for a request
560      */
561     @Override
562     public HttpSession newHttpSession(HttpServletRequest request)
563     {
564         AbstractSession session=newSession(request);
565         session.setMaxInactiveInterval(_dftMaxIdleSecs);
566         addSession(session,true);
567         return session;
568     }
569
570     /* ------------------------------------------------------------ */
571     @Override
572     public void removeEventListener(EventListener listener)
573     {
574         if (listener instanceof HttpSessionAttributeListener)
575             _sessionAttributeListeners.remove(listener);
576         if (listener instanceof HttpSessionListener)
577             _sessionListeners.remove(listener);
578     }
579     
580     /* ------------------------------------------------------------ */
581     /**
582      * Reset statistics values
583      */
584     @ManagedOperation(value="reset statistics", impact="ACTION")
585     public void statsReset()
586     {
587         _sessionsStats.reset(getSessions());
588         _sessionTimeStats.reset();
589     }
590
591     /* ------------------------------------------------------------ */
592     /**
593      * @param httpOnly
594      *            The httpOnly to set.
595      */
596     public void setHttpOnly(boolean httpOnly)
597     {
598         _httpOnly=httpOnly;
599     }
600
601     /* ------------------------------------------------------------ */
602     /**
603      * @param metaManager The metaManager used for cross context session management.
604      */
605     @Override
606     public void setSessionIdManager(SessionIdManager metaManager)
607     {
608         updateBean(_sessionIdManager, metaManager);
609         _sessionIdManager=metaManager;
610     }
611
612     /* ------------------------------------------------------------ */
613     /**
614      * @param seconds
615      */
616     @Override
617     public void setMaxInactiveInterval(int seconds)
618     {
619         _dftMaxIdleSecs=seconds;
620     }
621
622     /* ------------------------------------------------------------ */
623     public void setRefreshCookieAge(int ageInSeconds)
624     {
625         _refreshCookieAge=ageInSeconds;
626     }
627
628     /* ------------------------------------------------------------ */
629     public void setSessionCookie(String cookieName)
630     {
631         _sessionCookie=cookieName;
632     }
633
634     /* ------------------------------------------------------------ */
635     /**
636      * @param sessionHandler
637      *            The sessionHandler to set.
638      */
639     @Override
640     public void setSessionHandler(SessionHandler sessionHandler)
641     {
642         _sessionHandler=sessionHandler;
643     }
644
645
646     /* ------------------------------------------------------------ */
647     @Override
648     public void setSessionIdPathParameterName(String param)
649     {
650         _sessionIdPathParameterName =(param==null||"none".equals(param))?null:param;
651         _sessionIdPathParameterNamePrefix =(param==null||"none".equals(param))?null:(";"+ _sessionIdPathParameterName +"=");
652     }
653     /* ------------------------------------------------------------ */
654     /**
655      * @param usingCookies
656      *            The usingCookies to set.
657      */
658     public void setUsingCookies(boolean usingCookies)
659     {
660         _usingCookies=usingCookies;
661     }
662
663
664     protected abstract void addSession(AbstractSession session);
665
666     /* ------------------------------------------------------------ */
667     /**
668      * Add the session Registers the session with this manager and registers the
669      * session ID with the sessionIDManager;
670      */
671     protected void addSession(AbstractSession session, boolean created)
672     {
673         synchronized (_sessionIdManager)
674         {
675             _sessionIdManager.addSession(session);
676             addSession(session);
677         }
678
679         if (created)
680         {
681             _sessionsStats.increment();
682             if (_sessionListeners!=null)
683             {
684                 HttpSessionEvent event=new HttpSessionEvent(session);
685                 for (HttpSessionListener listener : _sessionListeners)
686                     listener.sessionCreated(event);
687             }
688         }
689     }
690
691     /* ------------------------------------------------------------ */
692     /**
693      * Get a known existing session
694      * @param idInCluster The session ID in the cluster, stripped of any worker name.
695      * @return A Session or null if none exists.
696      */
697     public abstract AbstractSession getSession(String idInCluster);
698
699     /**
700      * Prepare sessions for session manager shutdown
701      * 
702      * @throws Exception
703      */
704     protected abstract void shutdownSessions() throws Exception;
705
706
707     /* ------------------------------------------------------------ */
708     /**
709      * Create a new session instance
710      * @param request
711      * @return the new session
712      */
713     protected abstract AbstractSession newSession(HttpServletRequest request);
714
715
716     /* ------------------------------------------------------------ */
717     /**
718      * @return true if the cluster node id (worker id) is returned as part of the session id by {@link HttpSession#getId()}. Default is false.
719      */
720     public boolean isNodeIdInSessionId()
721     {
722         return _nodeIdInSessionId;
723     }
724
725     /* ------------------------------------------------------------ */
726     /**
727      * @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.
728      */
729     public void setNodeIdInSessionId(boolean nodeIdInSessionId)
730     {
731         _nodeIdInSessionId=nodeIdInSessionId;
732     }
733
734     /* ------------------------------------------------------------ */
735     /** Remove session from manager
736      * @param session The session to remove
737      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
738      * {@link SessionIdManager#invalidateAll(String)} should be called.
739      */
740     public void removeSession(HttpSession session, boolean invalidate)
741     {
742         AbstractSession s = ((SessionIf)session).getSession();
743         removeSession(s,invalidate);
744     }
745
746     /* ------------------------------------------------------------ */
747     /** Remove session from manager
748      * @param session The session to remove
749      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
750      * {@link SessionIdManager#invalidateAll(String)} should be called.
751      */
752     public boolean removeSession(AbstractSession session, boolean invalidate)
753     {
754         // Remove session from context and global maps
755         boolean removed = removeSession(session.getClusterId());
756
757         if (removed)
758         {
759             _sessionsStats.decrement();
760             _sessionTimeStats.set(round((System.currentTimeMillis() - session.getCreationTime())/1000.0));
761
762             // Remove session from all context and global id maps
763             _sessionIdManager.removeSession(session);
764             if (invalidate)
765                 _sessionIdManager.invalidateAll(session.getClusterId());
766
767             if (invalidate && _sessionListeners!=null)
768             {
769                 HttpSessionEvent event=new HttpSessionEvent(session);      
770                 for (int i = _sessionListeners.size()-1; i>=0; i--)
771                 {
772                     _sessionListeners.get(i).sessionDestroyed(event);
773                 }
774             }
775         }
776         
777         return removed;
778     }
779
780     /* ------------------------------------------------------------ */
781     protected abstract boolean removeSession(String idInCluster);
782
783     /* ------------------------------------------------------------ */
784     /**
785      * @return maximum amount of time session remained valid
786      */
787     @ManagedAttribute("maximum amount of time sessions have remained active (in s)")
788     public long getSessionTimeMax()
789     {
790         return _sessionTimeStats.getMax();
791     }
792
793     /* ------------------------------------------------------------ */
794     @Override
795     public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
796     {
797         return __defaultSessionTrackingModes;
798     }
799
800     /* ------------------------------------------------------------ */
801     @Override
802     public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
803     {
804         return Collections.unmodifiableSet(_sessionTrackingModes);
805     }
806
807     /* ------------------------------------------------------------ */
808     @Override
809     public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
810     {
811         _sessionTrackingModes=new HashSet<SessionTrackingMode>(sessionTrackingModes);
812         _usingCookies=_sessionTrackingModes.contains(SessionTrackingMode.COOKIE);
813         _usingURLs=_sessionTrackingModes.contains(SessionTrackingMode.URL);
814     }
815
816     /* ------------------------------------------------------------ */
817     @Override
818     public boolean isUsingURLs()
819     {
820         return _usingURLs;
821     }
822
823     /* ------------------------------------------------------------ */
824     @Override
825     public SessionCookieConfig getSessionCookieConfig()
826     {
827         return _cookieConfig;
828     }
829
830     /* ------------------------------------------------------------ */
831     private SessionCookieConfig _cookieConfig =
832         new CookieConfig();
833
834
835     /* ------------------------------------------------------------ */
836     /**
837      * @return total amount of time all sessions remained valid
838      */
839     @ManagedAttribute("total time sessions have remained valid")
840     public long getSessionTimeTotal()
841     {
842         return _sessionTimeStats.getTotal();
843     }
844
845     /* ------------------------------------------------------------ */
846     /**
847      * @return mean amount of time session remained valid
848      */
849     @ManagedAttribute("mean time sessions remain valid (in s)")
850     public double getSessionTimeMean()
851     {
852         return _sessionTimeStats.getMean();
853     }
854
855     /* ------------------------------------------------------------ */
856     /**
857      * @return standard deviation of amount of time session remained valid
858      */
859     @ManagedAttribute("standard deviation a session remained valid (in s)")
860     public double getSessionTimeStdDev()
861     {
862         return _sessionTimeStats.getStdDev();
863     }
864
865     /* ------------------------------------------------------------ */
866     /**
867      * @see org.eclipse.jetty.server.SessionManager#isCheckingRemoteSessionIdEncoding()
868      */
869     @Override
870     @ManagedAttribute("check remote session id encoding")
871     public boolean isCheckingRemoteSessionIdEncoding()
872     {
873         return _checkingRemoteSessionIdEncoding;
874     }
875
876     /* ------------------------------------------------------------ */
877     /**
878      * @see org.eclipse.jetty.server.SessionManager#setCheckingRemoteSessionIdEncoding(boolean)
879      */
880     @Override
881     public void setCheckingRemoteSessionIdEncoding(boolean remote)
882     {
883         _checkingRemoteSessionIdEncoding=remote;
884     }
885     
886     
887     /* ------------------------------------------------------------ */
888     /**
889      * Tell the HttpSessionIdListeners the id changed.
890      * NOTE: this method must be called LAST in subclass overrides, after the session has been updated
891      * with the new id.
892      * @see org.eclipse.jetty.server.SessionManager#renewSessionId(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
893      */
894     @Override
895     public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
896     {
897         if (!_sessionIdListeners.isEmpty())
898         {
899             AbstractSession session = getSession(newClusterId);
900             HttpSessionEvent event = new HttpSessionEvent(session);
901             for (HttpSessionIdListener l:_sessionIdListeners)
902             {
903                 l.sessionIdChanged(event, oldClusterId);
904             }
905         }
906
907     }
908
909     /**
910      * CookieConfig
911      * 
912      * Implementation of the javax.servlet.SessionCookieConfig.
913      */
914     public final class CookieConfig implements SessionCookieConfig
915     {
916         @Override
917         public String getComment()
918         {
919             return _sessionComment;
920         }
921
922         @Override
923         public String getDomain()
924         {
925             return _sessionDomain;
926         }
927
928         @Override
929         public int getMaxAge()
930         {
931             return _maxCookieAge;
932         }
933
934         @Override
935         public String getName()
936         {
937             return _sessionCookie;
938         }
939
940         @Override
941         public String getPath()
942         {
943             return _sessionPath;
944         }
945
946         @Override
947         public boolean isHttpOnly()
948         {
949             return _httpOnly;
950         }
951
952         @Override
953         public boolean isSecure()
954         {
955             return _secureCookies;
956         }
957
958         @Override
959         public void setComment(String comment)
960         {  
961             if (_context != null && _context.getContextHandler().isAvailable())
962                 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
963             _sessionComment = comment;
964         }
965
966         @Override
967         public void setDomain(String domain)
968         {
969             if (_context != null && _context.getContextHandler().isAvailable())
970                 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
971             _sessionDomain=domain;
972         }
973
974         @Override
975         public void setHttpOnly(boolean httpOnly)
976         {   
977             if (_context != null && _context.getContextHandler().isAvailable())
978                 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
979             _httpOnly=httpOnly;
980         }
981
982         @Override
983         public void setMaxAge(int maxAge)
984         {               
985             if (_context != null && _context.getContextHandler().isAvailable())
986                 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
987             _maxCookieAge=maxAge;
988         }
989
990         @Override
991         public void setName(String name)
992         {  
993                 if (_context != null && _context.getContextHandler().isAvailable())
994                     throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
995             _sessionCookie=name;
996         }
997
998         @Override
999         public void setPath(String path)
1000         {
1001             if (_context != null && _context.getContextHandler().isAvailable())
1002                 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started"); 
1003             _sessionPath=path;
1004         }
1005
1006         @Override
1007         public void setSecure(boolean secure)
1008         {
1009             if (_context != null && _context.getContextHandler().isAvailable())
1010                 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
1011             _secureCookies=secure;
1012         }
1013     }
1014
1015     /* ------------------------------------------------------------ */
1016     /* ------------------------------------------------------------ */
1017     /* ------------------------------------------------------------ */
1018     /**
1019      * Interface that any session wrapper should implement so that
1020      * SessionManager may access the Jetty session implementation.
1021      *
1022      */
1023     public interface SessionIf extends HttpSession
1024     {
1025         public AbstractSession getSession();
1026     }
1027
1028     public void doSessionAttributeListeners(AbstractSession session, String name, Object old, Object value)
1029     {
1030         if (!_sessionAttributeListeners.isEmpty())
1031         {
1032             HttpSessionBindingEvent event=new HttpSessionBindingEvent(session,name,old==null?value:old);
1033
1034             for (HttpSessionAttributeListener l : _sessionAttributeListeners)
1035             {
1036                 if (old==null)
1037                     l.attributeAdded(event);
1038                 else if (value==null)
1039                     l.attributeRemoved(event);
1040                 else
1041                     l.attributeReplaced(event);
1042             }
1043         }
1044     }
1045
1046     @Override
1047     @Deprecated
1048     public SessionIdManager getMetaManager()
1049     {
1050         throw new UnsupportedOperationException();
1051     }
1052 }