Adobe Flex for portlet Ui

There is lot of interest in using Adobe Flex for building Rich application interface for Portlet application, so i am spending some time learning about it. So i just built my first Hello Flex portlet.



This portlet is very simple it is just including a Flex object in the output using object tag like this


<%@page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" session="false"%>
<%@taglib uri="http://java.sun.com/portlet" prefix="portlet"%>
<h4>Hello Adobe Portlet</h4>

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
id="HelloWorld" width="100%" height="100%"
codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
<param name="movie" value='<%= renderResponse.encodeURL(renderRequest.getContextPath() + "/HelloWorld.swf") %>' />
<param name="quality" value="high" />
<param name="bgcolor" value="#869ca7" />
<param name="allowScriptAccess" value="sameDomain" />
<embed src="<%= renderResponse.encodeURL(renderRequest.getContextPath() + "/HelloWorld.swf") %>" quality="high" bgcolor="#869ca7"
width="100%" height="100%" name="HelloWorld" align="middle" play="true" loop="false" quality="high" allowScriptAccess="sameDomain"
type="application/x-shockwave-flash" pluginspage="http://www.adobe.com/go/getflashplayer">
</embed>
</object>


I used Adobe Flex Builder to build my first HelloWorld.swf application. My application does not have any action script so it just one page and the layout is defined by Helloworld.mxml which looks like this

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:aral="*" >
<mx:Panel title="My Application"
paddingTop="10"
paddingBottom="10"
paddingLeft="10"
paddingRight="10"
>
<mx:Label text="Hello World!" fontWeight="bold" fontSize="24"/>
</mx:Panel>
</mx:Application>


Once the HelloWorld.mxml was ready i used the Flash Builder to build HelloWorld.swf file from it and then copied the HelloWorld.swf in the root of my portlet application and then used renderResponse.encodeURL() for creating path to the HelloWorld.swf.

Using client side API for getting object ids

The client side tag library can be used in one interesting use case which is to get portletId and pageId of portlet where the portlet is getting rendered. In order to do that i built a simple portlet which forward control to JSP like this

</portlet-client-model:init>
<h4>Constants on page</h4>
<table>
<tr>
<td>Portlet Window Id</td>
<td><%=portletWindowID%></td>
</tr>
<tr>
<td>Portlet Page Id</td>
<td><%=portletPageID%></td>
</tr>

</table>


When i did that and added the portlet on a page i could see output like this



Now if i do export of the page where the portlet is added i can see output like this, as you can see the page id and portlet Ids that are getting displayed in the last screen shot

<content-node action="update" active="true" allportletsallowed="true" content-parentref="6_CGAH47L008IC40I4BOR2EO00I3" create-type="explicit" domain="rel" objectid="6_VVILMKG1084FA0IC6ARBEQ1000" ordinal="100" type="page">
<supported-markup markup="html" update="set"/>
<localedata locale="en">
<title>Hello World</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_VVILMKG1084FA0IC6ARBEQ1004" ordinal="100" orientation="H" skinref="undefined" type="container" width="undefined">
<component action="update" active="true" deletable="undefined" domain="rel" modifiable="undefined" objectid="7_VVILMKG1084FA0IC6ARBEQ1002" ordinal="100" orientation="V" skinref="undefined" type="container" width="undefined">
<component action="update" active="true" deletable="undefined" domain="rel" modifiable="undefined" objectid="7_VVILMKG1003E90IC67N6VF30G0" ordinal="200" skinref="undefined" type="control" width="undefined">
<portletinstance action="update" domain="rel" objectid="5_VVILMKG1003E90IC67N6VF3007" portletref="3_VVILMKG1003E90IC67N6VF3006"/>
<portletinstance action="update" domain="cust" objectid="5_VVILMKG1003E90IC67N6VF30G4" owner="uid=wasadmin,o=defaultWIMFileBasedRealm" parentref="5_VVILMKG1003E90IC67N6VF3007" portletref="3_VVILMKG1003E90IC67N6VF3006"/>
</component>
</component>
</component>
</content-node>

Writing for Developer Works

