Automating portlet deployment on WebSphere Portal using Apache Maven

If you download sample code from my blog you will notice that i use Apache Maven as build tool, my source code is structured in Maven format and you will find a pom.xml file which is maven build script.

I like to make my development environment as simple as possible and automate deployment so that i can test my changes rapidly. So i did create this very basic approach for automating deployment of portlet to WebSphere Portal using xmlaccess + maven. This approach is very similar to how you can automate deployment of portlet to Apache Pluto. You can download sample AjaxPortlet from here

I follow these steps whenever i create a new project

  • I create a simple portlet.xml that does not have any id portlet application id like this

    <?xml version="1.0" encoding="UTF-8"?>
    <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
    version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd
    http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
    <portlet>
    <portlet-name>AjaxPortlet</portlet-name>
    <display-name>Ajax Portlet</display-name>
    <portlet-class>com.webspherenotes.portlet.AjaxPortlet</portlet-class>
    <expiration-cache>0</expiration-cache>
    <supports>
    <mime-type>text/html</mime-type>
    <portlet-mode>view</portlet-mode>
    </supports>
    <portlet-info>
    <title>Ajax Portlet</title>
    <short-title>Ajax Portlet</short-title>
    <keywords>Ajax Portlet</keywords>
    </portlet-info>
    </portlet>
    </portlet-app>

    The value of portlet-name is important for deployment

  • Then i create a pom.xml file which is build script for for my portlet and has custom integration-test target that i use for deploying portlet

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    http://maven.apache.org/maven-v4_0_0.xsd">

    <!-- Change this to something akin to your java package structure -->
    <groupId>com.webspherenotes.performance</groupId>
    <modelVersion>4.0.0</modelVersion>
    <!-- Version of this app -->
    <version>1.0</version>
    <!-- Base name of the war file without .war ext -->
    <artifactId>AjaxPortlet</artifactId>
    <packaging>war</packaging>
    <name>${pom.artifactId}</name>
    <!-- Dependency Version Properties ======================================= -->
    <properties>
    <portlet-api.version>2.0</portlet-api.version>
    <servlet-api.version>2.4</servlet-api.version>
    <jsp-api.version>2.0</jsp-api.version>
    <wps.home>/software/IBM/WebSphere</wps.home>
    <wps.url>http://localhost:10040/wpcert/config</wps.url>
    <wps.admin.name>wasadmin</wps.admin.name>
    <wps.admin.password>wasadmin</wps.admin.password>
    <xmlaccess.path>/software/work/blog/performance/ajax/AjaxPortlet /UpdatePortlet.xml</xmlaccess.path>

    </properties>
    <dependencies>
    <dependency>
    <groupId>javax.portlet</groupId>
    <artifactId>portlet-api</artifactId>
    <version>${portlet-api.version}</version>
    <scope>provided</scope><!-- Prevents addition to war file -->
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>${servlet-api.version}</version>
    <scope>provided</scope>
    </dependency>
    </dependencies>

    <build>
    <finalName>${pom.name}</finalName>
    <plugins>

    <plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <executions>
    <execution>
    <phase>integration-test</phase>
    <configuration>
    <tasks>
    <property environment="env"/>
    <exec executable="/bin/bash" dir="${wps.home}/PortalServer/bin">
    <arg line="xmlaccess.sh -user ${wps.admin.name} -password $
    {wps.admin.password} -url ${wps.url} -in ${xmlaccess.path}"/>
    </exec>
    </tasks>
    </configuration>
    <goals>
    <goal>run</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
    <source>1.5</source>
    <target>1.5</target>
    </configuration>
    </plugin>
    </plugins>
    </build>
    </project>



    In this file you will have to set properties for following things

    • wps.home: Location of portal installation base on your machine

    • wps.url: Config URL for xmlaccess for your portal server it will be http://localhost:10040/wps/config by default

    • wps.admin.name: User name for WPS admin

    • wps.admin.password: Password for WPS admin

    • xmlaccess.path: Path of UpdatePortlet.xml, which is a xmlaccess file that i use for deploying my portlets


    In most of the cases when i create a new portlet only value that will change is xmlaccess.path to point to new updateportlet.xml which is already always in the same directory as that of pom.xml, i bet i can read that directory name using Apache Maven variable but i will have to get some time to find that out

    In my script i have a integration-test target which makes use of ant to execute xmlaccess script that updates the portlet, this ant script gets executed when i execute mvn integration-test

  • The final step is to create a UpdatePortlet.xml file like this

    <?xml version="1.0" encoding="UTF-8"?>
    <request
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="PortalConfig_1.4.xsd"
    type="update"
    create-oids="true">

    <!-- sample for updating a portlet from a new version of the WAR file -->
    <portal action="locate">

    <web-app action="update" active="true" uid="AjaxPortlet.war.webmod">
    <url>file:////software/work/blog/performance/ajax/AjaxPortlet/target/AjaxPortlet.war</url>
    <portlet-app action="update" active="true" uid="AjaxPortlet.war">
    <portlet action="update" active="true" objectid="thePortlet" name="AjaxPortlet">
    </portlet>
    </portlet-app>
    </web-app>

    </portal>
    </request>


    The UpdatePortlet.xml file needs information such as location of .war file, PortletName that i can get portlet.xml, .war file name that i can get from pom.xml and once i change the updateportlet.xml to use correct values i am all set



Once my initial setup is in place i execute the mvn integration-test command to install the portlet for first time or update it, when i do that it take some time to build and then deploy it looks like this

Setting additional headers while making Ajax request

In the How to prevent caching of Ajax request using Dojo i talked about how you can use preventCache flag to change the URL of Ajax request so that browser and caching proxy is not able to cache it.

But what if you want the response to be cached but validated, or you want to set Cache-Control: max-age=0 header so that the request is validated end to end, similar to what happens when you click on browser refresh button.

The dojo toolkit provides a way to set headers while making xhrGet() call like this

dojo.xhrGet({
url: "<portlet:resourceURL/>",
headers: {
"Cache-control" :"max-age=0",
"PreventCache": "nocache"
},

load: function(data, ioargs){
dojo.byId("resourceResponse").innerHTML = data;
},
error: function(error,ioargs){
alert(error);
}
});


You can set headers by adding header field with map of name and values while making xhrGet() call. In my case i am setting Cache-Control and PreventCache header. and this is what happens when i make the request. If the resource is cached by browser the browser will not make a conditional get request to verify if it is changed. But it will add the headers like this

Connecting to default portal database

Recently i had a requirement for which i wanted to connect to default Apache Derby database that WebSphere Portal uses for storing configuration. So i followed these steps to connect using Eclipse, but they can be applied anywhere else


  • If you dont connect to any other Apache Derby database on regular basis then first you will have to configure DB driver for Derby in Eclipse. For that click on Windows - Preferences, it will open a dialog box in that select Data Management - Connectivity - Driver definition and specify location of derby driver. If your using portal you can find the db driver in WebSphere/Appserver/Derby directory



  • Next step would be to define connection for connecting to portal database, that you can do by switching to Database development perspective and selecting new connection like this

    By default portal derby database is stored in the portalbase/wp_profile/PortalServer/derby/wpsdb directory so select that as database location and dont enter any userid password
    Important Note You could connect to portal db from either JDBC client or portal server at one time so before connecting to portal database shutdown your portal server. And don't forget to disconnect from the database before you restart portal or you will get some wired error

  • Once the connection is defined you can connect to database and you will see that the tables are divided into different schema, RELEASE schema holds all the portal level configuration such portlet, page definitions


How to prevent caching of Ajax request using Dojo

The Portlet Specification 2.0 allows you set cache-control header on response of serveResource() call. So what you can do is inside the serveResource() method set max-age for your response like this
PortletResponse.getCacheControl().setExpirationTime(100000);

Now the question is what if i want to prevent response from being cached if thats the case you can set preventCache flag to true while making dojo request like this


dojo.xhrGet({
url: "",
preventCache: true,
load: function(data, ioargs){
dojo.byId("resourceResponse").innerHTML = data;
},
error: function(error,ioargs){
alert(error);
}
});


What the preventCache flag would do is that it will append dojo.preventCache query parameter to the url before making request so in my case it will take the <portlet:resourceURL/> and append the dojo.preventCache query parameter and the value of dojo.preventCache parameter will change every time the xhr call is made so server will always return the full response.

Using mod_rewrite to serve new version of cached resource

