Using A Custom HttpSessionListener In Development

288 views
Skip to first unread message

Timothy Washington

unread,
Mar 17, 2015, 12:39:43 AM3/17/15
to pedesta...@googlegroups.com

Is there a way to inject a custom HttpSessionListener into a Ring or Pedestal application? For a running jar, you'd simply configure your custom listener in your jar's "/WEB-INF/web.xml" file. However, is there a way of doing this at runtime, during development? These are the only things close to what I'm looking for. But they don't use HttpSessionListener.

Hmm

Tim Washington 

Timothy Washington

unread,
Mar 17, 2015, 12:52:30 AM3/17/15
to pedesta...@googlegroups.com
I should also mention that my application is trying to collect a list of active user sessions. I say it, in case there's not a 1-to-1 relationship between sessions and interceptors. And hopefully, in pedestal-service, that HttpSessionListener.sessionCreated reliably maintains all active user sessions.


Thanks 

Tim Washington 

Paul deGrandis

unread,
Mar 17, 2015, 7:15:46 AM3/17/15
to pedesta...@googlegroups.com
In Jetty, you should be able to adjust/set/change the session listener through the context:
http://stackoverflow.com/questions/17762832/embedded-jetty-implementing-httpsessionlistener

There is a service map option called `:context-configurator`, which you can use to get at the context.
An example of using the configurator can be found in the Servlet Filter sample:
https://github.com/pedestal/pedestal/blob/master/samples/servlet-filters-gzip/src/gzip/service.clj

Hope this helps!

Cheers,
Paul

Timothy Washington

unread,
Mar 22, 2015, 12:26:03 AM3/22/15
to pedesta...@googlegroups.com
Oops, should have put this to the group too ...

Tim Washington 


---------- Forwarded message ----------
From: Timothy Washington <twas...@gmail.com>
Date: Sat, Mar 21, 2015 at 1:52 PM
Subject: Re: Using A Custom HttpSessionListener In Development
To: Paul deGrandis <paul.de...@gmail.com>


That makes sense. But still no dice. 

A) I have a :context-configurator function that calls .setSessionHandler (also tried just .setHandler). To that function, I pass in a ServletContextHandler returned from a util method. 

Ultimately, I just want to track and manage all active jetty sessions. But with that code, I'm not seeing sessionCreated or sessionDestroyed events fired, when I go to a page. 

B) I also tried to pass in the context's sessionHandler to my function. 

(defn get-session-handler [^org.eclipse.jetty.server.session.SessionHandler session-handler]
  (let [session-listener (reify javax.servlet.http.HttpSessionListener
                           (sessionCreated [this event]
                             (println (str "sessionCreated CALLED: " event))
                             (swap! sessions ƒ(conj % (.getSession event))))
                           (sessionDestroyed [this event]
                             (println (str "sessionDestroyed CALLED: " event))
                             (swap! sessions (λ [sess]
                                               (remove ƒ(= (.getId (.getSession event))
                                                           (.getId sess))
                                                       sess)))))

        ;; i) this is the main attempt
        ;; session-manager (org.eclipse.jetty.server.session.HashSessionManager.)
        ; ;session-handler (org.eclipse.jetty.server.session.SessionHandler. session-manager)

        ;; ii) this replaces i)
        _ (.addEventListener session-handler session-listener)]
    session-handler))


But that didn't work either. The session-handler is null when first passed in by the create-server call. Any ideas?

2. Unhandled clojure.lang.Compiler$CompilerException
   Error compiling:
1. Caused by java.lang.NullPointerException
   (No message)
                      util.clj:   26  codepair.http.util/get-session-handler
                   service.clj:   86  codepair.http.service/fn
                     jetty.clj:  123  io.pedestal.http.jetty/create-server
                     jetty.clj:  140  io.pedestal.http.jetty/server
                      Var.java:  383  clojure.lang.Var/invoke
                      http.clj:  262  io.pedestal.http/server
                      http.clj:  276  io.pedestal.http/create-server
                    server.clj:   20  codepair.http.server/eval20659


Tim Washington 


On Thu, Mar 19, 2015 at 2:55 AM, Paul deGrandis <paul.de...@gmail.com> wrote:
You should look at the second link again.  It illustrates the use of a the `context-configurator`, which gives you direct access to the ServletContextHandler.  In that example, I'm using the context-configurator to add a ServletFilter, but you can do anything with it.  It's just a function that is passed a single argument, the context.

