You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by Christopher Schultz <ch...@christopherschultz.net> on 2024/03/21 19:23:46 UTC

HttpSession tracking

All,

After having written a solution using JMX to do something like this, I'd 
like to make it cleaner and I'm not sure it's entirely possible using 
just Servlet APIs.

I'd like to be able to track every HttpSession for the application.

for admin purposes, I'd like to be able to analyze:

1. The total number of sessions

2. The number of sessions which represent a logged-in user vs a 
crawler-session or someone who visited the home-page and got a session 
but never logged-in

3. Checking-out some specific roles of those logged-in users e.g. 
end-user, staff, admin

4. Be able to kill a session at will. For example "chris is already 
logged-in, kill his old session and let the new login remain"

I started with the obvious HttpSessionListener + 
HttpSessionActivationListener, but I tried this experiment and it didn't 
turn out how I expected:

1. Start the application and hit the front page

-> I get a call to HttpSessionListener.sessionCreated (expected)

2. Login

3. Logout

-> I get a call to HttpSessionListener.sessionDestroyed (expected)
-> I get a call to HttpSessionListener.sessionCreated (expected)
    (this second one happens because our home-page creates a session)

4. Login again

5. Stop Tomcat

-> No calls to anything I can see

6. Start Tomcat

-> No calls to anything I can see

7. Access a protected page

-> Access is allowed; I'm still logged-in.

When Tomcat shuts-down, it's saving the sessions using local 
persistence[1]. When the application comes back up, all those sessions 
are restored from the disk.

When my HttpSeessionListener starts, it's empty and doesn't know about 
any sessions. Tomcat doesn't notify it that any sessions are coming from 
that storage.

I would have expected calls to 
HttpSessionActivationListener.sessionWillPassivate and 
HttpSessionActivationListener.sessionDidActivate.

Do I have unrealistic expectations? Is there a way to capture these 
events so my in-memory session-watcher/manager is able to have an 
accurate view of what Tomcat can see?

Thanks,
-chris

[1] 
https://tomcat.apache.org/tomcat-8.5-doc/config/manager.html#Persistence_Across_Restarts

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: HttpSession tracking

Posted by Christopher Schultz <ch...@christopherschultz.net>.
Robert.

On 3/22/24 14:11, Robert Turner wrote:
> Thanks for figuring it out -- I will keep that in mind when I go to split
> our "mega session object" up as that will impact some of the decisions for
> sure.
> 
> Yeah, I guess you end up with a "dummy object" on the session as a result
> -- I guess we got lucky with ours -- our handlers on the session object
> call out to a "singleton" for tracking, so we probably never really thought
> too much about it. So if you have an object on the session already, it pays
> to just extend it and call out to the "singleton" instead (like we have).

Or, more likely, whoever implemented it discovered that you *must* have 
a singleton for this kind of thing, and didn't just get lucky. :)

> Some of the listener docs do often leave me wondering how exactly some of
> them work (like who creates the object instance for this thing - like for
> ServletContextListener).

The container does. It's in the spec :)

If you haven't read it, it's 100% worth reading and it's quite readable. 
It was written by real humans trying to get a job done and not lawyers. 
or whatever.

> Although the one  you quoted does seem to be
> "clear enough" at least to me (not sure if your comments were sarcastic or
> not).

Nope. I meant "I either didn't read this, or ignored what it said" 
because there really is no confusion, there.

> I'm not sure it's a good design though -- you would think session
> listeners would be independent of the objects on the sessions. Hopefully
> there was some logic behind the original design that isn't captured in the
> code docs (well, I hope there was).

I can't know why the original design was the way that it was. Would be 
interesting to be able to have an "observer" listener that doesn't have 
to be put into each and every session? Possibly. But this isn't 
particularly "expensive" to implement using the existing interfaces and 
their existing semantics.

My guess is that sessionWillPassivate and sessionDidActivate are there 
so that caches, connections to other systems, etc. can be re-created 
after a session comes back into memory for things that aren't 
appropriate to be Serializable. Maybe that's not appropriate for *every* 
session and also if you put 10 caches into a session you just implement 
each of your 10 re-creation algorithms separately.

If you had one uber-listener, it would have to understand how to 
re-create all 10 of those caches and so the fine-grained notification 
system is actually more flexible.

-chris

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: HttpSession tracking

Posted by Robert Turner <rt...@e-djuster.ca.INVALID>.
Thanks for figuring it out -- I will keep that in mind when I go to split
our "mega session object" up as that will impact some of the decisions for
sure.

Yeah, I guess you end up with a "dummy object" on the session as a result
-- I guess we got lucky with ours -- our handlers on the session object
call out to a "singleton" for tracking, so we probably never really thought
too much about it. So if you have an object on the session already, it pays
to just extend it and call out to the "singleton" instead (like we have).

Some of the listener docs do often leave me wondering how exactly some of
them work (like who creates the object instance for this thing - like for
ServletContextListener).  Although the one  you quoted does seem to be
"clear enough" at least to me (not sure if your comments were sarcastic or
not). I'm not sure it's a good design though -- you would think session
listeners would be independent of the objects on the sessions. Hopefully
there was some logic behind the original design that isn't captured in the
code docs (well, I hope there was).