You should use following best practice when setting Cache-Control/Expires header for your resource

  1. The dynamic HTML should never be cache

  2. The static resource such as images/ JavaScript and CSS should be cached for ever



The question would be what if say my JavaScript is cached and i realized that there is a bug and i want to roll out a new version of JavaScript, in that case you should change the reference to JavaScript file in your HTML.
Ex.
<script src="/resource/test.js">

Should be changed to

<script src="/resource/test-v1.js">

Important Note: Basic idea is you should somehow change the URL of the resource in your HTML and that will force browser to make a new request for the resource.

One way of doing that is changing the name of the file but sometimes its not possible to upload new version of JavaScript with different name on your HTTP server in that case you have one other option, you can update the test.js at same location but change URL to it in your HTML and let HttpServer rewrite the URL to same location


Ex.
<script src="/resource/test.js">

You have a new version of test.js at same location and you want to force browser to download new version in that case change reference to test.js in HTML like this

<script src="/resource/v1/test.js">

Browser will make a new request to the HTTP server and on the HTTP server you can create a URL rewrite rule like this


RewriteEngine on
RewriteLogLevel 3
RewriteLog "/tmp/rewrite.log"

RewriteRule /resource/v([0-9]+)/(.*) /$2


What this rule will do is it will take the incoming request /resource/v1/test.js and rewrite it to /resource/test.js and which will return a new copy of the test.js to browser

What is mod_rewrite ?

The Apache HTTP Server has a mod_rewrite module that you can use to rewrite the URL of the request that is coming into Apache HTTP Server

This module uses a rule-based rewriting engine (based on a regular-expression parser) to rewrite requested URLs on the fly. It supports an unlimited number of rules and an unlimited number of attached rule conditions for each rule, to provide a really flexible and powerful URL manipulation mechanism. The URL manipulations can depend on various tests, of server variables, environment variables, HTTP headers, or time stamps. Even external database look ups in various formats can be used to achieve highly granular URL matching.

This module operates on the full URLs (including the path-info part) both in per-server context (httpd.conf) and per-directory context (.htaccess) and can generate query-string parts on result. The rewritten result can lead to internal sub-processing, external request redirection or even to an internal proxy throughput.

If you want to use the functionality provided by the mod_rewrite module then you will have to first turn this module on by adding these line to the httpd.conf


RewriteEngine on


Then you can add the rewrite rules Ex. if you want to rewrite a incoming /test.js to say /new/test.js then you can add a rule like this


RewriteRule /test.js /new/test.js


If your new to the mod_rewrite module then you turn on log for the mod_rewrite module by adding these lines to your httpd.conf


RewriteLogLevel 3
RewriteLog "/tmp/rewrite.log"


Once you do that mod_rewrite will generate log for every incoming request and write statements for what is incoming request url and if it is applying rewrite rule to it and why

How to get Portlet Window ID

You can get WindowId of the portlet inside your portlet using request.getWindowID() method. This method was introduced in the JSR 286.


System.out.println("Portlet Window Id -> " +request.getWindowID());
System.out.println("Portlet Name Space -> " +response.getNamespace());


I tried printing the following two values in the System.out and this is what i get


Portlet Window Id -> 7_VVILMKG100N080IA0G757C00O3
Portlet Name Space -> ns_7_VVILMKG100N080IA0G757C00O3_


As you can see the name space is nothing but ns_portletwindowid_

If export page definition this is what you will get, as you can see the portletWindowId is nothing but objectId of the control element that holds this portlet


<?xml version="1.0" encoding="UTF-8"?>

<request xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" build="wp6103_201_01" type="update" version="6.1.0.3"
xsi:noNamespaceSchemaLocation="PortalConfig_6.1.0.2.xsd">
<portal action="locate">
<web-app action="locate" domain="rel" objectid="1_VVILMKG100N080IA0G757C0000" uid="ServletPopup.war.webmod">
<servlet action="locate" domain="rel" name="ServletPopup" objectid="V_VVILMKG100N080IA0G757C0002"/>
<portlet-app action="locate" domain="rel" name="Application Name not available for this Application" objectid="2_VVILMKG100N080IA0G757C0004"
uid="ServletPopup.war">
<portlet action="locate" domain="rel" name="ServletPopup" objectid="3_VVILMKG100N080IA0G757C0006"/>
</portlet-app>
</web-app>
<content-node action="locate" domain="rel" objectid="6_000000000000000000000000A0" uniquename="wps.content.root"/>
<content-node action="locate" domain="rel" objectid="6_CGAH47L008IC40I4BOR2EO00I3" uniquename="ibm.portal.Home"/>
<content-node action="locate" domain="rel" objectid="6_VVILMKG10GUS50I24V0O5Q2002"/>
<content-node action="locate" domain="rel" objectid="6_VVILMKG100OPD0II63CET20007"/>
<content-node action="update" active="true" allportletsallowed="true" content-parentref="6_VVILMKG100OPD0II63CET20007" create-type="explicit"
domain="rel" objectid="6_VVILMKG108SFA0IIDM2Q113007" ordinal="100" type="page">
<supported-markup markup="html" update="set"/>
<localedata locale="en">
<title>Popup Client Portlet</title>
</localedata>
<parameter name="com.ibm.portal.IgnoreAccessControlInCaches" type="string" update="set"><![CDATA[false]]></parameter>
<parameter name="com.ibm.portal.bookmarkable" type="string" update="set"><![CDATA[Yes]]></parameter>
<parameter name="com.ibm.portal.remote-cache-expiry" type="string" update="set"><![CDATA[0]]></parameter>
<parameter name="com.ibm.portal.remote-cache-scope" type="string" update="set"><![CDATA[NON-SHARED]]></parameter>
<access-control externalized="false" owner="uid=wasadmin,o=defaultWIMFileBasedRealm" private="false"/>
<component action="update" active="true" deletable="undefined" domain="rel" modifiable="undefined" objectid="7_VVILMKG108SFA0IIDM2Q1130G0"
ordinal="100" orientation="H" skinref="undefined" type="container" width="undefined">
<component action="update" active="true" deletable="undefined" domain="rel" modifiable="undefined" objectid="7_VVILMKG108SFA0IIDM2Q1130G4"
ordinal="100" orientation="V" skinref="undefined" type="container" width="undefined">
<component action="update" active="true" deletable="undefined" domain="rel" modifiable="undefined" objectid="7_VVILMKG100N080IA0G757C00O3"
ordinal="100" skinref="undefined" type="control" width="undefined">
<portletinstance action="update" domain="rel" objectid="5_VVILMKG100N080IA0G757C00O7" portletref="3_VVILMKG100N080IA0G757C0006"/>
</component>
</component>
</component>
</content-node>
</portal>
<status element="all" result="ok"/>
</request>

WSRP - Open a JSP/Servlet in dialog box from portlet

In the Open a JSP/Servlet in dialog box from portlet entry i talked about how you can open a JSP in dialog box from portlet. I wanted to see if that code will work in WSRP and how it will work so i tried consuming the sample portlet as WSRP portlet and tried clicking on the dialog box and it worked also it was able to pass query parameters.

This is the code in index.jsp that i use for creating URL pointing to popup.jsp

<input type="button" onclick="openPopup('<%=renderResponse.encodeURL(
renderRequest.getContextPath() + "/popup.jsp?userName=poup.jsp") %>')" value="Popup.jsp" />


When i look the HTML generated by this code in the local version of portlet this is what i see

<input type="button" value="Popup.jsp" onclick="openPopup('
/wpcert/PA_ServletPopup/popup.jsp?userName=poup.jsp')">


In my local the portal the ServletPopup is installed at /wpcert/PA_ServletPopup context so that's what get appended to the URL and the url goes unchanged in renderResponse.encodeURL()

THis markup is generated when i see same portlet in the WSRP world

<input type="button" value="Popup.jsp" onclick="openPopup(
'/wps/WsrpProxyPortlet/ResourceProxy/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48dT48Yj5odHRwOi8vbG9jYWxob3N0OjEwMDQwPC9iPjxwP
jxuPndzcnAtc2VjdXJlVVJMPC9uPjx2PmZhbHNlPC92PjwvcD48cD48bj53c3JwLXVybFR5cGU8L24-PHY-cmVzb3VyY2U8L3Y-
PC9wPjxwPjxuPnA8L24-PHY-MTJfVlZJTE1LRzEwODVGOTBJUzQ0SlJRNTMwMDc8L3Y-PC9wPjxwPjxuPmc8L24-
PHY-MV9WVklMTUtHMTAwTjA4MElBMEc3NTdDMDAwMDwvdj48L3A-PC91Pg!!/4UXoYMjvvzfhLvelKL9fiEIl5fs!/wpcert
/PA_ServletPopup/popup.jsp?userName=poup.jsp')">


