>Howdy, >>Reading the servlet spec raised a couple of thoughts about http session
>The servlet spec v2.4, I hope? The servlet spec v2.4, final release. >>"SRV.7.6 Last Accessed Times >>The getLastAccessedTime method of the HttpSession interface allows a >>servlet >>to determine the last time the session was accessed before the current >>request. The session is considered to be accessed when a request that is >>part >>of the session is first handled by the servlet container." >See the JavaDoc for this method in the 2.4 servlet spec: it's not the >same as what you quoted above. It's much cleaner, and tomcat implements >it exactly and correctly. Yes, the spec is not in sync with the javadoc. Neither one has been changed since the version 2.2 of the spec. Which one should one trust more? It's true that the javadoc version is a lot more cleaner, although it doesn't even specify the same thing. Nor does Tomcat implement it correctly. Doesn't "the last time the client sent a request associated with this session", if called inside the service() method, mean the time current request came in? I guess that wouldn't be the expected functionality. Outside the service() method the javadoc describes the functionality I would expect. But Tomcat returns the time of the second latest request instead of the latest request. (See the associated server log) So, Tomcat is trying to and failing to implement both the spec and the javadoc. >>If the session is created by the current request, the >>session.getLastAccessedTime() returns the session creation time. Should it >>return 0 instead? I'd find it a bit less incorrect. >I consider creation as a special type of access, and therefore I think >tomcat's behavior is correct. Does the spec say otherwise? I also >don't care much for "a bit less incorrect" -- if the spec gives leeway >to the container implementation, we need to have a very good reason to >change current behavior (thereby affecting many users who may rely on >it). It isn't specified anywhere anymore. (In 2.1 it had to return -1 if the session was new.) The SRV.7.6 does specify what "being accessed" means with no special cases though. But this isn't the main issue anyway. >>"SRV.7.5 Session Timeouts >>The session invalidation will not take effect until all servlets using that >>session have exited the service method." >> >>Tomcat does nothing to ensure this. >> >>To reproduce, set session timeout to 3mins and put the following code to >>service method: >> >>HttpSession session = request.getSession(); >>Thread.sleep(200 * 1000L); // a long operation =) >>session.getLastAccessedTime(); >> >>->IllegalStateException is thrown >That one is interesting. Are you sure the session has been invalidated >by tomcat and there's no other code running in your webapp that may have >caused this? I only had a test servlet with the code above in the service method and a sessionlistener to print out the stacktrace when the session dies. The thought wasn't based on real life case but on the knowledge of the tomcat codebase. The reproduction code and the server log is shown below. I used tomcat 5.0.16, so don't care about the session destroyed twice bug which has been fixed since. The log shows quite clearly that the session gets invalidated by the background clean up thread while the service method is being executed and that the session.getLastAccessedTime() is incorrect (the time of the first request) when the session is destroyed. web.xml === <web-app> <listener><listener-class>SessionBugListener</listener-class></listener> <servlet> <servlet-name>SessionBug</servlet-name> <servlet-class>SessionBugServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>SessionBug</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> <session-config> <session-timeout>1</session-timeout> </session-config> </web-app> SessionBugServlet.java === import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public final class SessionBugServlet extends HttpServlet { public static final String KEY = "KEY"; public SessionBugServlet() { super(); } protected void doGet(HttpServletRequest arg0, HttpServletResponse arg1) throws ServletException, IOException { HttpSession session = arg0.getSession(); Object lastRequest = session.getAttribute(KEY); Long now = new Long(System.currentTimeMillis()); System.out.println("doGet starts"); System.out.println("Last access: " + session.getLastAccessedTime()); if (lastRequest == null) { System.out.println("New request, saving " + now); session.setAttribute(KEY, now); } else { System.out.println("Old request, restoring " + lastRequest); System.out.println(", saving " + now); session.setAttribute(KEY, now); System.out.println("Starting wait"); try { Thread.sleep(1000L * 120); } catch (InterruptedException e) { System.out.println(e.getMessage()); } finally { System.out.println("Wait stopped: " + System.currentTimeMillis()); } try { session.getLastAccessedTime(); } catch (Exception e) { System.out.println("getLastAccessedTime: " + e.getMessage()); } } } } SessionBugListener.java === import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public final class SessionBugListener implements HttpSessionListener { public SessionBugListener() { super(); } public void sessionCreated(HttpSessionEvent arg0) { } public void sessionDestroyed(HttpSessionEvent arg0) { System.out.println("Session destroyed " + System.currentTimeMillis()); System.out.println("lastAccessedTime " + arg0.getSession().getLastAccessedTime()); System.out.println("last saved access " + arg0.getSession().getAttribute(SessionBugServlet.KEY)); new Exception().printStackTrace(); } } serverlog === doGet starts Last access: 1073482859847 New request, saving 1073482859847 doGet starts Last access: 1073482859847 Old request, restoring 1073482859847 , saving 1073482863910 Starting wait Session destroyed 1073482966255 lastAccessedTime 1073482859847 last saved access 1073482863910 java.lang.Exception at SessionBugListener.sessionDestroyed(SessionBugListener.java:31) at org.apache.catalina.session.StandardSession.expire(StandardSession.java:686) at org.apache.catalina.session.StandardSession.isValid(StandardSession.java:589) at org.apache.catalina.session.StandardManager.processExpires(StandardManager.java:815) at org.apache.catalina.core.StandardContext.backgroundProcess(StandardContext.java:4563) at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1659) at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1668) at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1668) at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1648) at java.lang.Thread.run(Thread.java:479) Session destroyed 1073482966255 lastAccessedTime 1073482859847 last saved access null java.lang.Exception <<stack trace cut out>> Wait stopped: 1073482983911 getLastAccessedTime: getLastAccessedTime: Session already invalidated -- Jarno Peltoniemi --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]