wildfly-clustering-spring-session is a Spring Session module providing a number of distributed SessionRepository
implementations based on WildFly's distributed session management and Infinispan.
Spring Session is intended to operate within any servlet container. The following describes how to install wildfly-clustering-spring-session support into a Tomcat distribution:
-
Enter directory of the desired session manager implementation:
$ cd session/infinispan/embedded
or:
$ cd session/infinispan/remote
-
Copy the maven artifact to Tomcat's
/lib
directory:$ mvn dependency:copy -DoutputDirectory=$CATALINA_HOME/lib
-
Copy runtime dependencies to Tomcat's
/lib
directory:$ mvn dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=$CATALINA_HOME/lib
Traditionally, users configure a SessionRepository
via XML or annotations.
The Spring Session documentation directs users to provide an implementation of org.springframework.web.WebApplicationInitializer
, which is supposed to auto-wire the requisite request filters and listeners.
e.g.
@EnableJdbcHttpSession
public class Config {
// ...
}
public class MyHttpSessionApplicationInitializer extends AbstractHttpSessionApplicationInitializer {
public MyHttpSessionApplicationInitializer() {
// This doesn't work!!!
super(Config.class);
}
}
However, this mechanism cannot possibly work correctly in a specification-compliant Jakarta Servlet container.
Spring Session's auto-wiring initiates from the AbstractHttpSessionApplicationInitializer.onStartup(ServletContext)
method, where it dynamically registers a ServletContextListener
.
Unfortunately for Spring, §4.4 of the Jakarta Servlet specification is very specific about how a container should treat ServletContext events for dynamically registered listeners:
If the ServletContext passed to the ServletContextListener’s contextInitialized method where the ServletContextListener was neither declared in web.xml or web-fragment.xml nor annotated with @WebListener then an UnsupportedOperationException MUST be thrown for all the methods defined in ServletContext for programmatic configuration of servlets, filters and listeners.
Consequently, the only feasible way to configure Spring Session via annotations for use in a specification-compliant Jakarta Servlet container is to create the WebApplicationContext from an explicitly defined ServletContextListener
, rather than from the HttpSessionApplicationInitializer
.
-
Define the session repository configuration using 1 of the 4 annotations described later in this section.
// Spring Session repository configuration @EnableHotRodHttpSession(...) public class Config { }
-
By omitting an
AbstractHttpSessionApplicationInitializer
implementation, we have denied Spring the ability to dynamically register itsServletContextListener
. In lieu of this, we create a listener extendingorg.wildfly.clustering.spring.web.context.ContextLoaderListener
constructed with the component classes that would otherwise have been registered byAbstractHttpSessionApplicationInitializer
.@WebListener public class SpringSessionContextLoaderListener extends org.wildfly.clustering.spring.web.context.ContextLoaderListener { public SpringSessionContextLoaderListener() { // Specify spring session repository component class to super implementation super(Config.class); } }
-
Add the servlet filter that would otherwise have been dynamically created and registered by
AbstractHttpSessionApplicationInitializer
.@WebFilter(filterName = AbstractHttpSessionApplicationInitializer.DEFAULT_FILTER_NAME, urlPatterns = "/*", dispatcherTypes = { DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC }, asyncSupported = true) public class SpringSessionFilter extends org.springframework.web.filter.DelegatingFilterProxy { public SpringSessionFilter() { super(AbstractHttpSessionApplicationInitializer.DEFAULT_FILTER_NAME); } }
Users will configure wildfly-clustering-spring-session using one of the four annotations defined below. The annotation type determines the implementation of the underlying session repository, as well as whether or not the repository supports indexing. Each annotation type is a composite of some subset of component annotations. See the corresponding linked section for the definition of a each component annotation.
-
@EnableHotRodHttpSession(manager = @SessionManager, config = @HotRod)
- Configures a SessionRepository that persists sessions to a remote Infinispan cluster via HotRod.
-
@EnableHotRodIndexedHttpSession(manager = @SessionManager, config = @HotRod, indexing = @Indexing)
- Configures a FindByIndexNameSessionRepository that persists sessions to a remote Infinispan cluster via HotRod.
-
@EnableInfinispanHttpSession(manager = @SessionManager, config = @Infinispan)
- Configures a SessionRepository that persists sessions to an embedded Infinispan cache.
-
@EnableInfinispanIndexedHttpSession(manager = @SessionManager, config = @Infinispan, indexing = @Indexing)
- Configures a FindByIndexNameSessionRepository that persists sessions to an embedded Infinispan cache.
This configuration component defines the behavior of the session manager that are common to each top-level configuration annotation.
The @SessionManager
annotation defines the following properties:
Property | Description |
---|---|
granularity | Defines how a session is mapped to entries in the cache. Supported granularities are enumerated by the org.wildfly.clustering.spring.context.SessionPersistenceGranularity enum. SESSION will store all attributes of a session in a single cache entry, while ATTRIBUTE will store each session attribute in a separate cache entry. Default is SESSION . |
marshaller | Specifies the marshaller used to serialize and deserialize session attributes. Supported marshallers are enumerated by the org.wildfly.clustering.spring.context.SessionAttribute enum and include: JAVA , i.e. Java serialization; JBOSS , i.e. JBoss Marshalling; PROTOSTREAM , i.e. protobuf. Default marshaller is JBOSS . |
maxActiveSessions | Defines the maximum number of sessions to retain within the data container, for embedded Infinispan; or within the HotRod near-cache, for a remote Infinispan cluster. By default, embedded Infinispan will use an unbounded data container, while HotRod will disable its near-cache. |
This configuration component defines the HotRod client configuration used by the HotRod-based session repository.
The @HotRod
annotation defines the following properties:
Property | Description |
---|---|
uri | Defines a HotRod URI, which includes a list of infinispan server instances and any authentication details. For details, see: https://infinispan.org/blog/2020/05/26/hotrod-uri/ |
template | Defines the server-side configuration template from which a deployment cache is created on the server. Default is org.infinispan.DIST_SYNC . |
This configuration component defines the HotRod client configuration used by an embedded Infinispan-based session repository.
The @Infinispan
annotation defines the following properties:
Property | Description |
---|---|
resource | The location of an Infinispan XML configuration within the deployment. Default resource location is /WEB-INF/infinispan.xml |
template | The name of the configuration template defined within the configuration XML from which a deployment cache will be created. If undefined, the configuration of the default cache configuration will be used. |
An example Infinispan XML can be found here
N.B. If the corresponding @SessionManager defines a value for maxActiveSessions, the Infinispan configuration should include a passivating or persistent cache store, e.g. <file-store/>
.
This configuration component defines the indexing configuration used by an indexing session repository.
An indexing session repository allows the user to look up a given Spring Session by an indexed session attribute.
The primary use case of an indexing session repository is to support Spring Security concurrency control.
The @Indexing
annotation defines the following properties:
Property | Description |
---|---|
indexes | Defines an array of @Index definitions that defines the index identifiers and attribute names by which sessions should should be indexed. Default value is configured for use with Spring Security concurrency control, i.e. { @Index(id = "SPRING_SECURITY_CONTEXT", name = "org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME") } |
resolverClass | Defines the class of the IndexResolver used to resolve all indexes for a given session. Default value is configured for use with Spring Security concurrency control, i.e. org.springframework.session.PrincipalNameIndexResolver.class |
Alternatively, the Spring Session repository can be configured via XML rather than via annotation.
When configuring Spring Session via XML, we also omit an AbstractHttpSessionApplicationInitializer
implementation to prevent dynamic ServletContextListener
registration, therefore we must also define manually the filter and listener required by Spring Session.
WEB-INF/web.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
/WEB-INF/applicationContext.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="org.wildfly.clustering.spring.web.infinispan.remote.config.HotRodWebSessionConfiguration">
<property name="uri">
<value type="java.net.URI">hotrod://127.0.0.1:11222</value>
</property>
<property name="properties">
<props>
<prop key="infinispan.client.hotrod.tcp_keep_alive">true</prop>
</props>
</property>
<property name="granularity">
<value type="org.wildfly.clustering.spring.context.SessionPersistenceGranularity">SESSION</value>
</property>
<property name="marshaller">
<value type="org.wildfly.clustering.spring.context.SessionAttributeMarshaller">PROTOSTREAM</value>
</property>
<property name="maxActiveSessions">1000</property>
</bean>
</beans>