]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/server/session/AbstractSessionManager.java
updating jetty to jetty-9.2.16.v2016040
[gigi.git] / lib / jetty / org / eclipse / jetty / server / session / AbstractSessionManager.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2016 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         final Server server=getSessionHandler().getServer();
231         synchronized (server)
232         {
233             if (_sessionIdManager==null)
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         if (listener instanceof HttpSessionIdListener)
579             _sessionIdListeners.remove(listener);
580         removeBean(listener);
581     }
582     
583     /* ------------------------------------------------------------ */
584     /**
585      * Reset statistics values
586      */
587     @ManagedOperation(value="reset statistics", impact="ACTION")
588     public void statsReset()
589     {
590         _sessionsStats.reset(getSessions());
591         _sessionTimeStats.reset();
592     }
593
594     /* ------------------------------------------------------------ */
595     /**
596      * @param httpOnly
597      *            The httpOnly to set.
598      */
599     public void setHttpOnly(boolean httpOnly)
600     {
601         _httpOnly=httpOnly;
602     }
603
604     /* ------------------------------------------------------------ */
605     /**
606      * @param metaManager The metaManager used for cross context session management.
607      */
608     @Override
609     public void setSessionIdManager(SessionIdManager metaManager)
610     {
611         updateBean(_sessionIdManager, metaManager);
612         _sessionIdManager=metaManager;
613     }
614
615     /* ------------------------------------------------------------ */
616     /**
617      * @param seconds
618      */
619     @Override
620     public void setMaxInactiveInterval(int seconds)
621     {
622         _dftMaxIdleSecs=seconds;
623     }
624
625     /* ------------------------------------------------------------ */
626     public void setRefreshCookieAge(int ageInSeconds)
627     {
628         _refreshCookieAge=ageInSeconds;
629     }
630
631     /* ------------------------------------------------------------ */
632     public void setSessionCookie(String cookieName)
633     {
634         _sessionCookie=cookieName;
635     }
636
637     /* ------------------------------------------------------------ */
638     /**
639      * @param sessionHandler
640      *            The sessionHandler to set.
641      */
642     @Override
643     public void setSessionHandler(SessionHandler sessionHandler)
644     {
645         _sessionHandler=sessionHandler;
646     }
647
648
649     /* ------------------------------------------------------------ */
650     @Override
651     public void setSessionIdPathParameterName(String param)
652     {
653         _sessionIdPathParameterName =(param==null||"none".equals(param))?null:param;
654         _sessionIdPathParameterNamePrefix =(param==null||"none".equals(param))?null:(";"+ _sessionIdPathParameterName +"=");
655     }
656     /* ------------------------------------------------------------ */
657     /**
658      * @param usingCookies
659      *            The usingCookies to set.
660      */
661     public void setUsingCookies(boolean usingCookies)
662     {
663         _usingCookies=usingCookies;
664     }
665
666
667     protected abstract void addSession(AbstractSession session);
668
669     /* ------------------------------------------------------------ */
670     /**
671      * Add the session Registers the session with this manager and registers the
672      * session ID with the sessionIDManager;
673      */
674     protected void addSession(AbstractSession session, boolean created)
675     {
676         synchronized (_sessionIdManager)
677         {
678             _sessionIdManager.addSession(session);
679             addSession(session);
680         }
681
682         if (created)
683         {
684             _sessionsStats.increment();
685             if (_sessionListeners!=null)
686             {
687                 HttpSessionEvent event=new HttpSessionEvent(session);
688                 for (HttpSessionListener listener : _sessionListeners)
689                     listener.sessionCreated(event);
690             }
691         }
692     }
693
694     /* ------------------------------------------------------------ */
695     /**
696      * Get a known existing session
697      * @param idInCluster The session ID in the cluster, stripped of any worker name.
698      * @return A Session or null if none exists.
699      */
700     public abstract AbstractSession getSession(String idInCluster);
701
702     /**
703      * Prepare sessions for session manager shutdown
704      * 
705      * @throws Exception
706      */
707     protected abstract void shutdownSessions() throws Exception;
708
709
710     /* ------------------------------------------------------------ */
711     /**
712      * Create a new session instance
713      * @param request
714      * @return the new session
715      */
716     protected abstract AbstractSession newSession(HttpServletRequest request);
717
718
719     /* ------------------------------------------------------------ */
720     /**
721      * @return true if the cluster node id (worker id) is returned as part of the session id by {@link HttpSession#getId()}. Default is false.
722      */
723     public boolean isNodeIdInSessionId()
724     {
725         return _nodeIdInSessionId;
726     }
727
728     /* ------------------------------------------------------------ */
729     /**
730      * @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.
731      */
732     public void setNodeIdInSessionId(boolean nodeIdInSessionId)
733     {
734         _nodeIdInSessionId=nodeIdInSessionId;
735     }
736
737     /* ------------------------------------------------------------ */
738     /** Remove session from manager
739      * @param session The session to remove
740      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
741      * {@link SessionIdManager#invalidateAll(String)} should be called.
742      */
743     public void removeSession(HttpSession session, boolean invalidate)
744     {
745         AbstractSession s = ((SessionIf)session).getSession();
746         removeSession(s,invalidate);
747     }
748
749     /* ------------------------------------------------------------ */
750     /** Remove session from manager
751      * @param session The session to remove
752      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
753      * {@link SessionIdManager#invalidateAll(String)} should be called.
754      */
755     public boolean removeSession(AbstractSession session, boolean invalidate)
756     {
757         // Remove session from context and global maps
758         boolean removed = removeSession(session.getClusterId());
759
760         if (removed)
761         {
762             _sessionsStats.decrement();
763             _sessionTimeStats.set(round((System.currentTimeMillis() - session.getCreationTime())/1000.0));
764
765             // Remove session from all context and global id maps
766             _sessionIdManager.removeSession(session);
767             if (invalidate)
768                 _sessionIdManager.invalidateAll(session.getClusterId());
769
770             if (invalidate && _sessionListeners!=null)
771             {
772                 HttpSessionEvent event=new HttpSessionEvent(session);      
773                 for (int i = _sessionListeners.size()-1; i>=0; i--)
774                 {
775                     _sessionListeners.get(i).sessionDestroyed(event);
776                 }
777             }
778         }
779         
780         return removed;
781     }
782
783     /* ------------------------------------------------------------ */
784     protected abstract boolean removeSession(String idInCluster);
785
786     /* ------------------------------------------------------------ */
787     /**
788      * @return maximum amount of time session remained valid
789      */
790     @ManagedAttribute("maximum amount of time sessions have remained active (in s)")
791     public long getSessionTimeMax()
792     {
793         return _sessionTimeStats.getMax();
794     }
795
796     /* ------------------------------------------------------------ */
797     @Override
798     public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
799     {
800         return __defaultSessionTrackingModes;
801     }
802
803     /* ------------------------------------------------------------ */
804     @Override
805     public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
806     {
807         return Collections.unmodifiableSet(_sessionTrackingModes);
808     }
809
810     /* ------------------------------------------------------------ */
811     @Override
812     public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
813     {
814         _sessionTrackingModes=new HashSet<SessionTrackingMode>(sessionTrackingModes);
815         _usingCookies=_sessionTrackingModes.contains(SessionTrackingMode.COOKIE);
816         _usingURLs=_sessionTrackingModes.contains(SessionTrackingMode.URL);
817     }
818
819     /* ------------------------------------------------------------ */
820     @Override
821     public boolean isUsingURLs()
822     {
823         return _usingURLs;
824     }
825
826     /* ------------------------------------------------------------ */
827     @Override
828     public SessionCookieConfig getSessionCookieConfig()
829     {
830         return _cookieConfig;
831     }
832
833     /* ------------------------------------------------------------ */
834     private SessionCookieConfig _cookieConfig =
835         new CookieConfig();
836
837
838     /* ------------------------------------------------------------ */
839     /**
840      * @return total amount of time all sessions remained valid
841      */
842     @ManagedAttribute("total time sessions have remained valid")
843     public long getSessionTimeTotal()
844     {
845         return _sessionTimeStats.getTotal();
846     }
847
848     /* ------------------------------------------------------------ */
849     /**
850      * @return mean amount of time session remained valid
851      */
852     @ManagedAttribute("mean time sessions remain valid (in s)")
853     public double getSessionTimeMean()
854     {
855         return _sessionTimeStats.getMean();
856     }
857
858     /* ------------------------------------------------------------ */
859     /**
860      * @return standard deviation of amount of time session remained valid
861      */
862     @ManagedAttribute("standard deviation a session remained valid (in s)")
863     public double getSessionTimeStdDev()
864     {
865         return _sessionTimeStats.getStdDev();
866     }
867
868     /* ------------------------------------------------------------ */
869     /**
870      * @see org.eclipse.jetty.server.SessionManager#isCheckingRemoteSessionIdEncoding()
871      */
872     @Override
873     @ManagedAttribute("check remote session id encoding")
874     public boolean isCheckingRemoteSessionIdEncoding()
875     {
876         return _checkingRemoteSessionIdEncoding;
877     }
878
879     /* ------------------------------------------------------------ */
880     /**
881      * @see org.eclipse.jetty.server.SessionManager#setCheckingRemoteSessionIdEncoding(boolean)
882      */
883     @Override
884     public void setCheckingRemoteSessionIdEncoding(boolean remote)
885     {
886         _checkingRemoteSessionIdEncoding=remote;
887     }
888     
889     
890     /* ------------------------------------------------------------ */
891     /**
892      * Tell the HttpSessionIdListeners the id changed.
893      * NOTE: this method must be called LAST in subclass overrides, after the session has been updated
894      * with the new id.
895      * @see org.eclipse.jetty.server.SessionManager#renewSessionId(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
896      */
897     @Override
898     public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
899     {
900         if (!_sessionIdListeners.isEmpty())
901         {
902             AbstractSession session = getSession(newClusterId);
903             HttpSessionEvent event = new HttpSessionEvent(session);
904             for (HttpSessionIdListener l:_sessionIdListeners)
905             {
906                 l.sessionIdChanged(event, oldClusterId);
907             }
908         }
909
910     }
911
912     /**
913      * CookieConfig
914      * 
915      * Implementation of the javax.servlet.SessionCookieConfig.
916      */
917     public final class CookieConfig implements SessionCookieConfig
918     {
919         @Override
920         public String getComment()
921         {
922             return _sessionComment;
923         }
924
925         @Override
926         public String getDomain()
927         {
928             return _sessionDomain;
929         }
930
931         @Override
932         public int getMaxAge()
933         {
934             return _maxCookieAge;
935         }
936
937         @Override
938         public String getName()
939         {
940             return _sessionCookie;
941         }
942
943         @Override
944         public String getPath()
945         {
946             return _sessionPath;
947         }
948
949         @Override
950         public boolean isHttpOnly()
951         {
952             return _httpOnly;
953         }
954
955         @Override
956         public boolean isSecure()
957         {
958             return _secureCookies;
959         }
960
961         @Override
962         public void setComment(String comment)
963         {  
964             if (_context != null && _context.getContextHandler().isAvailable())
965                 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
966             _sessionComment = comment;
967         }
968
969         @Override
970         public void setDomain(String domain)
971         {
972             if (_context != null && _context.getContextHandler().isAvailable())
973                 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
974             _sessionDomain=domain;
975         }
976
977         @Override
978         public void setHttpOnly(boolean httpOnly)
979         {   
980             if (_context != null && _context.getContextHandler().isAvailable())
981                 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
982             _httpOnly=httpOnly;
983         }
984
985         @Override
986         public void setMaxAge(int maxAge)
987         {               
988             if (_context != null && _context.getContextHandler().isAvailable())
989                 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
990             _maxCookieAge=maxAge;
991         }
992
993         @Override
994         public void setName(String name)
995         {  
996                 if (_context != null && _context.getContextHandler().isAvailable())
997                     throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
998             _sessionCookie=name;
999         }
1000
1001         @Override
1002         public void setPath(String path)
1003         {
1004             if (_context != null && _context.getContextHandler().isAvailable())
1005                 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started"); 
1006             _sessionPath=path;
1007         }
1008
1009         @Override
1010         public void setSecure(boolean secure)
1011         {
1012             if (_context != null && _context.getContextHandler().isAvailable())
1013                 throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
1014             _secureCookies=secure;
1015         }
1016     }
1017
1018     /* ------------------------------------------------------------ */
1019     /* ------------------------------------------------------------ */
1020     /* ------------------------------------------------------------ */
1021     /**
1022      * Interface that any session wrapper should implement so that
1023      * SessionManager may access the Jetty session implementation.
1024      *
1025      */
1026     public interface SessionIf extends HttpSession
1027     {
1028         public AbstractSession getSession();
1029     }
1030
1031     public void doSessionAttributeListeners(AbstractSession session, String name, Object old, Object value)
1032     {
1033         if (!_sessionAttributeListeners.isEmpty())
1034         {
1035             HttpSessionBindingEvent event=new HttpSessionBindingEvent(session,name,old==null?value:old);
1036
1037             for (HttpSessionAttributeListener l : _sessionAttributeListeners)
1038             {
1039                 if (old==null)
1040                     l.attributeAdded(event);
1041                 else if (value==null)
1042                     l.attributeRemoved(event);
1043                 else
1044                     l.attributeReplaced(event);
1045             }
1046         }
1047     }
1048
1049     @Override
1050     @Deprecated
1051     public SessionIdManager getMetaManager()
1052     {
1053         throw new UnsupportedOperationException();
1054     }
1055 }