As you can see the renderResponse.encodeURL() method generates a big string starting with /wps/WsrpProxyPortlet/ResourceProxy in the URL, so when you request a JSP the request will first go the consumer portal, there the ResourceProxy portlet will tunnel the request to producer and return the results back

Open a JSP/Servlet in dialog box from portlet

In the last few days i talked about what are different options for opening a dialog box from portlet and but all those approaches have there disadvantages. So the easiest option would be to open a dialog box pointing to a Servlet or JSP that resides in same porltet application. THis approach wont work if you need any portlet context but should work otherwise.

I built this sample portlet to demonstrate what i mean. My Sample portlet has a popup.jsp and when ever you click on open popup.jsp button it opens popup.jsp in dialog box, i am passing userName parameter to the JSP. You can download the sample code for portlet from here

THe doView() method of sample portlet forwards control to index.jsp which looks like this

<%@page language="java" contentType="text/html; %>
<%@taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="/WEB-INF/tld/portal.tld" prefix="portal" %>

<portlet:defineObjects />

<script type="text/javascript">
<!--
function openPopup(popupUrl){
if (window.showModalDialog) {
window.showModalDialog(popupUrl,"name","dialogWidth:455px;dialogHeight:450px");
} else {
window.open(popupUrl,'name',
'height=500,width=500,toolbar=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=no ,modal=yes');
}
}
//-->
</script>


<input type="button"
onclick="openPopup('<%=renderResponse.encodeURL(renderRequest.getContextPath() + "/popup.jsp?userName=poup.jsp") %>')"
value="Popup.jsp" />
<br/>


I am creating URL to a JSP which resides in same portlet application like this renderResponse.encodeURL(renderRequest.getContextPath() + "/popup.jsp?userName=poup.jsp"). I am passing userName as parameter to the popup.jsp, i used a JSP but it can be a servlet


THis is how my poup.jsp looks like


<html>
<body>
Hello from popup.jsp
<h1><%= request.getParameter("userName") %></h1>

</body>
</html>


The popup.jsp is pretty simple, all that it does is read value of userName request parameter and print it in HTML

Using portlet application name and portlet name to lookup a portlet

In the Generic method to get ObjectID from unique Name entry i talked about how you can find out ObjectID of a portlet/page or any other object from its unique name. BUt what if you want to find a portlet that does not have a uniuename assigned to it. In that case you can use combination of Portlet Application's uid and Portlet Name to find the portlet, you can use this method for that


private ObjectID getPortletObjectId(String portletAppId, String portletNameStr){
try {
InitialContext ctx = new InitialContext();
Name portletName = new CompositeName("portal:config/portletdefinition");
portletName.add(portletAppId ); //portal app Id
portletName.add(portletNameStr); //Portlet name
ObjectID portletDefOID = (ObjectID) ctx.lookup(portletName);
//ObjectID oidForUniqueName = (ObjectID) ctx.lookup(portletName);
return portletDefOID;
} catch (InvalidNameException e) {
e.printStackTrace(System.out);
} catch (NamingException e) {
e.printStackTrace(System.out);
}
return null;
}


This method takes portlet application id and the portletName to find out the portlet as argument and returns ObjectID. Now the question is what should be the value of the portlet object id and portlet name, you can find those values from the xmlaccess export of the portlet application


<web-app action="update" active="true" domain="rel"
objectid="1_VVILMKG100OPD0II63CET20000" removable="true" uid="PopupPortlet.war.webmod">
<url>file://localhost/$user_install_root$/PortalServer/deployed/archive/PopupPortlet.war.webmod/PopupPortlet.war</url>
<context-root>/wpcert/PA_Popup</context-root>
<display-name>PA_Popup</display-name>
<access-control externalized="false"
owner="uid=wasadmin,o=defaultwimfilebasedrealm" private="false"/>
<servlet action="update" active="true" domain="rel" name="PopupPortlet"
objectid="V_VVILMKG100OPD0II63CET20002" remote-cache-dynamic="false"/>
<portlet-app action="update" active="true" defaultlocale="en" domain="rel"
name="Application Name not available for this Application" objectid="2_VVILMKG100OPD0II63CET20004" uid="PopupPortlet.war" >
<access-control externalized="false" owner="uid=wasadmin,o=defaultwimfilebasedrealm" private="false"/>
<portlet action="update" active="true" defaultlocale="en" domain="rel"
name="PopupPortlet" objectid="3_VVILMKG100OPD0II63CET20006"
provided="false" servletref="V_VVILMKG100OPD0II63CET20002"
uniquename="com.webspherenotes.popup.portlet">
<access-control externalized="false"
owner="uid=wasadmin,o=defaultwimfilebasedrealm" private="false">
<role actionset="User" update="set">
<mapping subjectid="anonymous portal user"
subjecttype="user" update="set"/>
</role>
</access-control>
</portlet>
</portlet-app>
</web-app>



  • portletAppId: Value of uid attribute of portlet-app element

  • portletName: Value of name attribute of portlet element



I can use getPortletObjectId("PopupPortlet.war", "PopupPortlet"); to find out the ObjectID of the PopupPortlet

Problem with action-enable-page-as-extension-node-wp.dynamicui.config

The Exploiting the WebSphere Portal 5.1.0.1 programming model: Part 4: Making your portal dynamic and context sensitive article says that before you create pages you need to create a extension node, which is nothing but a portal page with some special property,

It says that you can use following configuration code to convert a page into extension node

wpsconfig.{bat|sh} enable-page-as-extension-node
-DPageUniqueName=extensionNode


But it seems that this task was renamed in the WPS 6.1 and you can call it like this

ConfigEngine.sh action-enable-page-as-extension-node-wp.dynamicui.config
-DPageUniqueName=pageUniqueName


I tried running this task on my WPS 6.1.5 but it always failed so i looked into what is going on and it turns out that this task actually creates a xmlaccess and invokes it for some reason the task was failing so i tried executing the xmlaccess manually and it worked and i was able to mark that page as extension node


<?xml version="1.0" encoding="UTF-8"?>
<request
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="PortalConfig_1.3.xsd"
type="update"
create-oids="true">

<!-- Locate the tpl transformation -->
<portal action="locate">
<web-app action="locate" active="true"
uid="wps.dynamicui.transformationapp.webmod">
<transformation-app action="locate" active="true"
uid="wps.dynamicui.transformationapp">
<transformation action="locate" active="true"
objectid="theTransformation" name="DynamicUITransformation" >
</transformation>
</transformation-app>
</web-app>

<!-- Enable page as task page container -->
<content-node action="update"
uniquename="pageUniqueName">
<transformationinstance action="update"
transformationref="theTransformation"/>
</content-node>

</portal>
</request>

Adding portlets to page on the fly

In the Creating portal pages on the fly, i talked about how you can create a portal page dynamically and redirect it to that page.

The DynamicUICtrl, has a addPortlet() methdo that you can use for adding portlet to page on the fly so i built this sample portlet to add com.webspherenotes.popup.portlet on the fly but it does not work


package com.webspherenotes.portlet;

import java.io.IOException;

import javax.naming.CompositeName;
import javax.naming.InitialContext;
import javax.naming.InvalidNameException;
import javax.naming.Name;
import javax.naming.NamingException;
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 com.ibm.portal.ObjectID;
import com.ibm.portal.dynamicui.DynamicUICtrl;
import com.ibm.portal.dynamicui.DynamicUIManagementException;
import com.ibm.portal.portlet.service.PortletServiceHome;
import com.ibm.portal.portlet.service.dynamicui.DynamicUIManagementFactoryService;
import com.ibm.portal.portlet.service.state.RedirectURLGeneratorFactoryService;
import com.ibm.portal.state.EngineURL;
import com.ibm.portal.state.RedirectURLGenerator;
import com.ibm.portal.state.exceptions.StateException;

