How to access servlet session attributes within a JSF bean inside a Liferay portlet request? - servlets

Is it possible to write just a single attribute to the original session without using <private-session-attributes>false</private-session-attributes> with Liferay 6.2.10 and Liferay-Faces-Bridge 3.2.4?
In a JSF-bean / portlet we configure a export file that must be downloadable via a servlet (inside the same WAR).
We want to share one specific Object via the session to get used by some JSTL-magic inside the portal.
I have found no other way than setting <private-session-attributes>false</private-session-attributes>, but that pollutes the session with lots of JSF-specific and even more portlet-specific objects that no one needs in the user-global session. As most portlets in that war need to communicate I would either have to switch all to public session attributes or use IPC.
I tried several ways that only yield positive results while not using private session attributes.
ServiceContextThreadLocal.getServiceContext().getRequest().getSession().setAttribute("SERVICE_CONTEXT", true);
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
// Does not matter which way
// PortletSession portletSession = (PortletSession)externalContext.getSession(false);
PortletSession portletSession = ((PortletRequest) externalContext.getRequest()).getPortletSession();
portletSession.setAttribute("PORTLET_SESSION_PORTLET_SCOPE", true, PortletSession.PORTLET_SCOPE);
portletSession.setAttribute("PORTLET_SESSION_APPLICATION_SCOPE", true, PortletSession.APPLICATION_SCOPE);
HttpServletRequest httpServletRequest = PortalUtil.getHttpServletRequest((PortletRequest) externalContext.getRequest());
httpServletRequest.getSession().setAttribute("EXTERNAL_CONTEXT_SERVLET_REQUEST_SESSION", true);
HttpServletRequest outerRequest = PortalUtil.getOriginalServletRequest(httpServletRequest);
outerRequest.getSession().setAttribute("EXTERNAL_CONTEXT_SERVLETS_SERVLET_REQUEST", true);
Other options that I would like to avoid would be:
use a javax.servlet.Filter with a ThreadLocal
save the generated document (or export configuration) to a database
transport the configuration via the client by re-posting it to the export servlet.
This answer suggests to use the portletSession with ApplicationScoped variables, but I couldn't get the PortletSession.
With setting <private-session-attributes>false</private-session-attributes> I get the following attributes set in the original session:
TEST_WITH_EXTERNAL_CONTEXT_SERVLET_REQUEST_SESSION
TEST_WITH_PORTLET_SESSION_APPLICATION_SCOPE
TEST_WITH_SERVICE_CONTEXT
war_app_name_whatever?TEST_WITH_PORTLET_SESSION_PORTLET_SCOPE
and a great number of other objects (>50) visible in the global users session.
Has anyone a good idea how to set just one session attribute?

Unwrapping the request until reaching a class that does not extend javax.servlet.http.HttpServletRequestWrapper solves the problem.
The request is kindly stored by Liferay and available via ServiceContextThreadLocal.getServiceContext().getRequest().
Liferays PortalUtil just unwraps if the request wrapper is in a package that starts with "com.liferay." and therefore does not work if a custom request wrapper is used.
public static <Type, ValueType extends Type> void setOnOriginalSession(Class<Type> type, ValueType value) {
HttpServletRequest request = ServiceContextThreadLocal.getServiceContext().getRequest();
HttpServletRequest originalRequest = unwrapOriginalRequest(request);
HttpSession originalSession = originalRequest.getSession();
String attributeNameForType = getAttributeNameForType(type);
originalSession.setAttribute(attributeNameForType, value);
}
private static HttpServletRequest unwrapOriginalRequest(HttpServletRequest request) {
while (request instanceof HttpServletRequestWrapper) {
HttpServletRequestWrapper httpServletRequestWrapper = (HttpServletRequestWrapper) request;
request = (HttpServletRequest) httpServletRequestWrapper.getRequest();
}
return request;
}

Related

Handling cookies based on route in a single request lifecycle in ASP.NET MVC?

