Liferay portlet rerenders init page after processAction - java

I'm working with portlets and was working through the sample on liferay's wiki: https://www.liferay.com/documentation/liferay-portal/6.1/development/-/ai/writing-the-my-greeting-portl-4
I'm having some issues with the portlet redirecting back to the init jsp after processing a form. My Java class is:
import java.io.IOException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import javax.portlet.PortletPreferences;
import com.liferay.util.bridges.mvc.MVCPortlet;
public class Directory extends MVCPortlet {
#Override
public void processAction(
ActionRequest actionRequest, ActionResponse actionResponse)
throws IOException, PortletException {
PortletPreferences prefs = actionRequest.getPreferences();
String greeting = actionRequest.getParameter("greeting");
if (greeting != null) {
prefs.setValue("greeting", greeting);
prefs.store();
}
super.processAction(actionRequest, actionResponse);
}
}
I have two jsps. view.jsp:
<%# taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%# page import="javax.portlet.PortletPreferences" %>
<portlet:defineObjects />
<%
PortletPreferences prefs = renderRequest.getPreferences();
String greeting = (String)prefs.getValue(
"greeting", "Hello! Welcome to our portal.");
%>
<p><%= greeting %></p>
<portlet:renderURL var="editGreetingURL">
<portlet:param name="mvcPath" value="/edit.jsp" />
</portlet:renderURL>
<p>Edit greeting</p>
And edit.jsp:
<%# taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%# taglib uri="http://liferay.com/tld/aui" prefix="aui" %>
<%# page import="com.liferay.portal.kernel.util.ParamUtil" %>
<%# page import="com.liferay.portal.kernel.util.Validator" %>
<%# page import="javax.portlet.PortletPreferences" %>
<portlet:defineObjects />
<%
PortletPreferences prefs = renderRequest.getPreferences();
String greeting = (String)prefs.getValue(
"greeting", "Hello! Welcome to our portal.");
%>
<portlet:actionURL var="editGreetingURL">
<portlet:param name="mvcPath" value="/edit.jsp" />
</portlet:actionURL>
<aui:form action="<%= editGreetingURL %>" method="post">
<aui:input label="greeting" name="greeting" type="text
value="<%=greeting%>
<aui:button type="submit" />
</aui:form>
<portlet:renderURL var="viewGreetingURL">
<portlet:param name="mvcPath" value="/view.jsp" />
</portlet:renderURL>
<p>← Back</p>
On the form submit of the edit.jsp, it's supposed to re-render that page. When I just use portlet:renderUrl instead of portlet:actionUrl, it does it correctly, but actionUrl isn't re-rendering. The tutorial says that it IS supposed to render edit.jsp again, so I'm not sure what I'm missing. Note: It is saving all my parameters correctly.