After my I received "IBM developerWorks Contributing Author" designation i got few emails from readers who want to publish articles on developerworks and thanks for that.

Let me start by clarifying that i am not IBM employee or i don't have any internal information about what type of content IBM Developerworks is looking for. But i do have experience of publishing 7-8 articles on IBM Developerworks so i know few things about how the process works. I am willing to help authors who want to write article by guiding them with the process


  1. If you have an idea about article, search on IBM Developerworks to find out if someone has already authored article on that topic.

  2. Create a rough article layout things like what would you like to cover in the article, what type of sample code you will like to write,..etc.

  3. Submit your article idea at Submit Content form

  4. You should get email from developerworks after you submit idea in 7-10 days. If they like your idea they will give you close to 2 months to finish the first draft of article

I received "IBM developerWorks Contributing Author" designation

Today i received email from IBM that i achieved "IBM developerWorks Contributing Author" designation. I can see my name on IBM Developerworks site

I have sufficient point to reach to next level which is "IBM DeveloperWorks Professional Author", but in order to reach that level i need to review 2 technical reviews and mentor 2 new technical authors. So if your interested in writing WebSphere Application Server, Portal Server related article or would like someone to review your article, please contact me at sdpatil@gmail.com

Client Side User Prorile Manipulation

I built a sample application for testing how to work with the User Profile on the client side. You can download the sample application from here

The sample application allows you to get values of user profile attributes and set the value for user profile attribute, It seems that either there is some problem with the setting user profile attribute server side code or the it requires some additional settings because i could not get it working.



The business logic of the user profile application is implemented in the userprofile.jsp like this.


<%@page
language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1" session="false"%><%@taglib
uri="http://java.sun.com/portlet" prefix="portlet"%><%@taglib
uri="http://www.ibm.com/xmlns/prod/websphere/portal/v6.1/portlet-client-model"
prefix="portlet-client-model"%><%@taglib
uri="http://java.sun.com/portlet" prefix="portletx"%><portlet-client-model:init>
<portlet-client-model:require module="ibm.portal.xml.*" />
<portlet-client-model:require module="ibm.portal.portlet.*" />
</portlet-client-model:init>
<portlet:defineObjects />
<script language="javascript">
function printUserProfile(){
var _portletWindow = new ibm.portal.portlet.PortletWindow("<%=portletWindowID%>");
_portletWindow.getUserProfile(printUserProfileCallback);
}
function printUserProfileCallback(portletWindow, status, userProfile){
if (status==ibm.portal.portlet.PortletWindow.STATUS_OK) {
var attributeList = ["cn","uid", "mobile","displayName","seeAlso","postalAddress","telephoneNumber","jpegPhoto","departmentNumber","viewIdentifiers","secretary","businessCategory","l","homePostalAddress","c","partyRoles","sn","pager","groups","businessAddress","carLinces","ibm-jobTitle","st","description","homeAddress","initials","postalCode","roomNumbr","title","stateOrProvinceName","givenName","children","street","manager","facsimileTelephoneNumber","countryName","localityName","uid","cn" ];
var userProfileStr = "";
for( var i = 0; i < attributeList.length; i++){
userProfileStr = userProfileStr + attributeList[i] + " - " + userProfile.getAttribute(attributeList[i]) +"<br/>";
}

dojo.byId("userPref").innerHTML = userProfileStr;
}else{
console.error("Error in getting user profile");
}
}
function setProfileAttribute(portletWindow, status, userProfile){
console.log("Entering setProfileAttribute()");
if (status==ibm.portal.portlet.PortletWindow.STATUS_OK) {
var profName = portletWindow.getAttribute("profName");
var profValue = portletWindow.getAttribute("profValue");
console.log("Setting User profile " + profName + " " +profValue);
userProfile.setAttribute(profName,profValue);
portletWindow.setUserProfile(userProfile,printUserProfileCallback);
}else{
alert("Error in loading user profile");
}
console.log("Exiting setProfileAttribute()");
}
function setUserProfileAttribute(){
console.log("Entering setUserProfileAttribute()");
console.log("Attribute Name " + dojo.byId("profName").value);
console.log("Attribute Value " + dojo.byId("profValue").value);
var _portletWindow = new ibm.portal.portlet.PortletWindow("<%=portletWindowID%>");
_portletWindow.setAttribute("profName",dojo.byId("profName").value );
_portletWindow.setAttribute("profValue",dojo.byId("profValue").value );
_portletWindow.getUserProfile(setProfileAttribute);

console.log("Exiting changePreference()");
return false;
}
printUserProfile();
</script>
<portlet:defineObjects />
<form method="post" onsubmit="setUserProfileAttribute">
<table>
<tr>
<td>Profile Attribute Name</td>
<td><input type="text" name="profName" id="profName" /></td>
</tr>
<tr>
<td>Profile Attribute Value</td>
<td><input type="text" name="profValue" id="profValue" /></td>
</tr>
<tr>
<td><input type="button" onclick="setUserProfileAttribute()" value="Set Profile Attribute" /></td>
<td><input type="button" onclick="printUserProfile()" value="Get Profile Attribute" /></td>