I'm writing a route that will allow the user to set a cookie with the version of some JSON object that the application will use to set client-side configurations. It is a fairly large JSON object that we don't want to store in a cookie alone. We want to store ONLY the version to be looked up and set from some map up in the cloud on every request since multiple versions of the client are running around and we want those to be separated on a per request basis.
Currently, I know the problem is due to my lack of understanding of the single request lifecycle of ASP.NET MVC as I'm sure the following code proves. I do know that the Application_BeginRequest Action is probably happening BEFORE the route is handled (correct me if I'm wrong here), but I am not sure where it SHOULD be happening so that the cookie is populated BEFORE it is retrieved. I also don't believe that Application_EndRequest would be better due to the same, but opposite issue.
Any and all suggestions that lead to my understanding of the lifecycle and an appropriate Action to handle that kind of cookie value getting will be welcomed!
// Working controller (cookie does get set, this is confirmed)
using System;
using System.Web;
using System.Web.Mvc;
using SMM.Web.Infrastructure.Filters;
namespace SMM.Web.Controllers
{
[NoCache]
public class SetCookieController : ApplicationController
{
private HttpCookie CreateVersionCookie(int versionId)
{
HttpCookie versionCookie = new HttpCookie("version_id");
versionCookie.Value = versionId.ToString();
return versionCookie;
}
public ActionResult SetCookie(int versionId)
{
Response.Cookies.Add(CreateVersionCookie(versionId));
return Redirect("/");
}
}
}
// In Global.asax.cs (this does not work to get the cookie)
private void LoadSomeJsonFromACookie()
{
HttpCookie someJsonThingCookie = HttpContext.Current.Request.Cookies["version_id"];
string jsonVersion = (string)staticVersionCookie.Value;
string json = FunctionToGetSomeJsonThingByVersion(jsonVersion); // This returns a stringified JSON object based on the jsonVersion supplied
dynamic someJsonThing = JsonConvert.DeserializeObject<dynamic>(json);
HttpContext.Current.Items["someJsonThing"] = someJsonThing;
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
RedirectToHttps();
// some other redirects happen here
LoadSomeJsonFromACookie();
}
Application_BeginRequest is the right place. Since in the code, you can see I'm firing a redirect back to root /, it will set the cookie before it ever needs the cookie.

Access HttpServletRequest and HttpServletResponse in a Thymeleaf dialect processor