- Paul

On Wed, Mar 18, 2015 at 11:35 PM, Timothy Washington <twas...@gmail.com> wrote:
The first link is definitely what I need. And I can definitely reify a SessionListener (my-session-listener) with sessionCreated and sessionDestroyed methods. However, I don't have access to the context (ServletContextHandler). Thus I can't invoke the below. 

context.setHandler(sessionHandler); 
sessionHandler.addEventListener(my-session-listener);

And the second link is good direction for adding a filter. But what I need in this case is a session listener


Thanks 

Tim Washington 

Timothy Washington

unread,
Mar 24, 2015, 1:57:35 AM3/24/15
to pedesta...@googlegroups.com
Ok, so I've confirmed it. The point in Jetty's lifecycle, where pedestal passes in a ^ServletContextHandler to the :context-configurator, the session handler is still null. 

A) In other words, for the :context-configurator example, the org.eclipse.jetty.servlet ServletContextHandler can add filters. But it does not yet yet have an org.eclipse.jetty.server.session.SessionHandler. Ie, I can't add an HttpSessionListener. 

B) (def one (.getSessionHandler myServletContextHandler)) gives us nil. Which means we can't call (.addEventListener one mySsessionListener)

C) And I'm pretty sure that that's how jetty is adding HttpSessionListeners. For anyone who's interested. You can reverse engineer how jetty handles web.xml, thusly. In my case, I wanted to see how HttpSessionListeners were pulled in.
  • get the jetty source from here ; http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/jetty-server
  • find "web.xml" in method isWebArchive() ; ./jetty-deploy/src/main/java/org/eclipse/jetty/deploy/util/FileID.java
  • in createContextHandler , we create a new XmlConfiguration; ./jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java
  • ./jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java
  • look at the method source "addEventListener" ; ./jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java
  •  there are several implementations of AbstractSessionManager; incl. HashSessionManager, JDBCSessionManager, etc


>> D) Is there anyway for Pedestal to pass in Jetty's ^ServletContextHandler at a later stage in Jetty's lifecycle (ie, when the ^SessionHandler exists)? 


Thanks 

Tim Washington 

Paul deGrandis

unread,
Mar 24, 2015, 7:39:14 AM3/24/15
to pedesta...@googlegroups.com
I think you may be mistaken a little bit.  According to the StackOverflow question I linked earlier in the chain (http://stackoverflow.com/questions/17762832/embedded-jetty-implementing-httpsessionlistener), you potentially need a ConnectHandler to have a SessionHandler.  That's not done in the current Pedestal setup - https://github.com/pedestal/pedestal/blob/master/jetty/src/io/pedestal/http/jetty.clj#L97-124

You could augment this by passing in your own `configurator` function (which is given the server itself, not the context-handler).

Pedestal also has a session interceptor (https://github.com/pedestal/pedestal/blob/master/service/src/io/pedestal/http/ring_middlewares.clj#L129-137), which can easily be toggled on in the `default-interceptors` (https://github.com/pedestal/pedestal/blob/master/service/src/io/pedestal/http.clj#L165-192).  Setting the default-interceptor options happens in the service-map.
You can also just attach the session interceptor to a certain subset of routes, as done here: https://github.com/pedestal/pedestal/blob/master/samples/ring-middleware/src/ring_middleware/service.clj

If you want to do this at the Jetty level, I'd consider some mix of `configurator` and `context-configurator`.  If you're just doing it for a one-off measurement to see how things work, I would consider JMX measurements that Jetty is already providing (perhaps you'd be able to drill in there).

The last option is to setup Jetty from a web.xml and package your Pedestal application as a WAR.  You can deploy the WAR to Jetty like you would any other application.

Cheers,
Paul

Timothy Washington

unread,
Mar 25, 2015, 11:23:11 AM3/25/15
to pedesta...@googlegroups.com
I'll try going with interceptors directly. I didn't use them at first, because I figured they were invoked per web client. But I can rig the interceptor to call into a more global location. Again, the goal being to i) capture session data for all users, and ii) have a way for those users to communicate with each other. Think chat, or in my case an rtc signalling server


Thanks Paul 

Tim Washington 


--
You received this message because you are subscribed to the Google Groups "pedestal-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pedestal-user...@googlegroups.com.
Visit this group at http://groups.google.com/group/pedestal-users.

Reply all
Reply to author
Forward
0 new messages