</tr>
</table>
</form>
<h4>User Profile</h4>
<div id="userPref">
</div


Getting the user profile attributes

You can get values for the user profile attributes by clicking on the "Get Profile Attribute" button, when you do that the control will go to printUserProfile(), this method will create object of PortletWindow class and then call its getUserProfile() method, passing it printUserProfileCallback as name of the call back function, the getUserProfile(), makes a call to get ATOM feed for the User Profile which looks something like this



Once this ATOM feed is retrieved, the printUserProfileCallback, method gets called with userProfile as third object. Inside the method first check the status of the request if it was OK then go through list of attribute names, get values for each one of them and set the resultant string as value of userPref div attribute.

These are the methods supported by UserProfile


Setting the user profile attribute

You can set value for UserProfile attribute by entering some value for "Profile Attribute Name" and "Profile Attribute Value" and clicking on "Set Profile Attribute", when you do that the setUserProfileAttribute() method gets called. This method creates object of PortletWindow and then calls its getUserProfile() method, in the call back method it is setting user profile attribute by calling userProfile.setAttribute(profName,profValue), which modifies the ATOM feed but in order to persist these changes you will have to call portletWindow.setUserProfile(userProfile,printUserProfileCallback), at that point the client side API will submit the ATOM feed to the server.

In my locale environment i do have WPS 6.1.5 environment with default File System Repository when i tried setting any user profile attribute i am getting this error

