View Javadoc

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