On Fri, Mar 22, 2024 at 2:02 PM Christopher Schultz <
chris@christopherschultz.net> wrote:

> All,
>
> On 3/22/24 09:59, Christopher Schultz wrote:
> > All,
> >
> > On 3/22/24 09:33, Robert Turner wrote:
> >> On Fri, Mar 22, 2024 at 9:28 AM Christopher Schultz <
> >> chris@christopherschultz.net> wrote:
> >>
> >>> Robert,
> >>>
> >>> On 3/21/24 15:31, Robert Turner wrote:
> >>>> We receive the sessionWillPassivate and sessionDidActivate callbacks
> >>>> on startup. Odd that you are not. That's how we achieve the same.
> >>> On 3/21/24 16:21, Robert Turner wrote:
> >>>> Just to add a bit more information, our handler class, for better or
> >>>> for
> >>>> worse, implements the following interfaces all in one class:
> >>>>
> >>>>    implements HttpSessionBindingListener,
> >>>> HttpSessionActivationListener,
> >>>> HttpSessionIdListener, HttpSessionListener, ServletContextListener
> >>>
> >>> Hmm.
> >>>
> >>> I'm already using HttpSessionListener and HttpSessionActivationListener
> >>> and logging every event I receive.
> >>>
> >>> HttpSessionIdListener only lets you know when ids are changed, and I
> >>> actually don't care about those events. I added it, and see no change
> in
> >>> behavior.
> >>>
> >>> HttpSessionBindingListener shouldn't do anything, here, as it will only
> >>> be called when objects are added or removed (and it only *that
> object*).
> >>> During activation and passivation, I wouldn't expect anything to be
> >>> added or removed.
> >>>
> >>> ServletContextListener wouldn't do anything in and of itself, except
> >>> possibly get the listener started earlier. I added it and do not see
> any
> >>> change in behavior.
> >>>
> >>>
> >> Yeah, I wasn't really suggesting adding all those listener interfaces --
> >> more just saying that's what we did in case somehow it made a difference
> >> for you. Certainly you shouldn't have to add them to get it to work.
> >>
> >>
> >>
> >>>> We also use that same class as our "session model" object that we
> >>>> bind as
> >>>> an attribute to the session itself (it's a bit of a mixed bag
> >>> historically
> >>>> that I want to clean up).
> >>>>
> >>>> And in terms of registration, we do not have any annotations on the
> >>> class,
> >>>> instead we register it in web.xml (in the application WAR file) using
> a
> >>>> standard listener entry:
> >>>>
> >>>> <listener>
> >>>>       <listener-class><<class name>></listener-class>
> >>>> </listener>
> >>>>
> >>>> Our web.xml is set at Servlet API version 3.0 (kind-of old), and we
> are
> >>>> running against Tomcat 9.5 (and this worked on 8.5 and before as
> well).
> >>>>
> >>>> Not sure if that adds anything Chris that you haven't already looked
> >>>> at.
> >>>
> >>> I believe mine is set up identically to yours at this point, except for
> >>> the HttpSessionBindingListener.
> >>>
> >>>> I would really prefer a way to query the sessions from the app, but
> >>>> as we
> >>>> know, that's not part of the current Servlet specification, or any
> >>>> extensions Tomcat currently provides.
> >>>
> >>> It wouldn't really be appropriate for Tomcat to provide any
> "extensions"
> >>> like this because it would make applications reliant on capabilities
> >>> that aren't standard. When companies do that, it's called "vendor
> >>> lock-in" and it's not a good look for ASF.
> >>>
> >>>
> >> Yeah, vendor lock-in isn't great -- and I wouldn't really suggest Tomcat
> >> doing that either; it would be better in the Servlet specification, but
> I
> >> doubt, for various reasons, it would get added.
> >>
> >> Your case is certainly odd -- I suppose you might have to resort to
> >> firing
> >> up a debugger and debug build and seeing what's going on in Tomcat...(at
> >> least you are more used to doing that on Tomcat than most of us).
> >
> > So... by went ahead and loaded-up this class with *everything* -
> > including putting the listener class instance into every session and I
> > do indeed get "session will passivate" and "session has activated" log
> > messgaes.
> >
> > So I started removing things and it kept working until ... it didn't.
> >
> > I'm trying to track-down exactly what difference makes it work, but it
> > doesn't many any sense to me.
> >
> > The StandardManager code looks like this:
> >
> >    if (log.isTraceEnabled()) {
> >        log.trace("Loading " + n + " persisted sessions");
> >    }
> >    for (int i = 0; i < n; i++) {
> >        StandardSession session = getNewSession();
> >        session.readObjectData(ois);
> >        session.setManager(this);
> >        sessions.put(session.getIdInternal(), session);
> >        session.activate();
> >        if (!session.isValidInternal()) {
> >            // If session is already invalid,
> >            // expire session to prevent memory leak.
> >            session.setValid(true);
> >            session.expire();
> >        }
> >        sessionCounter++;
> >    }
> >
> > and StandardSession.activate looks like this:
> >
> >      public void activate() {
> >
> >          // Initialize access count
> >          if (ACTIVITY_CHECK) {
> >              accessCount = new AtomicInteger();
> >          }
> >
> >          // Notify interested session event listeners
> >          fireSessionEvent(SESSION_ACTIVATED_EVENT, null);
> >
> >          // Notify ActivationListeners
> >          HttpSessionEvent event = null;
> >          String keys[] = keys();
> >          for (String key : keys) {
> >              Object attribute = attributes.get(key);
> >              if (attribute instanceof HttpSessionActivationListener) {
> >                  if (event == null) {
> >                      event = new HttpSessionEvent(getSession());
> >                  }
> >                  try {
> >                      ((HttpSessionActivationListener)
> > attribute).sessionDidActivate(event);
> >                  } catch (Throwable t) {
> >                      ExceptionUtils.handleThrowable(t);
> >
> >
> manager.getContext().getLogger().error(sm.getString("standardSession.attributeEvent"),
> t);
> >                  }
> >              }
> >          }
> >      }
> >
> > StandardSession.fireSessionEvent:
> >
> >      public void fireSessionEvent(String type, Object data) {
> >          if (listeners.size() < 1) {
> >              return;
> >          }
> >          SessionEvent event = new SessionEvent(this, type, data);
> >          SessionListener list[] = new SessionListener[0];
> >          synchronized (listeners) {
> >              list = listeners.toArray(list);
> >          }
> >
> >          for (SessionListener sessionListener : list) {
> >              sessionListener.sessionEvent(event);
> >          }
> >
> >      }
> >
> > In this case, SessionListener is a Tomcat interface and has nothing to
> > do with the Servlet Specification, so I can ignore that.
> >
> > So I think what is happening is that it's really the
> > HttpSessionBindingListener that's causing this to work.
> >
> > Time to read the specs and javadoc more closely to see if maybe it was
> > just my expectations that were wrong.
>
> Okay, this was apparently just my own ignorance about how these
> interfaces work. My expectation was that HttpSessionActivationListener
> was a listener that would be notified by the container any time a
> session was going to be passivated or activated. I was wrong. Here's the
> javadoc which is not at all confusing:
>
> "
> Objects that are bound to a session may listen to container events
> notifying them that sessions will be passivated and that session will be
> activated. A container that migrates session between VMs or persists
> sessions is required to notify all attributes bound to sessions
> implementing HttpSessionActivationListener.
> "
>
> So, I can't just create a class that implements
> HttpSessionActivationListener and configure it using <listener> in web.xml.
>
> I have to actually *put an object in the session* which implements that
> interface in order to get notifications. That means for my original
> goal, I need to:
>
> 1. Implement HttpSessionActivationListener
>
> 2. Implement HttpSessionListener to insert an object from (1) into each
> session, so my code can be notified
>
> 3. Implement Serializable, so the object-in-the-session doesn't prevent
> the session from being saved/loaded from secondary storage
>
> None of these interfaces are necessary:
>
> ServletContextListener
> HttpSessionIdListener (at least for my use case)
> HttpSessionBindingListener
>
> So, really, the only thing I was missing from my initial implementation
> was "don't forget to put a copy of the session-manager-thing into each
> session's attributes".
>
> I also need to ensure that only a single session "manager" object is
> created and used for all these operations. Without care, this could happen:
>
> 1. Application starts, and I have no sessions and a single instance of
> my session manager
> 2. Sessions are created; the one-instance of the session manager is
> stored in each session
> 3. The application stops, serializing all sessions to the disk
> 4. Upon de-serialization, new objects are created in-memory, though Java
> serialization ought to ensure that they are all the same instance linked
> through all sessions
> 5. ... but Tomcat has already created a new instance of this class to be
> its listener for the application.
>
> So to get this working, I've had to implement a semi-singleton where the
> constructor is allowed, but everything really consults a central
> singleton storage mechanism regardless of which object instance is used
> for what purpose.
>
> -chris
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: users-help@tomcat.apache.org
>
>

