View Javadoc

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