In the presented introduction about Apache Pluto, we’ve mainly discussed the most simple Portlet application that you may have. The business scenarios are more complicated then normal and they require a full knowledge about the Portlet, Portlet Life cycle and how could provide a compliant enhanced Portlet that complying with the latest standards presented at the Portlet field.
Portlet Tutorial
This tutorial will help you getting this practice applied, while it should take you through the following subjects:
Little Bit More About GenericPortlet
A subclass of GenericPortlet should either use one of the following annotations: @ProcessAction
, @ProcessEvent
and @RenderMode
or override at least one method of the following:
processAction
: to handle action requests (will be discussed later on in this tutorial).doView
: to handle render requests when the Portlet is in the View mode.doEdit
: to handle render requests when the Portlet is in the Edit mode.doHelp
: to handle render requests when the Portlet is in the Help mode.init
anddestroy
: to manage resources that are held for the life of the Servlet.
Normally, no need to override the render
or the doDispatch
methods, as the render
method handles render requests, setting the title of the Portlet in the response and invoking the doDispatch
.
In turn of the doDispatch
method, the request is dispatched to one of the doView, doEdit or doHelp method depending on the Portlet mode indicated in the request. The major issue that you might find it so important, is that the Portlet is running in a multi threaded manner, so be careful when it comes to handle shared resources. Shared resources may take different forms; it’s include in-memory data such as instance or class variables, external resources like a files, database connections and network connections.
In the proposed sample that you’ve looked for at the introduction, we’ve overridden the doView method for getting message displayed. We do, some modifications against the sample itself, so you should notice the following:
- We’ve eliminated the overriding of
doView
method, as we used@RenderMode
annotation to annotate a new method calledinspectRenderMode
. - We’ve updated the
pom.xml
file, so no need to get your generated WAR every time and copy it into your Tomcat’swebapp
deployment folder. That’s done by using anintegration-test
phase that asksAnt
to make that in behind of you.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package com.journaldev.portlet; import java.io.IOException; import javax.portlet.GenericPortlet; import javax.portlet.PortletConfig; import javax.portlet.PortletException; import javax.portlet.RenderMode; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class HelloWorldPortlet extends GenericPortlet { @RenderMode(name="View") public void inspectRenderMode(RenderRequest request, RenderResponse response) throws PortletException, IOException { response.getWriter().print( "Jounral Dev Provides you Hello World Portlet Using Annotations !"); } public void init( PortletConfig config ) throws PortletException { super.init( config ); } } |
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 |
<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>HelloWorldPortlet</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>HelloWorldPortlet Maven Webapp</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> <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> <phase>integration-test</phase> <configuration> <tasks> <!-- Use this to build and deploy the testsuite war. PORTLET_DEPLOY_DIR is an environmental variable pointing to the hot-deploy directory of your portal. You can also use -Ddeploy.dir=<path to deployment dir> on the command line when invoking maven: mvn -Ddeploy.dir=/pluto-1.1.4/webapps integration-test --> <property environment="env" /> <property name="deploy.dir" value="${env.PORTLET_DEPLOY_DIR}" /> <copy file="target/${pom.build.finalName}.war" tofile="${deployFolder}/${pom.build.finalName}.war" /> </tasks> </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:
- JSR-286 has introduced the using of annotations for controlling things were controlled normally through overriding
GenericPortlet
methods. We’ve used@renderMode
instead of overriding doView method done like before. - We’ve added
antrun
plugin to replace the old way of deploying that we were used. Now, what’s really you want to do is to executemvn clean integration-test package
to get your WAR copied and deployed directly underneath of your Tomcat’s webapp folder. This procedure will be used henceforward.
Finally, if you have a special kind of business that you would like to provide such a type of integration, you may make a subclass of GenericPortlet and let your developers extend it.
Little Bit More About PortletRequest & PortletResponse
The PortletRequest
interface in the Portlet API represents the common functionality in an ActionRequest and RenderRequest, where both of them are actually subclass of PortletRequest.
The PortletRequest
provides all needed information about the user’s request (e.g request’s parameters), user’s session, Portal, Portlet application and different information about the Portlet itself.
The Portlet container passes all Portlet request and response objects to the Portlet whenever the Portlet get requested.
In the other side, the Portlet sends a response object back to the Portal after every request. Usually, the response object contains the content fragment of the Portlet, any requested Portlet modes or window states and several other pieces of information that get discussed later.
Actually, the Portlet developers haven’t worked with actual instances of PortletRequest and PortletResponse
. However, the developers will work with RenderRequest, ActionRequest, RenderResponse and ActionResponse.
You may be asking about the difference between RenderRequest, RenderResponse and ActionRequest, ActionResponse and why they are categorized like that. Following figure shows you the main differences shortly before getting all detailed clarifications in the next coming sections.
As you’ve noticed in the figure shown, Portlet specification defines two different types of request/response, render’s request/response and action’s request/response are mainly the only objects you may revolve around at each time you got into a Portlet development mission.
Such that distinction is very helpful when it comes to deal with the Portlet business, as a Portlet may be rendered at every time the contained Portal page is refreshed, while the action is getting executed at a certain specific time the user has asked for. To make clear, following sample shows you the difference in between calling render()
and processAction()
.
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 |
package com.journaldev.portlet; import java.io.IOException; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.GenericPortlet; import javax.portlet.PortletConfig; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class HelloWorldPortlet extends GenericPortlet { private static int renderCount = 0; private static int actionCount = 0; public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { synchronized(this){ renderCount++; } response.getWriter().print("<form action="+response.createActionURL()+">" + "<p>Render has executed "+renderCount+"</p>" + "<p>Render has executed "+actionCount+"</p>" + "<input type="submit"/>" + "</form>"); } public void processAction(ActionRequest actionRequest, ActionResponse actionResponse){ synchronized(this){ actionCount++; } } public void init( PortletConfig config ) throws PortletException { super.init( config ); } } <img class="alignnone wp-image-31157 size-full" src="https://all-learning.com/wp-content/uploads/2014/09/Defining-View-Mode-Using-Portlet-Specifications.png" alt="Defining-View-Mode-Using-Portlet-Specification" width="1200" height="628" /> |
Here’s detailed clarifications for code listed above:
- You may notice that the render counter will be incremented by one at every time you’ve got the Portal page rendered.
GenericPortlet
render method has delegated the control intodoView
that’s overrode at theHelloWorldPortlet
. - Action counter won’t be incremented until you’ve clicked on the submit action.
GenericPortlet
processAction
method has delegated the control intoprocessAction
method that’s overrode at theHelloWorldPortlet
. - We’ve implemented both of render and action using the Portlet specification 1.0 as no annotation has been used.
- As being render method had called at every time you’ve got your Portal page refreshed, it’s very clear that’s why
render
isn’t the proper location to put any business logic you may require. If we’ve assumed that we have a business logic like mail sending putting there, so the mail would be sending at every time the Portal page getting refreshed.
Enhancing Portlet
Portlet capabilities aren’t stopped here as we’re going to make some improvements to demonstrate other sides of these capabilities. Usually, the Portlet doesn’t render a plain HTML, it’s contained other resources like JavaScript, images and StyleSheets.
Also, the view mode isn’t the only mode that you lets the end user contact with, EDIT and HELP modes are also supported by the Portlets. Mainly, you may provide an EDIT mode for a kind of configuration, meanwhile, the HELP mode is a good place to provide a helpful information about the Portlet itself.
Beside that, providing Portlet Title isn’t so complex mission, as you can provide the default value using the portlet-info
XML tag, you can also change the Title programmatically.
Change The Portlet Title
Generally, Portlet specification has introduced the portlet-info XML tag to allow developers specifying these information related to the Portlet’s title, short-title and keywords that may be used for categorization purpose.
You have three different scenarios that can you have to let the Portlet get its title, you may be using the most simplest way; define the title at the portlet.xml
file within portlet-info
or using a resource bundle for getting internationalization descriptive title or providing the title programmatically. Following example shows you how can you leverage two additional scenarios to make sure the desired title is set as we’ve had the first scenario previously.
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 |
package com.journaldev.portlet; import java.util.Locale; import java.util.ResourceBundle; import javax.portlet.GenericPortlet; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class TitleChangedPortlet extends GenericPortlet{ public void doView(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException { response.getWriter().println("My Name Is:"+this.getPortletName()); } public java.lang.String getTitle(RenderRequest request) { // Check whether the name of the portlet is programmaticTitleChangePortlet if(this.getPortletName().equals("ProgrammaticTitleChangePortlet")){ // If it's like that, just get the defined bundle ResourceBundle bundle = this.getPortletConfig().getResourceBundle(new Locale("en")); // Retrun the string that's corresponded for anyTitle property return (String)bundle.getObject("anyTitle"); } // else return the default, either that's defined in the portlet.xml or that's defined in the bundle return super.getTitle(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 31 32 33 34 |
package com.journaldev.portlet; import java.io.IOException; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.GenericPortlet; import javax.portlet.PortletConfig; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class HelloWorldPortlet extends GenericPortlet { private static int renderCount = 0; private static int actionCount = 0; public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { synchronized(this){ renderCount++; } response.getWriter().print("<form action="+response.createActionURL()+">" + "<p>Render has executed "+renderCount+"</p>" + "<p>Action has executed "+actionCount+"</p>" + "<input type="submit"/>" + "</form>"); } public void processAction(ActionRequest actionRequest, ActionResponse actionResponse){ synchronized(this){ actionCount++; } } public void init( PortletConfig config ) throws PortletException { super.init( config ); } } |
portlet.xml
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 |
<?xml version="1.0" encoding="UTF-8"?> <portlet-app xmlns="https://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" version="1.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd https://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"> <portlet> <description>Write here a short description about your portlet.</description> <portlet-name>HelloWorldPortlet</portlet-name> <display-name>HelloWorldPortlet Portlet</display-name> <portlet-class>com.journaldev.portlet.HelloWorldPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>VIEW</portlet-mode> </supports> <portlet-info> <title>Title Is Set Using Simplest Way Portlet-Info XML Tag</title> <short-title>Hello World</short-title> <keywords>Hello,pluto</keywords> </portlet-info> </portlet> <portlet> <description>It's a Portlet that's setting title using Resource Bundle</description> <portlet-name>ResourceBundleTitleChangePortlet</portlet-name> <display-name>ResourceBundleTitleChangePortlet</display-name> <portlet-class>com.journaldev.portlet.TitleChangedPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>VIEW</portlet-mode> </supports> <supported-locale>en</supported-locale> <resource-bundle>message</resource-bundle> </portlet> <portlet> <description>It's a Portlet that's setting title programmatically</description> <portlet-name>ProgrammaticTitleChangePortlet</portlet-name> <display-name>ProgrammaticTitleChangePortlet</display-name> <portlet-class>com.journaldev.portlet.TitleChangedPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>VIEW</portlet-mode> </supports> <supported-locale>en</supported-locale> <resource-bundle>message</resource-bundle> </portlet> </portlet-app> |
message.properties
1 2 3 4 5 |
javax.portlet.title=Title Is Set By Resource Bundle anyTitle=Title Is Set Programmatically <img class="alignnone wp-image-31158 size-full" src="https://all-learning.com/wp-content/uploads/2014/09/Defining-View-Mode-Using-Portlet.png" alt="Defining-View-Mode-Using-Portlet-Specification" width="1200" height="628" /> |
Here’s detailed explanation for code shown above:
- It’s possible to use the same Portlet with different names. We’ve used
TitleChangedPortlet
twice; once underResourceBundleTitleChangePortlet
name and second withProgrammaticTitleChangePortlet
. HelloWorldPortlet
uses the title that’s defined within theportlet.xml
file.ResourceBundleTitleChangePortlet
uses the title that’s defined inside resource bundle. As you’ve noticed you must use the well-known constants as they must be compliant with Portlet specification to be considered. Following below the constants for the Portlet level resource bundle:
- Typically, you should define your resources within the same package as your Portlet to make sure every Portlet has used its relevant messages.
ProgrammaticTitleChangePortlet
uses the title that’s changed programmatically. Even that we can provide a static text, but we’ve provide another use of bundle messages.- In case you need to provide the title programmatically, you must do override for the
getTitle()
method that’s already inherited from theGenericPortlet
. - If you’re used resource bundle for getting Portlet’s accompanies text defined, you must make sure that you have your resource files (e.g message.properties) located into your class path as you’ve defined it in the
portlet.xml
. - Another way to change the title programmatically, you can invoke
setTitle()
againstrenderResponse
object inside any delegated method (i.e doView, doEdit and doHelp).
Portlet URLs
As we knew, the Portlet isn’t a standalone resource like Servlet that can be accessed through a well defined URL (i.e https://servername:port/web-context/servlet-mapping). The Portlet can be accessed only via using a Portal page, as it’s reside there.
However, a Portlet can create a URL that targets itself in the Portlet container (i.e by clicking a link or submitting a form) the result is a new client request to the Portal targeted to the Portlet as we already did when submitting HelloWorldPortlet above.
Anyway, these URL’s are called portlet URLs. In that, a Portlet URL will reference the instance of the portlet in the Portal page (In case you have two different Portlets referenced the same Portlet class, they will have different Portlet URLs).
Creation of these URLs is a Portlet container responsibility as it’s also parsed the Portlet URL into parameters for the Portlet request. The Portlet creates PortletURL
objects that represent Portlet URLs. The Portlet itself can use one of two methods on the RenderResponse
class to create these PortletURL
objects:
createActionURL()
: Is used to make action URLs for HTML forms or links. These actions URLs are useful for processing actions (i.e Portlet’s state modifications) on the Portlet (i.e calling ofprocessAction()
overrode method against your Portlet).createRenderURL()
: Is used to make render URLs for any tasks that doesn’t contain Portlet’s state modifications.
Even that you can add Portlet URLs into your content with no parameters, but you can also set your parameters on a Portlet URLs by invoking setParameter(name, value)
against your PortletURL
object that’s returned once you invoked createActionURL()
or createRenderURL()
.
More clarifications and samples would be delivered in the next coming tutorial that’s aimed to cover all technical specifics for accompanies render’s request/response and action’s request/response and how’s the parameter can be set and consumed after then.
doEdit & doHelp Methods
Practically, we’ve not used doEdit
method for any type of normal rendition, it’s mainly used for specifying the Portlet’s states for controlling Portlte’s behavior at normal rendition invocation that’s done using doView
.
At the same time, some Portlets have provided so complicated business, the user may need some sort of help to get the Port
let working properly and for that purpose the doHelp
is given.
Defining the Portlet’s modes isn’t so complex mission, all what you need for is to use portlet-mode
within supports Tag for specifying which modes the Portlet has supported. Following example shows you the way that in which you are able of seeing Edit & Help modes.
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 |
package com.journaldev.portlet; import java.util.Locale; import java.util.ResourceBundle; import javax.portlet.GenericPortlet; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class TitleChangedPortlet extends GenericPortlet{ public void doView(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException { response.getWriter().println("My Name Is:"+this.getPortletName()); } public void doEdit(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException { response.getWriter().println("Edit Mode On Portlet Name :"+this.getPortletName()); } public void doHelp(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException { response.getWriter().println("Help Mode On Portlet Name :"+this.getPortletName()); } public java.lang.String getTitle(RenderRequest request) { // Check whether the name of the portlet is programmaticTitleChangePortlet if(this.getPortletName().equals("ProgrammaticTitleChangePortlet")){ // If it's like that, just get the defined bundle ResourceBundle bundle = this.getPortletConfig().getResourceBundle(new Locale("en")); // Retrun the string that's corresponded for anyTitle property return (String)bundle.getObject("anyTitle"); } // else return the default, either that's defined in the portlet.xml or that's defined in the bundle return super.getTitle(request); } } |
portlet.xml
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 |
<?xml version="1.0" encoding="UTF-8"?> <portlet-app xmlns="https://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" version="1.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd https://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"> <portlet> <description>Write here a short description about your portlet.</description> <portlet-name>HelloWorldPortlet</portlet-name> <display-name>HelloWorldPortlet Portlet</display-name> <portlet-class>com.journaldev.portlet.HelloWorldPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>VIEW</portlet-mode> </supports> <portlet-info> <title>Title Is Set Using Simplest Way Portlet-Info XML Tag</title> <short-title>Hello World</short-title> <keywords>Hello,pluto</keywords> </portlet-info> </portlet> <portlet> <description>It's a Portlet that's setting title using Resource Bundle</description> <portlet-name>ResourceBundleTitleChangePortlet</portlet-name> <display-name>ResourceBundleTitleChangePortlet</display-name> <portlet-class>com.journaldev.portlet.TitleChangedPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>VIEW</portlet-mode> <portlet-mode>EDIT</portlet-mode> <portlet-mode>HELP</portlet-mode> </supports> <supported-locale>en</supported-locale> <resource-bundle>message</resource-bundle> </portlet> <portlet> <description>It's a Portlet that's setting title programmatically</description> <portlet-name>ProgrammaticTitleChangePortlet</portlet-name> <display-name>ProgrammaticTitleChangePortlet</display-name> <portlet-class>com.journaldev.portlet.TitleChangedPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>VIEW</portlet-mode> <portlet-mode>EDIT</portlet-mode> <portlet-mode>HELP</portlet-mode> </supports> <supported-locale>en</supported-locale> <resource-bundle>message</resource-bundle> </portlet> </portlet-app> |
Here’s detailed explanation for code listed above:
- You must define the modes that you want to make your Portlet support for.
- HelloWorldPortlet does support View mode as it’s Portlet with no Edit or Help.
- Other two Portlets have defined all of modes are available.
- Portal provides you the mode user interface for toggling between modes that are supported by the Portlet.
Summary
Portlet specification provide vast amount of APIs that you have to learn to be Portlet developer. This tutorial aimed to provide you additional details about GenericPortlet, PortletRequest, PortletResponse and others. Contribute us by commenting below and find downloaded source code.