Re: HttpSession tracking

Posted by Christopher Schultz <ch...@christopherschultz.net>.
All,

On 3/22/24 09:59, Christopher Schultz wrote:
> All,
> 
> On 3/22/24 09:33, Robert Turner wrote:
>> On Fri, Mar 22, 2024 at 9:28 AM Christopher Schultz <
>> chris@christopherschultz.net> wrote:
>>
>>> Robert,
>>>
>>> On 3/21/24 15:31, Robert Turner wrote:
>>>> We receive the sessionWillPassivate and sessionDidActivate callbacks
>>>> on startup. Odd that you are not. That's how we achieve the same.
>>> On 3/21/24 16:21, Robert Turner wrote:
>>>> Just to add a bit more information, our handler class, for better or 
>>>> for
>>>> worse, implements the following interfaces all in one class:
>>>>
>>>>    implements HttpSessionBindingListener, 
>>>> HttpSessionActivationListener,
>>>> HttpSessionIdListener, HttpSessionListener, ServletContextListener
>>>
>>> Hmm.
>>>
>>> I'm already using HttpSessionListener and HttpSessionActivationListener
>>> and logging every event I receive.
>>>
>>> HttpSessionIdListener only lets you know when ids are changed, and I
>>> actually don't care about those events. I added it, and see no change in
>>> behavior.
>>>
>>> HttpSessionBindingListener shouldn't do anything, here, as it will only
>>> be called when objects are added or removed (and it only *that object*).
>>> During activation and passivation, I wouldn't expect anything to be
>>> added or removed.
>>>
>>> ServletContextListener wouldn't do anything in and of itself, except
>>> possibly get the listener started earlier. I added it and do not see any
>>> change in behavior.
>>>
>>>
>> Yeah, I wasn't really suggesting adding all those listener interfaces --
>> more just saying that's what we did in case somehow it made a difference
>> for you. Certainly you shouldn't have to add them to get it to work.
>>
>>
>>
>>>> We also use that same class as our "session model" object that we 
>>>> bind as
>>>> an attribute to the session itself (it's a bit of a mixed bag
>>> historically
>>>> that I want to clean up).
>>>>
>>>> And in terms of registration, we do not have any annotations on the
>>> class,
>>>> instead we register it in web.xml (in the application WAR file) using a
>>>> standard listener entry:
>>>>
>>>> <listener>
>>>>       <listener-class><<class name>></listener-class>
>>>> </listener>
>>>>
>>>> Our web.xml is set at Servlet API version 3.0 (kind-of old), and we are
>>>> running against Tomcat 9.5 (and this worked on 8.5 and before as well).
>>>>
>>>> Not sure if that adds anything Chris that you haven't already looked 
>>>> at.
>>>
>>> I believe mine is set up identically to yours at this point, except for
>>> the HttpSessionBindingListener.
>>>
>>>> I would really prefer a way to query the sessions from the app, but 
>>>> as we
>>>> know, that's not part of the current Servlet specification, or any
>>>> extensions Tomcat currently provides.
>>>
>>> It wouldn't really be appropriate for Tomcat to provide any "extensions"
>>> like this because it would make applications reliant on capabilities
>>> that aren't standard. When companies do that, it's called "vendor
>>> lock-in" and it's not a good look for ASF.
>>>
>>>
>> Yeah, vendor lock-in isn't great -- and I wouldn't really suggest Tomcat
>> doing that either; it would be better in the Servlet specification, but I
>> doubt, for various reasons, it would get added.
>>
>> Your case is certainly odd -- I suppose you might have to resort to 
>> firing
>> up a debugger and debug build and seeing what's going on in Tomcat...(at
>> least you are more used to doing that on Tomcat than most of us).
> 
> So... by went ahead and loaded-up this class with *everything* - 
> including putting the listener class instance into every session and I 
> do indeed get "session will passivate" and "session has activated" log 
> messgaes.
> 
> So I started removing things and it kept working until ... it didn't.
> 
> I'm trying to track-down exactly what difference makes it work, but it 
> doesn't many any sense to me.
> 
> The StandardManager code looks like this:
> 
>    if (log.isTraceEnabled()) {
>        log.trace("Loading " + n + " persisted sessions");
>    }
>    for (int i = 0; i < n; i++) {
>        StandardSession session = getNewSession();
>        session.readObjectData(ois);
>        session.setManager(this);
>        sessions.put(session.getIdInternal(), session);
>        session.activate();
>        if (!session.isValidInternal()) {
>            // If session is already invalid,
>            // expire session to prevent memory leak.
>            session.setValid(true);
>            session.expire();
>        }
>        sessionCounter++;
>    }
> 
> and StandardSession.activate looks like this:
> 
>      public void activate() {
> 
>          // Initialize access count
>          if (ACTIVITY_CHECK) {
>              accessCount = new AtomicInteger();
>          }
> 
>          // Notify interested session event listeners
>          fireSessionEvent(SESSION_ACTIVATED_EVENT, null);
> 
>          // Notify ActivationListeners
>          HttpSessionEvent event = null;
>          String keys[] = keys();
>          for (String key : keys) {
>              Object attribute = attributes.get(key);
>              if (attribute instanceof HttpSessionActivationListener) {
>                  if (event == null) {
>                      event = new HttpSessionEvent(getSession());
>                  }
>                  try {
>                      ((HttpSessionActivationListener) 
> attribute).sessionDidActivate(event);
>                  } catch (Throwable t) {
>                      ExceptionUtils.handleThrowable(t);
> 
> manager.getContext().getLogger().error(sm.getString("standardSession.attributeEvent"), t);
>                  }
>              }
>          }
>      }
> 
> StandardSession.fireSessionEvent:
> 
>      public void fireSessionEvent(String type, Object data) {
>          if (listeners.size() < 1) {
>              return;
>          }
>          SessionEvent event = new SessionEvent(this, type, data);
>          SessionListener list[] = new SessionListener[0];
>          synchronized (listeners) {
>              list = listeners.toArray(list);
>          }
> 
>          for (SessionListener sessionListener : list) {
>              sessionListener.sessionEvent(event);
>          }
> 
>      }
> 
> In this case, SessionListener is a Tomcat interface and has nothing to 
> do with the Servlet Specification, so I can ignore that.
> 
> So I think what is happening is that it's really the 
> HttpSessionBindingListener that's causing this to work.
> 
> Time to read the specs and javadoc more closely to see if maybe it was 
> just my expectations that were wrong.

