What's the recommended approach to intercepting session.getAttribute() and session.setAttribute()? This is in a Spring based application so something AOP-based would be great. I'd like to avoid having to override core Tomcat classes if possible.
Update: I want to store the actual serialized attributes in a web service.
I am not familiar with AOP or Spring (or Tomcat). :) But I am familliar with Java
The way I do it is set up a filter, and replace the request variable with my own object
request = new MyRequest(request);
Then override getSession() and getSession(boolean) to return an instance of MySession
the javax.servlet.HttpServletRequest and javax.servlet.HttpSession classes are Java EE standard and not Tomcat specific.
You could implement your own session org.apache.catalina.Manager and swap it into Tomcat's configuration, although the interface looks rather lengthy - so perhaps look at extending ManagerBase or StandardManager first.
As an alternative, register a HttpSessionAttributeListener to be notified whenever a session attribute is added/removed/updated. This won't change the default storage mechanism - the session data will still be kept in-memory as well - but it would let you persist the data with an alternative mechanism as well.
Related
I'm trying to use Liferay shared session attributes.
I would like to to use the same attribute on two different portlets in different WAR file-s on different URL-s using the following code based on:
Liferay 7 not able to set the global session attribute
Value I want to save: A single string
Setting in portlet 1:
String sharedKey = "LIFERAY_SHARED_" + key;
HttpSession session = PortalSessionThreadLocal.getHttpSession();
session.setAttribute(sharedKey, bean);
Portlet 1 is able to retain, reset and use attribute fine.
Reading in portlet 2:
key = "LIFERAY_SHARED_" + key;
HttpSession session = PortalSessionThreadLocal.getHttpSession();
Object bean = session.getAttribute(key);
This value is always null.
Both portlets are Spring MVC portlets.
Both portlets have:
<instanceable>false</instanceable>
<private-session-attributes>false</private-session-attributes>
<requires-namespaced-parameters>false</requires-namespaced-parameters>
In their liferay portlet XML-s.
Also both portlets extend org.springframework.web.portlet.DispatcherPortlet.
Liferay version:
Liferay DXP Digital Enterprise 7.0.10 GA1
Any help would be greatly appreciated.
Let me know if anyone needs any clarification.
Thanks a lot,
PeTer
Kyle Stiemann has recently written a nice article on using sessions in portlets. TL;DR: You're using the HttpSession with an attribute prefixed "LIFERAY_SHARED_", but you should use the portlet session: That's what Liferay manages, the HttpSession might be "simulated", e.g. it might not be the object that tomcat manages.
To quote one of the options from his article:
Use Liferay session.shared.attributes prefixes (such as
LIFERAY_SHARED_) to share one or more session attributes between
portlets in different applications/WARs.
Liferay exposes certain session attributes to all portlets based on
certain prefix values. Although these prefixes are configurable via
portal-ext.properties, I recommend using one of the default prefixes:
LIFERAY_SHARED_.
For example:
// Portlet A
portletRequest.getPortletSession(true)
.setAttribute("LIFERAY_SHARED_" + CONSTANTS.ATTR_NAME, "value",
PortletSession.APPLICATION_SCOPE);
// Portlet B (in a different WAR)
String attrValue = portletRequest.getPortletSession(true)
.getAttribute("LIFERAY_SHARED_" + CONSTANTS.ATTR_NAME,
PortletSession.APPLICATION_SCOPE);
Pros:
Only exposes the necessary attribute(s) to other portlets (instead of exposing the entire session).
Cons:
Exposes session attribute(s) to all portlets.
Tight coupling without indicating which other portlets might be utilizing this data.
Non-standard method of sharing session data.
Note the strong recommendation to use only primitive types as session attributes. Eliminate the need for custom serialization and classloading problems. Also note that the variant of getPortletSession with the additional scope parameter is required here.
But, as much as this technically provides an answer to your question, you also want to read "Session Storage is Evil".
TL;DR: Don't use the technique above. Rather eliminate the use of sessions.
I have created a Web-Application that shows documents to the user.
The application uses RESTFUL Webservices to get this documents from the backend.
The EJB responsible for creating / loading the documents needs some information stored in the http session.
(There was a requirement to determine how long a document has been openend, how long loading took etc., there is data stored in the session to determine which document in which browser window was closed or updated)
My solutions to get this informations looks like this (in the SSB and an Interceptor for another SSB):
#Context
private HttpServletRequest request;
...
this.request.getSession().getAttribute(WINDOW_SET);
This solution is working.
I am not sure this is the "right way" to do it, since #Context is not really meant for use in an EJB as far as I researched until now.
I can not just pass the needed information to the EJB as an argument, since this would not work with the Interceptor used.
The question is:
Is this a valid way to build this functionality?
If not, what are the drawbacks of this solution and how could it be done better?
No. The bean is annotated as stateless: it must be stateless. You can't expect injection of the request or the session to even work.
What a 'stateless session bean' actually means is another question, and one which I've been pondering since about 1998.
Is it possible to access to session id from RealmBase.athenticate method? I need it from my custom realm class in order to store into a database additional info associated to the session and retrieve it during the following requests. I'm using Tomcat 8. Thank you!
No.
Authentication does not require the use of a session, so it's not baked into the API. Authentication is also supposed to be separated from session management, so again it's not baked-into the API.
You might still be able to do this, but it requires some trickery.
Write a Valve that uses a ThreadLocal containing a Request object.
Register the Valve before the AuthenticationValve in the valve chain.
In that Valve, stuff the user's request into the ThreadLocal, then call down the chain.
In your custom Realm, fetch the object from the ThreadLocal.
Always remember to remove the request from the ThreadLocal after the call chain comes back... even if an exception is thrown.
I would like to delete some temporal files when user session finishes. The information associated with the files is stored in an object annotated with #SessionAttributes.
The only way I've found to deal with this is creating an HttpSessionListener.
Is there a higher level, simplified, Springy way to listen to the session end event where I could easily get my annotated object?
You most likely will need to create a HttpSessionListener.
Another stackoverflow answer:
Detect session timeout in Spring 3/Spring Security 2.0.5
Also and example on how to load spring beans into it:
http://www.mkyong.com/spring/spring-how-to-do-dependency-injection-in-your-session-listener/
Two options to use HttpSessionListener with spring beans:
The first is to use WebApplicationContextUtils.getRequiredApplicationContext(servletContext) to obtain the servlet context. From there you have two sub-options:
use getBean(..)
If you want to use #Autowired / #Inject use getAutowireCapablyBeanFactory().autowireBean(this). You will have to do this only once (check if the fields are null), because the listener is singleton.
The second option is to use AspectJ and #Configurable on the listener.
Not directly related, but might be an interesting project to look at.
https://github.com/shawnmclean/Idle.js
Session deletion typically happens on the server side, when the session expires (usually 30mn). The project above allows to detect user behaviors in the front end.
It seems to me that "session" scope is another means to keep objects in session as
using setAttrubute / getAttribute
Correct?
You know, dont know why, it does not work for me.
<bean id="sabreUser" class="util.MyUser" factory-method="getSomeUser" scope="session">
<const args...>
What I see is that after the initialization and initial deploy the MyUser properties are correct.
Then, in the first session I change MyUser property, the session is closed.
The second session runs and it sees the last set value from the previous session!
What does that mean?
I expect this object to be initialized whenever a new session starts. But it better looks as singleton, though you see - "session" attribute is set.
I can see the cause of the problem in that a Servlet's fields is initialized with #Autowired
so, once it is initialized, every other session will see its fields set and does not "ReWire" this properties. It happens once? How to overcome this problem?
The Spring session does not exactly match the HttpSession, and even the Spring documentation on the #SessionAttributes annotation says that it might be stored in the session or "some conversational storage". I got that from The Spring docs for 2.5
I've basically quit trying to make sense of it, and just got on with my life, if I want something stored in the HttpSession, I just have Spring inject the HttpSession to me, assuming you're using Spring MVC its pretty easy, instructions on the same page.
Session-scoped beans are beans that live throughout the http session. They are stored in the session via setAttribute in some way.
So - yes.
Session scoped beans are stored in Http Session by Spring framework. This scope is valid only in the context of Web application.It also works for Portlet envionments . When using in Portlet environment, there are two notions of session, application scope and portlet scope (default).