public class DynamicUIPortlet extends GenericPortlet {

public static final String EXTENSION_PAGE_UNIQUENAME="com.webspherenotes.popup.extension";
public static final String TEMPLATE_PAGE_UNIQUENAME="com.webspherenotes.popup.dynamic";
public static final String TEMPLATE_PORTLET_UNIQUENAME = "com.webspherenotes.popup.portlet";

public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, IOException {
System.out.println("Entering DynamicUIPortlet.processAction()");

try {
RedirectURLGenerator redirectURLGenerator = redirectURLGeneratorService.getURLGenerator(request, response);

DynamicUICtrl dynamicUIControl= dynamicUIManagerFactoryService.getDynamicUICtrl(request, response, EXTENSION_PAGE_UNIQUENAME);

ObjectID templatePortletObjectId = getObjectID(TEMPLATE_PORTLET_UNIQUENAME);
System.out.println("Template Portlet Object Id " + templatePortletObjectId);

ObjectID dynamicPortletObjectId = dynamicUIControl.addPortlet(templatePortletObjectId, new LocalizedImpl("Dynamic page", "Dynamic page demo"), null);

System.out.println("Lauch portlet object id " + dynamicPortletObjectId);
if(dynamicPortletObjectId == null)
return;

EngineURL dynamicPageURL = redirectURLGenerator.createPortletURL(dynamicPortletObjectId);
String dynamicPageURLStr = dynamicPageURL.toString();

System.out.println("Dynamic Page URL " + dynamicPageURLStr);
response.sendRedirect(dynamicPageURLStr);
} catch (DynamicUIManagementException e) {
e.printStackTrace(System.out);
} catch (StateException e) {
e.printStackTrace(System.out);
}
System.out.println("Exiting DynamicUIPortlet.processAction()");
}

protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering DynamicUIPortlet.doView()");
getPortletContext().getRequestDispatcher("/index.jsp").include(request, response);
System.out.println("Exiting DynamicUIPortlet.doView()");
}

RedirectURLGeneratorFactoryService redirectURLGeneratorService;
DynamicUIManagementFactoryService dynamicUIManagerFactoryService;

public void init() throws PortletException {
System.out.println("ENtering DynamicUIPortlet.init()");
try {
InitialContext ctx = new InitialContext();
final PortletServiceHome dynamicUIManagerFactoryServiceHome = (PortletServiceHome) ctx
.lookup("portletservice/com.ibm.portal.portlet.service.dynamicui.DynamicUIManagementFactoryService");
dynamicUIManagerFactoryService = (DynamicUIManagementFactoryService) dynamicUIManagerFactoryServiceHome
.getPortletService(DynamicUIManagementFactoryService.class);

final PortletServiceHome redirectServiceHome = (PortletServiceHome) ctx
.lookup("portletservice/com.ibm.portal.portlet.service.state.RedirectURLGeneratorFactoryService");
redirectURLGeneratorService = (RedirectURLGeneratorFactoryService) redirectServiceHome
.getPortletService(RedirectURLGeneratorFactoryService.class);
} catch (NamingException e) {
e.printStackTrace(System.out);
}
System.out.println("Exiting DynamicUIPortlet.init()");
}

private ObjectID getObjectID(String uniqueNameStr) {
try {
InitialContext ctx = new InitialContext();
final Name uniqueName = new CompositeName("portal:uniquename");
uniqueName.add(uniqueNameStr);
ObjectID oidForUniqueName = (ObjectID) ctx.lookup(uniqueName);
return oidForUniqueName;
} catch (InvalidNameException e) {
e.printStackTrace(System.out);
} catch (NamingException e) {
e.printStackTrace(System.out);
}
return null;
}
}


The dynamicUIControl.addPortlet(templatePortletObjectId, new LocalizedImpl("Dynamic page", "Dynamic page demo"), null) method always returns null, i tried quite few combination but they do not help, i saw few question on ibm developerworks forum where other developers are also facing some problem but it seems that we will have to open a PMR with IBM and see how it goes

Creating portal pages on the fly

WebSphere Portal has concept of DynamicUIManagementFactoryService that you can use to create portal pages on the fly, add portlets to the pages,..etc. The pages that you create or portlets that you add with this method are available only in the current user session, so once user logs off these changes would be lost.

I followed the instructions in Exploiting the WebSphere Portal 5.1.0.1 programming model: Part 4: Making your portal dynamic and context sensitive article to create a sample portlet that will create a dynamic page and then display that page in dialog box. THis is how my sample portlet looks like


package com.webspherenotes.portlet;

import java.io.IOException;

import javax.naming.CompositeName;
import javax.naming.InitialContext;
import javax.naming.InvalidNameException;
import javax.naming.Name;
import javax.naming.NamingException;
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 com.ibm.portal.ObjectID;
import com.ibm.portal.dynamicui.DynamicUICtrl;
import com.ibm.portal.dynamicui.DynamicUIManagementException;
import com.ibm.portal.portlet.service.PortletServiceHome;
import com.ibm.portal.portlet.service.dynamicui.DynamicUIManagementFactoryService;
import com.ibm.portal.portlet.service.state.RedirectURLGeneratorFactoryService;
import com.ibm.portal.state.EngineURL;
import com.ibm.portal.state.RedirectURLGenerator;
import com.ibm.portal.state.exceptions.StateException;

public class DynamicUIPortlet extends GenericPortlet {

public static final String EXTENSION_PAGE_UNIQUENAME="com.webspherenotes.popup.extension";
public static final String TEMPLATE_PAGE_UNIQUENAME="com.webspherenotes.popup.dynamic";

public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, IOException {
System.out.println("Entering DynamicUIPortlet.processAction()");

try {
RedirectURLGenerator redirectURLGenerator = redirectURLGeneratorService.getURLGenerator(request, response);


DynamicUICtrl dynamicUIControl= dynamicUIManagerFactoryService.getDynamicUICtrl(request, response, EXTENSION_PAGE_UNIQUENAME);

ObjectID templatePageObjectId = getObjectID(TEMPLATE_PAGE_UNIQUENAME);
System.out.println("Template Page Object Id " + templatePageObjectId);
ObjectID dynamicPageObjectId = dynamicUIControl.addPage(templatePageObjectId, new LocalizedImpl("Dynamic page", "Dynamic page demo"), null);

System.out.println("Lauch page object id " + dynamicPageObjectId);

EngineURL dynamicPageURL = redirectURLGenerator.createPageURL(dynamicPageObjectId);
String dynamicPageURLStr = dynamicPageURL.toString();

System.out.println("Dynamic Page URL " + dynamicPageURLStr);
response.sendRedirect(dynamicPageURLStr);
} catch (DynamicUIManagementException e) {
e.printStackTrace(System.out);
} catch (StateException e) {
e.printStackTrace(System.out);
}
System.out.println("Exiting DynamicUIPortlet.processAction()");
}

protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering DynamicUIPortlet.doView()");
getPortletContext().getRequestDispatcher("/index.jsp").include(request, response);
System.out.println("Exiting DynamicUIPortlet.doView()");
}

RedirectURLGeneratorFactoryService redirectURLGeneratorService;
DynamicUIManagementFactoryService dynamicUIManagerFactoryService;

public void init() throws PortletException {
System.out.println("ENtering DynamicUIPortlet.init()");
try {
InitialContext ctx = new InitialContext();
final PortletServiceHome dynamicUIManagerFactoryServiceHome = (PortletServiceHome) ctx
.lookup("portletservice/com.ibm.portal.portlet.service.dynamicui.DynamicUIManagementFactoryService");
dynamicUIManagerFactoryService = (DynamicUIManagementFactoryService) dynamicUIManagerFactoryServiceHome
.getPortletService(DynamicUIManagementFactoryService.class);

final PortletServiceHome redirectServiceHome = (PortletServiceHome) ctx
.lookup("portletservice/com.ibm.portal.portlet.service.state.RedirectURLGeneratorFactoryService");
redirectURLGeneratorService = (RedirectURLGeneratorFactoryService) redirectServiceHome
.getPortletService(RedirectURLGeneratorFactoryService.class);
} catch (NamingException e) {
e.printStackTrace(System.out);
}
System.out.println("Exiting DynamicUIPortlet.init()");
}

private ObjectID getObjectID(String uniqueNameStr) {
try {
InitialContext ctx = new InitialContext();
final Name uniqueName = new CompositeName("portal:uniquename");
uniqueName.add(uniqueNameStr);
ObjectID oidForUniqueName = (ObjectID) ctx.lookup(uniqueName);
return oidForUniqueName;
} catch (InvalidNameException e) {
e.printStackTrace(System.out);
} catch (NamingException e) {
e.printStackTrace(System.out);
}
return null;
}
}


