- Introduction
- Supported Tomcat Version
- Compiling the Plugin
- Plugin Components
- Configuration Parameters
- Docker Setup
- Local Environment Setup (from source code)
- Connection Pool Configuration
- Enabling DEBUG Logging
- Tracking Changes in Session Attributes
- Persistence Policies
- Acknowledgements
This session manager is an implementation that stores sessions in Redis for easy distribution of requests across a cluster of Tomcat servers. Sessions are implemented as non-sticky -- that is, each request is able to go to any server in the cluster (unlike the Apache provided Tomcat clustering setup.)
Sessions are stored into Redis immediately as soon as they meet at least one of the following two conditions:
- They are being created right after a User logs into the dotCMS back-end, or an authenticated page in the front-end.
- The switch for storing absolutely all sessions in Redis is enabled -- more on this later on.
Sessions are loaded as requested directly from Redis (but subsequent requests for the session during the same request context will return a ThreadLocal cache rather than hitting Redis multiple times). In order to prevent collisions (and lost writes) as much as possible, session data is only updated in Redis if the session has been modified.
The manager relies on the native expiration capability of Redis to expire keys for automatic session expiration to avoid the overhead of constantly searching the entire list of sessions for expired sessions. However, for usual front-end sessions -- or one-time hits coming from bots or image requests -- the expiration process will fall back to how Tomcat works originally.
Additionally, it's very important to note that absolutely all data stored in the session must be Serializable and not null. Otherwise, a warning message will be printed in the logs stating so, and indicating what specific attribute was not added to the Session.
As of September 2025, this plugin can run on Tomcat 9.0.108. Keep in mind that every single plugin version is not guaranteed to be backwards compatible with older Tomcat versions.
This plugin provides a Gradle Wrapper that you can use to generate the expected .JAR file. Just open up a Terminal in the directory where this project is located, and run the following command:
./gradlew clean jar
The most important parts of this plugin are the following:
RedisSessionManager
: Provides the session creation, saving, and loading functionality.RedisSessionHandlerValve
: Hooks up the session manager with Tomcat, and ensures that sessions are saved after a request is finished processing.
Note: This architecture differs from the Apache PersistentManager
implementation which implements persistent sticky sessions. Because that implementation expects all requests from a specific session to be routed to the same server, the timing persistence of sessions is non-deterministic since it is primarily for failover capabilities.
The expected XML configuration must be added to the Tomcat context.xml
file (or the context block of the server.xml
, if applicable) in order to tell Tomcat that the Session Management will be customized. Different plugin and generic pool properties can be added as required. For instance:
<Valve className="com.dotcms.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.dotcms.tomcat.redissessions.RedisSessionManager"
host="localhost" <!-- optional: defaults to "localhost" -->
port="6379" <!-- optional: defaults to "6379" -->
database="0" <!-- optional: defaults to "0" -->
sessionPersistPolicies="PERSIST_POLICY_1,PERSIST_POLICY_2,.." <!-- optional -->
sentinelMaster="SentinelMasterName" <!-- optional -->
sentinels="sentinel-host-1:port,sentinel-host-2:port,.." <!-- optional --> />
It's very important to note that the Valve
tag must be declared before the Manager
tag.
This plugin relies on the following JAR files, which must always be present in the {TOMCAT_BASE}/lib/
directory:
commons-pool2-2.11.1.jar
jedis-4.4.6.jar
tomcat-redis-session-manager-VERSION.jar
(the JAR generated by this project, of course)
If changing the configuration parameters directly in the context.xml
file is not feasible or secure, they can be specified via Environment Variables, or Java Properties in the dotCMS startup script. Here's a list with the properties that are most commonly used by the plugin:
TOMCAT_REDIS_SESSION_CONFIG
: Allows you to set the actual configuration tags placed in the Tomcatcontext.xml
file, in required. By default, the tags added to the XML file are:
<Valve className="com.dotcms.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.dotcms.tomcat.redissessions.RedisSessionManager" />
TOMCAT_REDIS_SESSION_HOST
: Sets the host name for your Redis ServerTOMCAT_REDIS_SESSION_PORT
: Sets the port for your Redis ServerTOMCAT_REDIS_SESSION_USERNAME
: In case the Redis Server requires username authentication, this allows you set the expected username. This is particularly useful when having an ACL for Redis.TOMCAT_REDIS_SESSION_PASSWORD
: When the username is set, this allows you to set the required password for the connection to the Redis Server.TOMCAT_REDIS_SESSION_DATABASE
: The default Redis database selected by the Jedis Client.TOMCAT_REDIS_SESSION_TIMEOUT
: The default timeout for the Jedis Client when trying to connect to the Redis Server.TOMCAT_REDIS_USER_SESSION_TIMEOUT
: The default timeout, in seconds, for the user session in the browser. By default, it's set to Tomcat's Timeout, which is usually 1800 seconds -- 30 minutes.TOMCAT_REDIS_SESSION_PERSISTENT_POLICIES
: The available persistence policies for determining when the user session must be persisted to Redis:DEFAULT
: Selected by default. It tells the manager to persist the session ONLY if its current attributes differ compared to the ones from the session in Redis. This is the default behavior with the least overhead.ALWAYS_SAVE_AFTER_REQUEST
: It tells the manager to force persisting the session as soon as the HTTP Request finishes. This option adds a little more overhead to the session save process.SAVE_ON_CHANGE
: It tells the manager to immediately persist the session as soon as any session attribute is added/removed. Because of that, this is the option that adds the most overhead, so please take this into consideration.
TOMCAT_REDIS_MANUAL_DIRTY_TRACKING_SUPPORT
: When enabled, it allows developers to manually mark a session as dirty, which will trigger the persistence of the session to Redis.TOMCAT_REDIS_MANUAL_DIRTY_TRACKING_SUPPORT_KEY
: When theTOMCAT_REDIS_MANUAL_DIRTY_TRACKING_SUPPORT
property is enabled, this property allows you to set the name of the flag attribute that developers will use in order to let the Session Manager know that the session must be persisted.TOMCAT_REDIS_PERSIST_ON_DEMAND
: When enabled, allows developers to force persisting the session to Redis. This is useful when developers update an attribute directly in the HTTP Session that is referenced in memory, and the HTTP Request is not involved or has not been issued. An example of this is the status of the Integrity Checker operation, which is set directly as a session attribute without issuing a request, but inside a Java Thread. The process of persisting the session will happen immediately, regardless of the configured persistence policies.TOMCAT_REDIS_PERSIST_ON_DEMAND_ATTR
: When theTOMCAT_REDIS_PERSIST_ON_DEMAND
property is enabled, this property allows you to set the name of the attribute that developers will use in order to let the Session Manager know that the session must be persisted right away.TOMCAT_REDIS_MAX_CONNECTIONS
: The maximum number of connections that the Jedis Client will maintain to the Redis Server.TOMCAT_REDIS_MAX_IDLE_CONNECTIONS
: The maximum number of idle connections that the Jedis Client will maintain to the Redis Server.TOMCAT_REDIS_MIN_IDLE_CONNECTIONS
: The minimum number of idle connections that the Jedis Client will maintain to the Redis Server.TOMCAT_REDIS_ENABLED_FOR_ANON_TRAFFIC
: When enabled, instructs the Session Manager to persist absolutely all the Sessions it manages to the Redis server, not just the ones generated from a back-end or front-end login.TOMCAT_REDIS_UNDEFINED_SESSION_TYPE_TIMEOUT
: Specifies the TTL (time-to-live) for the Sessions that may not be associated with an authenticated request yet, but may be a few moments later. This is extremely important to take into consideration in dotCMS clustered environments.DOT_DOTCMS_CLUSTER_ID
: The ID of the cluster that dotCMS is running in. This is used to identify the cluster that the Session Manager is associated to.
Additionally, once dotCMS starts up, a section describing the initialization values for all these properties will be displayed in the catalina.out
or dotcms.log
file so that it can be easily monitored by System Administrators. Here's an example of what such an output could look like with most of the default configuration parameters:
16:56:53.309 INFO redissessions.RedisSessionManager -
========================================================================
Redis-based Tomcat Session plugin
========================================================================
16:56:53.309 INFO redissessions.RedisSessionManager - -> Attaching 'com.dotcms.tomcat.redissessions.RedisSessionManager' to 'com.dotcms.tomcat.redissessions.RedisSessionHandlerValve'
16:56:53.309 INFO redissessions.RedisSessionManager - -> Initializing Java Serializer: 'com.dotcms.tomcat.redissessions.JavaSerializer'
16:56:53.309 INFO redissessions.RedisSessionManager - -> Loading configuration parameters...
16:56:53.320 INFO redissessions.RedisSessionManager -
[✓] TOMCAT_REDIS_SESSION_HOST: redis
[✓] TOMCAT_REDIS_SESSION_PORT: 6379
[✓] TOMCAT_REDIS_SESSION_USERNAME: - Not Set -
[✓] TOMCAT_REDIS_SESSION_PASSWORD: - Set -
[✓] TOMCAT_REDIS_SESSION_SSL_ENABLED: false
[✓] TOMCAT_REDIS_SESSION_SENTINEL_MASTER: null
[✓] TOMCAT_REDIS_SESSION_SENTINELS: null
[✓] TOMCAT_REDIS_SESSION_DATABASE: 0
[✓] TOMCAT_REDIS_SESSION_TIMEOUT: 2000
[✓] TOMCAT_REDIS_USER_SESSION_TIMEOUT (defaults to Tomcat's Session Timeout): 1800
[✓] TOMCAT_REDIS_SESSION_PERSISTENT_POLICIES: DEFAULT
[✓] TOMCAT_REDIS_MANUAL_DIRTY_TRACKING_SUPPORT: true
[✓] TOMCAT_REDIS_MANUAL_DIRTY_TRACKING_SUPPORT_ATTR: __dot_session_attribute_changed__
[✓] TOMCAT_REDIS_PERSIST_ON_DEMAND: true
[✓] TOMCAT_REDIS_PERSIST_ON_DEMAND_ATTR: __dot_session_persist_now__
[✓] TOMCAT_REDIS_MAX_CONNECTIONS: 128
[✓] TOMCAT_REDIS_MAX_IDLE_CONNECTIONS: 100
[✓] TOMCAT_REDIS_MAX_IDLE_CONNECTIONS: 32
[✓] TOMCAT_REDIS_ENABLED_FOR_ANON_TRAFFIC: false
[✓] TOMCAT_REDIS_UNDEFINED_SESSION_TYPE_TIMEOUT: 15
[✓] DOT_DOTCMS_CLUSTER_ID (Redis Key Prefix): dotcms-redis-cluster
16:56:53.320 INFO redissessions.RedisSessionManager - -> Initializing Redis connection...
16:56:53.392 INFO redissessions.RedisSessionManager -
Successful! Redis-based Tomcat Sessions will expire after 1800 seconds.
It's worth noting that, for security reasons, the values of following two properties:
TOMCAT_REDIS_SESSION_USERNAME
TOMCAT_REDIS_SESSION_PASSWORD
Will NOT be displayed in the log. Instead, the String - Set -
will be displayed if a value has been set for them, and - Not Set -
if it hasn't.
The success message at the bottom is the key indicator that the plugin has successfully connected to Redis and is ready to receive data. It also confirms the TTL set for every user session. By default, only sessions created by dotCMS for either the back-end or the front-end will be persisted to Redis. If you want to persist sessions created by anonymous traffic, you can set the TOMCAT_REDIS_ENABLED_FOR_ANON_TRAFFIC
property to true
.
Allowing multiple clusters to share the same session Redis store can be a very smart strategy. In order to accomplish this, you can specify the ID of the cluster via the DOT_DOTCMS_CLUSTER_ID
property which is used to prefix all keys persisted to Redis.
In your docker-compose.yml
file, go to the environment
section of your dotCMS node setup and add all the configuration properties you deem necessary -- please refer to the Configuration Parameters
section. For instance:
dotcms-node:
image: dotcms/dotcms:trunk
environment:
TOMCAT_REDIS_SESSION_ENABLED: 'true'
TOMCAT_REDIS_SESSION_HOST: 'redis'
TOMCAT_REDIS_SESSION_PORT: '6379'
TOMCAT_REDIS_SESSION_PASSWORD: 'MY_SECRET_P4SS'
TOMCAT_REDIS_SESSION_SSL_ENABLED: 'false'
TOMCAT_REDIS_SESSION_PERSISTENT_POLICIES: 'DEFAULT'
DOT_DOTCMS_CLUSTER_ID: 'dotcms-redis-cluster'
...
..
.
Notice that there's a property called TOMCAT_REDIS_SESSION_ENABLED
in the example configuration. If you remove it or set its value to 'false'
and restart your dotCMS container, the plugin will NOT be activated during startup and the application will let Tomcat handle all Sessions in memory as usual.
In your local environment, you need to go to the Tomcat context.xml
file, scroll down to the bottom, and add the following code:
<Valve className="com.dotcms.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.dotcms.tomcat.redissessions.RedisSessionManager"
password="YOUR_P4SS_HERE"/>
For the plugin to be activated when dotCMS starts up. This configuration is what actually enables this plugin, so once you comment it back, dotCMS will go back to letting Tomcat handle its Sessions, as usual.
A local Redis Server must be up and running before the Redis Session Manager is enabled -- i.e., added to the context.xml
file -- and dotCMS is started. Here's an example of a docker-compose
file that sets up a simple password-protected Redis Server:
networks:
redis_net:
volumes:
redisdata:
services:
redis:
image: "redis:latest"
command: redis-server --requirepass YOUR_P4SS_HERE
ports:
- "6379:6379"
volumes:
- redisdata:/data
networks:
- redis_net
IMPORTANT: If you need to set up a Redis Server that requires both username and password, please refer to the sample docker-compose
file here: docker-compose-examples/redis-with-usr-pwd/redis/docker-compose.yml
.
Please refer to the Configuration Parameters
section in case you need to enable/disable additional configuration properties for Redis via Environment Variables or Java Properties. For instance, when using the docker-compose
file above, you'll need to specify both the username
and password
attributes.
All the configuration options from both org.apache.commons.pool2.impl.GenericObjectPoolConfig
and org.apache.commons.pool2.impl.BaseObjectPoolConfig
are also configurable for the Redis connection pool used by the session manager. To configure any of these attributes (e.g., maxIdle
and testOnBorrow
) just use the config attribute name prefixed with connectionPool
(e.g., connectionPoolMaxIdle
and connectionPoolTestOnBorrow
) and set the desired value in the <Manager>
declaration in your Tomcat's context.xml
file.
By default, and for security reasons, minimal initialization information is logged when dotCMS starts up. This is basically meant to let you know that the plugin is actually present, and is enabled. If more detailed information is required, you just need to follow these steps:
- Go to the
{TOMCAT_HOME}/conf/logging.properties
file. - Scroll down to the bottom, and add an entry for every class in this plugin for which you want to increase the logging level. For instance, if you need the
RedisSessionManager
class to log more information, add this:
com.dotcms.tomcat.redissessions.RedisSessionManager.level = FINE
- Restart dotCMS and check the
catalina.out
file.
It's important to notice that this operation should only be carried out under specific circumstances. Given the number of threads spawned by Tomcat, the additional logging can be overwhelming, hard to read, and fill the logs in a very short time. For more information on the different logging levels, please refer to the Apache Tomcat Logging documentation.
In order to prevent colliding writes, the Redis Session Manager only serializes the session object into Redis if the session object has changed (it always updates the expiration separately, however.) This dirty tracking marks the session as needing serialization according to the following rules:
- Calling
session.removeAttribute(key)
always marks the session as dirty (needing serialization.) - Calling
session.setAttribute(key, newAttributeValue)
marks the session as dirty if any of the following are true:previousAttributeValue == null && newAttributeValue != null
previousAttributeValue != null && newAttributeValue == null
!newAttributeValue.getClass().isInstance(previousAttributeValue)
!newAttributeValue.equals(previousAttributeValue)
This feature can have the unintended consequence of hiding writes if you implicitly change a key in the session or if the object's equality does not change even though the key is updated. For example, assuming the session already contains the key "myArray"
with an Array instance as its corresponding value, and has been previously serialized, the following code would not cause the session to be serialized again:
List myArray = session.getAttribute("myArray");
myArray.add(additionalArrayValue);
If your code makes this kind of change, this plugin provides a mechanism by which you can mark the session as dirty in order to guarantee serialization at the end of the HTTP Request. All you have to do is add the __dot_session_attribute_changed__
to the session object, like this:
List myArray = session.getAttribute("myArray");
myArray.add(additionalArrayValue);
session.setAttribute("__dot_session_attribute_changed__", true);
You can disable that behavior by setting the TOMCAT_REDIS_MANUAL_DIRTY_TRACKING_SUPPORT
environment variable to your docker-compose.yml
file:
TOMCAT_REDIS_MANUAL_DIRTY_TRACKING_SUPPORT: 'false'
You can also have your own attribute key to be used to mark the session as dirty. You just need to add the TOMCAT_REDIS_MANUAL_DIRTY_TRACKING_SUPPORT_KEY
environment variable to your docker-compose.yml
file:
TOMCAT_REDIS_MANUAL_DIRTY_TRACKING_SUPPORT_KEY: 'customDirtySessionFlag'
And then, update your code to look like this:
List myArray = session.getAttribute("myArray");
myArray.add(additionalArrayValue);
session.setAttribute("customDirtySessionFlag");
Now, there's another attribute that forces the plugin to persist the session on demand, whether the HTTP Request has finished or not. All you have to do is add the __dot_session_persist_now__
to the session object, like this:
List myArray = session.getAttribute("myArray");
myArray.add(additionalArrayValue);
session.setAttribute("__dot_session_persist_now__", true);
And the plugin will trigger an immediate save on the session object. Keep in mind that this is intended for specific purposes, as constantly persisting the session involves a slight performance hit.
You can also have your own attribute key to be used to persist the session on demand. You just need to add the TOMCAT_REDIS_PERSIST_ON_DEMAND_ATTR
environment variable to your docker-compose.yml
file:
TOMCAT_REDIS_PERSIST_ON_DEMAND_ATTR: 'customPersistSessionOnDemandFlag'
With a persistent session storage, there is going to be the distinct possibility of race conditions when requests for the same session overlap/occur concurrently. Additionally, because the session manager works by serializing the entire session object into Redis, concurrent updating of the session will exhibit last-write-wins behavior for the entire session (not just specific session attributes).
Since each situation is different, the manager gives you several options which control the details of when/how sessions are persisted. Each of the following options may be selected by setting the sessionPersistPolicies"
attribute in your manager declaration in Tomcat's context.xml
file, or via the TOMCAT_REDIS_SESSION_PERSISTENT_POLICIES
environment variable. Unless noted otherwise, the various options are all combinable.
SAVE_ON_CHANGE
: Every time either thesession.setAttribute()
orsession.removeAttribute()
methods are called, the session will be saved.- Note: This feature cannot detect changes made to objects already stored in a specific session attribute -- unless you use the Manual Dirty Tracking or Persist On Demand features explained above.
- Tradeoff: This option will degrade performance slightly as any change to the session will save it synchronously to Redis.
ALWAYS_SAVE_AFTER_REQUEST
: Force saving after every request, whether the manager has detected changes to the session or not. This option is particularly useful if you make changes to objects already stored in a specific session attribute.- Tradeoff: This option make actually increase the likelihood of race conditions if not all of your requests change the session.
The architecture of this project was based on the following project: https://github.com/jcoleman/tomcat-redis-session-manager