To use session replacement in a web application, we can add session replacement library in
the WEB-INF/lib directory of the web application. In order to be sure that the session
is replaced before any filters are triggered, add SesssionFilter as the first one
in pipeline of filters.
Note that when using this method, there is no support for many standard HttpSession features.
Most notably, HttpSessionListener support is missing.
We can add session replacement to all web applications running in a servlet container. To do this, container JVM has to be started with the session replacement agent, and session replacement library must be available in the classpath of each web application. Following paragraphs explain how to do it in many popular application servers.
This method enables all features of session replacement library.
In following examples, it is assumed that SESSION_PATH points to directory where session replacement distribution archive was unpacked.
Agent can be added as via JAVA_OPTS environment variable.
export JAVA_OPTS=-javaagent:SESSION_PATH/session-agent.jar
# on Windows: set JAVA_OPTS=-javagent:SESSION_PATH/session-agent.jarStandard way of doing this is to modify the bootstrap script configuration file. When using
standalone script, the default name of that file is standalone.conf when using shell, or,
on Windows, it is standalone.conf.bat or standalone.conf.ps1. Different file can be specified
using RUN_CONF environment variable.
In JBoss, the classes loaded in system class path are not available to web application unless
they are explicitly published. For example java.* JDK classes are explicitly published, but
com.amadeus.session are not. There are several ways of making them available, and the
chosen approach is to use global module called com.amadeus.session-replacement.
As the library needs to be installed as a global Wildlfy/JBoss module, we suggest one of the following options.
The global support is enabled in jboss configuration file:
Unpack the module archive in the root of your Wildfly installation. This will create a module
at following location modules/system/layers/base/com/amadeus/session.
Modify standalone.xml to load requested module as global module. E.g.
<subsystem xmlns="urn:jboss:domain:ee:1.2" >
<global-modules>
<module name="com.amadeus.session" slot="main" />
</global-modules>
</subsystem>If you want to activate this feature on per-application basis, you should use
jboss-deployment-structure.xml mechanism:
<jboss-deployment-structure>
<deployment>
<dependencies>
<module name="com.amadeus.session" />
</dependencies>
</deployment>
</jboss-deployment-structure>You may unpack the module in the location of your choice. Assuming JBOSS_HOME is set to installation
directory of your Wildfly/JBoss server, and that SESSION_PATH is location where you unpacked the module,
start Wildfly/JBoss using
$JBOSS_HOME/bin/standalone.sh -mp $JBOSS_HOME/modules:$SESSION_PATH/modules
# on Windows: %JBOSS_HOME%\bin\standalone.bat -mp %JBOSS_HOME%\modules;%SESSION_PATH%\modules
# on Windows: $JBOSS_HOME\bin\standalone.ps1 -mp $JBOSS_HOME\modules;$SESSION_PATH\modulesURL session propagation is not supported in JBoss 6.x. It works in versions 7.x and in all Wildfly verions.
TODO explain configuration using system properties in standalone.xml
The session replacement library can be added to classpath using standard CLASSPATH environment
variable or it can be added via command line option --lib=SESSION_PATH/session-replacement-shaded.jar.
Agent can be added as a command line option or, when using jetty.sh script, via JAVA_OPTS
environment variable.
export CLASSPATH=$CLASSPATH:SESSION_PATH/session-replacement-shaded.jar
# on Windows: set CLASSPATH=%CLASSPATH%;SESSION_PATH/session-replacement-shaded.jar
java -javagent:SESSION_PATH/session-agent.jar -jar ../start.jar
java -javagent:SESSION_PATH/session-agent.jar -jar ../start.jar --lib=SESSION_PATH/session-replacement-shaded.jar
export JAVA_OPTS=-javagent:SESSION_PATH/session-agent.jar
bin/jetty.sh --lib=SESSION_PATH/session-replacement-shaded.jarIf the JAVA_OPTS variable is specified in one of the following locations it will be loaded
automatically by the script: /etc/default/jetty, /etc/jetty or ~/.jettyrc.
If the script jetty.sh was renamed, the name of configuration files change to file name part without
extension of the renamed script. E.g. if script was renamed to myjetty.sh, the name of the
configuration file is /etc/default/myjetty, /etc/myjetty or ~/.myjettyrc
In following instructions CATALINA_HOME represents location where Tomcat is installed, and
CATALINA_BASE represents base location of your server. By default they are same, but you may
override the values using respective environment variables.
To start agent, set or add to environment variable CATALINA_OPTS. For example:
CATALINA_OPTS=-javagent:SESSION_PATH/session-agent.jarYou may add this variable into setenv.sh (or *nix) or setenv.bat (on Windows) files located either
in CATALINA_BASE/bin or CATALINA_HOME/bin directories.
You may add session replacement library to class path in several ways.
Copy the session-replacement-shaded.jar into CATALINA_HOME/lib directory. This method assumes
that you haven't modified common.loader line in CATALINA_BASE/conf/catalina.properties.
Copy the library to directory of your choice. Modify configuration of common
class loader (common.loader) to point to directory where the library is located.
For example:
common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar,SESSION_PATH/*.jarIf you have session replacement present in your webapp class path (WEB-INF/lib), and if the common library is present, the one in webapp this library has precedence over the common one, if the common one is present. This may generate issues at runtime. Usually this manifested with class cast exceptions. It is recommended to give preference to the common classes. See Tomcat Class Loader for details.
Tomcat 6.x supports Servlet 2.5 specification. This environment has few limitations and prerequisites:
- your web application must have at least one
Filter, otherwise there will be no session replacement. - if you can modify your web application, add
com.amadeus.session.servlet.SessionFilteras first filter of your web application. - to support sending events to
HttpSessionListenerandHttpSessionAttributeListener, start the agent withsession=2.xargument or set system propertycom.amadeus.session.servlet.api=2.x - URL session propagation is not supported in Tomcat 6.x.
The library is fully compatible with Spring library, and there is no need additional for any special configuration. Note, however, that you can't use Spring Session and Http Session Replacement at the same time.
The library replaces containers own implementation for Servlet PrintWriter with
a standard PrintWriter implementation. In undertow based containers like
Wildfly and JBoss EAP this may have impact on servlet performance. It is
possible to deactivate this replacement and delegate calls to container's own
implementation. This is done by setting system property or servlet
initialization parameter com.amadeus.session.delegate.writer to true.
Redis configuration can be passed in arguments of the session agent:
In JAVA_OPTS
-javagent:session-agent.jar=provider=redis,distributable=true
-Dcom.amadeus.session.namespace=APPNAME
-Dcom.amadeus.session.redis.mode=SINGLE
-Dcom.amadeus.session.redis.port=6380
-Dcom.amadeus.session.redis.ssl=true
-Dcom.amadeus.session.redis.tls=[TLSv1, TLSv1.1, TLSv1.2]
-Dcom.amadeus.session.redis.expiration=ZRANGE
-Dcom.amadeus.session.sticky=false
-Dcom.amadeus.session.redis.host=localhost
-Dcom.amadeus.session.redis.password="password"
Redis can be configured via ServletContext initialization parameters:
<web-app>
...
<!-- Redis server -->
<context-param>
<param-name>com.amadeus.session.redis.host</param-name>
<param-value>localhost</param-value>
</context-param>
<!-- Expiration startegy -->
<context-param>
<param-name>com.amadeus.session.redis.expiration</param-name>
<param-value>ZRANGE</param-value>
</context-param>
...
</web-app>Doesn't work with redis CLUSTER
When using NOTIF expiration strategy, redis needs to be started with expiration events activated. That can be done either in command line:
redis --notify-keyspace-events Exor in redis.conf:
notify-keyspace-events Ex
For more details, see redis keyspace notifications.
When using single Redis instance or twemproxy, the only required configuration is host address of server and, when not using default port 6379, the Redis port. Default ssl is false and password is optional.
See redis sentinel for more information. It is recommended to
have at least 3 sentinels and one or two slaves for the master. The configuration should provide
list of sentinels in host or com.amadeus.session.redis.host configuration element and
master address in master or com.amadeus.session.redis.master configuration element.
See redis cluster tutorial for more information.
When configuring for Redis Cluster, host or com.amadeus.session.redis.host configuration element
contains list of cluster nodes of which at least one must be reachable.
When using redis cluster mode, if some of hash slots are not covered (i.e. when one master goes down), other masters will be replying with CLUSTERDOWN error until all slots are covered (e.g. new master is elected). This will result in full outage of the session storage until everything is fine. Fortunately, redis cluster can be configured to give results even if there is no full coverage. NOTIF expiration strategy doesn't work with cluster mode.
# By default Redis Cluster nodes stop accepting queries if they detect there
# is at least an hash slot uncovered (no available node is serving it).
# This way if the cluster is partially down (for example a range of hash slots
# are no longer covered) all the cluster becomes, eventually, unavailable.
# It automatically returns available as soon as all the slots are covered again.
#
# However sometimes you want the subset of the cluster which is working,
# to continue to accept queries for the part of the key space that is still
# covered. In order to do so, just set the cluster-require-full-coverage
# option to no.
#
cluster-require-full-coverage no