In the doView() method of the DynamicUIPortlet, i am forwarding control to index.jsp page, which looks like this

<%@page language="java" contentType="text/html; %>
<%@taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="/WEB-INF/tld/portal.tld" prefix="portal" %>

<portlet:defineObjects />

<script type="text/javascript">
<!--
function openPopup(popupUrl){
if (window.showModalDialog) {
window.showModalDialog(popupUrl,"name","dialogWidth:455px;dialogHeight:450px");
} else {
window.open(popupUrl,'name',
'height=500,width=500,toolbar=no,directories=no,status=no,menubar=no,scrollbars=no,
resizable=no ,modal=yes');
}
}
//-->
</script>



<input type="button" onclick="openPopup('<portlet:actionURL/>')" value="Dynamic Page" />
<br/>


When you click on the DYnamic Page button it creates a actionURL and opens it in the dialog box.

The DynamicUIService has restriction that you can call it only during the action phase of the portlet, so inside the processAction() method i am calling dynamicUIControl.addPage() method to add a dynamic page and then create redirectURL to the newly created page by calling redirectURLGenerator.createPageURL() method and then redirecting it to that URL. This is the UI that i get when i click on the Dynamic Page button



Creating a URL to portlet in solo state

In the Using PortletStateManagerService for creating URLs from within the portlet blog i talked about how to create a URL to portal page from inside portlet.

But what if you want to create a URL to portlet in solo state, in the solo state WebSphere portal wont display navigation of the page and looks much better suited for opening portal page in dialog box. You can use following code to create a URL to portlet in solo state


private String getPortletSoloStateURL(PortletRequest request,
PortletResponse response) {
PortletStateManager portletStateManager = null;
try {
portletStateManager = portletStateManagerService
.getPortletStateManager(request, response);
final URLFactory urlFct = portletStateManager.getURLFactory();
final EngineURL url = urlFct.newURL(null);

final SelectionAccessorFactory selectionAccessorFactory = portletStateManager
.getAccessorFactory(SelectionAccessorFactory.class);
SelectionAccessorController selectionAccessorCOntroller = selectionAccessorFactory
.getSelectionController(url.getState());
selectionAccessorCOntroller
.setSelection("com.webspherenotes.popup.static");

final PortletAccessorFactory portletFct = (PortletAccessorFactory) portletStateManager
.getAccessorFactory(PortletAccessorFactory.class);
final PortletAccessorController portletCtrl = portletFct
.getPortletController("com.webspherenotes.popup.control",
url.getState());
portletCtrl.getParameters().put("userName",
new String[] { "soloStateSample" });

final SoloAccessorFactory soloAccessorFactory = portletStateManager.getAccessorFactory(SoloAccessorFactory.class);
SoloAccessorController soloStateController = soloAccessorFactory.getSoloAccessorController(url.getState());
soloStateController.setSoloPortlet("com.webspherenotes.popup.control");

String finalURL = url.toString();
System.out.println("Portal Page URL " + finalURL);
return finalURL;
} catch (InvalidConstantException e) {
e.printStackTrace(System.out);
} catch (CannotInsertSelectionNodeException e) {
e.printStackTrace(System.out);
} catch (CannotCloneDocumentModelException e) {
e.printStackTrace(System.out);
} catch (CannotCreateDocumentException e) {
e.printStackTrace(System.out);
} catch (StateNotInRequestException e) {
e.printStackTrace(System.out);
} catch (MissingUniqueNameException e) {
e.printStackTrace(System.out);
} catch (UnknownUniqueNameException e) {
e.printStackTrace(System.out);
} catch (StateManagerException e) {
e.printStackTrace(System.out);
} catch (UnknownAccessorTypeException e) {
e.printStackTrace(System.out);
} catch (CannotInstantiateAccessorException e) {
e.printStackTrace(System.out);
} catch (CannotInstantiateURLFactoryException e) {
e.printStackTrace(System.out);
}
return null;
}


You can use the SoloAccessorFactory for invoking a portlet in solo state. This is how my target page looks like in the solo state

Creating a URL to different portlet mode and window state

In the Using PortletStateManagerService for creating URLs from within the portlet blog i talked about how to create a URL to portal page from inside portlet.

But what if you want to create a URL that will invoke the target portlet in say Edit mode and maximized window state, in that case you can use following code


private String getEditModeMaximizedURL(PortletRequest request, PortletResponse response){
PortletStateManager portletStateManager = null;
try {
portletStateManager = portletStateManagerService
.getPortletStateManager(request, response);
final URLFactory urlFct = portletStateManager.getURLFactory();
final EngineURL url = urlFct.newURL(null);

final SelectionAccessorFactory selectionAccessorFactory = portletStateManager
.getAccessorFactory(SelectionAccessorFactory.class);
SelectionAccessorController selectionAccessorCOntroller = selectionAccessorFactory
.getSelectionController(url.getState());
selectionAccessorCOntroller
.setSelection("com.webspherenotes.popup.static");

final PortletAccessorFactory portletFct =
(PortletAccessorFactory) portletStateManager
.getAccessorFactory(PortletAccessorFactory.class);
final PortletAccessorController portletCtrl =
portletFct
.getPortletController("com.webspherenotes.popup.control",
url.getState());
portletCtrl.getParameters().put("userName",
new String[] { "editModeSample" });

portletCtrl.setPortletMode(PortletMode.EDIT);
portletCtrl.setWindowState(WindowState.MAXIMIZED);


String finalURL = url.toString();
System.out.println("Portal Page URL " + finalURL);
return finalURL;
} catch (InvalidConstantException e) {
e.printStackTrace(System.out);
} catch (CannotInsertSelectionNodeException e) {
e.printStackTrace(System.out);
} catch (CannotCloneDocumentModelException e) {
e.printStackTrace(System.out);
} catch (CannotCreateDocumentException e) {
e.printStackTrace(System.out);
} catch (StateNotInRequestException e) {
e.printStackTrace(System.out);
} catch (MissingUniqueNameException e) {
e.printStackTrace(System.out);
} catch (UnknownUniqueNameException e) {
e.printStackTrace(System.out);
} catch (StateManagerException e) {
e.printStackTrace(System.out);
} catch (UnknownAccessorTypeException e) {
e.printStackTrace(System.out);
} catch (CannotInstantiateAccessorException e) {
e.printStackTrace(System.out);
} catch (CannotInstantiateURLFactoryException e) {
e.printStackTrace(System.out);
}
return null;
}



You can use the PortletAccessorController for setting a different portlet mode and window state for a portlet. After setting this is how my target page looks like in the edit mode

Creating action URL with parameters using PortletStateManagerService

In the Using PortletStateManagerService for creating URLs from within the portlet blog i talked about how to create a URL to portal page from inside portlet.

But what if you want to create a action url pointing to portlet on particular page and you also want to send some parameters while calling the action, this will result in the processAction() method of the target method being called, you can use following method


private String getPortletActionURL(PortletRequest request,
PortletResponse response) {
PortletStateManager portletStateManager = null;
try {
portletStateManager = portletStateManagerService
.getPortletStateManager(request, response);
final URLFactory urlFct = portletStateManager.getURLFactory();
final EngineURL url = urlFct.newURL(null);

final SelectionAccessorFactory selectionAccessorFactory = portletStateManager
.getAccessorFactory(SelectionAccessorFactory.class);
SelectionAccessorController selectionAccessorCOntroller = selectionAccessorFactory
.getSelectionController(url.getState());
selectionAccessorCOntroller
.setSelection("com.webspherenotes.popup.static");

final PortletAccessorFactory portletFct =
(PortletAccessorFactory) portletStateManager
.getAccessorFactory(PortletAccessorFactory.class);
final PortletAccessorController portletCtrl =
portletFct
.getPortletController("com.webspherenotes.popup.control",
url.getState());
portletCtrl.getParameters().put("userName",
new String[] { "soloStateSample" });


final PortletTargetAccessorFactory portletTargetAccessorFactory =
(PortletTargetAccessorFactory)portletStateManager.getAccessorFactory(PortletTargetAccessorFactory.class);
final PortletTargetAccessorController portletTargetAccessor =
portletTargetAccessorFactory.getPortletTargetAccessorController(url.getState());

portletTargetAccessor.setActionTarget("com.webspherenotes.popup.control");
portletTargetAccessor.getParameters().put("actionParam", new String[]{"actionParamValue" });


String finalURL = url.toString();
System.out.println("Portal Page URL " + finalURL);
return finalURL;
} catch (InvalidConstantException e) {
e.printStackTrace(System.out);
} catch (CannotInsertSelectionNodeException e) {
e.printStackTrace(System.out);
} catch (CannotCloneDocumentModelException e) {
e.printStackTrace(System.out);
} catch (CannotCreateDocumentException e) {
e.printStackTrace(System.out);
} catch (StateNotInRequestException e) {
e.printStackTrace(System.out);
} catch (MissingUniqueNameException e) {
e.printStackTrace(System.out);
} catch (UnknownUniqueNameException e) {
e.printStackTrace(System.out);
} catch (StateManagerException e) {
e.printStackTrace(System.out);
} catch (UnknownAccessorTypeException e) {
e.printStackTrace(System.out);
} catch (CannotInstantiateAccessorException e) {
e.printStackTrace(System.out);
} catch (CannotInstantiateURLFactoryException e) {
e.printStackTrace(System.out);
}
return null;
}


You can use PortletTargetAccessor for creating action URLs pointing to particular portlet, it also allows you to set action parameters. This is how the target page looks like

Creating render URL with parameters using PortletStateManagerService

In the Using PortletStateManagerService for creating URLs from within the portlet blog i talked about how to create a URL to portal page from inside portlet.

But what if you want to create a RenderURL pointing to portlet on particular page and you also want to send some render parameters to that portlet. If thats the case you can use the following method


private String getPortletRenderURLWithParams(PortletRequest request,
PortletResponse response) {
PortletStateManager portletStateManager = null;
try {
portletStateManager = portletStateManagerService
.getPortletStateManager(request, response);
final URLFactory urlFct = portletStateManager.getURLFactory();
final EngineURL url = urlFct.newURL(null);

final SelectionAccessorFactory selectionAccessorFactory = portletStateManager
.getAccessorFactory(SelectionAccessorFactory.class);
SelectionAccessorController selectionAccessorCOntroller = selectionAccessorFactory
.getSelectionController(url.getState());
selectionAccessorCOntroller
.setSelection("com.webspherenotes.popup.static");

final PortletAccessorFactory portletFct = (PortletAccessorFactory) portletStateManager
.getAccessorFactory(PortletAccessorFactory.class);
final PortletAccessorController portletCtrl = portletFct
.getPortletController("com.webspherenotes.popup.control",
url.getState());
portletCtrl.getParameters().put("userName",
new String[] { "renderParameterSample" });

String finalURL = url.toString();
System.out.println("Portal Page URL " + finalURL);
return finalURL;
} catch (InvalidConstantException e) {
e.printStackTrace(System.out);
} catch (CannotInsertSelectionNodeException e) {
e.printStackTrace(System.out);
} catch (CannotCloneDocumentModelException e) {
e.printStackTrace(System.out);
} catch (CannotCreateDocumentException e) {
e.printStackTrace(System.out);
} catch (StateNotInRequestException e) {
e.printStackTrace(System.out);
} catch (MissingUniqueNameException e) {
e.printStackTrace(System.out);
} catch (UnknownUniqueNameException e) {
e.printStackTrace(System.out);
} catch (StateManagerException e) {
e.printStackTrace(System.out);
} catch (UnknownAccessorTypeException e) {
e.printStackTrace(System.out);
} catch (CannotInstantiateAccessorException e) {
e.printStackTrace(System.out);
} catch (CannotInstantiateURLFactoryException e) {
e.printStackTrace(System.out);
}
return null;
}


In this method first i am using SelectionAccessorFactory for creating a URL to page (com.webspherenotes.popup.static) then i am using PortalAccessorFactory to target portlet window whose unique name is com.webspherenotes.popup.control and then i am setting render parameters

Now when i click on the URL to open the portlet render url this is what i see

Using PortletStateManagerService for creating URLs from within the portlet

In the Creating Portal/Portlet URL from outside the portal entry i built a sample for how you can use PortalStateManagerServiceHome for creating URL to other portal pages or portlets from either outside the portal or theme and skin.

But what if you want to create a URL from inside a portlet to other portal page, WPS provides PortletStateManagerService, that you can use from inside the portlet to create URL to other portal pages. I wanted to learn how it works so i built a sample portlet that can be used to ful-fill different use cases, you can download that sample portlet from here


package com.webspherenots.popup;

import java.io.IOException;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.PortletMode;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.WindowState;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ibm.portal.portlet.service.PortletServiceHome;
import com.ibm.portal.state.EngineURL;
import com.ibm.portal.state.PortletStateManager;
import com.ibm.portal.state.URLFactory;
import com.ibm.portal.state.accessors.exceptions.CannotInsertSelectionNodeException;
import com.ibm.portal.state.accessors.exceptions.MissingUniqueNameException;
import com.ibm.portal.state.accessors.exceptions.StateNotInRequestException;
import com.ibm.portal.state.accessors.exceptions.UnknownUniqueNameException;
import com.ibm.portal.state.accessors.portlet.PortletAccessorController;
import com.ibm.portal.state.accessors.portlet.PortletAccessorFactory;
import com.ibm.portal.state.accessors.portlet.PortletTargetAccessorController;
import com.ibm.portal.state.accessors.portlet.PortletTargetAccessorFactory;
import com.ibm.portal.state.accessors.selection.SelectionAccessorController;
import com.ibm.portal.state.accessors.selection.SelectionAccessorFactory;
import com.ibm.portal.state.accessors.solo.SoloAccessorController;
import com.ibm.portal.state.accessors.solo.SoloAccessorFactory;
import com.ibm.portal.state.accessors.themetemplate.ThemeTemplateAccessorController;
import com.ibm.portal.state.accessors.themetemplate.ThemeTemplateAccessorFactory;
import com.ibm.portal.state.accessors.url.URLAccessorFactory;
import com.ibm.portal.state.exceptions.CannotCloneDocumentModelException;
import com.ibm.portal.state.exceptions.CannotCreateDocumentException;
import com.ibm.portal.state.exceptions.CannotInstantiateAccessorException;
import com.ibm.portal.state.exceptions.CannotInstantiateURLFactoryException;
import com.ibm.portal.state.exceptions.InvalidConstantException;
import com.ibm.portal.state.exceptions.StateManagerException;
import com.ibm.portal.state.exceptions.UnknownAccessorTypeException;
import com.ibm.portal.state.service.PortletStateManagerService;

public class PortletStateManagerServicePortlet extends GenericPortlet {

PortletStateManagerService portletStateManagerService;

public void init() throws PortletException {
System.out.println("Entering PortletStateManagerServiceHome.init()");
try {
InitialContext context = new InitialContext();

final PortletServiceHome serviceHome = (PortletServiceHome) context
.lookup("portletservice/com.ibm.portal.state.service.PortletStateManagerService");
portletStateManagerService = (PortletStateManagerService) serviceHome
.getPortletService(PortletStateManagerService.class);

} catch (NamingException e) {
e.printStackTrace(System.out);
}
System.out.println("Exiting PortletStateManagerServiceHome.init()");
}

protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering PortletStateManagerServiceHome.doView()");
response.setContentType("text/html");

String portalPageURL = getPortalPageURL(request, response);
request.setAttribute("portalPageURL", portalPageURL);
String portletRenderURL = getPortletRenderURLWithParams(request, response);
request.setAttribute("portletRenderURL", portletRenderURL);
String soloStateURL = getPortletSoloStateURL(request, response);
request.setAttribute("soloStateURL", soloStateURL);
String actionURL = getPortletActionURL(request, response);
request.setAttribute("actionURL", actionURL);

String editModeURL = getEditModeMaximizedURL(request,response);
request.setAttribute("editModeURL", editModeURL);

String themeURL = getDifferentThemeURL(request, response);
request.setAttribute("themeURL", themeURL);
getPortletContext().getRequestDispatcher("/index.jsp").include(request,
response);
System.out.println("Exiting PortletStateManagerServiceHome.doView()");
}

private String getPortalPageURL(PortletRequest request,
PortletResponse response) {
PortletStateManager portletStateManager = null;
try {
portletStateManager = portletStateManagerService
.getPortletStateManager(request, response);
final URLFactory urlFct = portletStateManager.getURLFactory();
final EngineURL url = urlFct.newURL(null);


final SelectionAccessorFactory selectionAccessorFactory = portletStateManager
.getAccessorFactory(SelectionAccessorFactory.class);
SelectionAccessorController selectionAccessorCOntroller = selectionAccessorFactory
.getSelectionController(url.getState());
selectionAccessorCOntroller
.setSelection("com.webspherenotes.popup.static");


String finalURL = url.toString();
System.out.println("Portal Page URL " + finalURL);
return finalURL;
} catch (StateManagerException e) {
e.printStackTrace(System.out);
} catch (UnknownAccessorTypeException e) {
e.printStackTrace(System.out);
} catch (CannotInstantiateAccessorException e) {
e.printStackTrace(System.out);
} catch (InvalidConstantException e) {
e.printStackTrace(System.out);
} catch (CannotCloneDocumentModelException e) {
e.printStackTrace(System.out);
} catch (CannotCreateDocumentException e) {
e.printStackTrace(System.out);
} catch (StateNotInRequestException e) {
e.printStackTrace(System.out);
} catch (CannotInsertSelectionNodeException e) {
e.printStackTrace(System.out);
} catch (MissingUniqueNameException e) {
e.printStackTrace(System.out);
} catch (UnknownUniqueNameException e) {
e.printStackTrace(System.out);
} catch (CannotInstantiateURLFactoryException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (portletStateManager != null)
portletStateManager.dispose();
}
return null;
}
}


