views:

81

answers:

4

I would like to expire all my sessions in Tomcat. We test our product under Fitnesse and some sessions remain and the end of the session causes dependency between the tests. I do it by hand with the following code, but some sessions remain (I can check it with the http://localhost:8080/manager/html/list url)

public static void expireAllSessions() {
    String[] applications = { "a", "b", "c", "d",  "e" };
    for (String application : applications) {
        try {
            expireAllSessions(application);
        } catch (Exception e) {
            logger.error(e);
        }
    }
}

private static void expireAllSessions(final String application) throws Exception {
    // cf doc http://hc.apache.org/httpclient-3.x/authentication.html
    HttpClient client = new HttpClient();
    client.getParams().setAuthenticationPreemptive(true);
    Credentials userPassword = new UsernamePasswordCredentials("tomcat", "tomcat");
    client.getState().setCredentials(AuthScope.ANY, userPassword);

    String url = "http://localhost:8080/manager/html/expire";
    NameValuePair[] parametres = new NameValuePair[] {
            new NameValuePair("path", "/" + application),
            new NameValuePair("idle", "0")
    };
    HttpMethod method = new GetMethod(url);
    method.setQueryString(parametres);
    client.executeMethod(method);
}

Is there a way to do it more efficiently and immediate with no remaining session ?

+2  A: 

You could use HttpSessionListener to save the references to sessions in a data structure and then use it at your leisure

sh4d0000
A: 

Can't you tell Fitnesse to flush the cookies for each separate request/session/test, so that the session data of one does not affect other tests?

matt b
A: 

Have you looked at Lamda Probe? For each of your web applications, you can view the contents of the session and expire them (all).

alt text

If you are keen on writing your own app to do this instead; Lamda Probe is open source, so you could take code from them to help accomplish your goal.

Sean
+2  A: 

I'm assuming your applications are really independent contexts. I have done something similar to what you are asking using an HttpSessionListener for every context. The tricky part here is that you need to have a Session Collection that is loaded by the root class loader as opposed to the context class loader. This is how I remember it:

Create a class which holds the active sessions for each context. This class must reside in the tomcat /lib directory so that it becomes accessible by every context. It can't be part of any of the contexts.

public class SessionMap {

  private static Map<ServletContext, Set<HttpSession>> map =
    new HashMap<ServletContext, Set<HttpSession>>();

  private SessionMap() {
  }

  public static Map<ServletContext, Set<HttpSession>> getInstance() {
    return map;
  }

  public static void invalidate(String[] contexts) {
    synchronized (map) {
      List<String> l = Arrays.asList(contexts);     
      for (Map.Entry<ServletContext, Set<HttpSession>> e : map.entrySet()) {
        // context name without the leading slash
        String c = e.getKey().getContextPath().substring(1);
        if (l.contains(c)) {
          for (HttpSession s : e.getValue()) 
            s.invalidate();
        }
      }
    }
  }

}

Create a listener for every context.

public class ApplicationContextListener implements HttpSessionListener {

  public void sessionCreated(HttpSessionEvent event) {
    ConcurrentMap<ServletContext, Set<HttpSession>> instance = SessionMap.getInstance();
    synchronized (instance) {
      ServletContext c = event.getSession().getServletContext();
      Set<HttpSession> set = instance.get(c);
      if (c == null) {
        set = new HashSet<HttpSession>();
        instance.put(c, set);
      }
      set.add(event.getSession());
    }
  }

  public void sessionDestroyed(HttpSessionEvent event) {
    ConcurrentMap<ServletContext, Set<HttpSession>> instance = SessionMap.getInstance();
    synchronized (map) {
      ServletContext c = event.getSession().getServletContext();
      Set<HttpSession> set = instance.get(c);
      if (c != null) {
        set.remove(event.getSession());
      }
    }
  }

}

Registered each listener in the corresponding context's web.xml.

<listener>
  <listener-class>ApplicationContextListener</listener-class>
</listener>

You can then call the following line to invalidate everything from any context.

SessionMap.invalidate();

I'm synchronizing on the map just to be on the safe side.

rmarimon