[1/12/10 22:34:12:909 PST] 00000058 WebApp E [Servlet Error]-[PROTECTED_HANDLER_47555912]: java.lang.NullPointerException
at com.ibm.portal.rest.userprofile.UserProfileFeedRequestProxy.getPathInfo(UserProfileFeedRequestProxy.java:116)
at com.ibm.portal.rest.userprofile.pathelements.PathElementStateMachine.handle(PathElementStateMachine.java:146)
at com.ibm.portal.rest.userprofile.pathelements.PathElementStateMachine.handleRequest(PathElementStateMachine.java:133)
at com.ibm.portal.rest.userprofile.UserProfileDataSource.calculate(UserProfileDataSource.java:137)
at com.ibm.portal.rest.userprofile.UserProfileDataSink.parse(UserProfileDataSink.java:129)
at com.ibm.wps.resolver.data.ByteXmlDataSink.read(ByteXmlDataSink.java:159)
at com.ibm.wps.resolver.servlet.ContentHandlerUpload.handleDownloadAndDispose(ContentHandlerUpload.java:482)
at com.ibm.wps.resolver.servlet.ContentHandlerUpload.handleUpload(ContentHandlerUpload.java:636)
at com.ibm.wps.resolver.servlet.ContentHandlerUpload.doFilter(ContentHandlerUpload.java:200)
at com.ibm.wps.resolver.servlet.AbstractFilter.doFilter(AbstractFilter.java:93)
at com.ibm.wps.resolver.servlet.FilterChainElement.doFilter(FilterChainElement.java:57)
at com.ibm.wps.resolver.servlet.ContentHandlerGzip.internalDoFilter(ContentHandlerGzip.java:413)
at com.ibm.wps.resolver.servlet.ContentHandlerGzip.doFilter(ContentHandlerGzip.java:248)
at com.ibm.wps.resolver.servlet.AbstractFilter.doFilter(AbstractFilter.java:93)
at com.ibm.wps.resolver.servlet.FilterChainElement.doFilter(FilterChainElement.java:57)
at com.ibm.wps.resolver.servlet.ContentHandlerByteRange.doFilter(ContentHandlerByteRange.java:98)
at com.ibm.wps.resolver.servlet.AbstractFilter.doFilter(AbstractFilter.java:93)
at com.ibm.wps.resolver.servlet.FilterChainElement.doFilter(FilterChainElement.java:57)
at com.ibm.wps.resolver.servlet.ContentHandlerPortal.doFilter(ContentHandlerPortal.java:211)
at com.ibm.wps.resolver.servlet.AbstractFilter.doFilter(AbstractFilter.java:93)
at com.ibm.wps.resolver.servlet.FilterChainElement.doFilter(FilterChainElement.java:57)
at com.ibm.wps.resolver.servlet.ContentHandlerOptions.doFilter(ContentHandlerOptions.java:107)
at com.ibm.wps.resolver.servlet.AbstractFilter.doFilter(AbstractFilter.java:93)
at com.ibm.wps.resolver.servlet.FilterChainElement.doFilter(FilterChainElement.java:57)
at com.ibm.wps.resolver.servlet.ContentHandlerBouncer.doFilter(ContentHandlerBouncer.java:238)
at com.ibm.wps.resolver.servlet.AbstractFilter.doFilter(AbstractFilter.java:93)
at com.ibm.wps.resolver.servlet.FilterChainElement.doFilter(FilterChainElement.java:57)
at com.ibm.wps.resolver.servlet.ContentHandlerDecoder.doFilter(ContentHandlerDecoder.java:132)
at com.ibm.wps.resolver.servlet.AbstractFilter.doFilter(AbstractFilter.java:93)
at com.ibm.wps.resolver.servlet.FilterChainElement.doFilter(FilterChainElement.java:57)
at com.ibm.wps.resolver.servlet.ContentHandlerVirtualPortal.doFilter(ContentHandlerVirtualPortal.java:198)
at com.ibm.wps.resolver.servlet.AbstractFilter.doFilter(AbstractFilter.java:93)
at com.ibm.wps.resolver.servlet.FilterChainElement.doFilter(FilterChainElement.java:57)
at com.ibm.wps.resolver.servlet.ContentHandlerCleanup.doFilter(ContentHandlerCleanup.java:648)
at com.ibm.wps.resolver.servlet.AbstractFilter.doFilter(AbstractFilter.java:93)
at com.ibm.wps.resolver.servlet.FilterChainElement.doFilter(FilterChainElement.java:57)
at com.ibm.wps.resolver.servlet.ContentHandlerLogin.doFilter(ContentHandlerLogin.java:89)
at com.ibm.wps.resolver.servlet.AbstractFilter.doFilter(AbstractFilter.java:93)
at com.ibm.wps.resolver.servlet.FilterChainElement.doFilter(FilterChainElement.java:57)
at com.ibm.wps.resolver.servlet.FilterChainServlet.service(FilterChainServlet.java:342)
at com.ibm.wps.resolver.servlet.AbstractServlet.service(AbstractServlet.java:530)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1146)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1087)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:145)
at com.ibm.wps.state.filter.StateCleanup.doFilter(StateCleanup.java:94)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:190)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:130)
at com.ibm.wps.engine.ExtendedLocaleFilter.doFilter(ExtendedLocaleFilter.java:113)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:190)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:130)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain._doFilter(WebAppFilterChain.java:87)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:837)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:680)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:588)
at com.ibm.ws.wswebcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:524)
at com.ibm.ws.webcontainer.servlet.CacheServletWrapper.handleRequest(CacheServletWrapper.java:90)
at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:751)
at com.ibm.ws.wswebcontainer.WebContainer.handleRequest(WebContainer.java:1478)
at com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:125)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:458)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewInformation(HttpInboundLink.java:387)
at com.ibm.ws.http.channel.inbound.impl.HttpICLReadCallback.complete(HttpICLReadCallback.java:102)
at com.ibm.ws.tcp.channel.impl.AioReadCompletionListener.futureCompleted(AioReadCompletionListener.java:165)
at com.ibm.io.async.AbstractAsyncFuture.invokeCallback(AbstractAsyncFuture.java:217)
at com.ibm.io.async.AsyncChannelFuture.fireCompletionActions(AsyncChannelFuture.java:161)
at com.ibm.io.async.AsyncFuture.completed(AsyncFuture.java:136)
at com.ibm.io.async.ResultHandler.complete(ResultHandler.java:196)
at com.ibm.io.async.ResultHandler.runEventProcessingLoop(ResultHandler.java:751)
at com.ibm.io.async.ResultHandler$2.run(ResultHandler.java:881)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1497)

