We’ve introduced all of these concepts that would be used while you’re dealing with the Portlet development. Portlet and Portlet container had used several objects that make obligation between both of them to get their functionality achieved.
However, no one of these tutorials have provided you a full detailed explanation for these fine-grained objects. To get into real development, you should be aware of these objects as well as you must have to know what these objects are closely, how can they are used and when their usage should be to get the most benefit.
Portlet Tutorial
This tutorial intended to help you getting these information in an organised manner as it’s also will make you use all of them willingly. Following below core concepts that you would pass through:
Portlet Requests
As we’ve stated earlier, the PortletRequest interface represents the common functionality that’s lying in RenderRequest interface and ActionRequest interface as both of them have extended the PortletRequest interface.
PortletRequest interface provides methods for accessing various types of needed information. These information would be:
Request Attributes: request attributes used mainly to pass Java objects between Portlets, Servlets and JSP pages. These attributes are formed as name/value pairs, in that the String will form the name of the attribute while Java Object is the value.
As soon as the Portlet receives a new action request, the attributes that were sent previously are no longer available and they will be replaced by new ones. The Portlet may set, update, delete and read attributes during action processing and render processing by invoking request.getAttribute()
, request.setAttribute()
and request.getAttributeNames()
.
Basically, any attributes you’ve set against request while executing of processAction()
phase won’t be available by the request object while executing of render()
phase. Even Java Portlet Specification 2.0 has provided the PLT.10.4.4 Runtime Option javax.portlet.actionScopedRequestAttributes
that will make your processAction()
attributes available while executing of render()
phase but unfortunately this feature isn’t implemented in Apache Pluto 2.0.3 as you can refer for Apache Pluto’s JIRA.
A more detailed sample would be discussed, especially when it comes to use the JSF bridge that already facilitate sending objects through using of request attributes.
Request Parameters:Request parameters aren’t so differ from discussed request attributes, they’re pairs of name/value where both of them are a String object. The most important issue here are the sources that request parameter come from. Typically, request parameters are those appended onto Portlet URL (e.g ActionURL
, RenderURL
), submitted by the form or set during execution of action request.
The parameters sent to the Portlet from an action request are valid only while the action request is processed. Meanwhile, the render parameters are valid until the Portlet has received a new request. According to Java Portlet Specification 2.0, all these requests that are responsible for change the Portlet’s window state or mode from the user interface elements on the Portal page shouldn’t reset the render parameters for the Portlet.
Both of processAction()
and render are read the request parameters using the same methods; getParamaterMap()
, getParameter()
, getParameterNames()
and getParamaterValues()
are used for this purpose. Following sample shows you how can we develop simple Login form where you must enter journaldev as a username and password to get logged into.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
package com.journaldev.portlet; import java.io.IOException; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.GenericPortlet; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class RequestParameters extends GenericPortlet{ public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { // Check if the loggedIn is null for initial state if(request.getParameter("loggedIn") == null){ response.getWriter().println("<form action="+response.createActionURL()+">" + "Enter Username : <input type="text" id='username' name="username"/>" + "Enter Password : <input type="password" id='password' name="password"/>" + "<input type="submit" value="Login"/>" + "</form>"); } else { // Get loggedIn value from the request parameter boolean loggedIn = Boolean.parseBoolean(request.getParameter("loggedIn")); if(loggedIn){ response.getWriter().println("<form action="+response.createActionURL()+">" + "<p>You're logged in</p>" + "</form>"); } else { response.getWriter().println("<form action="+response.createActionURL()+">" + "<span style="color:red">Try another</span><br/>" + "Enter Username : <input type="text" id='username' name="username"/>" + "Enter Password : <input type="password" id='password' name="password"/>" + "<input type="submit" value="Login"/>" + "</form>"); } } } public void processAction(ActionRequest request, ActionResponse response) throws PortletException { // Fetch username and password from the request parameters of action String username = request.getParameter("username"); String password = request.getParameter("password"); // Do some checking procedure if(username != null && username.equals("journaldev") && password != null && password.equals("journaldev")){ // Use render parameters to pass the result response.setRenderParameter("loggedIn", "true"); } else { // Use render parameters to pass the result response.setRenderParameter("loggedIn", "false"); } } } |
portlet.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?xml version="1.0" encoding="UTF-8"?> <portlet-app xmlns="https://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"> <portlet> <display-name>Request Attributes</display-name> <portlet-name>requestAttributes</portlet-name> <portlet-class>com.journaldev.portlet.RequestParameters</portlet-class> <description>Print out all attributes located at the PortletRequest</description> <portlet-info> <title>Request Attributes</title> <keywords>request, attributes</keywords> <short-title>Request Attributes</short-title> </portlet-info> </portlet> </portlet-app> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.journaldev</groupId> <artifactId>PortletConcepts</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>PortletConcepts</name> <url>https://maven.apache.org</url> <properties> <deployFolder>D:/Apache Pluto/pluto-2.0.3/webapps</deployFolder> </properties> <dependencies> <!-- Java Portlet Specification V2.0 --> <dependency> <groupId>org.apache.portals</groupId> <artifactId>portlet-api_2.0_spec</artifactId> <version>1.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <finalName>${project.artifactId}</finalName> <plugins> <!-- bind 'pluto2:assemble' goal to 'process-resources' lifecycle --> <!-- This plugin will read your portlet.xml and web.xml and injects required lines --> <plugin> <groupId>org.apache.portals.pluto</groupId> <artifactId>maven-pluto-plugin</artifactId> <version>2.1.0-M3</version> <executions> <execution> <phase>generate-resources</phase> <goals> <goal>assemble</goal> </goals> </execution> </executions> </plugin> <!-- configure maven-war-plugin to use updated web.xml --> <!-- This plugin will make sure your WAR will contain the updated web.xml --> <plugin> <artifactId>maven-war-plugin</artifactId> <version>2.1.1</version> <configuration> <webXml>${project.build.directory}/pluto-resources/web.xml</webXml> </configuration> </plugin> <plugin> <artifactId>maven-antrun-plugin</artifactId> <executions> <execution> <id>copy</id> <phase>integration-test</phase> <configuration> <tasks> <copy file="target/${project.artifactId}.war" tofile="${deployFolder}/${project.artifactId}.war" /> </tasks> </configuration> <goals> <goal>run</goal> </goals> </execution> <execution> <id>delete</id> <phase>clean</phase> <configuration> <tasks> <delete file="${deployFolder}/${pom.build.finalName}.war" /> <delete dir="${deployFolder}/${pom.build.finalName}" /> </tasks> <detail>true</detail> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> </project> |
Here’s detailed explanation for the code listed above:
- You can use
RenderResponse
to createactionURL
orrenderURL
. - It’s possible to pass the parameters as appended name/value into both of action request and render request.
- Action request has the ability to add any new parameters for render request.
- If you’ve tried to get username and password parameters by using render request, you will get null for both of them.
Context Path: The Portlet’s context path is that part of URL corresponds to the Portlet’s context. Acquiring of context path is very helpful especially when a reference to a web resources is required. You may redirect your request into other web resources like Servlet by adding the context path into resource name.
Context path can be specified by using either of action or render requests.
Preferred Locales & Internationalization: It’s also possible for you to query the current locale that will be used for rendering the content. You may invoke the getLocale()
or getLocales()
for getting the current locale used and the list of locales for which the Portlet could provide the content for the Portal.
Retrieving The Schema, Server Name and Port: Similar for HttpServletRequest
, PortletRequest
provides you the needed methods for inquiring the schema of the URL used for the request (e.g http, https, ftp, etc), the server’s name and the port number that the application is listening for.
Even you’re probably won’t get used these methods as you don’t need to create the needed URLs by yourself. Methods like createActionURL()
and createRenderURL()
will be used alternatively.
Render Request
As we’ve seen previously, render() method takes a RenderRequest as one of the arguments. RenderRequest is used to represent the request for the render phase.
Action Request & File Uploading
At the same time, the request that’s passed for processAction() method is of type ActionRequest and it represents the request for the action phase.
ActionReqeust provides you two different methods that will help you read the content of the request body, getReader() and getPortletInputStream() are used for this purpose. Mainly, if your form posted data its MIME type is application/x-www-form-urlencoded, the Portlet container will read the request body on behalf of you and you will get the form’s data as a request parameters.
As soon as you’ve called a getReader() or getPortletInputStream() while reading the request body from a form’s its MIME type is application/x-www-form-urlencoded, you’ve surly got an IllegalStateException that print out the message says User request HTTP POST data is of type application/x-www-form-urlencoded. This data has been already processed by the portlet-container and is available as request parameters.
You’re able of getting read the form’s data that MIME value is multipart/form-data. But be careful as you can open one reader at the same time. As soon as the getReader() has been used, using of getPortletInputStream() will throw an IllegalStateException and the same case when opposite is happened. Following sample shows you how can we submit two different forms, one of them is aimed to upload the file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
package com.journaldev.portlet; import java.io.IOException; import java.util.List; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.GenericPortlet; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.portlet.PortletFileUpload; public class ActionRequestReader extends GenericPortlet{ public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { response.getWriter().println("<form action="+response.createActionURL()+" method='POST' enctype="application/x-www-form-urlencoded">" + "Enter message : <input type="text" id='message' name="message"/>" + "<input type="submit" value="Submit Form Message"/><br/>" + "</form>" + "<form action="+response.createActionURL()+" method='POST' enctype="multipart/form-data">" + "Upload Your File: <input type="file" name="fileUpload"/>" + "<input type="submit" value="Upload File"/>" + "</form>"); } public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException { // Handle default MIME type request if(request.getContentType().equals("application/x-www-form-urlencoded")){ String message = request.getParameter("message"); System.out.println(message); } // Handle multipart request else if(request.getContentType().contains("multipart/form-data")){ // Create FileItemFactory FileItemFactory factory = new DiskFileItemFactory(); // Create PortletFileUpload instance PortletFileUpload fileUpload = new PortletFileUpload(factory); try { // Instead of parsing the request ourselves, let Apache PortletFileUpload do that List<FileItem> files = fileUpload.parseRequest(request); // Iterate over files for(FileItem item : files){ // Print out some of information System.out.println("File Uploaded Name Is : "+item.getName()+" , Its Size Is :: "+item.getSize()); } } catch (FileUploadException e) { e.printStackTrace(); } } } } |
Here’s detailed explanation for the code listed above:
- GetContentType is used for determining which type of request we’ve handled.
- URL-encoded request cannot be read as the Portlet container will do that. So you can find your form’s parameters through using of request.getParameters().
- Multipart request can be read using getReader() or getPortletInputstream(). But one can be used per request.
- Instead of processing the request manually to get the file uploaded, we used Apache commons upload file library. This library is very smart one as it can handle normal file upload request or Portlet file upload request. See required libraries provided within the pom.xml.
- Although, we handle the uploaded file using the very trivial way (Just Print out some of file information), but you can use ImageIO api for getting image saved onto your disk space.
- To get ActionRequestReader Portlet deployed, don’t forget to list it inside portlet.xml file.
Portlet Response
The Portlet sends the response object back to the Portal after every request. The response object contains the Portlet fragment that will be drawn into Portal page. Fragments collective mission is a Portal job.
PortletResponse provides you a three methods only, setProperty()
, getProperty()
and encodeURL()
. These methods that are relevant for properties will be discussed while introducing the Portlet cache concept. Using of encodeURL()
is mandatory for some of Portlet container implementations, in that some resources (e.g Servlet, JSP, etc) within the Portlet application need to be referenced using some additional data like SessionID
and may others.
According to Java Portlet Specification, if encoding isn’t needed, the Portlet container will return the URL unchanged.
Render Response
Mainly, we’ve used RenderResponse object for writing content fragment into Portal page. RenderResponse object has provided two methods for getting content drawn, getWriter() and getPortletOutputStream() are available for that purpose.
But as soon as you’ve used getWriter(), you cannot able of using the getPortletOutputStream() and the opposition is valid. In case you’ve used both of them to handle the content draw, you will get an IllegalStateException.
The major facility has provided by the RenderResponse object is the getNamespace() method that’s used in a JavaScript code for variable naming identification. Following sample shows you two Portlets have referenced the same Portlet class and they’re provided the same JavaScript method. Let’s look at the scenario before getting getNamespace() used.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package com.journaldev.portlet; import java.io.IOException; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.GenericPortlet; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class RenderResponseNamespaceOne extends GenericPortlet{ public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { response.getWriter().println("<html>" + "<head>" + "<script>" + "var message="Handle Submit, Portlet One";" + "function handleSubmit(){" + " alert('Handle Submit, The Portlet Is '+message);" + "}" + "</script>" + "</head>" + "<form action="+response.createActionURL()+">" + " <input type="button" value="Click On Me" onclick='handleSubmit()'/>" + "</form>" + "</html>"); } public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException { System.out.println("Handled Request .."); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package com.journaldev.portlet; import java.io.IOException; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.GenericPortlet; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class RenderResponseNamespaceTwo extends GenericPortlet{ public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { response.getWriter().println("<html>" + "<head>" + "<script>" + "var message="Handle Submit, Portlet Two";" + "function handleSubmit(){" + " alert('Handle Submit, The Portlet Is '+message);" + "}" + "</script>" + "</head>" + "<form action="+response.createActionURL()+">" + " <input type="button" value="Click On Me" onclick='handleSubmit()'/>" + "</form>" + "</html>"); } public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException { System.out.println("Handled Request .."); } } |
Here’s detailed explanation for the code listed above:
- As the same message variable is defined for both of the Portlet, the last value of message variable will be used.
- Both of Portlets will use the same message variable and specifically the last one of this variable.
To get this issue resloved, getNamespace() can be used, following below the solution that you may use as long as your JavaScript code is listed on the Portlet itself.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package com.journaldev.portlet; import java.io.IOException; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.GenericPortlet; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class RenderResponseNamespaceOne extends GenericPortlet{ public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { response.getWriter().println("<html>" + "<head>" + "<script>" + "var "+response.getNamespace()+"message="Handle Submit, Portlet One";" + "function "+response.getNamespace()+"handleSubmit(){" + " alert('Handle Submit, The Portlet Is '+"+response.getNamespace()+"message);" + "}" + "</script>" + "</head>" + "<form action="+response.createActionURL()+">" + " <input type="button" value="Click On Me" onclick='"+response.getNamespace()+"handleSubmit()'/>" + "</form>" + "</html>"); } public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException { System.out.println("Handled Request .."); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package com.journaldev.portlet; import java.io.IOException; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.GenericPortlet; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class RenderResponseNamespaceTwo extends GenericPortlet{ public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { response.getWriter().println("<html>" + "<head>" + "<script>" + "var "+response.getNamespace()+"message="Handle Submit, Portlet Two";" + "function "+response.getNamespace()+"handleSubmit(){" + " alert('Handle Submit, The Portlet Is '+"+response.getNamespace()+"message);" + "}" + "</script>" + "</head>" + "<form action="+response.createActionURL()+">" + " <input type="button" value="Click On Me" onclick='"+response.getNamespace()+"handleSubmit()'/>" + "</form>" + "</html>"); } public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException { System.out.println("Handled Request .."); } } |
Here’s detailed explanation for the code listed above:
- If you’ve used the sample above, you should find two different handleSubmit() method as well as two different message variables that are named
Pluto_PortletConcepts_RenderResponseNamespaceOne__1332422274_0_message
andPluto_PortletConcepts_RenderResponseNamespaceTwo__1332422274_1_message
respectively. - This strategy would help as long as the JavaScript code is provided within the Portlet. If you’ve set your JavaScript code inside an external file, this strategy isn’t so helpful.
Action Response
Typically, action response used for two different major missions; for passing the parameters for the render phase through using of setRenderParamater()
and for redirect the request into any other resources than let the normal flow proceeds. Redirection has been done using of sendRedirect() as you can redirect into any resources like Servlet or JSP. Both of these behaviors are discussed and we also had provided samples for both of them.
Summary
Portlet API contains a lot of concepts that we must be aware of. This tutorial has discussed the most APIs that you should be familiar with as they would be helpful in the next coming tutorials. Contribute us by commenting below and find downloaded source code.