Okay, this was apparently just my own ignorance about how these 
interfaces work. My expectation was that HttpSessionActivationListener 
was a listener that would be notified by the container any time a 
session was going to be passivated or activated. I was wrong. Here's the 
javadoc which is not at all confusing:

"
Objects that are bound to a session may listen to container events 
notifying them that sessions will be passivated and that session will be 
activated. A container that migrates session between VMs or persists 
sessions is required to notify all attributes bound to sessions 
implementing HttpSessionActivationListener.
"

So, I can't just create a class that implements 
HttpSessionActivationListener and configure it using <listener> in web.xml.

I have to actually *put an object in the session* which implements that 
interface in order to get notifications. That means for my original 
goal, I need to:

1. Implement HttpSessionActivationListener

2. Implement HttpSessionListener to insert an object from (1) into each 
session, so my code can be notified

3. Implement Serializable, so the object-in-the-session doesn't prevent 
the session from being saved/loaded from secondary storage

None of these interfaces are necessary:

ServletContextListener
HttpSessionIdListener (at least for my use case)
HttpSessionBindingListener

So, really, the only thing I was missing from my initial implementation 
was "don't forget to put a copy of the session-manager-thing into each 
session's attributes".

I also need to ensure that only a single session "manager" object is 
created and used for all these operations. Without care, this could happen:

