At the Portlet concepts detailed Tutorial – PART I, we’ve explained a lot details about concepts like PortletRequest
, PortletResponse
, RenderRequest
, ActionRequest
, RenderResponse
, ActionReponse
and all their respective functionality as we saw how can they play roles when they’re coming to handle users’ requests.
This tutorial is a follow-up for what already we’ve started at the PART-I. Portlet Context, Sessions, Inter-portlet communication and Caching, all of these concepts would be discussed here as well as a set of samples have to be introduced making these concepts very simple to understand.
Portlet Context
Just like Servlets, Portlet uses the Portlet Context object to get access for logging, resources, attributes, and initialization parameters that are defined in the Portlet application (WAR).
The Portlet context object is available for every deployed Portlet application, in case you’ve deployed more than one Portlet in the Portlet application, they can share the Portlet context object as they can use it for set or retrieve application-wide data.
Similar for Servlets and JSPs, they are also sharing the Portlet context instance through using of ServletContext. In case you’ve provided a Servlets and JSPs inside your Portlet application, they (Portlets, JSPs, Servlets) are able of getting the same Portlet context.
Following sample shows you a simple demonstration of how can access the Portlet context from within different resources got deployed at the same Portlet application.
HelloPortlet.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package com.journaldev.portlet; import java.io.IOException; import javax.portlet.GenericPortlet; import javax.portlet.PortletConfig; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class HelloPortlet extends GenericPortlet{ public void init(PortletConfig config) throws PortletException { super.init(config); // Set variable along context scope config.getPortletContext().setAttribute("anonymousVariable", "Just Variable"); } public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException{ response.getWriter().print("<p>Hello Portlet</p>"); } } |
HelloServlet.java
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 |
package com.journaldev.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class HelloServlet */ public class HelloServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public HelloServlet() { super(); } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().print("<p>Portlet context anonymous variable value Is "+getServletContext().getAttribute("anonymousVariable")+"</p>"); } } |
Here’s detailed explanation for the code listed above:
- You can get
PortletContext
instance by accessinggetPortletContext()
against config object that’s already provided by the Portlet container while init() the Portlet. Also, any Portlet that extends the GenericPortlet has the ability to access the Portlet context by calling this.getPortletContext() from anywhere inside the Portlet. - Setting variables along context scope would be accessible via all Portlets, Servlets and JSPs that are located within the same Portlet application (WAR).
- Servlet context is the way that you must use for accessing the Portlet context variables as both of them is relevant for the same Portlet application. At the same way, Portlet context can be access any variables that already set by the Servlet context.
- The attributes on the Portlet context are shared data for Portlets and Servlets in a Portlet application. These attributes are pairs of name/value, name is a String object and value is an Object that can be any type of objects inside your domain.
- SetAttributes(), getAttributes(), getAttributesNames() and removeAttributes() are methods used for setting, reading and removing the attributes along context scope.
Context Initialization Parameters
Context initialization parameters are those variables defined inside web.xml file. As being of Portlets and Servlets can be defined at the same Portlet application, it’s also possible for the Portlet to access the context initialization parameters. Following sample is just a trivial update for the sample shown above, but this time a context parameter will be read by both of Servlet and Portlet.
HelloPortlet.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package com.journaldev.portlet; import java.io.IOException; import javax.portlet.GenericPortlet; import javax.portlet.PortletConfig; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class HelloPortlet extends GenericPortlet{ public void init(PortletConfig config) throws PortletException { super.init(config); } public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException{ response.getWriter().print("<p>Anonymous Context Variable Value Is "+this.getPortletContext().getInitParameter("contextParam")+"</p>"); } } |
HelloServlet.java
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 |
package com.journaldev.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class HelloServlet */ public class HelloServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public HelloServlet() { super(); } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().print("<p>Portlet context anonymous variable value Is "+getServletContext().getInitParameter("contextParam")+"</p>"); } } |
web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "https://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.journaldev.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <context-param> <param-name>contextParam</param-name> <param-value>contextValue</param-value> </context-param> </web-app> |
Here’s detailed explanation for the code listed above:
- It’s very trivial for getting context parameters accessed through Servlet. But you must use the PortletContext object that already passed to your Portlet for get read of context parameters.
- Use of getInitParameterNames() against of PortletContext instance will list to you all defined context parameters.
- Portlets may have their own initialization parameters that could be accessed via using of getInitParameter() against PortletConfig() object. You can find below sample Portlet deployment descriptor that has defined an initialization parameters.
portlet.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?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> <init-param> <name>PortletParam</name> <value>ParamValue</value> </init-param> <display-name>Hello Portlet</display-name> <portlet-name>Hello</portlet-name> <portlet-class>com.journaldev.portlet.HelloPortlet</portlet-class> <description>Hello Portlet</description> <portlet-info> <title>Hello Portlet</title> <keywords>Hello, Portlet</keywords> <short-title>Hello Portlet</short-title> </portlet-info> </portlet> </portlet-app> |
Access Resources
Beside all of these discussed facilities that the PortletContext provides, it’s also applicable for you to access resources either defined within your web application or located at any path within your system folder using the getResource(), getResourceAsStream() and getRealPath(). Following sample shows you how can we use PortletContext object for reading resources that are defined differently.
HelloPortlet.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.journaldev.portlet; import java.io.IOException; import java.io.InputStream; import javax.portlet.GenericPortlet; import javax.portlet.PortletConfig; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class HelloPortlet extends GenericPortlet{ public void init(PortletConfig config) throws PortletException { super.init(config); } public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException{ System.out.println("Got File :: "+this.getPortletContext().getResource("/index.html")); System.out.println("Got File :: "+this.getPortletContext().getRealPath("file:///c:/index.html")); InputStream input = this.getPortletContext().getResourceAsStream("/index.html"); byte [] bytes = new byte[input.available()]; input.read(bytes, 0, input.available()); response.getPortletOutputStream().write(bytes); } |
Here’s detailed explanation for code listed above:
- Get resources is applicable via using of Portlet Context instance.
- When it comes to read the resources via getResources or any methods relevant for, you should provide a forward slash before the remaining path.
- GetRealPath is used for reading the files located at the externally (i.e outside of Portlet application).
- GetResourcePaths() method it takes partial path as argument and returns all resources’ paths that start with the path.
Logging
The Portlet may write the messages using logging mechanism. Like Servlet logging, the location of the log messages is dependent on the implementation of the container as it may be file, database entry, console or any other way the container may choose.
Apache Pluto has been configured to print messages out onto your console. Apache has always preferred to use Apache’s Log4j instead of using default provided implementation. Following sample shows you how can we use both of APIs (either default logging messages or Log4j) for getting messages logged.
HelloPortlet.java
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 |
package com.journaldev.portlet; import java.io.IOException; import java.io.InputStream; import javax.portlet.GenericPortlet; import javax.portlet.PortletConfig; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import org.apache.log4j.Logger; public class HelloPortlet extends GenericPortlet{ Logger logger = Logger.getLogger(HelloPortlet.class); public void init(PortletConfig config) throws PortletException { super.init(config); } public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException{ this.getPortletContext().log("Got File :: "+this.getPortletContext().getResource("/index.html")); this.getPortletContext().log("Got File :: "+this.getPortletContext().getRealPath("file:///c:/index.html")); logger.debug("Got File :: "+this.getPortletContext().getResource("/index.html")); logger.debug("Got File :: "+this.getPortletContext().getRealPath("file:///c:/index.html")); InputStream input = this.getPortletContext().getResourceAsStream("/index.html"); byte [] bytes = new byte[input.available()]; input.read(bytes, 0, input.available()); response.getPortletOutputStream().write(bytes); } } |
log4j.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 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration debug="true" xmlns:log4j='https://jakarta.apache.org/log4j/'> <appender name="console" class="org.apache.log4j.ConsoleAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" /> </layout> </appender> <appender name="file" class="org.apache.log4j.FileAppender"> <param name="file" value="${catalina.home}/logs/log.log" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" /> </layout> </appender> <root> <level value="ALL" /> <appender-ref ref="console" /> <appender-ref ref="file" /> </root> </log4j:configuration> |
log.log
1 2 3 4 |
2014-09-22 21:31:26 DEBUG HelloPortlet:24 - Got File :: jndi:/localhost/PortletConceptsPartTwo/index.html 2014-09-22 21:31:27 DEBUG HelloPortlet:25 - Got File :: D:Apache Plutopluto-2.0.3webappsPortletConceptsPartTwofile:c:index.html |
Here’s important notes you should notice:
- We’ve used default logging mechanism that’s provided by Apache Pluto, in that the messages will be logged out onto your console as you’ve seen with INFO messages above.
- We’ve also logged out the same messages using the Log4J API. Apache has always recommended you to adopt it into your application. You can see the messages onto your console beside newly created file log.log. Those DEBUG messages are printed out using Log4J logging mechanism.
- Log4j requires defining of Appenders, as such, two Appenders are defined; one for console logging while other for file purpose.
- Take in your consideration that the log4j.xml file should be located beneath your WEB-INF/classes to be considered once the logger object get initialized.
Sessions
Portlet application can use PortletSession
for tracking the user across multiple client requests for Portal page. PortletSession
tracking mechanism that Portlet adopts is very similar for what’s already defined in the Servlet API.
A Portlet can get access for the PortletSession
object by calling getPortletSession()
method against PortletRequest
instance (i.e RenderRequest
or ActionRequest
). Similar for Servlet
, getPortletSession()
has been provided with two different favors; getPortletSession()
and getPortletSession(boolean)
. Invoking the first method will aid you get the already created PortletSession, in case there is no PortletSession
created, a new one will be created. Second method is useful to determine whether there is a current PortletSession
or not.
True value will make the getPortletSession(boolean)
similar for getPortletSession()
, while False value won’t allow the PortletSession
to be created in case there is no default one. Null value will be returned in case no PortletSession
there.
Every Portlet application has its own PortletSession
as you can’t share the PortletSession
between different Portlet applications. Beside that, every users has its own PortletSession
object as you can’t share the same instance of PortletSession
between more than one user.
Sessions – Portlet Scope Vs Application Scope
As we’ve stated earlier, Portals don’t share the PortletSession object neither between users nor between Portlet applications. But it’s important to know that when it comes to use the PortletSession for getting your attributes retained, that the attributes would be retained at the PortletScope level unless you’ve mentioned something else.
Following sample shows you how can we use the PortletSession for retaining objects against PortletSession/PortletScope level and what’s the effect that way could made for other Portlets.
HelloPortlet.java
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 |
package com.journaldev.portlet; import java.io.IOException; import java.util.Enumeration; 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; import org.apache.log4j.Logger; public class HelloPortlet extends GenericPortlet{ Logger logger = Logger.getLogger(HelloPortlet.class); public void init(PortletConfig config) throws PortletException { super.init(config); } public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException{ Enumeration<String> names = request.getPortletSession().getAttributeNames(); StringBuffer buffer = new StringBuffer(); while(names.hasMoreElements()){ String name = names.nextElement(); buffer.append(name+" :: "+request.getPortletSession().getAttribute(name)+"n"); } response.getWriter().print("<form action="+response.createActionURL()+">" + "<p>Portlet Session Attributes</p>" + buffer + "<input type="submit" value="Just Do Action"/>" + "</form>"); } public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException{ if(this.getPortletName().equals("HelloOne")){ request.getPortletSession().setAttribute("anonymousObject", "PortletSession Attribute"); } } } |
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 |
<?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> <init-param> <name>PortletParam</name> <value>ParamValue</value> </init-param> <display-name>Hello Portlet One</display-name> <portlet-name>HelloOne</portlet-name> <portlet-class>com.journaldev.portlet.HelloPortlet</portlet-class> <description>Hello Portlet One</description> <portlet-info> <title>Hello Portlet One</title> <keywords>Hello, Portlet, One</keywords> <short-title>Hello Portlet One</short-title> </portlet-info> </portlet> <portlet> <init-param> <name>PortletParam</name> <value>ParamValue</value> </init-param> <display-name>Hello Portlet Two</display-name> <portlet-name>HelloTwo</portlet-name> <portlet-class>com.journaldev.portlet.HelloPortlet</portlet-class> <description>Hello Portlet Two</description> <portlet-info> <title>Hello Portlet Two</title> <keywords>Hello, Portlet, Two</keywords> <short-title>Hello Portlet Two</short-title> </portlet-info> </portlet> </portlet-app> |
Here’s some important points you may have to notice:
- We’ve defined two different Portlets using the same
HelloPortlet
instance.HelloOne
andHelloTwo
are two different Portlets have used the same Portlet instance. - Both of them have defined a submit action. Just one Portlet is able of adding a new attribute against
PortletSession
and it’s for sure that one has the nameHelloOne
. HelloTwo
Portlet can’t read the PotletSession’s attribute that was set byHelloOne
. Render phase ofHelloTwo
Portlet can’t find any attributes registered againstPortletSession
, although ananonymousObject
attribute has been set byHelloOne
.- Calling of
setAttribute(name,value)
againstPortletSession
will retain the attributes againstPortletSession/PortletScope
scope. PortletScope
attributes are stored for a given instance of the Portlet on the Portal page, if there is more than one instance of the Portlet on a page, thePortletScope
will be different for each.- When an object is saved on the Session in the PortletScope, it has a
namespace
prefixed appended to the attribute name. The name space is different for each instance of the Portlet on the page.
Now, let’s see what’s the difference that can we make if the attributes are got saved on PortletSession/ApplicationScope
. Following sample shows you how can we retain an Object against PortletSession/ApplicationScope
using one Portlet and read it using another one.
HelloPortlet.java
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 |
package com.journaldev.portlet; import java.io.IOException; import java.util.Enumeration; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.GenericPortlet; import javax.portlet.PortletConfig; import javax.portlet.PortletException; import javax.portlet.PortletSession; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import org.apache.log4j.Logger; public class HelloPortlet extends GenericPortlet{ Logger logger = Logger.getLogger(HelloPortlet.class); public void init(PortletConfig config) throws PortletException { super.init(config); } public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException{ // Get all attributes' names that are defined along side of Application Scope Enumeration<String> names = request.getPortletSession().getAttributeNames(PortletSession.APPLICATION_SCOPE); StringBuffer buffer = new StringBuffer(); while(names.hasMoreElements()){ String name = names.nextElement(); // Get the attribute's value buffer.append(name+" :: "+request.getPortletSession().getAttribute(name,PortletSession.APPLICATION_SCOPE)+"n"); } response.getWriter().print("<form action="+response.createActionURL()+">" + "<p>Portlet Session Attributes</p>" + buffer + "<input type="submit" value="Just Do Action"/>" + "</form>"); } public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException{ if(this.getPortletName().equals("HelloOne")){ // Define attribute along side of Application Scope request.getPortletSession().setAttribute("anonymousObject", "PortletSession Attribute",PortletSession.APPLICATION_SCOPE); } } } |
Here’s detailed explanation for code listed above:
- Instead of using default
setAttribute()
, we’ve used overloaded method which accepts a scope as parameter. PortletSession
defines two constants;PORTLET_SCOPE
andAPPLICATION_SCOPE
.HelloOne
has set the attribute andHelloTwo
has read it.- Any Portlet in the Portlet application can retrieve or modify any attributes in the user’s session stored as application scope.
- You can use
getAttributesNames()
to get all names of the attributes that are defined along side ofPortletSession
. - To remove attribute from the
PortletSession
you can useremoveAttributes()
. - For each PortletSession’s attribute method, an overloaded version that takes scope as a parameter.
PortletSession
has integrated seamlessly with the Servlet session and JSP. This concept would be discussed intensively later on.
Interportlet Communication
Before initiating a Java Portlet Specification, each Portal vendor has provided its own proprietary mechanism for getting interportelt communication achieved. After Java Portlet Specification got released, the interportlet communication has been organized in a standard way.
PortletSession
has been used for this purpose as you must be aware of the following issues:
- The Portlet places the parameters in the sessions’s application scope.
- In case you’ve used Portlet container that’s supported the cache mechanism, be sure not to enable caching for the Portlets. Some Portals won’t implement caching, if a Portlet that supports caching runs on a Portal that doesn’t support caching, the Portlet will run normally.
- Portlet’s modifications must occur during action request to guarantee that the updates propagate to every Portlet during render request phase.
Summary
As being Portal contains a lot of APIs that you must be aware of. This tutorial has focused on the remaining APIs that you should take care of when it comes to deal with any Portal providers and especially Apache Pluto. Contribute us by commenting below and find downloaded source code.