Tomcat: How to share data between two applications? - java

Is there any way to share data between to JSP applications using Tomcat 5.5?
The applications are running in the same server.
The shared data should not persist in the system for many time and cannot be stored in cookies because it's bigger than 4Kb.
Thanks!
:)

Just put the data in a file on the disk file system or a database server which both have access to.
Update: as per the update and the comments, the functional requirement seems to boil down to let the webapps on the same server share the same HttpSession (including all of its attributes). In that case, you need to set the emptySessionPath attribute of the <Connector> element in Tomcat's /conf/server.xml to true.
<Connector emptySessionPath="true">

You could look at the crossContext attribute to allow you to share data via the context object. Previous Stackoverflow here:
What does the crossContext attribute do in Tomcat? Does it enable session sharing?

Related

Session sharing across multiple war files [duplicate]

We want to split a working application in two different .war files in order to be able to update one app without affecting the other. Each webapp will have different a UI, different users and different deploy schedule.
The easiest path seems to be sharing the same session, so if app A set session.setAttribute("foo", "bar") app B will be able to see it.
Is there a way to share the HttpSession state for both apps in the same Tomcat instance?
Our app is running on a dedicated Tomcat 5.5, there are no other apps running on the same tomcat instance, so any security concerns regarding the session sharing are not a problem. We're running multiple Tomcat instances, but the balancer is using sticky sessions.
If it's not possible or this session sharing is a really bad idea please leave a comment.
You should not share HttpSession; but you can share other objects. For example, you can register an object via JNDI and access the same object in all your apps (databases use this to pool connections).
One thing to be aware of is that two web apps will use different classloaders. If you want to share objects, they need to use the same version of the class from the same classloader (or else you will get LinkageErrors). That means either putting them in a classloader shared by both web apps (system classpath for example) OR using serialization to effectively drain and reconstitute the object in the right classloader with the correct version of the class.
If you want to use Spring, there's a project called Spring Session:
https://github.com/spring-projects/spring-session
Quoting: "HttpSession - allows replacing the HttpSession in an application container (i.e. Tomcat) neutral way"
For Tomcat 8 I use the following configuration to share a session across 2 webapps:
conf/context.xml
<Context sessionCookiePath="/">
<Valve className="org.apache.catalina.valves.PersistentValve"/>
<Manager className="org.apache.catalina.session.PersistentManager">
<Store className="org.apache.catalina.session.FileStore" directory="${catalina.base}/temp/sessions"/>
</Manager>
...
</Context>
I deploy the same simple webapp twice log.war and log2.war:
/log
/log2
I can now log-in to /log and have the user displayed in /log2, this does not work with the tomcat default configuration.
The session value is set and read:
HttpSession session=request.getSession();
session.setAttribute("name",name);
HttpSession session=request.getSession(false);
String name=(String)session.getAttribute("name");
I used this project as example: https://www.javatpoint.com/servlet-http-session-login-and-logout-example
Most examples/solutions use a in-memory database which requires more setup work:
redis
hazelcast
If the two webapps are so closely coupled that they need to share objects, why are you splitting it in two? Even if you manage them somewhat independently any decent build management system should be able to create a single WAR file for deployment.
A solution like Aaron suggest with JNDI will work, but only if both webapps are running on the same server. If the units are tightly coupled and you are going to be running it on the same server anyway ... might as well have a single WAR.
If you really do want them to stand independently I'd seriously examine the data exchange between the two. Ideally you'd want them to only share relevant data with one another. This data could be passed back and forth via POST (or GET if more appropriate) parameters, you might even consider using cookies.
One way of doing this is described in this blog post: Session sharing in Apache Tomcat
Summary: Add emptySessionPath to the Connector configuration and crossContext to the Context
redison download
conf/context.xml
<Context sessionCookiePath="/">
...
<Manager className="org.redisson.tomcat.RedissonSessionManager"
configPath="${catalina.base}/conf/redisson.yaml"
readMode="REDIS" />
</Context>
conf/redisson.yaml
singleServerConfig:
address: "redis://<host>:6379"
sessionCookiePath="/" makes Tomcat use the same session id for different web apps.
RedissonSessionManager makes session to be persisted in 'shared space'
I was not able to achieve desired result with org.apache.catalina.session.FileStore PersistentManager in shared context.xml, I faced issues with session deserialization in background expiration monitor thread. It failed to deseriazile the session because it was using common classloader without webapp serializable models in classpath. Theoretically PersistentManager could be configured for each web app separately (to have proper classpath) in WEB-INF/context.xml but I failed to make it work.
org.apache.catalina.session.JDBCStore PersistentManage was promising because it expose last_access column for the session so it is not required to deserialize session_data, but it was saving app_name all the time causing same session id to be written as different rows for diffrent web apps. Thus session data was not stored in the shared place.
Spring Session has it`s own way to create session id. I was not able to find solution to force Spring Session to create same session id for different web apps.
Solution with core tomcat session id generation (with ability to generate the same for different web apps and RedissonSessionManager, which store data using session id as the only key and has it's own expiration mechanism) finally worked for me. The solution works perfectly with #SessionScope spring beans.
You can do by taking servlet context by your context root.
For retrieving variable.
request.getSession().getServletContext().getContext("/{applicationContextRoot}").getAttribute(variableName)
For setting variable:
request.getSession().getServletContext().getContext("/{applicationContextRoot}").setAttribute(variableName,variableValue)
Note: Both the applications should deployed in the same server.
Pls let me know if you find any problem
Tomcat 8 :
i had to do : <Context crossContext="true" sessionCookiePath="/"> in conf/context.xml
more details on config attributes here
and then to set the value(like #Qazi's answer):
ServletContext servletContext =request.getSession().getServletContext().getContext("contextPath")
servletContext.setAttribute(variableName,variableValue)
to get the value:
ServletContext servletContext =request.getSession().getServletContext().getContext("contextPath")
servletContext.getAttribute("user");
I developed session state server for tomcat using python.
Due to this I don't need to change the code already written for creating/accessing and destroying session. Also as there is separate server/service which is handling and storing session so not master cluster is needed. There is no session replication (as in tomcat clustering) in this case, rather this is session sharing between web farming.
You should not split your app that way in order by have high availability. You could deploy the whole app on many tomcat instances.

Creating Cookie/Session in Tomcat [duplicate]

I have been looking at solutions for sharing session data between mutliple war files. I came across the following solution http://www.fwd.at/tomcat/sharing-session-data-howto.html
The basic idea of it is that if you have more than one war file, you can set a cookie using the sessionid of the first context that is used.
The cookie can be set using a path that will apply to all contexts/applications.
For example, if I have the following configuration for 3 applications
/myapp/app1
/myapp/app2
/myapp/app3
I can set a cookie as follows
/myapp sessionid.
The sessionid cookie will then be sent to any request with /myapp in the address. This allows the session id to then be used by any of the contexts.
The only problem with this approach is that it was written in 2003 and tested on Tomcat 4.
What are your opinions of this approach? Is there a better way of doing it?
Thanks
That article is indeed heavily outdated.
On Tomcat 5.5 and 6.0 you can just set emptySessionPath attribute to true in the <Connector> element in /conf/server.xml.
<Connector ... emptySessionPath="true">
On Tomcat 7.0 this has changed because this is now configureable from the Servlet 3.0 API on. It's then on Tomcat's side configureable by setting sessionCookiePath to / in <Context> element in any responsible context.xml file.
<Context ... sessionCookiePath="/">
As said, there's a new Servlet 3.0 API which allows you to configure the session cookie through the standard API. You can do it either declaratively by adding the following to the web.xml:
<session-config>
<cookie-config>
<path>/</path>
</cookie-config>
</session-config>
or programmatically by SessionCookieConfig which is available by ServletContext#getSessionCookieConfig().
getServletContext().getSessionCookieConfig().setPath("/");
You could do this in ServletContextListener#contextInitialized() or HttpServlet#init().
See also:
Tomcat 5.5 HTTP connector documentation
Tomcat 6.0 HTTP connector documentation - mentions potential security hole
Tomcat 7.0 context documentation
To my knowledge there is no direct way to do this, you can however use a domain level cookie if these contexts share the same domain.
You can either put the data in the cookie (I don't recommend that).
Or put a secured session Id that you can use to access some form of storage (DB or distributed cache etc) to retrieve the data you need.
If the amount of data is not astronomical and the data itself isn't changing too rapidly, you might want to consider using JNDI. This solution was designed exactly for what you are looking for.
You can have a look at official documentation or this post to tomcat-user mailing list for references & examples.
For Tomcat 8 I use the following configuration to share a session across 2 webapps:
conf/context.xml
<Context sessionCookiePath="/">
<Valve className="org.apache.catalina.valves.PersistentValve"/>
<Manager className="org.apache.catalina.session.PersistentManager">
<Store className="org.apache.catalina.session.FileStore" directory="${catalina.base}/temp/sessions"/>
</Manager>
...
</Context>
I deploy the same simple webapp twice log.war and log2.war:
/log
/log2
I can now log-in to /log and have the user displayed in /log2, this does not work with the tomcat default configuration.
The session value is set and read:
HttpSession session=request.getSession();
session.setAttribute("name",name);
HttpSession session=request.getSession(false);
String name=(String)session.getAttribute("name");
I used this project as example: https://www.javatpoint.com/servlet-http-session-login-and-logout-example
Most examples/solutions use a in-memory database which requires more setup work:
redis
hazelcast

Share session data between 2 subdomains

I am using tomcat 7.0.6 with jdk 1.6.0_22
Is it possible to share session data between 2 different domains with a common subdomain such as a.mydomain.com and b.mydomain.com ?
With the default java servlet a.mydomain.com and b.mydomain.com get different sessions, but is it not possible to create a shared session for all subdomains in mydomain.com?
The problem is also that I don't directly control the commen subdomain (mydomain.com) so I can't serve any servlets from mydomain.com
Set the sessionCookieDomain attribute of <Context> element of the webapp in question to .mydomain.com (note the leading dot, this is very important). This will allow the webbrowser to share cookies among all subdomains.
If you actually have multiple webapp contexts and you want to share the session between them as well, then you also need to set sessionCookiePath attribute of <Context> element of the webapps in question to /.
In a nutshell:
<Context sessionCookieDomain=".mydomain.com" sessionCookiePath="/">
See also:
Tomcat 7 configuration reference - The Context container
For Tomcat 6 users: note that this was introduced in Tomcat 6.0.27. For those who can't upgrade, you would need a Valve to modify the cookie domain, eventually in combination with emptySessionPath attribute in <Connector> element in /conf/server.xml for the case that you've multiple webapp contexts for which you'd like to share the session.
Servlet Spec 3.0 (which is what Tomcat 7 supports) allows this by calling setDomain on SessionCookieConfig.
Details here:
http://download.oracle.com/javaee/6/api/javax/servlet/SessionCookieConfig.html
You get SessionCookieConfig progammatically at webapp init time with a ServletContextListner - or you should be able to set it the value in web.xml.
You can create your own session implementation using cookies. Sessions are handled (in most server side languages) using cookies and server side database or files. You create a token (using md5 on timestamp) and save it in file or database along with all session variables.

How to maintain session attributes in a multitenant architecture?

In case of SaaS applications, where the same server plays host to multiple applications. How are session attributes maintained? To elaborate the question:
AppA, and AppB are hosted on the same machine, I now create UserA for AppA and UserB for AppB. AppA and AppB belong to different organizations so they are not linked. Some details about the user are stored at http session level (until the session times out). So now if I log in to both AppA and AppB from the same browser using different tabs, I may end up seeing some of UserA/AppA details on the UserB/AppB screen or vice-versa. How can such a problem be solved?
One solution I can think is to create subdomains like appa.example.org and appb.example.org. Is there any other/better way?
Normally you will not see details from one app in another app.
When a session is created it is created inside the web application and identified by a key. This session-id is what is stored in a cookie or passed in some other way to identify which session object to refer to on the next request.
If you would present this session id to another webapp it won't find the attributes because they live in the other webapp.
Now, that is 'normally'. In practice this can be configured in all directions, like storing all atributes in the cookie (very useful in extreme failover scenarios), storing the session in a shared memcached layer or shared database table (then you would get the same object back in the other application of course), and so on, and so on.
The best solution I've come up with was inspired by this question. I've pointed multiple contexts to the same war file:
<Service ...>
<Engine ...>
<Host ... autoDeploy="false">
<Context docBase="myapp.war" path="/tenant1"/>
<Context docBase="myapp.war" path="/tenant2"/>
</Host>
</Engine>
</Service>
This is essentially the same as making copies of myapp.war called tenant1.war, tenant2.war, etc. Each tenant is technically running thier own webapp, even though they're all running the same code.
If you have users with credentials on two or more tenants, they can log on to both at the same time, and each webapp will get its own session, because the JSESSIONID cookies containing the session ID are each tied to a specific context path.
There are drawbacks to this approach. For one, all the classes in the war file get reloaded for each tenant, so I'll have to keep an eye on PermGen space. For another, I'll have to edit server.xml every time a new tenant comes along. Have you found a better solution?

Any way to share session state between different applications in tomcat?

We want to split a working application in two different .war files in order to be able to update one app without affecting the other. Each webapp will have different a UI, different users and different deploy schedule.
The easiest path seems to be sharing the same session, so if app A set session.setAttribute("foo", "bar") app B will be able to see it.
Is there a way to share the HttpSession state for both apps in the same Tomcat instance?
Our app is running on a dedicated Tomcat 5.5, there are no other apps running on the same tomcat instance, so any security concerns regarding the session sharing are not a problem. We're running multiple Tomcat instances, but the balancer is using sticky sessions.
If it's not possible or this session sharing is a really bad idea please leave a comment.
You should not share HttpSession; but you can share other objects. For example, you can register an object via JNDI and access the same object in all your apps (databases use this to pool connections).
One thing to be aware of is that two web apps will use different classloaders. If you want to share objects, they need to use the same version of the class from the same classloader (or else you will get LinkageErrors). That means either putting them in a classloader shared by both web apps (system classpath for example) OR using serialization to effectively drain and reconstitute the object in the right classloader with the correct version of the class.
If you want to use Spring, there's a project called Spring Session:
https://github.com/spring-projects/spring-session
Quoting: "HttpSession - allows replacing the HttpSession in an application container (i.e. Tomcat) neutral way"
For Tomcat 8 I use the following configuration to share a session across 2 webapps:
conf/context.xml
<Context sessionCookiePath="/">
<Valve className="org.apache.catalina.valves.PersistentValve"/>
<Manager className="org.apache.catalina.session.PersistentManager">
<Store className="org.apache.catalina.session.FileStore" directory="${catalina.base}/temp/sessions"/>
</Manager>
...
</Context>
I deploy the same simple webapp twice log.war and log2.war:
/log
/log2
I can now log-in to /log and have the user displayed in /log2, this does not work with the tomcat default configuration.
The session value is set and read:
HttpSession session=request.getSession();
session.setAttribute("name",name);
HttpSession session=request.getSession(false);
String name=(String)session.getAttribute("name");
I used this project as example: https://www.javatpoint.com/servlet-http-session-login-and-logout-example
Most examples/solutions use a in-memory database which requires more setup work:
redis
hazelcast
If the two webapps are so closely coupled that they need to share objects, why are you splitting it in two? Even if you manage them somewhat independently any decent build management system should be able to create a single WAR file for deployment.
A solution like Aaron suggest with JNDI will work, but only if both webapps are running on the same server. If the units are tightly coupled and you are going to be running it on the same server anyway ... might as well have a single WAR.
If you really do want them to stand independently I'd seriously examine the data exchange between the two. Ideally you'd want them to only share relevant data with one another. This data could be passed back and forth via POST (or GET if more appropriate) parameters, you might even consider using cookies.
One way of doing this is described in this blog post: Session sharing in Apache Tomcat
Summary: Add emptySessionPath to the Connector configuration and crossContext to the Context
redison download
conf/context.xml
<Context sessionCookiePath="/">
...
<Manager className="org.redisson.tomcat.RedissonSessionManager"
configPath="${catalina.base}/conf/redisson.yaml"
readMode="REDIS" />
</Context>
conf/redisson.yaml
singleServerConfig:
address: "redis://<host>:6379"
sessionCookiePath="/" makes Tomcat use the same session id for different web apps.
RedissonSessionManager makes session to be persisted in 'shared space'
I was not able to achieve desired result with org.apache.catalina.session.FileStore PersistentManager in shared context.xml, I faced issues with session deserialization in background expiration monitor thread. It failed to deseriazile the session because it was using common classloader without webapp serializable models in classpath. Theoretically PersistentManager could be configured for each web app separately (to have proper classpath) in WEB-INF/context.xml but I failed to make it work.
org.apache.catalina.session.JDBCStore PersistentManage was promising because it expose last_access column for the session so it is not required to deserialize session_data, but it was saving app_name all the time causing same session id to be written as different rows for diffrent web apps. Thus session data was not stored in the shared place.
Spring Session has it`s own way to create session id. I was not able to find solution to force Spring Session to create same session id for different web apps.
Solution with core tomcat session id generation (with ability to generate the same for different web apps and RedissonSessionManager, which store data using session id as the only key and has it's own expiration mechanism) finally worked for me. The solution works perfectly with #SessionScope spring beans.
You can do by taking servlet context by your context root.
For retrieving variable.
request.getSession().getServletContext().getContext("/{applicationContextRoot}").getAttribute(variableName)
For setting variable:
request.getSession().getServletContext().getContext("/{applicationContextRoot}").setAttribute(variableName,variableValue)
Note: Both the applications should deployed in the same server.
Pls let me know if you find any problem
Tomcat 8 :
i had to do : <Context crossContext="true" sessionCookiePath="/"> in conf/context.xml
more details on config attributes here
and then to set the value(like #Qazi's answer):
ServletContext servletContext =request.getSession().getServletContext().getContext("contextPath")
servletContext.setAttribute(variableName,variableValue)
to get the value:
ServletContext servletContext =request.getSession().getServletContext().getContext("contextPath")
servletContext.getAttribute("user");
I developed session state server for tomcat using python.
Due to this I don't need to change the code already written for creating/accessing and destroying session. Also as there is separate server/service which is handling and storing session so not master cluster is needed. There is no session replication (as in tomcat clustering) in this case, rather this is session sharing between web farming.
You should not split your app that way in order by have high availability. You could deploy the whole app on many tomcat instances.

Categories

Resources