Client Side User Preferences Manipulation

I just built a sample Porltet to demonstrate how to use Client Side API for manipulating the User Preferences. You can download the source code for sample from here

This is the screen shot of how my portlet looks like



The portlet has two parts, first part allows you to enter a key value pair and that will be set as preference for user, using client side API and the second part is Current User Preferences, that displays the user preferences for the portlet.

The portlet has one userpref.jsp file that has all the business logic


<%@page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1" session="false"%>
<%@taglib uri="http://java.sun.com/portlet" prefix="portlet"%>
<%@taglib
uri="http://www.ibm.com/xmlns/prod/websphere/portal/v6.1/portlet-client-model"
prefix="portlet-client-model"%>
<%@taglib uri="http://java.sun.com/portlet" prefix="portletx"%>
<portlet-client-model:init>
<portlet-client-model:require module="ibm.portal.xml.*" />
<portlet-client-model:require module="ibm.portal.portlet.*" />
</portlet-client-model:init>
<script language="javascript">
function printPreferences(){
var _portletWindow = new ibm.portal.portlet.PortletWindow("<%=portletWindowID%>");
_portletWindow.getPortletPreferences(printPreferencesCallback);
}
function printPreferencesCallback(portletWindow, status, portletPrefs){
if (status==ibm.portal.portlet.PortletWindow.STATUS_OK) {
var prefs = portletPrefs.getMap();
var prefStr = "";
for (var i=0; i<prefs.length; i++) {
prefStr = prefStr + prefs[i].name+ " - "+prefs[i].values +"<br/>";
}
dojo.byId("userPref").innerHTML = prefStr;
}else{
console.error("Error in getting preferences");
}
}
function loadPreferences(portletWindow, status, portletPrefs){
console.log("Entering loadPreferences()");
if (status==ibm.portal.portlet.PortletWindow.STATUS_OK) {
var preferenceName = portletWindow.getAttribute("preferenceName");
var preferenceValue = portletWindow.getAttribute("preferenceValue");
portletPrefs.setValue(preferenceName,preferenceValue);
portletWindow.setPortletPreferences(portletPrefs,printPreferencesCallback);
}else{
alert("Error in loading preference");
}
console.log("Entering loadPreferences()");
}
function changePreference(){
console.log("Entering changePreference()");
console.log("Preference Name " + dojo.byId("preferenceName").value);
console.log("Preference Value " + dojo.byId("preferenceValue").value);
var _portletWindow = new ibm.portal.portlet.PortletWindow("<%=portletWindowID%>");
_portletWindow.setAttribute("preferenceName",dojo.byId("preferenceName").value );
_portletWindow.setAttribute("preferenceValue",dojo.byId("preferenceValue").value );
_portletWindow.getPortletPreferences(loadPreferences);

console.log("Exiting changePreference()");
return false;
}
printPreferences();
</script>
<portlet:defineObjects />
<form method="post" onsubmit="changePreference()">
<table>
<tr>
<td>Preference Name</td>
<td><input type="text" name="preferenceName" id="preferenceName" /></td>
</tr>
<tr>
<td>Preference Value</td>
<td><input type="text" name="preferenceValue"
id="preferenceValue" /></td>
</tr>
<tr>
<td><input type="button" onclick="changePreference()"
value="Set Preference" /></td>
<td><input type="button" onclick="printPreferences()"
value="Print Preference" /></td>