I'm trying to create a Thymeleaf dialect processor which performs a ServletDispatcher.include. I have extended the AbstractElementTagProcessor and overridden the doProcess method. The relevant code fragment is:
#Override
protected void doProcess(final ITemplateContext context, final IProcessableElementTag tag, final IElementTagStructureHandler structureHandler) {
ServletContext servletContext = null; // TODO: get servlet context
HttpServletRequest request = null; // TODO: get request
HttpServletResponse response = null; // TODO: get response
// Retrieve dispatcher to component JSP view
RequestDispatcher dispatcher = servletContext.getRequestDispatcher("/something");
// Create wrapper (acts as response, but stores output in a CharArrayWriter)
CharResponseWrapper wrapper = new CharResponseWrapper(response);
// Run the include
dispatcher.include(request, wrapper);
String result = wrapper.toString();
// Create a model with the returned string
final IModelFactory modelFactory = context.getModelFactory();
final IModel model = modelFactory.parse(context.getTemplateData(), result);
// Instruct the engine to replace this entire element with the specified model
structureHandler.replaceWith(model, false);
I wrote similar code in the past in the form of a custom JSP tag. Problem is: I don't know how to access the ServletContext, HttpServletRequest and the HttpServletResponse!
Can this be done at all, or should I just accept that Thymeleaf is too good at hiding the HTTP context?
You can access request (by using #request object that gives you the direct access to javax.servlet.http.HttpServletRequest object) parameters and session (with #session object that gives you direct access to the javax.servlet.http.HttpSession object) attributes directly in Thymeleaf views:
${#request.getAttribute('foo')}
${#request.getParameter('foo')}
${#request.getContextPath()}
${#request.getRequestName()}
<p th:if="${#request.getParameter('yourParameter') != null
th:text="${#request.getParameter('yourParameter')}"}">Request Param</p>
${#session.getAttribute('foo')}
${#session.id}
${#session.lastAccessedTime}
<p th:if="${session != null}"> th:text="${session.yourAttribute}"</p>
Read more here.
I found myself with a very similar requirement of accessing the request from an implementation of IExpressionObjectFactory.
The way i solved it (following #Sebastian Marsching advise in a previous comment) is by accessing the objects registered in IExpressionContext that are available from the view in the context of template evaluation (all those objects described in Appendix A and Appendix B of thymeleaf documentation), so you have access to request, response, servletContext and many other utility objects.
Speaking in code:
IExpressionObjects expressionObjects = context.getExpressionObjects();
HttpServletRequest request = (HttpServletRequest)expressionObjects.getObject("request");
There is also an expressionObjects.getObjectNames() method you can call to get a Set<String> with the names of all registered objects, which in my case gives the following list:
[i18nutils, ctx, root, vars, object, locale, request, response, session,
servletContext, conversions, uris, calendars, dates, bools, numbers, objects,
strings, arrays, lists, sets, maps, aggregates, messages, ids, execInfo,
httpServletRequest, httpSession, fields, themes, mvc, requestdatavalues]

CQ5 Sling default servlets, same extension but different accepts methods

So I have 2 servlets annotated like this:
#Component
#Service(value = {Servlet.class, NonExistingResourceServlet.class})
#Properties({
#Property(name = "sling.servlet.resourceTypes",
value = {"sling/servlet/default"},
propertyPrivate = true),
#Property(name = "sling.servlet.extensions",
value = {"xml"},
propertyPrivate = true),
#Property(name = "sling.servlet.methods",
value = {"GET"},
propertyPrivate = true))
For both I override the accepts method
#Override
public boolean accepts(SlingHttpServletRequest request) {
String requestURI = request.getRequestURI();
if (StringUtils.isNotBlank(requestURI)){
return requestURI.endsWith("/sevlet1.xml"); //different for the other servlet
} else {
return false;
}
}
IN CQ /system/console/servletresolver one of them is not resolved. Do I have to be more specific in configuration. The accepts method is not enough?
Found on Apache Sling doc
If a registered servlet implements the OptingServlet interface, Sling uses that servlet's accepts(SlingHttpServletRequest request) method to refine the servlet resolution process.
In this case, the servlet is only selected for processing the current request if its accept method returns true.
For one of them I added a selector and now the difference is made.
My question is why do I need to add the selector if I override the accepts method?
The 2 servlets are like this:
/content/myapp/sevlet1.xml
/content/myapp/sevlet2.xml
I'm not sure whether the /system/console/servletresolver tool takes OptingServlet into account, you might also put a debugger breakpoint in your accept methods to check that they are actually called when the corresponding HTTP requests come in. Note also that your servlets need to be declared "implements OptingServlet", which I suppose is the case as you have an #Override annotation on your accepts method.
Note that an OptingServlet that checks the request URI is usually not recommended in Sling, dispatching based on resource types or selectors is the recommended best practice.
(edit:) You might also compare your code with the example OptingServlet from the Sling integration tests.

Smart way to add parameters' signature with Square's Retrofit

`I need to contact an API which requires that every request contain the signature of all parameters and url + nonce.
Example:
#GET("/users/{type}")
public void getUsers(
#Path("type") String type,
#Query("sort") boolean sort)
I should add a X-Signature header with contains signature(nonce+"/users/"+type+"sort="+sort).
I thought I could do this with a RequestInterceptor's addHeader(String name, String value) but I can`t as the signature varies for every request.
Is there a smart way to do this with Retrofit or will I just have to manually sign every request?
Am I right in thinking that your signature is generated from [nonce]+[path]+[query params]
You could look at implementing a custom client and passing this into your RestAdapter.Builder().setClient(new CustomClient) method.
Something like CustomClient extends OkClient and then override the execute(Request) method. You will need to create a new Request object and pass that to super.execute(updatedRequest).
#Override
public Response execute(Request request) throws IOException {
List<Header> headers = new ArrayList<>();
// do work here to parse the request.getUrl() and extract path/params and generate the signature
headers.addAll(request.getHeaders());
headers.add(new Header("X-Signature", "signature"));
Request updated = new Request(request.getMethod(), request.getUrl(), headers, request.getBody());
return super.execute(updated);
}
If however there is no consistency to the generation of the signature then you will need to create the signature manually and add a #Header value in your call to your client.

Pass bean object from MVC to Spring WebFlow

I need to pass a bean object from MVC to webFlow. Currently, I am achieving it this way:
Storing my bean object as request attribute in controller.
Forwarding to flow.
Accessing the object from flowRequestContext on-start of my flow and setting it in flowScope.
#RequestMapping(value = "/ProcessUser", method=RequestMethod.POST)
public String processForm(LoginUser loginUser, HttpServletRequest request){
....
request.setAttribute("registrationDetails", registrationDetails);
return "forward:/chineseFlow"; //Call to flow
}
chineseFlow.xml
<on-start>
<evaluate expression="userDetailsService.getRegistrationDetails(flowRequestContext)" result="flowScope.registrationDetails"/>
</on-start>
UserDetailsService
public RegistrationDetails getRegistrationDetails(RequestContext requestContext){
HttpServletRequest httpRequest = (HttpServletRequest) requestContext.getExternalContext().getNativeRequest();
RegistrationDetails registrationDetails = (RegistrationDetails)httpRequest.getAttribute("registrationDetails");
return registrationDetails;
}
I don't want to pass multiple request parameters as input to my flow. Is this the correct way to pass the bean to SWF or is there any other better way to achieve the same?
There are not many options. Proper way would be to redesign your application so that whole process happens within the same flow, then you can store your values in flowscope to begin with. The only alternatives would be either a request attribute (which you are doing already), or session-scoped bean/session attribute. Out of these request attribute(s) is preferred as otherwise you will end up polluting your session scope, and introduce potential bugs that stem from leftover values in session scope.

Resources