Coherence*Web: Sharing an httpSession Among Applications in Different Oracle WebLogic Clusters
by Jordi Villena
October 2013
An easy approach to extending Coherence*Web to enable session sharing.
Downloads
Standard Configuration
Coherence*Web permits a full session offload so Oracle WebLogic instances can be focused on processing Oracle WebLogic requests; however, in some cases, http sessions are large, and there is a trade-off between the benefits of offloading the whole user session, and reducing the network usage by offloading only the object that needs to be shared among the different web applications. Coherence*Web and Oracle Coherence itself are built with low-latency, non-blocking I/O as a prime function, but it still takes time and bandwidth to transmit when working with large volumes of data. In such situations, it would be useful to be able to select which session attributes must be stored in the local storage of the Oracle WebLogic instances, and which should be leveraged to an Oracle Coherence distributed cache.
The storage configuration in Coherence*Web can be configured in a wide variety of ways. To balance between offloading Oracle WebLogic instances and mitigating the performance lost by storing the whole http session in an Oracle Coherence dedicated node, a subset of session attributes can be stored in the Oracle Coherence external cache via the use of a custom session distribution controller.
Another reason to offload some session attributes from Oracle WebLogic is to share this set of attributes with web applications deployed in different Oracle WebLogic clusters. A session distribution controller allows us to add logic to deduce whether a session attribute must be stored in the distributed cache.
The following sample shows a session distribution controller that stores session attributes whose name begins with "shared_" in the distributed Coherence Layer, as well as any other attribute in the local storage of the Oracle WebLogic managed instance that handles the user request:
package com.beax.cohweb;
import com.tangosol.coherence.servlet.HttpSessionCollection;
import com.tangosol.coherence.servlet.HttpSessionModel;
import com.tangosol.coherence.servlet.HttpSessionCollection.SessionDistributionController;
public class CustomSessionDistributionController implements
SessionDistributionController {
@Override
public void init(HttpSessionCollection arg0) {
}
@Override
public boolean isSessionAttributeDistributed(HttpSessionModel arg0, String arg1) {
// Distribute attributes starting by shared_
return arg1.startsWith("shared_");
}
@Override
public boolean isSessionDistributed(HttpSessionModel arg0) {
// All the sessions will be distributed
return true;
}
}
Once the custom session distribution controller has been created, it must be added to all the web applications that will use it. A reference to it must also be added as JAVA OPTION in the Oracle WebLogic instances in which web applications have been deployed:
coherence.distributioncontroller.class=com.beax.cohweb.CustomSessionDistributionController
This parameter can also be specified as a context parameter in the web.xml descriptor file:
<context-param>
<param-name>coherence-sessioncollection-class</param-name>
<param-value>com.tangosol.coherence.servlet.SplitHttpSessionCollection</param-value>
</context-param>
The use of a sessionDistributionController requires extra configuration to ensure the synchronization of the shared information, and to ensure the visibility of the locally stored session attributes. A specific Oracle Coherence configuration must be put in place for this purpose; it must:
- Enable a locking mechanism at the application or member level
- Disable the Coherence Near Topology for Coherence*Web caches
- Disable coherence-sticky-sessions optimization
- Enable storage in the appropriate nodes: because session attributes to be distributed should reside only in the Coherence-dedicated nodes, storage should be enabled only in those nodes:
Storage configuration for WebLogic nodes
tangosol.coherence.session.localstorage=false
tangosol.coherence.distributed.localstorage=false
Storage configuration for Coherence Processes
tangosol.coherence.session.localstorage=true
tangosol.coherence.distributed.localstorage=true
Tricky Configuration
While it might seem strange at first glance, this situation could in fact be quite common: you need to share only a few sets of session attributes among a set of applications deployed in different Oracle WebLogic clusters.
The last customer I worked with wished to configure an out-of-process Coherence cluster to store some of the session attributes of web applications deployed among the Oracle WebLogic clusters. The objective was to be able to share such attributes among the different web applications.
When this configuration must be spread among applications deployed in different Oracle WebLogic clusters, certain limitations arise that are not currently handled by Coherence*Web:
- Disabling coherence-sticky-sessions optimization
- Ensuring Coherence Near Topology is not used
- Extending sessions over more than one WebLogic Cluster via cookie management
Disabling Coherence-Sticky-Sessions Optimization
Using a Custom Session Distribution Controller (CSDC), Coherence*Web can be optimized to clean sessions from Oracle WebLogic Server (WLS) nodes that should not handle further requests for a specific user session. Because this optimization will clean user session objects that might be needed in further user requests when Coherence*Web is used to share session attributes among applications deployed in more than one Oracle WebLogic cluster, this optimization must be disabled.
Oracle documentation states that the coherence-sticky-sessions parameter (sticky-session- optimization) must be set to "true" when a custom Session Distribution controller is used.
This parameter enables some optimization to enhance the memory management in the WLS nodes when a sessionDistributionController is used, cleaning any attribute of the session stored in WLS nodes when new requests are redirected to different WLS sessions. In our scenario, because we plan to accept requests from different WLS clusters, different WLSs will attend user requests for the same user session; therefore, in order to prevent cleaning the session in WLS nodes before needed this optimization must be disabled (not set to "true".)
Figure 1
Ensure Coherence Near Topology is Not Used
Using the Coherence Near Topology, Coherence cannot ensure all the copies are synchronized in some boundary situations [1]. To be sure that the Coherence Near Topology is not used, the following parameter needs to be set to "false":
coherence.session.optimizeModifiedSessions=false
Note: Boundary Situation
A situation in which a session attribute could be requested by one Oracle WebLogic instance, while it is updated by a different Oracle WebLogic instance. Near Topology has an event mechanism system that ensures all copies of an object are synchronized at once. However, in case of overload situations, this event mechanism cannot ensure that all the copies of an object are up-to-date when they're accessed from different nodes of the front layer using the Coherence Near Topology.
If previous parameters do not prevent the creation of local copies of the objects inside the local storage of the WLS nodes, the following change should be made to the session-cache-config.xml file used by the WLS nodes:
Change this piece of the xml:
<!--
The clustered cache used to store Session attributes.
-->
<cache-mapping>
<cache-name>session-storage</cache-name>
<scheme-name><span style="color:red;">session-near</scheme-name>
</cache-mapping>
To this:
<!--
The clustered cache used to store Session attributes.
-->
<cache-mapping>
<cache-name>session-storage</cache-name>
<scheme-name><span style="color:red;">session-distributed</scheme-name>
</cache-mapping>
</span></span>
Note:
Boundary Situation: A situation in which a session attribute could be requested by one Oracle WebLogic instance, while it is updated by a different Oracle WebLogic instance. Near Topology has an event mechanism system that ensures all copies of an object are synchronized at once. However, in case of overload situations, the event mechanism cannot ensure that all the copies of an object are up-to-date when they're accessed from different nodes of the front layer using the Coherence Near Topology.
Cookie Management Extending Sessions Over More Than One WebLogic Cluster
Coherence*Web can be used to share user sessions among different WebLogic clusters, but when you need to share only part of the user session among the applications deployed in different WebLogic clusters, some specific configurations must be put in place because the cookie generated by WebLogic is used at two levels:
Coherence*Web can be used to share user sessions among different WebLogic clusters, but when you need to share only part of the user session among the applications deployed in different WebLogic clusters, some specific configurations must be put in place because the cookie generated by Oracle WebLogic is used at two levels:
- Front web layer: WebLogic proxy plugin reads the JSESSION cookie provided by the browser to identify which WebLogic managed instance owns the primary/secondary copy of the session.
- Oracle WebLogic/Coherence layer: When the request reaches the correct Oracle WebLogic managed instance, if Coherence*Web has been configured, Oracle Coherence will re-read the JSESSION cookie to identify the user session. If a custom SessionDistributionController were configured, when a session attribute needs to be retrieved:
- It will be retrieved from the Oracle WebLogic local storage if it is stored locally
- It will be retrieved from the Oracle Coherence distributed layer if it is stored in the shared layer
How Coherence*Web Uses the Oracle WebLogic Cookie
Coherence*Web relies on the cookie generated by the WLS to identify the user session for each user request. This cookie works in the following way:
- WLS posts a cookie in response to the first user request related to a new session. The cookie includes the following information:
<SESSION_ID>!<JVM_ID1>!<JVM_ID2>
Where:- <SESSION_ID>: Identifies the user session when the request reaches the WebLogic instance.
- <JVM_ID1>: Is used by the WebLogic proxy plugin in the front Web Server to identify the WebLogic instance that owns the primary copy of the session.
- <JVM_ID2>: Is used by the WebLogic proxy plugin in the front Web Server to identify, in environments with session failover enabled, the WebLogic instance that owns the secondary copy of the session. This identifier will be used if the WLS instance that owns the primary copy of the session is not reachable. (Session failover is not enabled for the Picasso project.)
- In further requests, <SESSION_ID> will remain constant until the user session is dropped. <JVM_ID1> and <JVM_ID2> will be updated in case any former Oracle WebLogic instance goes down or becomes unreachable.
- The cookie is used at two levels:
- Proxy plugin installed in the Apache Web Server uses the JVM Identifier stored in the cookie to forward each user request to the WLS instance that owns the primary copy of the session.
- When the request is forwarded to an Oracle WebLogic instance, the instance identifies the user session using the Session Identifier stored in the cookie.
The Problem
Think of the case in which one request is forwarded to one instance on one of the Oracle WebLogic clusters, and further requests are forwarded to an application deployed in a different Oracle WebLogic cluster.
- Using the same cookie name for the JSESSION cookie, among applications that need to share some session attributes:
- Coherence*Web will be able to correlate sessions among requests to the different applications.
- The Oracle WebLogic proxy plugin won't be able to identify the correct managed instance inside the WebLogic cluster where the application has been deployed. This is because each time a new request is redirected to an application deployed in a different cluster, the portion of the cookie containing the information related to the Java Virtual Machine (JVM) identifier of the Oracle WebLogic managed instance that handles the request will be overwritten with the information of the managed instance of the current Oracle WebLogic cluster, losing the reference to any other managed instance from the other Oracle WebLogic cluster.
- Using different cookie names for the JSESSION cookie in each of the applications: This configuration will prevent the overwrite of the JVM identifiers, because each application will use a different cookie, but the session identifier will be also different in each cookie, so Coherence*Web won't be able to correlate sessions for each application related with a single user.
The Solution
The scenario and requirement above lead us to a situation in which the standard cookie mechanism cannot handle two different applications, deployed on two different clusters, sharing the same user session, when the user session is split, storing part of the session in a distributed cache and the remaining session attributes in the local storage of the WLS instances.
The simplest way to solve this problem is to store the whole user session in the distributed cache, but, in some cases, this configuration could have a big impact on the network usage because of the size of the sessions. In such a case, the scope of the session attributes that are not expected to be shared should remain at application level. This will prevent having collisions between session attributes with the same name among the different applications. This configuration can be reached by leveraging the Session Attribute Scoping feature of Coherence*Web.
The best way to deal with this issue, however, is to configure different session cookies in each of the applications that will share part of the session, and to ensure that all of them use the same session identifier. This can be achieved by planting all the cookies used by any of the applications that are configured to share attributes of the session only in the first user request made to any of these applications:
Figure 2
Sample configuration
The following configuration must be put in place for this purpose:
- 1. Configure each application to use its own session cookie by editing the weblogic.xml of each web application to specify a different cookie name:
<wls:session-descriptor>
<wls:timeout-secs>3000</wls:timeout-secs>
<wls:cookie-name>WLS1SID</wls:cookie-name>
<wls:cookie-path>/</wls:cookie-path>
<!-- wls:cookie-max-age-secs>60</wls:cookie-max-age-secs> -->
<wls:persistent-store-type>MEMORY</wls:persistent-store-type>
</wls:session-descriptor>
- 2. Install a J2EE servlet filter to plant the cookies when needed:
CODE OF THE SERVLET FILTER
package com.beax.sample.cohweb;
import java.io.IOException;
import java.util.StringTokenizer;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Servlet Filter implementation class CookieFilter
*/
public class CookieFilter implements Filter {
public CookieFilter() {
}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// add cookies if needed
cookieWork(request, response);
// pass the request along the filter chain
chain.doFilter(request, response);
}
public void init(FilterConfig fConfig) throws ServletException {
}
/*
* Add cookies for any related environment
*/
private void cookieWork(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
String localCookieName = System.getProperty("cookie.name");
StringTokenizer cookieList = new StringTokenizer(System.getProperty("cookie.list"), ";");
HttpServletRequest myReq = (HttpServletRequest) request;
HttpServletResponse myResp = (HttpServletResponse) response;
Cookie [] cookies = myReq.getCookies();
HttpSession ses = myReq.getSession(true);
String sessId = ses.getId();
String cookie_sessId = null;
while(cookieList.hasMoreElements()) {
boolean createCookie = true;
String nextCookie = (String)cookieList.nextElement();
//
// Only create cookies that are not created automatically by WLS.
//
if (nextCookie.equals(localCookieName)) {
createCookie= false;
} else {
if (cookies != null) {
//
// Check existing cookies:
// - If any cookie of the "cookie.list" does not exist or
// if the sessId does not match, cookie must be created.
//
for(int j=0; j<cookies.length; j++) {
Cookie cookie = cookies[j];
if (cookie.getName().equals(nextCookie)) {
int sep = cookie.getValue().indexOf("!");
if (sep > 0) {
cookie_sessId = cookie.getValue().substring(0, sep);
if (sessId.startsWith(cookie_sessId)) {
createCookie = false;
break;
}
}
}
}
} else {
//
// Create cookie if it does not exist
//
createCookie = true;
}
}
if (createCookie) {
Cookie cookie = new Cookie(nextCookie, sessId + "!");
cookie.setPath("/");
//cookie.setMaxAge(60);
myResp.addCookie(cookie);
}
}
}
}
REFERENCE TO THE SERVLET FILTER IN web.xml DESCRIPTOR
<filter>
<display-name>CookieFilter</display-name>
<filter-name>CookieFilter</filter-name>
<filter-class>sample.coherence.CookieFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CookieFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Notes:
- In order for the planted to span the life of the session, do not define any any expiration time.
- The cookie path must match the path of the cookie used by the Oracle WebLogic instance.
- To give the servlet filter the information related to the cookies to be planted, define two parameters in the startup JAVA_OPTIONS of each of the Oracle WebLogic Servers involved. For example:
WebLogic Cluster 1 nodes
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dcookie.name=WLS1SID
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dcookie.list=WLS1SID;WLS2SID;WLSNSID
WebLogic Cluster 2 nodes
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dcookie.name=WLS2SID
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dcookie.list=WLS1SID;WLS2SID;WLS3NID
WebLogic Cluster N nodes
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dcookie.name=WLSNSID
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dcookie.list=WLS1SID;WLS2SID;WLSNSID
During a session-start-event the J2EE servlet filter will, on applications using a WLS1SID cookie, plant a session cookie for the other applications, and vice-versa. This ensures that the user will have the same session ID on all related applications:
First request to an applicative resource served by the application which uses cookie WLS1SID
- Cookie autogenerated by WebLogic Server:
WLS1SID: bhYgZacv61QaJ6Gh4SV9MjtaTu3BdIoE1T98i2rNJB2y4XsypU3r!-202330354!1365442637731
- Cookies generated by the developed Servlet Filter:
WLS2SID: bhYgZacv61QaJ6Gh4SV9MjtaTu3BdIoE1T98i2rNJB2y4XsypU3r!-202330354!1365442637731
WLSNSID: bhYgZacv61QaJ6Gh4SV9MjtaTu3BdIoE1T98i2rNJB2y4XsypU3r!-202330354!1365442637731
Further requests forwarded to the application using the cookie WLS2SID or WLSNSID will update the JVM identifiers of the corresponding cookie.
First request to a resource served by the application which uses cookie WLSNSID
- Updated cookie by WebLogic Server:
WLSNSID: bhYgZacv61QaJ6Gh4SV9MjtaTu3BdIoE1T98i2rNJB2y4XsypU3r!934311264!1365442637731
Conclusion
This article illustrates how easy it is to extend the possibilities offered by Coherence*Web, enabling session sharing in circumstances in which that might otherwise have seemed unlikely.
Acknowledgments
Many thanks to my colleagues Jonathan Birch and Stefano Mantini for their original thoughts around how to implement this solution.
About the Author
Jordi Villena is a SOA Solution Architect with Oracle, where he is focused on the latest middleware technologies and products. He has a particular interest in Oracle WebLogic, Oracle Coherence, and Oracle Tuxedo, and also enjoys working with Oracle Service Bus, Oracle WebLogic Portal, and JRockit. Jordi came to Oracle from BEA, and has lately been involved in several strategic Oracle SOA Suite and Oracle ADF projects.LinkedIn