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.Collections;
24  import java.util.Enumeration;
25  import java.util.EventListener;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.concurrent.CopyOnWriteArrayList;
30  
31  import javax.servlet.ServletRequest;
32  import javax.servlet.http.HttpServletRequest;
33  import javax.servlet.http.HttpSession;
34  import javax.servlet.http.HttpSessionAttributeListener;
35  import javax.servlet.http.HttpSessionBindingEvent;
36  import javax.servlet.http.HttpSessionContext;
37  import javax.servlet.http.HttpSessionEvent;
38  import javax.servlet.http.HttpSessionListener;
39  
40  import org.eclipse.jetty.http.HttpCookie;
41  import org.eclipse.jetty.server.AbstractConnector;
42  import org.eclipse.jetty.server.Request;
43  import org.eclipse.jetty.server.Server;
44  import org.eclipse.jetty.server.SessionIdManager;
45  import org.eclipse.jetty.server.SessionManager;
46  import org.eclipse.jetty.server.handler.ContextHandler;
47  import org.eclipse.jetty.util.component.AbstractLifeCycle;
48  import org.eclipse.jetty.util.log.Logger;
49  import org.eclipse.jetty.util.statistic.CounterStatistic;
50  import org.eclipse.jetty.util.statistic.SampleStatistic;
51  
52  /* ------------------------------------------------------------ */
53  /**
54   * An Abstract implementation of SessionManager. The partial implementation of
55   * SessionManager interface provides the majority of the handling required to
56   * implement a SessionManager. Concrete implementations of SessionManager based
57   * on AbstractSessionManager need only implement the newSession method to return
58   * a specialised version of the Session inner class that provides an attribute
59   * Map.
60   * <p>
61   */
62  @SuppressWarnings("deprecation")
63  public abstract class AbstractSessionManager extends AbstractLifeCycle implements SessionManager
64  {
65      final static Logger __log = SessionHandler.LOG;
66      public final static String SESSION_KNOWN_ONLY_TO_AUTHENTICATED="org.eclipse.jetty.security.sessionKnownOnlytoAuthenticated";
67      
68      /* ------------------------------------------------------------ */
69      public final static int __distantFuture=60*60*24*7*52*20;
70  
71      static final HttpSessionContext __nullSessionContext=new HttpSessionContext()
72      {
73          public HttpSession getSession(String sessionId)
74          {
75              return null;
76          }
77          
78          @SuppressWarnings({ "rawtypes", "unchecked" })
79          public Enumeration getIds()
80          {
81              return Collections.enumeration(Collections.EMPTY_LIST);
82          }
83      };
84      
85      private boolean _usingCookies=true;
86  
87      /* ------------------------------------------------------------ */
88      // Setting of max inactive interval for new sessions
89      // -1 means no timeout
90      protected int _dftMaxIdleSecs=-1;
91      protected SessionHandler _sessionHandler;
92      protected boolean _httpOnly=false;
93      protected SessionIdManager _sessionIdManager;
94      protected boolean _secureCookies=false;
95      protected final List<HttpSessionAttributeListener> _sessionAttributeListeners = new CopyOnWriteArrayList<HttpSessionAttributeListener>();
96      protected final List<HttpSessionListener> _sessionListeners= new CopyOnWriteArrayList<HttpSessionListener>();
97  
98      protected ClassLoader _loader;
99      protected ContextHandler.Context _context;
100     protected String _sessionCookie=__DefaultSessionCookie;
101     protected String _sessionIdPathParameterName = __DefaultSessionIdPathParameterName;
102     protected String _sessionIdPathParameterNamePrefix =";"+ _sessionIdPathParameterName +"=";
103     protected String _sessionDomain;
104     protected String _sessionPath;
105     protected int _maxCookieAge=-1;
106     protected int _refreshCookieAge;
107     protected boolean _nodeIdInSessionId;
108     protected boolean _checkingRemoteSessionIdEncoding;
109 
110     protected final CounterStatistic _sessionsStats = new CounterStatistic();
111     protected final SampleStatistic _sessionTimeStats = new SampleStatistic();
112     
113     
114     /* ------------------------------------------------------------ */
115     public static HttpSession renewSession (HttpServletRequest request, HttpSession httpSession, boolean authenticated)
116     {
117         Map<String,Object> attributes = new HashMap<String, Object>();
118 
119         for (Enumeration<String> e=httpSession.getAttributeNames();e.hasMoreElements();)
120         {
121             String name=e.nextElement();
122             attributes.put(name,httpSession.getAttribute(name));
123             httpSession.removeAttribute(name);
124         }
125 
126         httpSession.invalidate();       
127         httpSession = request.getSession(true);
128         if (authenticated)
129             httpSession.setAttribute(SESSION_KNOWN_ONLY_TO_AUTHENTICATED, Boolean.TRUE);
130         for (Map.Entry<String, Object> entry: attributes.entrySet())
131             httpSession.setAttribute(entry.getKey(),entry.getValue());
132         return httpSession;
133     }
134     
135     /* ------------------------------------------------------------ */
136     public AbstractSessionManager()
137     {
138     }
139 
140     /* ------------------------------------------------------------ */
141     public ContextHandler.Context getContext()
142     {
143         return _context;
144     }
145 
146     /* ------------------------------------------------------------ */
147     public ContextHandler getContextHandler()
148     {
149         return _context.getContextHandler();
150     }
151     
152     /* ------------------------------------------------------------ */
153     public HttpCookie access(HttpSession session,boolean secure)
154     {
155         long now=System.currentTimeMillis();
156 
157         AbstractSession s = ((SessionIf)session).getSession();
158 
159         if (s.access(now))
160         {
161             // Do we need to refresh the cookie?
162             if (isUsingCookies() &&
163                     (s.isIdChanged() ||
164                             (getMaxCookieAge()>0 && getRefreshCookieAge()>0 && ((now-s.getCookieSetTime())/1000>getRefreshCookieAge()))
165                     )
166             )
167             {
168                 HttpCookie cookie=getSessionCookie(session,_context==null?"/":(_context.getContextPath()),secure);
169                 s.cookieSet();
170                 s.setIdChanged(false);
171                 return cookie;
172             }
173         }
174         return null;
175     }
176 
177     /* ------------------------------------------------------------ */
178     public void addEventListener(EventListener listener)
179     {
180         if (listener instanceof HttpSessionAttributeListener)
181             _sessionAttributeListeners.add((HttpSessionAttributeListener)listener);
182         if (listener instanceof HttpSessionListener)
183             _sessionListeners.add((HttpSessionListener)listener);
184     }
185 
186     /* ------------------------------------------------------------ */
187     public void clearEventListeners()
188     {
189         _sessionAttributeListeners.clear();
190         _sessionListeners.clear();
191     }
192 
193     /* ------------------------------------------------------------ */
194     public void complete(HttpSession session)
195     {
196         AbstractSession s = ((SessionIf)session).getSession();
197         s.complete();
198     }
199 
200     /* ------------------------------------------------------------ */
201     @Override
202     public void doStart() throws Exception
203     {
204         _context=ContextHandler.getCurrentContext();
205         _loader=Thread.currentThread().getContextClassLoader();
206 
207         if (_sessionIdManager==null)
208         {
209             final Server server=getSessionHandler().getServer();
210             synchronized (server)
211             {
212                 _sessionIdManager=server.getSessionIdManager();
213                 if (_sessionIdManager==null)
214                 {
215                     _sessionIdManager=new HashSessionIdManager();
216                     server.setSessionIdManager(_sessionIdManager);
217                 }
218             }
219         }
220         if (!_sessionIdManager.isStarted())
221             _sessionIdManager.start();
222 
223         // Look for a session cookie name
224         if (_context!=null)
225         {
226             String tmp=_context.getInitParameter(SessionManager.__SessionCookieProperty);
227             if (tmp!=null)
228                 _sessionCookie=tmp;
229 
230             tmp=_context.getInitParameter(SessionManager.__SessionIdPathParameterNameProperty);
231             if (tmp!=null)
232                 setSessionIdPathParameterName(tmp);
233 
234             // set up the max session cookie age if it isn't already
235             if (_maxCookieAge==-1)
236             {
237                 tmp=_context.getInitParameter(SessionManager.__MaxAgeProperty);
238                 if (tmp!=null)
239                     _maxCookieAge=Integer.parseInt(tmp.trim());
240             }
241 
242             // set up the session domain if it isn't already
243             if (_sessionDomain==null)
244                 _sessionDomain=_context.getInitParameter(SessionManager.__SessionDomainProperty);
245 
246             // set up the sessionPath if it isn't already
247             if (_sessionPath==null)
248                 _sessionPath=_context.getInitParameter(SessionManager.__SessionPathProperty);
249             
250             tmp=_context.getInitParameter(SessionManager.__CheckRemoteSessionEncoding);
251             if (tmp!=null)
252                 _checkingRemoteSessionIdEncoding=Boolean.parseBoolean(tmp);
253         }
254 
255         super.doStart();
256     }
257 
258     /* ------------------------------------------------------------ */
259     @Override
260     public void doStop() throws Exception
261     {
262         super.doStop();
263 
264         invalidateSessions();
265 
266         _loader=null;
267     }
268 
269     /* ------------------------------------------------------------ */
270     /**
271      * @return Returns the httpOnly.
272      */
273     public boolean getHttpOnly()
274     {
275         return _httpOnly;
276     }
277 
278     /* ------------------------------------------------------------ */
279     public HttpSession getHttpSession(String nodeId)
280     {
281         String cluster_id = getSessionIdManager().getClusterId(nodeId);
282 
283         AbstractSession session = getSession(cluster_id);
284         if (session!=null && !session.getNodeId().equals(nodeId))
285             session.setIdChanged(true);
286         return session;
287     }
288 
289     /* ------------------------------------------------------------ */
290     /**
291      * @return Returns the metaManager used for cross context session management
292      * @deprecated Use {@link #getSessionIdManager()}
293      */
294     public SessionIdManager getIdManager()
295     {
296         return getSessionIdManager();
297     }
298     
299     /* ------------------------------------------------------------ */
300     /**
301      * @return Returns the SessionIdManager used for cross context session management
302      */
303     public SessionIdManager getSessionIdManager()
304     {
305         return _sessionIdManager;
306     }
307 
308     /* ------------------------------------------------------------ */
309     public int getMaxCookieAge()
310     {
311         return _maxCookieAge;
312     }
313 
314     /* ------------------------------------------------------------ */
315     /**
316      * @return seconds
317      */
318     public int getMaxInactiveInterval()
319     {
320         return _dftMaxIdleSecs;
321     }
322     
323     /* ------------------------------------------------------------ */
324     /**
325      * @see #getSessionsMax()
326      */
327     @Deprecated
328     public int getMaxSessions()
329     {
330         return getSessionsMax();
331     }
332 
333     /* ------------------------------------------------------------ */
334     /**
335      * @return maximum number of sessions
336      */
337     public int getSessionsMax()
338     {
339         return (int)_sessionsStats.getMax();
340     }
341 
342     /* ------------------------------------------------------------ */
343     /**
344      * @return total number of sessions
345      */
346     public int getSessionsTotal()
347     {
348         return (int)_sessionsStats.getTotal();
349     }
350 
351     /* ------------------------------------------------------------ */
352     /**
353      * @deprecated use {@link #getSessionIdManager()}
354      */
355     @Deprecated
356     public SessionIdManager getMetaManager()
357     {
358         return getSessionIdManager();
359     }
360 
361     /* ------------------------------------------------------------ */
362     /**
363      * @deprecated always returns 0. no replacement available.
364      */
365     @Deprecated
366     public int getMinSessions()
367     {
368         return 0;
369     }
370 
371     /* ------------------------------------------------------------ */
372     public int getRefreshCookieAge()
373     {
374         return _refreshCookieAge;
375     }
376 
377 
378     /* ------------------------------------------------------------ */
379     /**
380      * @return Returns the secureCookies.
381      */
382     public boolean getSecureCookies()
383     {
384         return _secureCookies;
385     }
386 
387     /* ------------------------------------------------------------ */
388     public String getSessionCookie()
389     {
390         return _sessionCookie;
391     }
392 
393     /* ------------------------------------------------------------ */
394     public HttpCookie getSessionCookie(HttpSession session, String contextPath, boolean requestIsSecure)
395     {
396         if (isUsingCookies())
397         {
398             String sessionPath = (_sessionPath==null) ? contextPath : _sessionPath;
399             sessionPath = (sessionPath==null||sessionPath.length()==0) ? "/" : sessionPath;
400             String id = getNodeId(session);
401             HttpCookie cookie=new HttpCookie(
402                     _sessionCookie,
403                     id,
404                     _sessionDomain,
405                     sessionPath,
406                     getMaxCookieAge(),
407                     getHttpOnly(),
408                     requestIsSecure&&getSecureCookies());      
409                     
410             return cookie;
411         }
412         return null;
413     }
414 
415     public String getSessionDomain()
416     {
417         return _sessionDomain;
418     }
419 
420     /* ------------------------------------------------------------ */
421     /**
422      * @return Returns the sessionHandler.
423      */
424     public SessionHandler getSessionHandler()
425     {
426         return _sessionHandler;
427     }
428 
429     /* ------------------------------------------------------------ */
430     /**
431      * @deprecated  Need to review if it is needed.
432      */
433     @SuppressWarnings("rawtypes")
434     public Map getSessionMap()
435     {
436         throw new UnsupportedOperationException();
437     }
438 
439     /* ------------------------------------------------------------ */
440     public String getSessionPath()
441     {
442         return _sessionPath;
443     }
444 
445     /* ------------------------------------------------------------ */
446     public int getSessions()
447     {
448         return (int)_sessionsStats.getCurrent();
449     }
450 
451     /* ------------------------------------------------------------ */
452     public String getSessionIdPathParameterName()
453     {
454         return _sessionIdPathParameterName;
455     }
456 
457     /* ------------------------------------------------------------ */
458     public String getSessionIdPathParameterNamePrefix()
459     {
460         return _sessionIdPathParameterNamePrefix;
461     }
462 
463     /* ------------------------------------------------------------ */
464     /**
465      * @return Returns the usingCookies.
466      */
467     public boolean isUsingCookies()
468     {
469         return _usingCookies;
470     }
471 
472     /* ------------------------------------------------------------ */
473     public boolean isValid(HttpSession session)
474     {
475         AbstractSession s = ((SessionIf)session).getSession();
476         return s.isValid();
477     }
478 
479     /* ------------------------------------------------------------ */
480     public String getClusterId(HttpSession session)
481     {
482         AbstractSession s = ((SessionIf)session).getSession();
483         return s.getClusterId();
484     }
485 
486     /* ------------------------------------------------------------ */
487     public String getNodeId(HttpSession session)
488     {
489         AbstractSession s = ((SessionIf)session).getSession();
490         return s.getNodeId();
491     }
492 
493     /* ------------------------------------------------------------ */
494     /**
495      * Create a new HttpSession for a request
496      */
497     public HttpSession newHttpSession(HttpServletRequest request)
498     {
499         AbstractSession session=newSession(request);
500         session.setMaxInactiveInterval(_dftMaxIdleSecs);
501         addSession(session,true);
502         return session;
503     }
504 
505     /* ------------------------------------------------------------ */
506     public void removeEventListener(EventListener listener)
507     {
508         if (listener instanceof HttpSessionAttributeListener)
509             _sessionAttributeListeners.remove(listener);
510         if (listener instanceof HttpSessionListener)
511             _sessionListeners.remove(listener);
512     }
513     
514     /* ------------------------------------------------------------ */
515     /**
516      * @see #statsReset()
517      */
518     @Deprecated
519     public void resetStats()
520     {
521         statsReset();
522     }
523 
524     /* ------------------------------------------------------------ */
525     /**
526      * Reset statistics values
527      */
528     public void statsReset()
529     {
530         _sessionsStats.reset(getSessions());
531         _sessionTimeStats.reset();
532     }
533 
534     /* ------------------------------------------------------------ */
535     /**
536      * @param httpOnly
537      *            The httpOnly to set.
538      */
539     public void setHttpOnly(boolean httpOnly)
540     {
541         _httpOnly=httpOnly;
542     }
543 
544     /* ------------------------------------------------------------ */
545     /**
546      * @param metaManager The metaManager used for cross context session management.
547      * @deprecated use {@link #setSessionIdManager(SessionIdManager)}
548      */
549     public void setIdManager(SessionIdManager metaManager)
550     {
551         setSessionIdManager(metaManager);
552     }
553     
554     /* ------------------------------------------------------------ */
555     /**
556      * @param metaManager The metaManager used for cross context session management.
557      */
558     public void setSessionIdManager(SessionIdManager metaManager)
559     {
560         _sessionIdManager=metaManager;
561     }
562 
563     /* ------------------------------------------------------------ */
564     public void setMaxCookieAge(int maxCookieAgeInSeconds)
565     {
566         _maxCookieAge=maxCookieAgeInSeconds;
567 
568         if (_maxCookieAge>0 && _refreshCookieAge==0)
569             _refreshCookieAge=_maxCookieAge/3;
570 
571     }
572 
573     /* ------------------------------------------------------------ */
574     /**
575      * @param seconds
576      */
577     public void setMaxInactiveInterval(int seconds)
578     {
579         _dftMaxIdleSecs=seconds;
580     }
581 
582     /* ------------------------------------------------------------ */
583     /**
584      * @deprecated use {@link #setSessionIdManager(SessionIdManager)}
585      */
586     @Deprecated
587     public void setMetaManager(SessionIdManager metaManager)
588     {
589         setSessionIdManager(metaManager);
590     }
591 
592     /* ------------------------------------------------------------ */
593     public void setRefreshCookieAge(int ageInSeconds)
594     {
595         _refreshCookieAge=ageInSeconds;
596     }
597 
598 
599     /* ------------------------------------------------------------ */
600     /**
601      * Set if the session manager should use SecureCookies.
602      * A secure cookie will only be sent by a browser on a secure (https) connection to 
603      * avoid the concern of cookies being intercepted on non secure channels.
604      * For the cookie to be issued as secure, the {@link ServletRequest#isSecure()} method must return true.
605      * If SSL offload is used, then the {@link AbstractConnector#customize(org.eclipse.jetty.io.EndPoint, Request)}
606      * method can be used to force the request to be https, or the {@link AbstractConnector#setForwarded(boolean)}
607      * can be set to true, so that the X-Forwarded-Proto header is respected.
608      * <p>
609      * If secure session cookies are used, then a session may not be shared between http and https requests.
610      * 
611      * @param secureCookies If true, use secure cookies.
612      */
613     public void setSecureCookies(boolean secureCookies)
614     {
615         _secureCookies=secureCookies;
616     }
617 
618     public void setSessionCookie(String cookieName)
619     {
620         _sessionCookie=cookieName;
621     }
622 
623     public void setSessionDomain(String domain)
624     {
625         _sessionDomain=domain;
626     }
627 
628     /* ------------------------------------------------------------ */
629     /**
630      * @param sessionHandler
631      *            The sessionHandler to set.
632      */
633     public void setSessionHandler(SessionHandler sessionHandler)
634     {
635         _sessionHandler=sessionHandler;
636     }
637 
638     /* ------------------------------------------------------------ */
639     /**
640      * @see org.eclipse.jetty.server.SessionManager#setSessionPath(java.lang.String)
641      */
642     public void setSessionPath(String path)
643     {
644         _sessionPath=path;
645     }
646 
647     /* ------------------------------------------------------------ */
648     public void setSessionIdPathParameterName(String param)
649     {
650         _sessionIdPathParameterName =(param==null||"none".equals(param))?null:param;
651         _sessionIdPathParameterNamePrefix =(param==null||"none".equals(param))?null:(";"+ _sessionIdPathParameterName +"=");
652     }
653     /* ------------------------------------------------------------ */
654     /**
655      * @param usingCookies
656      *            The usingCookies to set.
657      */
658     public void setUsingCookies(boolean usingCookies)
659     {
660         _usingCookies=usingCookies;
661     }
662 
663 
664     protected abstract void addSession(AbstractSession session);
665 
666     /* ------------------------------------------------------------ */
667     /**
668      * Add the session Registers the session with this manager and registers the
669      * session ID with the sessionIDManager;
670      */
671     protected void addSession(AbstractSession session, boolean created)
672     {
673         synchronized (_sessionIdManager)
674         {
675             _sessionIdManager.addSession(session);
676             addSession(session);
677         }
678 
679         if (created)
680         {
681             _sessionsStats.increment();
682             if (_sessionListeners!=null)
683             {
684                 HttpSessionEvent event=new HttpSessionEvent(session);
685                 for (HttpSessionListener listener : _sessionListeners)
686                     listener.sessionCreated(event);
687             }
688         }
689     }
690 
691     /* ------------------------------------------------------------ */
692     /**
693      * Get a known existing session
694      * @param idInCluster The session ID in the cluster, stripped of any worker name.
695      * @return A Session or null if none exists.
696      */
697     public abstract AbstractSession getSession(String idInCluster);
698 
699     protected abstract void invalidateSessions() throws Exception;
700 
701 
702     /* ------------------------------------------------------------ */
703     /**
704      * Create a new session instance
705      * @param request
706      * @return the new session
707      */
708     protected abstract AbstractSession newSession(HttpServletRequest request);
709 
710 
711     /* ------------------------------------------------------------ */
712     /**
713      * @return true if the cluster node id (worker id) is returned as part of the session id by {@link HttpSession#getId()}. Default is false.
714      */
715     public boolean isNodeIdInSessionId()
716     {
717         return _nodeIdInSessionId;
718     }
719 
720     /* ------------------------------------------------------------ */
721     /**
722      * @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.
723      */
724     public void setNodeIdInSessionId(boolean nodeIdInSessionId)
725     {
726         _nodeIdInSessionId=nodeIdInSessionId;
727     }
728 
729     /* ------------------------------------------------------------ */
730     /** Remove session from manager
731      * @param session The session to remove
732      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
733      * {@link SessionIdManager#invalidateAll(String)} should be called.
734      */
735     public void removeSession(HttpSession session, boolean invalidate)
736     {
737         AbstractSession s = ((SessionIf)session).getSession();
738         removeSession(s,invalidate);
739     }
740 
741     /* ------------------------------------------------------------ */
742     /** Remove session from manager
743      * @param session The session to remove
744      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
745      * {@link SessionIdManager#invalidateAll(String)} should be called.
746      */
747     public void removeSession(AbstractSession session, boolean invalidate)
748     {
749         // Remove session from context and global maps
750         boolean removed = removeSession(session.getClusterId());
751         
752         if (removed)
753         {
754             _sessionsStats.decrement();
755             _sessionTimeStats.set(round((System.currentTimeMillis() - session.getCreationTime())/1000.0));
756             
757             // Remove session from all context and global id maps
758             _sessionIdManager.removeSession(session);
759             if (invalidate)
760                 _sessionIdManager.invalidateAll(session.getClusterId());
761             
762             if (invalidate && _sessionListeners!=null)
763             {
764                 HttpSessionEvent event=new HttpSessionEvent(session);
765                 for (HttpSessionListener listener : _sessionListeners)
766                     listener.sessionDestroyed(event);
767             }
768         }
769     }
770 
771     /* ------------------------------------------------------------ */
772     protected abstract boolean removeSession(String idInCluster);
773     
774     /* ------------------------------------------------------------ */
775     /**
776      * @return maximum amount of time session remained valid
777      */
778     public long getSessionTimeMax()
779     {
780         return _sessionTimeStats.getMax();
781     }
782 
783     /* ------------------------------------------------------------ */
784     /**
785      * @return total amount of time all sessions remained valid
786      */
787     public long getSessionTimeTotal()
788     {
789         return _sessionTimeStats.getTotal();
790     }
791     
792     /* ------------------------------------------------------------ */
793     /**
794      * @return mean amount of time session remained valid
795      */
796     public double getSessionTimeMean()
797     {
798         return _sessionTimeStats.getMean();
799     }
800     
801     /* ------------------------------------------------------------ */
802     /**
803      * @return standard deviation of amount of time session remained valid
804      */
805     public double getSessionTimeStdDev()
806     {
807         return _sessionTimeStats.getStdDev();
808     }
809 
810     /* ------------------------------------------------------------ */
811     /**
812      * @see org.eclipse.jetty.server.SessionManager#isCheckingRemoteSessionIdEncoding()
813      */
814     public boolean isCheckingRemoteSessionIdEncoding()
815     {
816         return _checkingRemoteSessionIdEncoding;
817     }
818 
819     /* ------------------------------------------------------------ */
820     /**
821      * @see org.eclipse.jetty.server.SessionManager#setCheckingRemoteSessionIdEncoding(boolean)
822      */
823     public void setCheckingRemoteSessionIdEncoding(boolean remote)
824     {
825         _checkingRemoteSessionIdEncoding=remote;
826     }
827     
828     /* ------------------------------------------------------------ */
829     /* ------------------------------------------------------------ */
830     /* ------------------------------------------------------------ */
831     /**
832      * Interface that any session wrapper should implement so that
833      * SessionManager may access the Jetty session implementation.
834      *
835      */
836     public interface SessionIf extends HttpSession
837     {
838         public AbstractSession getSession();
839     }
840 
841     public void doSessionAttributeListeners(AbstractSession session, String name, Object old, Object value)
842     {
843         if (!_sessionAttributeListeners.isEmpty())
844         {
845             HttpSessionBindingEvent event=new HttpSessionBindingEvent(session,name,old==null?value:old);
846 
847             for (HttpSessionAttributeListener l : _sessionAttributeListeners)
848             {
849                 if (old==null)
850                     l.attributeAdded(event);
851                 else if (value==null)
852                     l.attributeRemoved(event);
853                 else
854                     l.attributeReplaced(event);
855             }
856         }
857     }
858 }