</tr>
</table>
</form>
<h4>Current Preferences</h4>
<div id="userPref">
</div>


Getting User Preferences
You can print the user preferences by clicking on "Print Preferences" button. When you do that printPreferences() method will get called. This method get the current PortletWindow object by using constructor like this new ibm.portal.portlet.PortletWindow("<%=portletWindowID%>"). Once you have the PortletWindow object you can call its getPortletPreferences(), and pass it name of the callback function. When you do that the client side API will make a call to server to get preference ATOM feed and once the feed is available it will call the printPreferencesCallback() method.

The printPreferencesCallback() takes three argument PortletWindow, status and PortletPreferences. Status indicates if the request was successful or not, the PortletWindow object is the object that you used for making request. Inside the callback method check if the request was successful if yes then you can start working with portletPref object which is object of ibm.portal.portlet.PortletPreferences. Once you have that object you can use the methods of ibm.portal.portlet.PortletPreferences to manipulate preferences.

The ibm.portal.portlet.PortletPreferences object is equivalent to the javax.portlet.PortletPreference object on the client side and provides near about same methods.



Setting User Preferences

You can set preferences by entering value for "Preference Name" and "Preference Value" and then clicking on "Setting Preferences" button, when you do that it will pass control to changePreference method, which will read values entered by user, then it will create object of PortletWindow and call its getPrefernces method, passing loadPreferences as callback method name.

When the getPreferences() method gets called portal will make HTTP Get call to get portlet preferences in the ATOM feed format, then inside the method i am setting preference by calling portletPrefs.setValue(preferenceName,preferenceValue), but the setValue only udpates the ATOM feed for the preferences. In order to persist these changes on the server you will have to call portletWindow.setPortletPreferences(portletPrefs,printPreferencesCallback) method, passing it the modified preferences object. This method will PUT the modified ATOM feed for portlet preferences to the server side by making HTTP PUT call.

On the server side there is ContentHandler Servlet which takes care of reading ATOM feed and updating the underlying portlet data model.

Client Side API documentation

I was trying to find the client side API documentation for sometime and finally i am able to get it at http://www.ibm.com/developerworks/websphere/library/specs/0608_wp6javadoc.html once on that page you can search for and download Portal V6.1 Web 2.0 Javadoc.

The .zip file has Java Doc style API documentation so unzip it and then start going through it, this is the screen shot of how the PortletWindow documentation

Enhanced portal theme

WPS 6.1.5 introduces a new theme called Enhanced Portal theme, which introduces lot of new features such as page builder,...

One big change in the enhanced theme is that it makes use of DIV + CSS instead of tables. I am not expert in HTML so i did search on google to find out what is difference between div and table and it seems that there are quite few articles that list out problems with table layout, things like the table layout is difficult to maintain, CSS should be used for managing layout instead of table, some stack overflow problems in the browser,..etc. This is one of the article

I tried using firebug to see the difference in traditional theme and enhanced theme. I did apply it to same page and then outlined all tables using Web developer plugin.
This is screen shot of the enhanced theme


Screen shot of the traditional theme


As you can see the enhanced theme does not make use of the tables for layout as heavily as that of the traditional theme

Client side API documentation in the source code

Starting from version 6.1, IBM provides very powerful client side API. The client side API is nothing but set of dojo classes that are provided as public API by IBM. One problem with the client side API is that there is not much documentation available around it, i was able to find very good documentation in the source code.

The WebSphere\wp_profile\installedApps\sunpa\wps.ear\wps.war\themes\dojo\portal_dojo\ibm\portal\portlet\portelet.js file has all the source code for the client side API and good things is it has very good inline documentation. The portlet.js has close to 1700 lines of code, but the code is built using Dojo so it very easy to read and well structured.

