Java monitor active web sessions

I have implementated this functionality with a generic approach using standard APIs and specifications, no 3rd party frameworks or libraries. This solution has been used extensively with many enterprise grade systems deployed in glassfish application server and jboss. It has also been used successfully with weblogic (12c). However, the approach should work in any application server or servlet container supporting the standard JMX specification.

The tldr; version of this is to create two JMX bean interfaces and one http session listener. One of the JMX bean interfaces creates one instance per application monitored and is responsible of tracking all sessions from each application being monitored, it basically provides statistics of all sessions for each application. The other JMX bean interface creates one instance for every session created within each monitored application. The http session listener monitors the sessions of each application and does two things. Informs the first JMX bean corresponding to this application about the sessions created/destroyed in order for the statistics to be updated. Registers or unregisters JMX instances corresponding to a session, from the JMX service.

Once everything is setup it can be used from a JMX client like jconsole and visualvm that come with the jdk. From a jmx client it is possible to view all properties of JMX beans and also call any of their methods.

The following screenshots are from a test application using jconsole.

enter image description here

These are attributes from the JMX bean instance corresponding to each application monitored.

enter image description here

These are operations that can be executed on the specific session selected.

If more than one applications are monitored then more app contexts with their own structures will be present i.e. /TestApplication , /Application2 etc under each of the jmx bean interfaces.

HOW TO

Initially it is required to create the two JMX bean interfaces (simple tutorial) and then one HttpSessionListener (plenty of tutorials online).

1.The first JMX bean interface will have only one instance per application monitored and will store all information related to to sessions created from any of the apps monitored. It is basically used for persistence. I only keep the data in memory, which means that the data will be lost if the server goes down but usually it is only required to check the stats as long as the server is up. If you want to persist the data to a log or db in order to always have this information you can certainly do that within the implementation of the interface.

So this could be as follows,

public interface SessionsMXBean {

    /**
     * Get Indicates whether the data should be persisted in memory.
     */
    public boolean getPersistData();

    /**
     * Set Indicates whether the data should be persisted in memory.
     */
    public void setPersistData(boolean value);

    /**
     * Get All active sessions that have been persisted.
     */
    public String getActiveSessions();

    /**
     * Get All dates of each active session that has been persisted.
     */
    public String getDatesOfSessions();

    /**
     * Get The threshold for the number of session, after which persistence will
     * take place. If -1 all are persisted.
     */
    public int getSessionsThreshold();

    /**
     * Set The threshold for the number of session, after which persistence will
     * take place. If -1 all are persisted.
     */
    public void setSessionsThreshold(int value);

    /**
     * Set The limit of size to be persisted in KB. If -1 then no size limit.
     */
    public void setPersistenceSize(long value);

    /**
     * Clears all persisted data.
     */
    public void clearData();

    /**
     * Unregisters this instance
     */
    public void unregisterThis();
}

Then you have to create an implementation of this interface that will eventually hold this kind of data.

    public class SessionsImpl implements SessionsMXBean {
    /*
    here you need to implement the interface and have all kind of objects you require
    */
      public synchronized void incrementSessions() {
        ....
      }

      public synchronized void decrementSessions() {
        .....
      }

2.The second JMX bean interface will have one instance for every session created in each one of your monitored apps. This interface will store the session object and will also have methods that can be called from a jmx client, to invalidate those sessions. This can be as follows,

public interface HttpSessionMXBean {

    /**
     * Get HTTP Session id
     */
    public String getSessionId();

    /**
     * Get the date created
     */
    public String getDateCreated();

    /**
     * Get the date created in milliseconds
     */
    public long getMillisCreated();

    /**
     * Get attributes from http session
     *
     * @param attrName Attribute Name
     * @return java.lang.String
     */
    public String getAttribute(String attrName);

    /**
     * Invalidate this session
     */
    public void invalidate();

    /**
     * Unregisters this instance
     */
    public void unregisterThis();
}

And again an implementation is required,

public class HttpSessionMXBeanImpl implements HttpSessionMXBean {
....

3.Then you create the HttpSessionListener that will create/remove instances of the 2nd bean interface and register/unregister them from your server's JMX service. This will happen as the sessions are created and invalidated/expired. So you will have one listener per application that has it defined in its web.xml.

HttpSessionListener

        ....
        public class MyJMXHTTPSessionListener implements HttpSessionListener {
        ....
          private SessionsImpl sesssionsImpl;
          private Map<String, HttpSessionMXBeanImpl> httpSessionMXBeans

          @Override
          public void sessionCreated(HttpSessionEvent se) {
            //requires synchronized block here with this i.e.
            synchronized (this) {
            /*check if a jmx bean instance of the 1st interface exists otherwise create one*/
    if(sessionsImpl==null){
    sesssionsImpl= new SesssionsImpl();
/* take care here to create a nice and unique path per instance 
of the application in order to be nicely presented on the JMX tree of the JMX clients  */
                        String id = ("services.jmx.beans:type=Sessions,"+ "realm=" + se.getSession().getServletContext().getContextPath());
                        sessionManagerMXBean.setId(id);
                        ObjectName objectName = new ObjectName(id);
                        if (ManagementFactory.getPlatformMBeanServer().isRegistered(objectName)) {
                            ManagementFactory.getPlatformMBeanServer().
                                    unregisterMBean(objectName);
                        }
                        ManagementFactory.getPlatformMBeanServer().
                                registerMBean(sesssionsImpl,
                                objectName);
    }

    sesssionsImpl.inrementSessions();

    /*

               create a jmx bean instance of the 2nd interface

 and register it to the jmx service as already shown using the unique session id

 and a nice path indicating the 2nd interface jmx beans.
    */

          }

          @Override
          public void sessionDestroyed(HttpSessionEvent se) {
            //requires synchronized block here with this i.e.
            synchronized (this) {
            /*unregister the jmx bean instance of the 2nd interface, 

remove it from the list 

and call decrementSessions() on the jmx bean instance corresponding to this app*/
            }
          }
        }

This functionality can be easily activated anytime for any web app if you define the HttpSessionListener within web.xml file by adding the following few lines,

web.xml

<listener>
  <listener-class>
    myservices.myhttpsessionlisteners.MyJMXHTTPSessionListener
  </listener-class>
</listener>

Did you check out the psi-probe project?

It is an advanced manager and monitor for Apache Tomcat, forked from Lambda Probe.


@melc's answer offers a great generic solution. If it's only going to run on Tomcat you can also use a simpler version:

In one of your servlets implement the org.apache.catalina.ContainerServlet interface (You can find it in <Tomcat install path>\lib\catalina.jar).

import org.apache.catalina.Context;
import org.apache.catalina.Session;
import org.apache.catalina.Wrapper;

public void setWrapper(Wrapper w) {
    this.wrapper = w;
    context = (Context) wrapper.getParent();
    // This'll give you all sessions:
    org.apache.catalina.Session[] sessions = context.getManager().findSessions();
    for (int i = 0; i < sessions.length; i++) {
        System.out.println(sessions[i]);
    }
    host = (Host) context.getParent();
    // contexts are the list of applications deployed on Tomcat
    Context[] contexts = (Context[]) host.findChildren();
    for (Context context:contexts) {
        //Or to access session list by application, 
        //call findSessions() on the target context
        org.apache.catalina.Session[] sessions = context.getManager().findSessions();
    }
}

By the way you need to define your Context in context.xml as privileged:

<Context privileged="true">