>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]

Reply via email to