The PortletStateManagerServicePortlet.java has a getPortalPageURL() method, that is using SelectionAccessorFactory, for creating a URL to portal page, with unique name equal to com.webspherenotes.popup.static

This is same as that of the In the Creating Portal/Portlet URL from outside the portal, with the difference that i am using PortletStateManagerService from inside the portlet instead of PortalStateManagerService from servlet

Assigning unique name to theme

WebSphere Portal SPI allows you to assign a theme to a page or skin to portlet window pro-grammatically, but if you want to do that then you will have to assign unique name to theme but i could not find any way to assign unique name to theme using Admin console so this is what i did

  • First install theme on portal using Theme and Skins portlet

  • Then i did full export of the portal using xmlaccess

  • Then i did find the theme to which i want to assign unique name and copied into a different xml like this

    <?xml version="1.0" encoding="UTF-8"?>
    <request xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" build="wp6103_201_01" type="update"
    version="6.1.0.3" xsi:noNamespaceSchemaLocation="PortalConfig_6.1.0.2.xsd">
    <portal action="locate">
    <theme action="update" active="true" default="false" defaultskinref="K_VVILMKG10GV1F0IIS98H1C0002" domain="rel"
    objectid="J_VVILMKG10GV1F0IIS98H1C0004" resourceroot="empty"
    uniquename="com.webspherenotes.emptytheme" >
    <localedata locale="en">
    <title>Empty</title>
    </localedata>
    </theme>
    </portal>
    </request>

    In this theme element i did add uniquename attribute with value equal to the unique name that i want to assign

  • Import this xmlaccess script into the portal

