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