Liferay 7 Shared Session Attributes - java

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.

Related

Using http session in Stateless Session Bean via #Context

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.

Get Portlet Session on server

My question is:
I have two different Portlets (nothing but war file) deployed in a portal server called first and second; whenever user clicks firstportlet (first) I use the following code to set session object!:
Code in first portlet:
String application="Welcome";
PortletRequest portletRequest = (PortletRequest) webAppAccess
.getHttpServletRequest()
.getAttribute(Constants.PORTLET_REQUEST);
portletRequest.getPortletSession(true).setAttribute("application",
sessionValue,
PortletSession.APPLICATION_SCOPE);
log.Info("SESSION hole value:---" + portletRequest.getPortletSession(false));
Whenever user clicks secondportlet(second) I am using follwing code to retrieve the session which was set in firstportlet.
Code in second portlet:
PortletRequest portletRequest = (PortletRequest) webAppAccess
.getHttpServletRequest()
.getAttribute(Constants.PORTLET_REQUEST);
log.Info("SESSION hole value:---"+ portletRequest.getPortletSession(false));
log.Info("SESSION VALUE in second Portlet:----"
+ portletRequest.getPortletSession(false).getAttribute("application",
PortletSession.APPLICATION_SCOPE));
Error:
But in second portlet I am always getting null value, please any solution?
The portletSession.APPLICATION_SCOPE mechanism makes it possible for portlets to share session data if they are within the same portlet application. If you have two portlets that are not in the same war they are not in the same portlet application and thus, the session data is not shared either.
To solve this you need to put the two portlets into the same portlet application (same war, same portlet.xml but still two different portlets).
DanielBarbarian's answer is correct. To share the global session, the portlets need to be in the same war. But there are other ways to share data between portlets on the same page, but not necessarily in the same war. (I'm assuming your portal container supports the JSR286 standard)
Use interportlet communication. This can notify other portlets of events. It is a simple publish/subscribe system that is not difficult to set up.
Use global render parameters.
There are examples on the web for both options.

Is the Java HttpSession ID visible to FreeMarker templates?

From a FreeMarker template in a Java EE web application, I need to access the sessionID (i.e. HttpSession.getId()). Long story short, it's being passed to an outside analytics provider so they can build reports about what users do during their browsing session.
Obviously, I just add the session ID to the model object which gets passed to FreeMarker. However, this application is a bit of a mess... one FreeMarker template may be used by multiple controllers passing various model objects. For maintainability, it would be far easier if I could just access the session ID from the FreeMaker template without having to change all those model classes.
I've found FreeMarker documentation referencing "application", "session", and "request" scope. However, this format does not return a value:
${Session.id}
I suspect "scope attributes" doesn't include all getter methods on the HttpSession object (such as getId())... but rather just those values which can be fetched from HttpSession.getAttribute().
Is there an easy way to access the Java EE session ID from a FreeMarker template, without having to explicitly pass it through the model object?
You can put it in the model before rendering the template.
Normally you can put the request object in the model in some base method, which is invoked when rendering all templates. That way all templates will have the request, which you need anyway. Then you access the session via the request object. That's what we've done, and it works fine.

Intercept calls to HttpSession in Tomcat 6

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.

Best way to show the user full name in a JSP header file with a spring/struts framework?

I have a JSP struts application that uses Spring to integrate with the services/dao/database. Basically, struts uses spring to get the data from the DB, it builds the form and forward them to the JSP files.
I have a header file that is injected in each JSP file using Tiles. I would like to show "Welcome John Doe" on each page inside the header. Where "John Doe" is the name of the currently logged user.
What would be the best approach to do that? The solution that I can think of is:
Use a Spring Filter the catch the http request. Load the user from the database using a cookie that contains the user id(*) and put the name in a session bean named "CurrentUser"
In "header.jsp", get the spring application context. Using it, load the bean "CurrentUser" and get the name. Put the name in the html.
I think I could get this to work. But I'm not certain this is the best way to do it. Any thought on my approach?
(*) Of course, the cookie will be encrypted
Although it may be an extremely large hammer for your fairly simple use-case, we have gotten a really neat spring-jsp integration (jsp 2.1 required!) by using ELResolver. By following this tutorial you can basically inject any spring managed bean into your el-context and allow it to be accessed using jsp-el like this:
${spring.mybean.myproperty}
You can choose to inject pre-defined beans into your el-context or simply pass "mybean" to getBean and allow almost anything spring-managed to be accessible from jsp. mybean could easily be a session-scoped spring bean.
I'm not totally sure how this would align with tiles, though.
Are you not already storing some sort of User object in Session?
If so, I would just add a "getFullName()" method to this domain object and have the DAO populate it when it returns it. Ideally you should populate the User object when the user logs in, store it in session, and not need to load all of the user's details again from the database on each and every page request.
(Are you not using Spring Security? If so, they provide a pretty simple way to store a UserDetails-like object in Session, and easy access to it.)
I'd vote against both of your approaches because
This means (at least) an extra database call per page request
This wouldn't work if other users shared the same bean in the same context. Also, you really shouldn't have JSP files (which are your presentation layer) interacting with data services directly.

Categories

Resources