Assigning unique name to skin

WebSphere Portal SPI allows you to assign a theme to a page or skin to portlet window pro-grammatically, but if you want to do that then you will have to assign unique name to skin but i could not find any way to assign unique name to skin using Admin console so this is what i did


  • First install skin on portal using Theme and Skins portlet

  • Then i did full export of the portal using xmlaccess

  • Then i did find the skin to which i want to assign unique name and copied into a different xml like this

    <?xml version="1.0" encoding="UTF-8"?>
    <request xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" build="wp6103_201_01"
    type="update" version="6.1.0.3" xsi:noNamespaceSchemaLocation="PortalConfig_6.1.0.2.xsd">
    <portal action="locate">
    <skin action="update" active="true" default="false" domain="rel" objectid="K_VVILMKG10GV1F0IIS98H1C0002"
    resourceroot="empty" type="default"
    uniquename="com.webspherenotes.emptyskin" >
    <localedata locale="en">
    <title>Empty</title>
    </localedata>
    </skin>
    </portal>
    </request>

    In this skin element i did add uniquename attribute with value equal to the unique name that i want to assign

  • Import this xmlaccess script into the portal

What happens if you dont set either the Cache-Control or Expires header

If you don't set either the Cache-Control or Expires header on the response it does not mean that resource wont get cached at all or the browser will validate it with the originating server every time, instead how that is handled depends on your browser.

The HTTP Specification says " Since origin servers do not always provide explicit expiration times, HTTP caches typically assign heuristic expiration times, employing algorithms that use other header values (such as the Last-Modified time) to estimate a plausible expiration time. The HTTP/1.1 specification does not provide specific algorithms, but does impose worst-case constraints on their results. Since heuristic expiration times might compromise semantic transparency, they ought to used cautiously, and we encourage origin servers to provide explicit expiration times as much as possible."

That means, if you dont set Expires or Cache-Control and if your request is going through the proxy server then there is good chance the response will get cached based for different duration based on the algorithm used by the proxy or browser.

Internet Explorer
If you have a css style sheet say http_server_styles.css on your page and it does not have a cache related information, then when you go the page that has this style sheet, the IE will download that stylesheet and keep it until you close the browser. It wont check with originating server to see if the style sheet is changed until you close the browser.

But if you close the browser, reopen it and access the page, it will make a conditional get request to server, to check if the style sheet is actually changed, if not the server will respond with HTTP 304 NOt Modified. IF yes the server will return HTTP 200 with full body of the response

Firefox

The Firefox handles this differently, if you dont set the cache-control or expires header, Firefox will use the following formula to calculate expiration date


Expiration Time = Now + 0.1 * (Time since Last-Modified)


Ex. in my case the resource is not changed since 26th of March 2010, so it decides to catch the resource for 12 days i.e. till 31st of July 2010 like this

What happens when you click on F5 - Refresh button of browser

You can ask browser to ignore content from its cache and get new content from originating server by clicking on F5 or Shift + F5(Firefox) or Ctrl + F5(Internet explorer). I did some research on how Firefox handles refresh button

I have a simple html page on Apache Server and it has a static image which is also served by Apache Http Server. My Http Server is configured to set cache time of 3 months for images like this


ExpiresActive On

<FilesMatch "\.(gif|jpg|jpeg|png|swf)$">
ExpiresDefault "access plus 3 month"
Header append Cache-Control "public,s-maxage=2592000"
</FilesMatch>


h4. Refresh

I went to the html page first and i did let the image download, now if i access the html page it does not download the image at all. So i did go to the page and i clicked on F5(Refresh) in firefox and this is what i saw



When we click Refresh button the Firefox is ignoring the cached copy of image and it is setting following additional headers

If-Modified-Since Thu, 15 Jul 2010 22:54:21 GMT
If-None-Match "9c334-9933-fba1e140"
Cache-Control max-age=0


The browser is sending the If-Modified-Since and If-None-Match as it does for any validation request. But in addition to that it is sending max-age=0, which means that if the request goes through proxy, it is telling the proxy that, it will not accept cached resource, instead proxy should check with the originating server to see if the response that it has is fresh, by sending conditional get.

When we click on Refresh button the browser will send Cache-Control max-age=0 for all the embedded request.

Important Note: When you send F5 request, it will result in end-to-end invalidation, so if the image is not actually changed then originating server will send HTTP 304 response

h4. Shift + F5/ Ctrl + F5

When you click on F5, your sending end to end validation request, that means every component in the chain should verify with the originating server if the cached copy that it has is fresh if not get fresh copy. But what if you dont want cached copy at all instead you want fresh response from the orignating server, in that case you should click Shift + F5 in firefox or Ctrl + F5 in IE. I tried that on my test page and this is what happens




If you look at the request header you will notice two things there is no If-Modified-Since or If-None-Match validation header and there are these two additional headers


Pragma no-cache
Cache-Control no-cache


The Cache-control: no-cache header tells the proxy that request is going through that it wont accept any cached response. So proxy must make request to originating server. Now since the request header does not have any If-Modified-Since or If-None-Match header, it cant return 304, instead it must return the full body of response with 200 status code. This will overwrite the cached copy.

Important Note: When you send Shift + F5 request your saying that it should be end to end full response fetch and originating server must send the new full copy

Mixing mod_expires and mod_headers

The cache-control header gives you granular control over the caching behavior, it has directives like s-maxage, public, private in addition to the max-age directive.

The mod_expires module can be used for setting only max-age directive but what is you want to configure the proxy related directives say you want to set Cache-Control to something like this


Cache-Control max-age=7776000, public,s-maxage=2592000


This means the browser can cache the resource for 3 months but the public cache, such as cache proxy can cache it for 1 month.