This screen shot is from my machine which shows documentation for setPortletState method



The setPortletState methos is part of ibm.portal.portlet.PortletWindow and allows you to change the portlet state on the client side, if the method is called from page built using the Client Side Aggregation theme then the callback function decides how to react to the state change but in case of Server side aggregation theme it will result in page refresh

Code generated by portlet-client-model:init tags

Starting from version 6.1, WebSphere Portal server ships with client side API, that lets you do following things

  • Get and Set preferences

  • Change portlet modes and window states

  • Manipulate user profile



The Rational Application Server 7.5 provides you with tools to work with the client side API, You might have noticed that when you create a JSP inside portlet it includes these portlet-client-model tags

<portlet-client-model:init>
<portlet-client-model:require module="ibm.portal.xml.*" />
<portlet-client-model:require module="ibm.portal.portlet.*" />
</portlet-client-model:init>


If you look at the generated source code you will notice that the above JSP code generates following HTML code

<script>
if(typeof dojo=='undefined') {
document.writeln("<scr"+"ipt src='/wps/themes/dojo/portal_dojo/dojo/dojo.js' >
</scr"+"ipt>");
}
</script>
<script>
dojo.require('ibm.portal.xml.xpath');
dojo.require('ibm.portal.xml.xslt');
</script>
<script>
dojo.require('ibm.portal.portlet.portlet');
</script>
<script>
if(typeof(ibmPortalConfig) == "undefined") {
ibmPortalConfig = {contentHandlerURI: "/wps/mycontenthandler/!ut/p/digest!p3CnkU0nAEWw1oYE_wKYdg/nm/oid:wps.portal.root"};} else if(!ibmPortalConfig["contentHandlerURI"])
{ibmPortalConfig["contentHandlerURI"] = "/wps/mycontenthandler/!ut/p/digest!p3CnkU0nAEWw1oYE_wKYdg/nm/oid:wps.portal.root";} </script>
<div id='com.ibm.wps.web2.portlet.root.7_8000CB1A00QN00IC8BRUOG00O5' style='display: none;'>/wps/mycontenthandler/!ut/p/digest!p3CnkU0nAEWw1oYE_wKYdg/pm/oid:--portletwindowid--@oid:6_8000CB1A00QN00IC8BRUOG00O4</div>
<div id='com.ibm.wps.web2.portlet.preferences.7_8000CB1A00QN00IC8BRUOG00O5' style='display: none;' pageid='6_8000CB1A00QN00IC8BRUOG00O4' configid='3_8000CB1A00QN00IC8BRUOG0081' editdefaultsid='5_8000CB1A00QN00IC8BRUOG00O3'
editid='5_8000CB1A00QN00IC8BRUOG00O7'
>
</div>
<div id='com.ibm.wps.web2.portlet.user.7_8000CB1A00QN00IC8BRUOG00O5' style='display: none;'>/wps/mycontenthandler/!ut/p/digest!p3CnkU0nAEWw1oYE_wKYdg/um/secure/currentuser/profile?expandRefs=true</div>


As you can see the start of portlet-client-model:init tag is used for initializing the client side API on your page, it is checking if dojo is included on page already if not including it.

The call to portlet-client-model:require is getting converted into one or many dojo.require calls, the dojo.require is similar to import statement in the java it is used for importing classes into your code. In my case we are including classes from ibm.portal.xml and ibm.portal.portlet packages. In case of Dojo the dojo.require() call is used for including a .js file in your page.
The dojo classes are stored in the Wps.ear folder like this. You can create your custom classes and add theme to wps.ear



The portlet-client-model:init tag also generates some div tags that hold information required for client side API. For example the <div id='com.ibm.wps.web2.portlet.user.7_8000CB1A00QN00IC8BRUOG00O5' style='display: none;'>/wps/mycontenthandler/!ut/p/digest!p3CnkU0nAEWw1oYE_wKYdg/um/secure/currentuser/profile?expandRefs=true</div> holds information about how to load user profile information for current user.