ShowTable of Contents
Introduction
The use of DynaCache is recommended by IBM® since it is better managed and is a built-in IBM WebSphere® Application Server (WAS) feature. In situations in which we need to maintain large objects and/or object graphs for a user session, it is highly undesirable to maintain them in an httpsession or a portletsession due to their large memory footprint.
Using the proof-of-concept (POC) approach, we can leverage the benefits of WAS DynaCache by storing only the key in the portlet session while storing the actual object DynaCache; in that way, the session's memory footprint will be quite light.
Using the POC approach we can also share the large objects between two or more portlets/portlet applications by leveraging WAS DynaCache. Using SessionListener we can remove objects when the user logs off or the session times out, so that the DynaCache does not get swamped with unnecessary data. Also we can monitor Cache using CacheViewer portlet provided by IBM.
The objectives of this POC are to:
- Demonstrate that WAS assigns the same session ID to all Web application sessions connected to a specific user.
- Validate that sharing of cached data not only applies to portlets within one portlet application, but also to other portlet applications and to themes and skins.
- Implement SessionListener, which removes the key from DynaCache in the event of session timeout or logout.
To execute this POC, you must have WebSphere Application Server v5.1 or later and IBM WebSphere Portal Server v5.1 or later installed. (The POC for this article was executed on WebSphere Portal v7.0 and WebSphere Application Server v7.0.) Also, the developer executing this POC must be well versed in WebSphere Portal / WAS concepts.
For this POC we first create two JSR168 or JSR286 portlets, namely, TestPortlet1 and TestPortlet2.
TestPortlet1
In this portlet we enter some text on the sample portlet form, submit the form, and then save the text value into DynaCache with key as a sessionId. Here are the steps:
- Create a basic JSR168 or JSR286 portlet, using any integrated development environment (IDE) tool such as IBM Rational® Application Developer / Rational Software Architect / Eclipse (with the Portlet tools plug-in).
- The text entered on the form will be stored in a DynaCache. There are two buttons on the portlet form; clicking the Update button updates the DynaCache, and clicking the Submit button will perform no action if there's already a value in DynaCache. Listing 1 shows the sample JavaTMServer Pages (JSP) code.
Listing 1. Sample JSP code
<%@page session="false" contentType="text/html" pageEncoding="ISO-8859-1" import="java.util.*, javax.portlet.*, testportlet1.*" %>
<%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<portlet:defineObjects/>
<%
testportlet1.TestPortlet1SessionBean sessionBean = (testportlet1.TestPortlet1SessionBean)renderRequest.getPortletSession().getAttribute(testportlet1.TestPortlet1.SESSION_BEAN);
%>
<DIV style="margin: 6px">
<H3 style="margin-bottom: 3px">Proof of Concept for Caching - Portlet 1</H3>
<DIV style="margin: 12px; margin-bottom: 36px">
<%
/******** Start of sample code ********/
%>
<%
String formText = sessionBean.getFormText();
if( formText.length()>0 ) {
%>
Text stored in DynaCache for Session ID <b>(<%=sessionBean.getSessionId()%>)</b> : <b><%=formText%></b>
<%} else {%> No value is stored in DynaCache. <%
}
%>
<FORM method="POST" action="<portlet:actionURL/>">
<LABEL for="<%=testportlet1.TestPortlet1.FORM_TEXT%>">Enter the text:</LABEL><BR><BR>
<INPUT name="<%=testportlet1.TestPortlet1.FORM_TEXT%>" type="text"/><BR>
<INPUT name="<%=testportlet1.TestPortlet1.FORM_SUBMIT%>" type="submit" value="Submit"/>
<INPUT name="<%=testportlet1.TestPortlet1.FORM_UPDATE%>" type="submit" value="Update"/>
</FORM>
<% /******** End of sample code *********/ %>
</DIV>
</DIV>
3. Add the getter/setter in the default SessionBean class (for example, TestPortlet1SessionBean) created by the IDE (see listing 2).
Listing 2. Getter/setter code
// sessionId
private String sessionId = "";
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
4. Modify the Portlet class for TestPortlet1:
(a) Include the following import statements:
import com.ibm.websphere.cache.DistributedMap;
import commonutils.CommonUtils;
NOTE: We will eventually add the CommonUtils to wp_root/shared/app directory .jar file, but in the meantime we manually add the .jar in the build path for compilation purposes.
(b) As shown in listing 3, the processAction method is invoked when the user performs any action on the portlet form (clicks either the Submit or Update button). When the user clicks the Submit button, the sessionId is retrieved.
If the sessionId already exists as a key in Dynacache, no updates are made to its value in Dynacache; else, the DistributedMap is updated with the key as the sessionId and the value as the value entered on the portlet form by the user.
When the user clicks the Update button, the sessionId is retrieved, and the value entered by the user on the portlet form is updated for the key as sessionId.
Listing 3. Code for the processAction method
public void processAction(ActionRequest request, ActionResponse response) throws PortletException, java.io.IOException {
// initialize the DynaCache map
DistributedMap ParameterMap = null;
ParameterMap = CommonUtils.getMap();
String textValue = "";
// if either Submit or Update clicked
if( null != request.getParameter(FORM_SUBMIT) || null != request.getParameter(FORM_UPDATE)) {
// Set form text in the session bean
TestPortlet1SessionBean sessionBean = getSessionBean(request);
// get the text entered on the portlet form sessionBean.setFormText(request.getParameter(FORM_TEXT));
// get the sessionId sessionBean.setSessionId(request.getPortletSession().getId());
if( null != sessionBean && (!("").equals(sessionBean.getFormText()) || null != sessionBean.getFormText())) {
textValue = (String)ParameterMap.get(request.getPortletSession().getId());
// if the object already exists in DynaCache perform no action else
Insert/update the DynaCache map
if(null == textValue || ("").equals(textValue) || null != request.getParameter(FORM_UPDATE)) {
ParameterMap.put(sessionBean.getSessionId(), sessionBean.getFormText());
} else {
sessionBean.setFormText(textValue);
}
}
}
}
TestPortlet2
In this portlet we retrieve the object from the DynaCache, using the sessionId as the key, and display the same on the portlet form:
- Create a basic JSR168 or JSR286 portlet using any IDE tool like RAD/RSA/Eclipse (with the Portlet tools plug-in).
- The object value stored in the DynaCache for the respective sessionId is displayed on the portlet JSP shown in listing 4.
Listing 4. Portlet JSP code
<%@page session="false" contentType="text/html" pageEncoding="ISO-8859-1" import="java.util.*, javax.portlet.*, testportlet2.*" %>
<%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<portlet:defineObjects/>
<%
testportlet2.TestPortlet2SessionBean sessionBean = (testportlet2.TestPortlet2SessionBean)renderRequest.getPortletSession().getAttribute(testportlet2.TestPortlet2.SESSION_BEAN);
%>
<DIV style="margin: 6px">
<H3 style="margin-bottom: 3px">Proof of Concept for Caching - Portlet 2</H3>
<DIV style="margin: 12px; margin-bottom: 36px">
<%
/******** Start of sample code ********/
%>
<%
String formText = sessionBean.getFormText();
if( formText.length()>0 ) {
%>
Text stored in DynaCache for Session ID (<%=sessionBean.getSessionId()%>) from Portlet 1 : <b><%=formText%></b>
<%} else {%> No value is stored in DynaCache. <%
}
%>
<% /******** End of sample code *********/ %>
</DIV>
</DIV>
3. Add the getter/setter in default SessionBean class (for example, TestPortlet2SessionBean) created by IDE (see listing 5).
Listing 5. Getter/setter code
// sessionId
private String sessionId = "";
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
4. Modify the Portlet class for TestPortlet2:
(a) Include following import statements:
import com.ibm.websphere.cache.DistributedMap;
import commonutils.CommonUtils;
NOTE: We will eventually add the CommonUtils to wp_root/shared/app .jar directory, but in the meantime we can manually add the .jar in the build path for compilation purposes.
(b)The doView method is a render method in which the DistributedMap is initialized and, using sessionId as a key, its the object value is retrieved from the Dynacache. The same object is set in a form bean for displaying its value on the portlet jsp (see listing 6).
Listing 6. Code for the doView method
public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {
// Set the MIME type for the render response
response.setContentType(request.getResponseContentType());
// Check if portlet session exists
TestPortlet2SessionBean sessionBean = getSessionBean(request);
if( sessionBean==null ) {
response.getWriter().println("<b>NO PORTLET SESSION YET</b>");
return;
}
// initialize the DynaCache map
DistributedMap ParameterMap = null;
ParameterMap = CommonUtils.getMap();
sessionBean.setSessionId(request.getPortletSession().getId());
// retrieve the object from DynaCache using the sessionId as the key
String textValue = (String)ParameterMap.get(sessionBean.getSessionId());
if( null != textValue || ("").equals(textValue)) {
sessionBean.setFormText(textValue);
}
// Invoke the JSP to render
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(getJspFilePath(request, VIEW_JSP));
rd.include(request,response);
}
Attached to this article are two portlets (TestPortlet1.war & TestPortlet2.war) and a .jar file (CommonUtils.jar) including the source code. You can set up and deploy the sample code by following the instructions in the next section.
NOTE: The code contained in the two portlets and .jar file is just sample code for demonstration purpose and may be further optimized to meet specific requirements/needs.
Testing the POC using the sample code
This POC assumes that WebSphere Portal v7.0 and WebSphere Application Server v7.0 are installed.
Here are the steps:
1. Create an Object Cache instance in WAS:
a) Log in to WebSphere Application Server Admin console
b) Navigate to Resources --- Cache instances --- Object cache instances (see figure 1).
Figure 1. Object cache instances
(c) Click New, and enter the configuration details as shown in figure 2; click OK.
Figure 2. Configuration tab
(d) On the following screen, click Save, and then click Save again on the next screen.
2. Change the session timeout time to 5 minutes (This is done so that we do not need to wait longer to validate the POC objectives. After the POC execution the session timeout can be reverted to its original set value, which is typically 30 minutes.):
(a) Log in to the WAS Admin console and navigate to Servers --- Server Types --- WebSphere application servers (see figure 3).
Figure 3. WebSphere application servers
(b) Select WebSphere_Portal, and then click the Session management link (see figure 4).
Figure 4. Session management link
(c) Change the Session timeout to 5 minutes, as shown in figure 5. Click OK.
Figure 5. Set timeout to 5 minutes
(d) Click OK again, and on the following screen click Save. Click Save again on the following screen.
(e) Repeat these steps for “server1”.
3. Copy the CommonUtils.jar file into the “WP_ROOT/shared/app” folder.
4. Add the SessionListener to web.xml:
(a) Open web.xml located at:
wp_profile_root\config\cells\<cellname>\applications\wps.ear\deployments\wps\wps.war\WEB-INF
(b) Add following lines in web.xml:
<listener>
<listener-class>commonutils.SessionListener</listener-class>
</listener>
(c) Save and close the file.
5. Restart Portal Server and create pages:
(a) Log in to WebSphere Portal Server Administration, and navigate to Portal User Interface --- Manage Pages (see figure 6).
Figure 6. Manage Pages
(b) Create two pages, say, Test Portlet Page 1 & Test Portlet Page 2, at the desired level in Content Root.
6. Deploy portlets:
(a) Log in to WebSphere Portal Server Administration, and navigate to Portlet Management --- Web Modules (see figure 7).
Figure 7. Web Modules
(b) Click Install, and browse to TestPortlet1.war; click Next.
(c) Click Finish on the next page.
(d) Repeat these steps deploying TestPortlet2.war.
7. Assign TestPortlet1 & TestPortlet2 to pages Test Portlet Page 1 & Test Portlet Page 2, respectively, created in Step 5.
8. Finally, to test the POC, log in to WebSphere Portal and, using wp_root\log\SystemOut.log, we can check the session ID being created:
(a) Click on TestPortlet1 deployed on Test Portlet Page 1; enter some text in the text box.
(b) Click either the Submit or Update button.
(c) On form submission, the value of text box is stored in DynaCache with key as sessionId; click on Test Portlet Page 2.
(d) Whatever value is stored in DynaCache for the respective sessionId will be now displayed.
(e) Log out of the portal.
(f) Using wp_root/log/SystemOut.log, we can check the session ID being destroyed.
(g) Repeat Steps (a) through (f) and wait for 5 minutes until the session times out.
Conclusion
This article discussed different aspects of using WAS Dynacache, demonstrating how Dynacache can be used as an alternative to sharing data between two or more portlets and portlet applications for the same user session. It also presented the idea of using WAS Dynacache for storing large objects in applications in which memory footprint is critical.
Resources
developerWorks WebSphere Portal product page:
http://www.ibm.com/developerworks/websphere/zones/portal/
developerWorks tutorial, “Using DynaCache to improve the performance of your WebSphere Process Server or WebSphere ESB solution:”
http://www.ibm.com/developerworks/websphere/tutorials/0912_telerman/
WebSphere Portal discussion forum:
http://www.ibm.com/developerworks/forums/forum.jspa?forumID=168About the author
Jaikisshan Lakhani designs and develops custom applications using WebSphere Portal and other WebSphere products. He has experience and interest in integrating WebSphere Portal with collaboration software including IBM Lotus® Sametime® and IBM Lotus Notes®. He led an effort in architecting solutions involving WebSphere Portal and several other J2EE products while he worked with IBM Global Services as a Senior Systems Analyst for 6 years.