1. Application starts, and I have no sessions and a single instance of 
my session manager
2. Sessions are created; the one-instance of the session manager is 
stored in each session
3. The application stops, serializing all sessions to the disk
4. Upon de-serialization, new objects are created in-memory, though Java 
serialization ought to ensure that they are all the same instance linked 
through all sessions
5. ... but Tomcat has already created a new instance of this class to be 
its listener for the application.

So to get this working, I've had to implement a semi-singleton where the 
constructor is allowed, but everything really consults a central 
singleton storage mechanism regardless of which object instance is used 
for what purpose.

-chris

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: HttpSession tracking

Posted by Christopher Schultz <ch...@christopherschultz.net>.
All,

On 3/22/24 09:33, Robert Turner wrote:
> On Fri, Mar 22, 2024 at 9:28 AM Christopher Schultz <
> chris@christopherschultz.net> wrote:
> 
>> Robert,
>>
>> On 3/21/24 15:31, Robert Turner wrote:
>>> We receive the sessionWillPassivate and sessionDidActivate callbacks
>>> on startup. Odd that you are not. That's how we achieve the same.
>> On 3/21/24 16:21, Robert Turner wrote:
>>> Just to add a bit more information, our handler class, for better or for
>>> worse, implements the following interfaces all in one class:
>>>
>>>    implements HttpSessionBindingListener, HttpSessionActivationListener,
>>> HttpSessionIdListener, HttpSessionListener, ServletContextListener
>>
>> Hmm.
>>
>> I'm already using HttpSessionListener and HttpSessionActivationListener
>> and logging every event I receive.
>>
>> HttpSessionIdListener only lets you know when ids are changed, and I
>> actually don't care about those events. I added it, and see no change in
>> behavior.
>>
>> HttpSessionBindingListener shouldn't do anything, here, as it will only
>> be called when objects are added or removed (and it only *that object*).
>> During activation and passivation, I wouldn't expect anything to be
>> added or removed.
>>
>> ServletContextListener wouldn't do anything in and of itself, except
>> possibly get the listener started earlier. I added it and do not see any
>> change in behavior.
>>
>>
> Yeah, I wasn't really suggesting adding all those listener interfaces --
> more just saying that's what we did in case somehow it made a difference
> for you. Certainly you shouldn't have to add them to get it to work.
> 
> 
> 
>>> We also use that same class as our "session model" object that we bind as
>>> an attribute to the session itself (it's a bit of a mixed bag
>> historically
>>> that I want to clean up).
>>>
>>> And in terms of registration, we do not have any annotations on the
>> class,
>>> instead we register it in web.xml (in the application WAR file) using a
>>> standard listener entry:
>>>
>>> <listener>
>>>       <listener-class><<class name>></listener-class>
>>> </listener>
>>>
>>> Our web.xml is set at Servlet API version 3.0 (kind-of old), and we are
>>> running against Tomcat 9.5 (and this worked on 8.5 and before as well).
>>>
>>> Not sure if that adds anything Chris that you haven't already looked at.
>>
>> I believe mine is set up identically to yours at this point, except for
>> the HttpSessionBindingListener.
>>
>>> I would really prefer a way to query the sessions from the app, but as we
>>> know, that's not part of the current Servlet specification, or any
>>> extensions Tomcat currently provides.
>>
>> It wouldn't really be appropriate for Tomcat to provide any "extensions"
>> like this because it would make applications reliant on capabilities
>> that aren't standard. When companies do that, it's called "vendor
>> lock-in" and it's not a good look for ASF.
>>
>>
> Yeah, vendor lock-in isn't great -- and I wouldn't really suggest Tomcat
> doing that either; it would be better in the Servlet specification, but I
> doubt, for various reasons, it would get added.
> 
> Your case is certainly odd -- I suppose you might have to resort to firing
> up a debugger and debug build and seeing what's going on in Tomcat...(at
> least you are more used to doing that on Tomcat than most of us).