According to the JSR 286 (aka Portlet Specification 2.0), PLT.11.1.1.2:
The portlet-container must not propagate parameters received in an
action or event request to subsequent render requests of the portlet.
This is what happens when the user clicks the Submit button on the browser page:
The browser sends an Action request to Liferay, which contains, as its parameters, "name" and "mvcPath".
The portlet is now in Action phase, and so it executes the processAction method.
When the processAction method terminates without errors, Liferay is still responding to the Action request and the browser is still waiting for a response, which will be the HTML of the current page.
Because Liferay now has to respond the browser with the HTML of the page, it calls the render method for every portlet in page. That is, if makes a Render request to these portlets.
For your portlet, which is a MVCPortlet, the render method reads the "mvcPath" parameter to find out which JSP page it has to render.
But now, the JSR 286 says that there's no parameter passing between the Action and the Render phases, so the mvcPath, which was part of the Action request, is not copied by Liferay to the Render request that Liferay makes to your portlet.
Now, solutions:
As you suggested, at the end of the processAction method (but usually you don't change the processAction method, instead you create different methods for different actions, they'll tell you how to do in the tutorials... have a look to http://dev.liferay.com), you can set actionResponse.setRenderParameter("mvcPath","/edit.jsp"). This is perfectly normal.
If you want that your Action parameters are always copied to the subsequent Render request that Liferay makes to your portlet, you can set the copy-request-parameters init parameter to true (have a look here)
But you'll see that usually Liferay portlets go back to the view after executing an "edit" action (passing a "redirect" parameter in the Action request), so it's up to you.
Sorry, there's a lot to say about this subject, hope what I wrote can be useful to you!

Related

renderRequest attributes are always null after calling renderURL

I can't seem to access data I want to pass to my .jsp files. I've simplified it to the bare-bones and it still doesn't work for me. This portlet is being deployed to a liferay portal.
Problem: I click on the link to call a renderURL for the next .jsp page. In the doView() method, I set an attribute with setAttribute. When I retrieve that attribute in the .jsp, it returns null.
Expected output: "Proper Value"
Actual output: "Default value"
NewPortlet.java (Controller)
public class NewPortlet extends MVCPortlet {
#Override
public void doView(RenderRequest request, RenderResponse response)
throws IOException, PortletException {
request.setAttribute("test", "Proper Value");
String path = ParamUtil
.getString(request, "path", "/html/new/view.jsp");
include(path, request, response);
}
}
view.jsp
<%# taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<portlet:defineObjects />
<portlet:renderURL var="testURL">
<portlet:param name="path" value="/html/new/edit.jsp"/>
</portlet:renderURL>
<p>Click me!</p>
edit.jsp
<%# taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%#page import="com.liferay.portal.kernel.util.ParamUtil"%>
<portlet:defineObjects />
<%
String test = ParamUtil.getString(renderRequest, "test", "default value");
%>
<p><%= test %></p>
I am not sure what exact usecase you have.
But here issue is, you are setting attribute and trying to fetch parameter, hence it returns null.
For solution to your issues as in code, use renderRequest.getAttribute("test") or use el, ${test}
The "view.jsp" is referring "edit.jsp" using portlet:renderURL and that can be an issue. The render requests may be executed sequentially or in parallel without any guaranteed order.
I am not sure why your workflow is this way (i.e. setting parameter in doView method), but try actionURL instead of renderURL.

JSP: About Session in a system with login in constraint

When I am doing a project requires a login in system, I found that jsp will automatically create session, so I add <%# page session="false" %> into all pages to disable their abilities to create session since I only want one servlet to be able to create session.
However, when it comes to using bean, I found that, I can't use bean with session scope because of <%# page session="false" %>, I would like to ask what is the possible solution to solve this deadlock.
Many thanks
If you are setting session attributes from servlet(after login) then when you move to another JSP from it, the session will retain and you do not need to write
<%# page session="false" %>
on that JSP. All the attributes that you set will be available for you in the session.
Here's a test code :
index page shows 'name' attribute set in servlet - MaintainSession also when you press 'next JSP' button, it takes you to another new JSP which again shows the 'name' attribute.
None of the JSP needs the <%# page session="false" %>.
index.jsp
<body>
<form action="MaintainSession" method="post">
<input type="submit" value="Set Session Attribs"/>
</form>
<h1>Name : ${sessionScope.name}</h1>
<h1>Name : <%=session.getAttribute("name")%></h1>
<form action="Next.jsp" method="post">
<input type="submit" value="Next JSP"/>
</form>
</body>
Next.jsp
<body>
<h1>Hello World!</h1>
<h1>Name : ${sessionScope.name}</h1>
<h1>Name : <%=session.getAttribute("name")%></h1>
</body>
MaintainSession.java
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession(true);
session.setAttribute("name", "MyName");
request.getRequestDispatcher("index.jsp").forward(request, response);
}
The flow :
index.jsp ----Press Set Session Attribs---> MaintainSession servlet ----> index.jsp ---- press Next JSP ----> Next.jsp

How to call servlet from a jsp

