0

From a custom Servlet Filter when trying to access a GemFire session object, it is taking the Container's session object instead. The session object is of the type:

org.apache.catalina.session.StandardSessionFacade@517957e2

But from the Controller, it is working fine. The session object is of the type: org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper@5afe18ce

On to how we configured GemFire:

We have a legacy retail application. On top, we have used the 2.0.5 version of GemFire. On the webappintializer startup,

AnnotationConfigWebApplicationContext rootContext = 
    new AnnotationConfigWebApplicationContext();

rootContext.register(GemfireConfig.class,RootConfig.class, SecurityConfig.class);

Since the springSessionRepositoryFilter bean was not added to the filter chain, we had to explicitly register the filter with DelegatingFilterProxy using the below:

FilterRegistration.Dynamic springSessionRepositoryFilter = 
    container.addFilter("springSessionRepositoryFilter", DelegatingFilterProxy.class);

springSessionRepositoryFilter.addMappingForUrlPatterns(
    EnumSet.allOf(DispatcherType.class), false, "/*");

On the data handling side in order to get the session object, we have a getSession method which returns a session object:

ServletRequestAttributes attr = 
    (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();

HttpSession session = attr.getRequest().getSession();

When we call the getSession() method from Controller it is working absolutely fine as designed. But calling the same from a Servlet Filter eventually gets the Container created session object.

Any help is much appreciated.

Reworked as per comment by @John Blum but still facing the same issue.

2 Answers2

0

In a nutshell, in order for Spring Session, and specifically, Spring Session for Pivotal GemFire (SSDG), to do its job, the SessionRepositoryFilter (Javadoc, Source) must be the first Servlet Filter in the filter chain when registered with your (Web) Application Container (e.g. Apache Tomcat, Eclipse Jetty, etc).

Otherwise, if Spring Session's SessionRepositoryFilter is not the first Servlet Filter in the filter chain, then Spring Session will not have intercepted the HTTP request (yet) and won't be able to exercise its opportunity to replace the Container session (by wrapping the HttpServletRequest with SessionRepositoryFilter.SessionRepositoryRequestWrapper, see here) with a Session provided by Spring Session using the appropriate provider (e.g. like GemFire with SSDG), as determined by the SessionRepository implementation, which is set on the SessionRepositoryFilter (here).

The reason why your Spring Web MVC application Controller works is, the Java EE Servlet Spec/Container guarantees that all Servlet Filters are invoked before any Servlets are called with the HTTP request (i.e. HttpServletRequest). And, since the Spring Web MVC DispatcherServlet is a proper HttpServlet and is responsible for invoking your application-defined Spring Web MVC Controllers, then the application Controllers are guaranteed to see the "replaced" HTTP request (and by extension, HTTP session object).

So, a few housekeeping items... I am not (exactly) certain what you mean by:

  1. 2.0.5 version of GemFire. 2.0.5 refers to the latest/current version of Spring Session for Pivotal GemFire.

  2. And webappinitializer?

For #2, did you mean that you specifically created a Spring WebApplicationInitializer to explicitly register the SessionRepositoryFilter, manually (as shown above in your code snippet)?

Did you know that Spring Session already provides such a class... o.s.session.web.context.AbstractHttpServletApplicationInitializer.

This class is responsible for registering the SessionRepositoryFilter using Spring's DelegatingFilterProxy class (here, then here, and here (notice the insertBeforeOtherFilters instance variable, which defaults to true), and in the right order, here, well, specifically, here).

Interestingly enough, it seems like you are doing the same, or similar, thing in your snippet of Filter registration code above.

NOTE: this (programmatical configuration of the Servlet container) only works in Servlet 3.0 containers and later.

You can see how Spring Session's AbstractHttpServletApplicationInitializer gets used in the samples, for instance, here. More details on the Initializer are in the corresponding guide docs for the sample.

One thing different about your Spring DelegatingFilterProxy class registration (named after the SessionRepositoryFilter bean, named "springSessionRepositoryFilter") that I noticed was, you are passing in the DelegatingFilterProxy.class as the second argument to servletContext.addFilter("filterName", <FilterType>);, as shown here...

FilterRegistration.Dynamic springSessionRepositoryFilter = 
    container.addFilter("springSessionRepositoryFilter", DelegatingFilterProxy.class);

However, Spring Session (core) itself actually constructs and initializes (with the "springSessionRepositoryFilter" bean name) an instance of the Spring DelegatingFilterProxy class and passes that "instance" to the ServletContext.addFilter(..) method (2nd argument) on registration.

I suspect the ServletContext.addFilter(..) API itself just uses the default constructor of the Spring DelegatingProxyFilter class when constructing/initializing an instance, which is probably the root of your problem, especially when registering the Servlet Filter programmatically.

Food for thought.

Hope this helps!

John Blum
  • 7,381
  • 1
  • 20
  • 30
-1

The issue still persists after trying out the suggestions. Now, we no longer use a separate registration for the SessionRepositoryFilter. We now use:

class WebAppInitializer extends AbstractHttpSessionApplicationInitializer {

 public WebAppInitializer() {
        super(GemfireConfig.class,X.class, Y.class); //X&Y are preexisting config classes in the application context

    }

@Override
public void onStartup(ServletContext container) throws ServletException {

    super.onStartup(container); // newly added inorder to call AbstractHttpSessionApplicationInitializer.startup()
    ...//existing code
    ...//

}

Removed the explicit registration of springSessionRepositoryFilter.

We could see this filter being invoked first when we look at the Stack Trace. So, the order of filter chain appears to be intact.

The filter, in which we are facing issue, is being invoked afterwards. This was the same behavior even without the mentioned changes.

Still, the session object in the filter is from the container.

John Blum
  • 7,381
  • 1
  • 20
  • 30
  • You don't need to override the `onStartup(:ServletContext)` method (minor detail, :-) Not that it should matter, but are you registering your custom, application-specific Servlet Filter using `web.xml`? So, if you see that the `SessionRepositoryFilter` is the first in the chain of Servlet Filters from your Stack Trace, then upon reviewing your code from the original post, I am sort of wondering what this `RequestContextHolder.currentRequestAttributes();` is doing followed by this... `HttpSession session = attr.getRequest().getSession();`? – John Blum Sep 13 '18 at 16:14
  • It appears you are using the `RequestContextHolder` and `ServletRequestAttributes` classes from the core Spring Framework to obtain the `HttpServletRequest` followed by the `HttpSession`. I suspect that Spring Session core may not be properly updating Spring's `RequestContextHolder` after the `springSessionRepositoryFilter` has had an opportunity to "replace" the HTTP request/session objects. Going to do some more digging... – John Blum Sep 13 '18 at 16:33
  • 1
    So, after further digging... 1) It is not the responsibility of Spring Session (and in particular, the `SessionRepositoryFilter`) to update the `RequestContextHolder` state. 2) It is questionable why you are using the `RequestContextHolder` class inside your custom, application-specific Servlet Filter (which as we discussed before, needs to run after the `SessionRepositoryFilter` in the filter chain) to access the `HttpServletRequest` and subsequently access/start an `HttpSession` since `Filter` interface, `doFilter(..)` method accepts the `ServletRequest` and `ServletResponse` instances. – John Blum Sep 13 '18 at 17:14
  • See here (https://javaee.github.io/javaee-spec/javadocs/javax/servlet/Filter.html#doFilter-javax.servlet.ServletRequest-javax.servlet.ServletResponse-javax.servlet.FilterChain-). You could simply cast the `ServletRequest` to an `HttpServletRequest` and call `((HttpServletRequest) servletRequest).getSession()` inside your custom, application-specific Servlet Filter's `doFilter(:ServletRequest, :ServletResponse)` method. – John Blum Sep 13 '18 at 17:16
  • However, if you must access the HTTP request and session using Spring's `RequestContextHolder` class, for whatever reason (which is not apparent to me, why) inside your custom, application-specific Servlet `Filter`, then you can also employ core Spring's `RequestContextFilter` (https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/filter/RequestContextFilter.html) as described in the Javadoc for `RequestContextHolder` (https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/context/request/RequestContextHolder.html). – John Blum Sep 13 '18 at 17:20
  • Then you must ensure that the Filters are registered in this order: { `SessionRepositoryFilter`, `RequestContextFilter`, "Your Custom, Application-Specific Servlet Filter", all other Servlet `Filters`. – John Blum Sep 13 '18 at 17:22
  • DISCLAIMER: I have not tried and tested this combination. Personally, I recommend you to use the `ServletRequest`/`ServletResponse` parameters from your custom, application-specific Servlet `Filter's`, `doFilter(..)` method, to access the HTTP request/session. Technically, Spring's `RequestContextHolder` is not initialized until the `DispatcherServlet` is invoked. If you are still experiencing issues after this, I suggest you to write a small test example and publish to GitHub for us to take a look at. Cheers! – John Blum Sep 13 '18 at 17:24
  • @John Blum ..It is working now as we changed the usage of RequestContextHolder with ServletRequest/ServletResponse. – Fredie Thomas Sep 14 '18 at 04:39
  • Excellent; glad you figured it out. – John Blum Sep 14 '18 at 22:59
  • @JohnBlum Amazing support from your end. – Fredie Thomas Sep 17 '18 at 09:03