So... by went ahead and loaded-up this class with *everything* - 
including putting the listener class instance into every session and I 
do indeed get "session will passivate" and "session has activated" log 
messgaes.

So I started removing things and it kept working until ... it didn't.

I'm trying to track-down exactly what difference makes it work, but it 
doesn't many any sense to me.

The StandardManager code looks like this:

   if (log.isTraceEnabled()) {
       log.trace("Loading " + n + " persisted sessions");
   }
   for (int i = 0; i < n; i++) {
       StandardSession session = getNewSession();
       session.readObjectData(ois);
       session.setManager(this);
       sessions.put(session.getIdInternal(), session);
       session.activate();
       if (!session.isValidInternal()) {
           // If session is already invalid,
           // expire session to prevent memory leak.
           session.setValid(true);
           session.expire();
       }
       sessionCounter++;
   }

and StandardSession.activate looks like this:

     public void activate() {

         // Initialize access count
         if (ACTIVITY_CHECK) {
             accessCount = new AtomicInteger();
         }

         // Notify interested session event listeners
         fireSessionEvent(SESSION_ACTIVATED_EVENT, null);

         // Notify ActivationListeners
         HttpSessionEvent event = null;
         String keys[] = keys();
         for (String key : keys) {
             Object attribute = attributes.get(key);
             if (attribute instanceof HttpSessionActivationListener) {
                 if (event == null) {
                     event = new HttpSessionEvent(getSession());
                 }
                 try {
                     ((HttpSessionActivationListener) 
attribute).sessionDidActivate(event);
                 } catch (Throwable t) {
                     ExceptionUtils.handleThrowable(t);
 
manager.getContext().getLogger().error(sm.getString("standardSession.attributeEvent"), 
t);
                 }
             }
         }
     }

StandardSession.fireSessionEvent:

     public void fireSessionEvent(String type, Object data) {
         if (listeners.size() < 1) {
             return;
         }
         SessionEvent event = new SessionEvent(this, type, data);
         SessionListener list[] = new SessionListener[0];
         synchronized (listeners) {
             list = listeners.toArray(list);
         }

         for (SessionListener sessionListener : list) {
             sessionListener.sessionEvent(event);
         }

     }

In this case, SessionListener is a Tomcat interface and has nothing to 
do with the Servlet Specification, so I can ignore that.

So I think what is happening is that it's really the 
HttpSessionBindingListener that's causing this to work.

Time to read the specs and javadoc more closely to see if maybe it was 
just my expectations that were wrong.

-chris

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: HttpSession tracking

Posted by Robert Turner <rt...@e-djuster.ca.INVALID>.
On Fri, Mar 22, 2024 at 9:28 AM Christopher Schultz <
chris@christopherschultz.net> wrote:

> Robert,
>
> On 3/21/24 15:31, Robert Turner wrote:
> > We receive the sessionWillPassivate and sessionDidActivate callbacks
> > on startup. Odd that you are not. That's how we achieve the same.
> On 3/21/24 16:21, Robert Turner wrote:
> > Just to add a bit more information, our handler class, for better or for
> > worse, implements the following interfaces all in one class:
> >
> >   implements HttpSessionBindingListener, HttpSessionActivationListener,
> > HttpSessionIdListener, HttpSessionListener, ServletContextListener
>
> Hmm.
>
> I'm already using HttpSessionListener and HttpSessionActivationListener
> and logging every event I receive.
>
> HttpSessionIdListener only lets you know when ids are changed, and I
> actually don't care about those events. I added it, and see no change in
> behavior.
>
> HttpSessionBindingListener shouldn't do anything, here, as it will only
> be called when objects are added or removed (and it only *that object*).
> During activation and passivation, I wouldn't expect anything to be
> added or removed.
>
> ServletContextListener wouldn't do anything in and of itself, except
> possibly get the listener started earlier. I added it and do not see any
> change in behavior.
>
>
Yeah, I wasn't really suggesting adding all those listener interfaces --
more just saying that's what we did in case somehow it made a difference
for you. Certainly you shouldn't have to add them to get it to work.