I have a question, how to call a servlet from a jsp(chart.jsp) without using <jsp:include page="/servletURL" />because I tried before and I don't know if this is the right reason but it crashes when I use this code above.
I put in my doGet() method to retrieve information from DB and populate my dropdownlist (in chart.jsp) using JSTL+option and then redirect to my page (the same page), what I believe is that everytime the browser writes a new page using c:forEachtag it calls again my servlet and there is a never ending loop (Again, that's just my presumptions)
Here is my code to make it more clear:
my servlet:
ArrayList<Machine> foundMachines = MachineDB.getAllMachines();
request.getSession().setAttribute("foundMachineList", foundMachines);
RequestDispatcher rd = request.getRequestDispatcher("charts/chart.jsp");
rd.forward(request, response);
my jsp:
<jsp:include page="/searchServlet" />
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:forEach var="machine" items="${sessionScope.foundMachineList}">
<option value="${machine.machineId}">${Machine.machineName}
</option>
</c:forEach>
So my question is why my <jsp:include page="/servletURL" /> tag crashes my page and how to fix it. Any sugestion is welcome
Use
response.sendRedirect("//your servlet name");
Most probably is not related but change
${Machine.machineName} to ${machine.machineName}

Using jsp:param / c:param in a Portlet Environment

I'm trying to include JSP pages with jsp:param in a Portlet environment (using the Pluto portlet container).
for example,
<jsp:include page="test.jsp">
<jsp:param name="foo" value="bar"/>
</jsp:include>
and in test.jsp,
<c:out value="${foo}"/> or <%= request.getParameter("foo") %>
The output is always null and i've also tried using c tags, but got the same result.
<c:import url="test.jsp">
<c:param name="foo" value="bar"/>
</c:import>
I've searched through the net and many people have faced the same problem, except that there is no solution to it.
Is this a limitation or is there a different way to do it?
This works fine in a normal Servlet environment, but I see from a bit of googling that the portlet environment seems to break it. This is a shame, but indicative that the portlet spec is, to put it bluntly, broken.
If <jsp:param> won't work for you, the alternative is to use request attributes instead:
<c:set var="foo" value="bar" scope="request"/>
<jsp:include page="test.jsp"/>
And in test.jsp:
<c:out value="${requestScope.foo}"/>
or maybe just:
<c:out value="${foo}"/>
It's not as neat and contained as using params, but it should work for portlets.
I had the same problem. My solution was to work with Portlet's renderRequest object (which is accessible from included jsp files). In my portlet I set the attribute on the RenderRequest object then in my JSP (included via jsp:include). I use Portlet API to access the implicit renderRequest object. Here is a sample:
MyPortlet:
public void doView(RenderRequest request, RenderResponse response) {
request.setAttribute("myBean", new MyBean());
getPortletContext().getRequestDispatcher("myMainJSP.jsp").include(request, response);
}
myMainJSP.jsp:
<jsp:include page="header.jsp"/>
header.jsp:
<%# taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<% page import="sample.MyBean" %>
<portlet:defineObjects/>
<%
MyBean myBean = (MyBean)renderRequest.getAttribute("myBean");
%>
... html code...

How to check if external URL content loads correctly into an <IFRAME> in JSP page using HTTP response code

I am looking into a solution that would allow to load an external URL content into an <iframe> element in a JSP page.
However, before displaying any content, JSP code would first check for HTTP response of included in iframe's src URL and if 200/OK returned then display it otherwise a custom message or another page is displayed instead. I'd like to do it on the server side only.
Is there a way of achieving it without AJAX / user side scripting that could have potential cross-browser incompatibilities?
You can make use of JSTL (just drop jstl-1.2.jar in /WEB-INF/lib) c:import tag to import an external resource, which will throw FileNotFoundException if the URL is invalid, which in turn can be catched using JSTL c:catch tag. You can finally use JSTL c:choose to check whether to display the iframe or the eventual error.
Here's an SSCCE, copy'n'paste'n'run it (with JSTL installed):
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!doctype html>
<html lang="en">
<head>
<title>SO question 2291085</title>
</head>
<body>
<c:set var="url" value="http://google.com" />
<c:catch var="e">
<c:import url="${url}" varReader="ignore" />
</c:catch>
<c:choose>
<c:when test="${empty e}">
<iframe src="${url}"></iframe>
</c:when>
<c:otherwise>
<p>Error! ${e}</p>
</c:otherwise>
</c:choose>
</body>
</html>
Change http://google.com to http://google.com/foo or something invalid, you'll see that the error shows instead.
Note that I used varReader="ignore" to have it buffered but unread, so that it won't entirely hauled in which may be expensive because after all you're requesting the same URL twice.
Update: Alternatively, you can use a Servlet for this which preprocesses the request inside doGet() method with help of java.net.URLConnection. Here's a kickoff example.
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
URL url = new URL("http://google.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
int status = connection.getResponseCode(); // 200 = OK, 404 = Not Found, etc.
if (status == 200) {
request.setAttribute("url", url);
} else {
// Do your thing to set custom message or request another page.
}
request.getRequestDispatcher("/WEB-INF/page.jsp").forward(request, response);
}
...and in page.jsp then just have something like
<c:if test="${not empty url}">
<iframe src="${url}"></iframe>
</c:if>
Map the servlet on an url-pattern of something like /foo and call it on http:/example.com/contexty/foo instead of the JSP.
The actual loading of the <iframe> is going to take place because the client loads it. There have simply got to be better ways of validating the URL than by trying to include it via JSP. Do it in Java, or just don't do it at all: have the server return a "not found" page that runs some Javascript to hide the iframe. (Have it show an error for people browsing with Javascript turned off.)

Categories