You should use mix of mod_expires and mod_headers to set these headers by adding following lines to the httpd.conf


ExpiresActive On

<FilesMatch "\.(gif|jpg|jpeg|png|swf)$">
ExpiresDefault "access plus 3 month"
Header append Cache-Control "public,s-maxage=2592000"
</FilesMatch>


This configuration will tell Apache HTTP Server to set 3 month caching for browser and 1 month caching for cache proxy only for resources with gif|jpg|jpeg|png|swf extension.

This is screen shot of Firefox when i try to access a image from HTTP server that has the above configuration

What is mod_headers

The mod_headers provides directives to control and modify HTTP request and response headers. Headers can be merged, replaced or removed.

For example i want to set Cache-control: public,s-maxage=7776000 header for every image that is served by Apache, so that these images can be cached by a proxy server for all the users for three months, so this is what i have to add to my httpd.conf file


<FilesMatch "\.(gif|jpg|jpeg|png|swf)$">
Header append Cache-Control "public,s-maxage=7776000"
</FilesMatch>


After doing that when i try accessing a static image from Http Server this is what i get

Difference between no-cache and no-store

The Cache-Control header has no-cache and no-store headers that look very similar and i wanted to figure out what is difference between them so i did few experiments and this is what i found.

I do have a simple Html page on my Apache Server and that page has a static image which is also served by Apache Http Server and i tried setting Cache-Control :no-cache and Cache-Control: no-store header for that image and i used Firefox to find the results.

no-cache

Tells the browser and cache that they cant reuse the content without checking with the originating server first. Ex. If you send no-cache header from the originating server, then the resource will be cached in browser as well as caching proxy, but next time when you request that resource, the cache it will send conditional GET request to check if the resource is changed. If yes then server will send HTTP 200 response, if the resource is not changed it will send HTTP 304 response

Important Note: The no-cache header tells browser that you can reuse the image but you should validate it with the originating server if the resource is actually changed

This is screen shot of my firefox when i tried accessing the sample page



Then i went to the firefox cache information page at about:cache and i looked for the cachesample.gif, and there is entry for the cachesample.gif in diskcache like this



Now whenever i go to the page, the browser makes conditional GET request to the server and since i am not changing image, it always gets HTTP 304 (Not Modified)

no-store

The no-store directive is more strict, it means the response cannot be written to the cache at all. Ex. If you send no-store for a response then cache wont store it at all and next time when it gets request for that resource it will send that request to the originating server, which will send the full response with HTTP 200 status

This is what i see when i try to access the same page, but now apache is configured to return Cache-Control: no-store header



After that i tried searching in the firefox cache configure (about:cache) page but i could not find the entry for the image in disk cache. Also when i go to the page that has this page, it makes a full request, without If-Modified-Since and server always returns full response with body and status code equal to 200.

Cache-control vs. Expires

If you want browser and cache devices to cache some of your resources, you have two options one is using Expires header and other is using max-age directive in cache-control header. Consider following things before deciding on whether you should use max-age or cache-control header


  • The Expires header is deprecated in the Http 1.1 specification

  • The Cache-control header was introduced in HTTP 1.1 specification, and there are quite a few HTTP 1.0 devices out there and some of them dont understand Cache-control

  • The HTTP 1.1 Header definition says this "If a response includes both an Expires header and a max-age directive, the max-age directive overrides the Expires header, even if the Expires header is more restrictive."

  • The cache-control directive gives you more fine grained control over the different aspects of caching.



I wanted to see how the browser reacts if i set both Expires and Cache-control header at the same time. I did create a servlet which sets

public class ResourceServingServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("Entering ResourceServingServlet.doGet()");

System.out.println("Request path " + request.getPathInfo());
System.out.println("Query String " + request.getQueryString());
printRequestHeaders(request);
response.setContentType("application/javascript");
response.setHeader("Cache-Control", "max-age=2592000");
response.setHeader("Expires", "Thu, 25 Jul 2010 18:26:10 GMT");
getServletContext().getRequestDispatcher("/js/test.js").include(request, response);
System.out.println("Exiting ResourceServingServlet.doGet()");
}
}

I am setting cache-control header with value of max-age equal to 2592000, which means 30 days. Then i set Expires date to Thu, 25 Jul 2010 18:26:10 GMT, and when i tried accessing this servlet on 19th of July, the Expires header was supposed to expire the content in 6 days.

When i tried accessing the ResourceServlet these are the headers that i got in the Firebug



As you can see i am setting both Expires and Cache-control header and value of Date field is 19th of July 2010, which means the response was returned from the server on 19th of July 2010. When i looked the cache entry in the firebug this is what i see



The Firefox entry is about to expire on the 18th of July 2010, which is 30 days. That means firefox ignores the value of Expires header even though it is more restrictive.

What is cache-control header

The cache-control header was introduced in HTTP 1.1 to replace Expires header, it lets you define the time for which a resource is cachable in seconds from the time response was generated. But cache-control is more complex, it has set of keywords that you can use to control different aspects of resource cachability

Following are the cache-control directives that can appear in the HTTP response

  • max-age: This directive is used to specify time in seconds for which the response is fresh. I.e. if you set value of max-age to say 3600, then browser can reuse the resource without validating for next 1 hr. Same thing with caching proxy it will tell caching proxy to cache resource for 1 hr.

  • private: The private directive gives the browser permission to store a response but prevent shared caching proxies from doing so. This directive is useful if the response contains content customized for particular user

  • public: The public directive means this response can be cached by both caching proxies and browsers. Also response cached by proxies for one user can be reused for other user. If you specify only max-age and don't add private header it will be considered public by default

  • s-maxage: The s-maxage is same as that of the max-age but with difference that it applies to the shared cache. Ex. if you set max-age equal to 3 hr and s-maxage equal to 1 hr. Then browser will consider the resource as fresh for 3 hours. But the caching proxy will consider it fresh for 1 hr.

  • must-validate: The HTTP allows caches to take liberties with the freshness of objects; by specifying this header, you're telling the cache that you want it to strictly follow your rules.

  • proxy-validate: The HTTP allows caches to take liberties with the freshness of objects; by specifying this header, you're telling the cache proxies that you want it to strictly follow your rules

  • no-cache: Tells the browser and cache that they cant reuse the content without checking with the originating server first. Ex. If you send no-cache then the resource will be cached, but next time when cache gets request for the cache it will send conditional GET request to check if the resource is changed. If yes then server will send HTTP 200 response, if the resource is not changed it will send HTTP 304 response

  • no-store: Means the response cannot be written to the cache cache at all. Ex. If you send no-store for a response then cache wont store it at all and next time when it gets request for that resource it will send that request to the originating server, which will send the full response with HTTP 200 status

What is Expires HTTP Header

The Expires header tells the cache exactly how long the response may be considered fresh. A response that includes an Expires header may be reused without validation until the expiration time is reached. The Expires header is deprecated in HTTP 1.1 because lots of servers and intermediate devices have un-synchronized on incorrect time.

Important Note: The presence of an Expires header can also turn an otherwise un-cachable response into cachable one. For example the response to POST requests are un-cachable by default but they can be cached if there is an Expires line in the reply header.

These are three ways in which we can set Expires header in Apache HTTP Server

  1. mod_expires: You can use mod_expires module to set both Cache-control and Expires header. BUt problem with this approach is that it does not let you set absolute time say Friday night for expiry of resource, instead you can set expiry time like either access time plus fixed time interval or modification time of a resource plus fixed time interval

    <FilesMatch "\.(gif|jpg|jpeg|png|swf)$">
    ExpiresDefault "access plus 3 month"
    </FilesMatch>

    This tells the Apache to set expires time of 3 moths from the time the resource was accessed.
    Since mod_expires sets both cache-control and expires header the cache-control will always take precedence and the value of Expires will be ignored unless the device is HTTP 1.0 and it does not understand cache-control header

  2. mod_header: The mod_headers is a simpler solution which you can use for setting any header on the response

    <FilesMatch "\.(gif|jpg|jpeg|png|swf)$">
    Header append Expires "Fri, 15 Oct 2010 16:49:25 GMT"
    </FilesMatch>

    This tells the Apache to set 15th of October as the expiry date for every image, which could be your next release date. But problem with this approach is that you will have to modify the expires date manually after 15th of October or it will set expiry date in the past and which would cause that resource to be un-cachable.

  3. mod_cern_meta: THe mod_cern_meata allows you to define list of HTTP headers in a file and then you can associate that file with resource