> > We also use that same class as our "session model" object that we bind as
> > an attribute to the session itself (it's a bit of a mixed bag
> historically
> > that I want to clean up).
> >
> > And in terms of registration, we do not have any annotations on the
> class,
> > instead we register it in web.xml (in the application WAR file) using a
> > standard listener entry:
> >
> > <listener>
> >      <listener-class><<class name>></listener-class>
> > </listener>
> >
> > Our web.xml is set at Servlet API version 3.0 (kind-of old), and we are
> > running against Tomcat 9.5 (and this worked on 8.5 and before as well).
> >
> > Not sure if that adds anything Chris that you haven't already looked at.
>
> I believe mine is set up identically to yours at this point, except for
> the HttpSessionBindingListener.
>
> > I would really prefer a way to query the sessions from the app, but as we
> > know, that's not part of the current Servlet specification, or any
> > extensions Tomcat currently provides.
>
> It wouldn't really be appropriate for Tomcat to provide any "extensions"
> like this because it would make applications reliant on capabilities
> that aren't standard. When companies do that, it's called "vendor
> lock-in" and it's not a good look for ASF.
>
>
Yeah, vendor lock-in isn't great -- and I wouldn't really suggest Tomcat
doing that either; it would be better in the Servlet specification, but I
doubt, for various reasons, it would get added.

Your case is certainly odd -- I suppose you might have to resort to firing
up a debugger and debug build and seeing what's going on in Tomcat...(at
least you are more used to doing that on Tomcat than most of us).

>
>

Re: HttpSession tracking

Posted by Christopher Schultz <ch...@christopherschultz.net>.
Robert,

On 3/21/24 15:31, Robert Turner wrote:
> We receive the sessionWillPassivate and sessionDidActivate callbacks
> on startup. Odd that you are not. That's how we achieve the same.
On 3/21/24 16:21, Robert Turner wrote:
> Just to add a bit more information, our handler class, for better or for
> worse, implements the following interfaces all in one class:
> 
>   implements HttpSessionBindingListener, HttpSessionActivationListener,
> HttpSessionIdListener, HttpSessionListener, ServletContextListener

Hmm.

I'm already using HttpSessionListener and HttpSessionActivationListener 
and logging every event I receive.

HttpSessionIdListener only lets you know when ids are changed, and I 
actually don't care about those events. I added it, and see no change in 
behavior.

HttpSessionBindingListener shouldn't do anything, here, as it will only 
be called when objects are added or removed (and it only *that object*). 
During activation and passivation, I wouldn't expect anything to be 
added or removed.

ServletContextListener wouldn't do anything in and of itself, except 
possibly get the listener started earlier. I added it and do not see any 
change in behavior.

> We also use that same class as our "session model" object that we bind as
> an attribute to the session itself (it's a bit of a mixed bag historically
> that I want to clean up).
> 
> And in terms of registration, we do not have any annotations on the class,
> instead we register it in web.xml (in the application WAR file) using a
> standard listener entry:
> 
> <listener>
>      <listener-class><<class name>></listener-class>
> </listener>
> 
> Our web.xml is set at Servlet API version 3.0 (kind-of old), and we are
> running against Tomcat 9.5 (and this worked on 8.5 and before as well).
> 
> Not sure if that adds anything Chris that you haven't already looked at.

I believe mine is set up identically to yours at this point, except for 
the HttpSessionBindingListener.

> I would really prefer a way to query the sessions from the app, but as we
> know, that's not part of the current Servlet specification, or any
> extensions Tomcat currently provides.

It wouldn't really be appropriate for Tomcat to provide any "extensions" 
like this because it would make applications reliant on capabilities 
that aren't standard. When companies do that, it's called "vendor 
lock-in" and it's not a good look for ASF.

-chris

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: HttpSession tracking

Posted by Robert Turner <rt...@e-djuster.ca.INVALID>.
Just to add a bit more information, our handler class, for better or for
worse, implements the following interfaces all in one class:

 implements HttpSessionBindingListener, HttpSessionActivationListener,
HttpSessionIdListener, HttpSessionListener, ServletContextListener

We also use that same class as our "session model" object that we bind as
an attribute to the session itself (it's a bit of a mixed bag historically
that I want to clean up).

And in terms of registration, we do not have any annotations on the class,
instead we register it in web.xml (in the application WAR file) using a
standard listener entry:

<listener>
    <listener-class><<class name>></listener-class>
</listener>

Our web.xml is set at Servlet API version 3.0 (kind-of old), and we are
running against Tomcat 9.5 (and this worked on 8.5 and before as well).

Not sure if that adds anything Chris that you haven't already looked at.

I would really prefer a way to query the sessions from the app, but as we
know, that's not part of the current Servlet specification, or any
extensions Tomcat currently provides.

Robert



On Thu, Mar 21, 2024 at 3:31 PM Robert Turner <rt...@e-djuster.ca> wrote:

> We receive the sessionWillPassivate and sessionDidActivate callbacks on
> startup. Odd that you are not. That's how we achieve the same.
>
> On Thu, Mar 21, 2024 at 3:25 PM Christopher Schultz <
> chris@christopherschultz.net> wrote:
>
>> All,
>>
>> After having written a solution using JMX to do something like this, I'd
>> like to make it cleaner and I'm not sure it's entirely possible using
>> just Servlet APIs.
>>
>> I'd like to be able to track every HttpSession for the application.
>>
>> for admin purposes, I'd like to be able to analyze:
>>
>> 1. The total number of sessions
>>
>> 2. The number of sessions which represent a logged-in user vs a
>> crawler-session or someone who visited the home-page and got a session
>> but never logged-in
>>
>> 3. Checking-out some specific roles of those logged-in users e.g.
>> end-user, staff, admin
>>
>> 4. Be able to kill a session at will. For example "chris is already
>> logged-in, kill his old session and let the new login remain"
>>
>> I started with the obvious HttpSessionListener +
>> HttpSessionActivationListener, but I tried this experiment and it didn't
>> turn out how I expected:
>>
>> 1. Start the application and hit the front page
>>
>> -> I get a call to HttpSessionListener.sessionCreated (expected)
>>
>> 2. Login
>>
>> 3. Logout
>>
>> -> I get a call to HttpSessionListener.sessionDestroyed (expected)
>> -> I get a call to HttpSessionListener.sessionCreated (expected)
>>     (this second one happens because our home-page creates a session)
>>
>> 4. Login again
>>
>> 5. Stop Tomcat
>>
>> -> No calls to anything I can see
>>
>> 6. Start Tomcat
>>
>> -> No calls to anything I can see
>>
>> 7. Access a protected page
>>
>> -> Access is allowed; I'm still logged-in.
>>
>> When Tomcat shuts-down, it's saving the sessions using local
>> persistence[1]. When the application comes back up, all those sessions
>> are restored from the disk.
>>
>> When my HttpSeessionListener starts, it's empty and doesn't know about
>> any sessions. Tomcat doesn't notify it that any sessions are coming from
>> that storage.
>>
>> I would have expected calls to
>> HttpSessionActivationListener.sessionWillPassivate and
>> HttpSessionActivationListener.sessionDidActivate.
>>
>> Do I have unrealistic expectations? Is there a way to capture these
>> events so my in-memory session-watcher/manager is able to have an
>> accurate view of what Tomcat can see?
>>
>> Thanks,
>> -chris
>>
>> [1]
>>
>> https://tomcat.apache.org/tomcat-8.5-doc/config/manager.html#Persistence_Across_Restarts
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
>> For additional commands, e-mail: users-help@tomcat.apache.org
>>
>>

Re: HttpSession tracking

Posted by Robert Turner <rt...@e-djuster.ca.INVALID>.
We receive the sessionWillPassivate and sessionDidActivate callbacks on
startup. Odd that you are not. That's how we achieve the same.

On Thu, Mar 21, 2024 at 3:25 PM Christopher Schultz <
chris@christopherschultz.net> wrote:

> All,
>
> After having written a solution using JMX to do something like this, I'd
> like to make it cleaner and I'm not sure it's entirely possible using
> just Servlet APIs.
>
> I'd like to be able to track every HttpSession for the application.
>
> for admin purposes, I'd like to be able to analyze:
>
> 1. The total number of sessions
>
> 2. The number of sessions which represent a logged-in user vs a
> crawler-session or someone who visited the home-page and got a session
> but never logged-in
>
> 3. Checking-out some specific roles of those logged-in users e.g.
> end-user, staff, admin
>
> 4. Be able to kill a session at will. For example "chris is already
> logged-in, kill his old session and let the new login remain"
>
> I started with the obvious HttpSessionListener +
> HttpSessionActivationListener, but I tried this experiment and it didn't
> turn out how I expected:
>
> 1. Start the application and hit the front page
>
> -> I get a call to HttpSessionListener.sessionCreated (expected)
>
> 2. Login
>
> 3. Logout
>
> -> I get a call to HttpSessionListener.sessionDestroyed (expected)
> -> I get a call to HttpSessionListener.sessionCreated (expected)
>     (this second one happens because our home-page creates a session)
>
> 4. Login again
>
> 5. Stop Tomcat
>
> -> No calls to anything I can see
>
> 6. Start Tomcat
>
> -> No calls to anything I can see
>
> 7. Access a protected page
>
> -> Access is allowed; I'm still logged-in.
>
> When Tomcat shuts-down, it's saving the sessions using local
> persistence[1]. When the application comes back up, all those sessions
> are restored from the disk.
>
> When my HttpSeessionListener starts, it's empty and doesn't know about
> any sessions. Tomcat doesn't notify it that any sessions are coming from
> that storage.
>
> I would have expected calls to
> HttpSessionActivationListener.sessionWillPassivate and
> HttpSessionActivationListener.sessionDidActivate.
>
> Do I have unrealistic expectations? Is there a way to capture these
> events so my in-memory session-watcher/manager is able to have an
> accurate view of what Tomcat can see?
>
> Thanks,
> -chris
>
> [1]
>
> https://tomcat.apache.org/tomcat-8.5-doc/config/manager.html#Persistence_Across_Restarts
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: users-help@tomcat.apache.org
>
>