Spring Framework 3.0.1 has support for Portlet Specification 2.0

Yesterday i was talking with my friend Sanjay and he said that Spring Framework Release 3.0.1 has Support for Portlet Specification 2.0. Today i looked at the documentation of Dispatcher, i can see that it has method like serveResource(), doEventService()

Creating application specific log in the Flex program

You can create application specific log in your flex application like this. In my case i am initializing myLogger in the initLogger() method at the time of document creation and once the logger is initialized we can write something at debug level by calling myLogger.debug() method.

Similar to debug there are methods to indicate log statement at different levels, same as that of log4j.


<?xml version="1.0"?>
<!-- logging/ButtonLifeCycle.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete='initLogging()'>
<mx:Script><![CDATA[
import mx.controls.Alert;
import mx.logging.ILogger;
import mx.logging.Log;
import mx.logging.LogEventLevel;
import mx.logging.targets.TraceTarget;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;

private var myLogger:ILogger;
private function initLogging():void {
var logTarget:TraceTarget = new TraceTarget();
logTarget.filters=["MyCustomClass"];
logTarget.level = LogEventLevel.ALL;
Log.addTarget(logTarget);
myLogger = Log.getLogger("MyCustomClass");
}


private function traceClick():void {
myLogger.debug("Entering traceClick");
useHttpService("http://www.google.com");
myLogger.debug("Exiting traceClick");
}

private var service:HTTPService
public function useHttpService(url:String):void {
myLogger.debug("Entering useHttpService");
service = new HTTPService();
service.url = url;
service.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
service.method = "GET";
service.addEventListener("result", httpResult);
service.addEventListener("fault", httpFault);
service.send(parameters);
myLogger.debug("Entering useHttpService");
}
public function httpResult(event:ResultEvent):void {
myLogger.debug("Entering httpResult");
var result:Object = event.result;
Alert.show(event.result.toString());
myLogger.debug("Entering httpResult");
}
public function httpFault(event:FaultEvent):void {
myLogger.debug("Entering httpFault");
var faultstring:String = event.fault.faultString;
Alert.show(faultstring + event.fault.faultDetail + event.fault.rootCause);
myLogger.debug("Entering httpFault");
}
]]></mx:Script>
<mx:Button id="b1" label="Click Me" click="traceClick()" />
</mx:Application>

Enabling log Flex applicationEnable log for HTTPService in Flex application

When i started with Flex development, i was trying to make HTTP call to the portlet and i was not able to get any response so i did add this code to my Flex program to enable the log so that i can see what was going on with the HTTPService.

In order to use the Logging you will have to Enable trace in the Flex program


<?xml version="1.0"?>
<!-- logging/ButtonLifeCycle.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete='initLogging()' >
<mx:Script><![CDATA[
import mx.controls.Alert;
import mx.logging.ILogger;
import mx.logging.Log;
import mx.logging.LogEventLevel;
import mx.logging.targets.TraceTarget;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;

private function initLogging():void {
var logTarget:TraceTarget = new TraceTarget();
logTarget.filters=["mx.rpc.*","mx.messaging.*"];
logTarget.level = LogEventLevel.ALL;
Log.addTarget(logTarget);
}

private function traceClick():void {
useHttpService("http://www.google.com");
}

private var service:HTTPService
public function useHttpService(url:String):void {
service = new HTTPService();
service.url = url;
service.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
service.method = "GET";
service.addEventListener("result", httpResult);
service.addEventListener("fault", httpFault);
service.send(parameters);
}
public function httpResult(event:ResultEvent):void {
var result:Object = event.result;
Alert.show(event.result.toString());
}
public function httpFault(event:FaultEvent):void {
var faultstring:String = event.fault.faultString;
Alert.show(faultstring + event.fault.faultDetail + event.fault.rootCause);
}
]]></mx:Script>
<mx:Button id="b1" label="Click Me" click="traceClick()" />
</mx:Application>


The log is disabled by default in Flex so first i had to create initLogging() function to enable trace and then call it from createComplete() so that the log for HTTPService gets enabled. After that i could see this log in flashlog.txt when i tried making call to www.google.com from Flex application

'7671B99A-ED63-A113-A196-E28CF5762B74' producer set destination to 'DefaultHTTP'.
'direct_http_channel' channel endpoint set to http:
'7671B99A-ED63-A113-A196-E28CF5762B74' producer sending message 'D672CE47-357B-884E-07D7-E28CF5D40D82'
'direct_http_channel' channel sending message:
(mx.messaging.messages::HTTPRequestMessage)#0
body = (Object)#1
clientId = (null)
contentType = "application/x-www-form-urlencoded"
destination = "DefaultHTTP"
headers = (Object)#2
httpHeaders = (Object)#3
messageId = "D672CE47-357B-884E-07D7-E28CF5D40D82"
method = "GET"
recordHeaders = false
timestamp = 0
timeToLive = 0
url = "http://www.google.com"
'7671B99A-ED63-A113-A196-E28CF5762B74' producer connected.
'7671B99A-ED63-A113-A196-E28CF5762B74' producer acknowledge of 'D672CE47-357B-884E-07D7-E28CF5D40D82'.
Decoding HTTPService response
Processing HTTPService response message:
(mx.messaging.messages::AcknowledgeMessage)#0
body = "<!doctype html><html onmousemove="google&&google.fade&&google.fade(event)"><head><meta http-equiv="content-type" content="text/html; charset=UTF-8"><title>Google</title><script>window.google={kEI:"upR9S5bhE4_MowTPsPWXCg",kEXPI:"17259,21965,23394,23807",kCSI:{e:"17259,21965,23394,23807",ei:"upR9S5bhE4_MowTPsPWXCg",expi:"17259,21965,23394,23807"},pageState:"#",kHL:"en",time:function(){return(new Date).getTime()},log:function(b,d,c){var a=new Image,e=google,g=e.lc,f=e.li;a.onerror=(a.onload=(a.onabort=function(){delete g[f]}));g[f]=a;c=c||"/gen_204?atyp=i&ct="+b+"&cad="+d+"&zx="+google.time();a.src=c;e.li=f+1},lc:[],li:0,j:{en:1,l:function(){},e:function(){},b:location.hash&&location.hash!="#",pl:[],mc:0,sc:0.5},Toolbelt:{}};(function(){for(var d=0,c;c=["ad","bc","p","pa","zd","ac","pc","pah","ph","sa","xx","zc","zz"][d++];)(function(a){google.j[a]=function(){google.j.pl.push([a,arguments])}})(c)})();
window.google.sn="webhp";window.google.timers={load:{t:{start:(new Date).getTime()}}};try{window.google.pt=window.gtbExternal&&window.gtbExternal.pageT();}catch(u){}window.google.jsrt_kill=1;
"
clientId = "DirectHTTPChannel0"
correlationId = "D672CE47-357B-884E-07D7-E28CF5D40D82"
destination = ""
headers = (Object)#1
DSStatusCode = 200
messageId = "3CAC4096-54F6-BF7C-EEB9-E28CF855D638"
timestamp = 0
timeToLive = 0
Warning: Ignoring 'secure' attribute in policy file from http://ad.doubleclick.net/crossdomain.xml. The 'secure' attribute is only permitted in HTTPS and socket policy files. See http://www.adobe.com/go/strict_policy_files for details.
Warning: Ignoring 'secure' attribute in policy file from http://ad.doubleclick.net/crossdomain.xml. The 'secure' attribute is only permitted in HTTPS and socket policy files. See http://www.adobe.com/go/strict_policy_files for details.
Warning: Domain www.youtube.com does not specify a meta-policy. Applying default meta-policy 'master-only'. This configuration is deprecated. See http://www.adobe.com/go/strict_policy_files to fix this problem.
Warning: Domain i3.ytimg.com does not specify a meta-policy. Applying default meta-policy 'master-only'. This configuration is deprecated. See http://www.adobe.com/go/strict_policy_files to fix this problem.
Warning: Domain i4.ytimg.com does not specify a meta-policy. Applying default meta-policy 'master-only'. This configuration is deprecated. See http://www.adobe.com/go/strict_policy_files to fix this problem.
Warning: Domain i2.ytimg.com does not specify a meta-policy. Applying default meta-policy 'master-only'. This configuration is deprecated. See http://www.adobe.com/go/strict_policy_files to fix this problem.
Warning: Domain www.youtube.com does not specify a meta-policy. Applying default meta-policy 'master-only'. This configuration is deprecated. See http://www.adobe.com/go/strict_policy_files to fix this problem.
Warning: Domain video-stats.video.google.com does not explicitly specify a meta-policy, but Content-Type of policy file http://video-stats.video.google.com/crossdomain.xml is 'text/x-cross-domain-policy'. Applying meta-policy 'by-content-type'.
Warning: Domain www.youtube.com does not specify a meta-policy. Applying default meta-policy 'master-only'. This configuration is deprecated. See http://www.adobe.com/go/strict_policy_files to fix this problem.
Warning: Domain video-stats.video.google.com does not explicitly specify a meta-policy, but Content-Type of policy file http://video-stats.video.google.com/crossdomain.xml is 'text/x-cross-domain-policy'. Applying meta-policy 'by-content-type'.

Flashbug for Flex developer

FlashBug is a very nice and useful extension of FireBug, if your serious Flash Developer



It lets you look at the flashlog.txt, policylog.txt in the console and provides ability to cleanup the log

Please note that you will have to Enable trace in the Flex program before you can use Flashbug

Enable trace in the Flex program

By default the Tracing is disabled in the flex player but you can enable it by going to C:\Documents and Settings\sunpatil\mm.cfg file and modifying value of TraceOutputFileEnabled to 1 instead of 0. Then restart your browser and execute your flex program again.


#flashlog
# Beginning with the Flash Player 9 Update, Flash Player ignores the TraceOutputFileName property.
# On Macintosh OS X, you should use colons to separate directories in the TraceOutputFileName path rather than slashes.
TraceOutputFileName=C:\Documents and Settings\<username>\Application Data\Macromedia\Flash Player\Logs\flashlog.txt # Set TraceOutputFileName to override the default name and location of the log file

ErrorReportingEnable=1 # Enables the logging of error messages. 0/1
TraceOutputFileEnable=1 # Enables trace logging. 0/1
MaxWarnings=100 # Sets the number of warnings to log before stopping.

#policyfiles
PolicyFileLog=1 # Enables policy file logging
PolicyFileLogAppend=1 # Optional; do not clear log at startup


Now open the C:\Documents and Settings\<username>\Application Data\Macromedia\Flash Player\Logs\flashlog.txt file in text editor and you should see the trace generated by your flex program. Other option is to use FlashBug which is a plugin in firebug

Global trace in your Adobe Flex program

Ability to trace message is very important in building any complex program. You can use the debugger version of Flash Player to capture output from the global trace() method and write that output to the client log file. You can use trace() statements in any ActionScript or MXML file in your application. Because it is a global function, you are not required to import any ActionScript classes packages to use the trace() method.


<?xml version="1.0"?>
<!-- logging/ButtonLifeCycle.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete='trace(employees)'>
<mx:Script><![CDATA[
private function traceClick():void {
trace("Entering traceClick");
trace("Exiting traceClick");
}
]]></mx:Script>
<mx:XMLList id="employees">
<employee>
<name>Christina Coenraets</name>
<phone>555-219-2270</phone>
<email>ccoenraets@fictitious.com</email>
<active>true</active>
</employee>
<employee>
<name>Joanne Wall</name>
<phone>555-219-2012</phone>
<email>jwall@fictitious.com</email>
<active>true</active>
</employee>
</mx:XMLList>
<mx:Button id="b1" label="Click Me" click="traceClick()" />
</mx:Application>


You can call trace() method from either the ActionScript or from MXML with either object or String as argument and that message will get traced to the flashlog.txt, if trace is enabled like this



Christina Coenraets
555-219-2270
ccoenraets@fictitious.com
true


Joanne Wall
555-219-2012
jwall@fictitious.com
true

Entering traceClick
Exiting traceClick

Debugger version of flash player

The debugger version of Flash player can play the swf files similar to the flash player but in addition to that it lets you do following things


  • Output statements and application errors to the debugger version of the Flash Player local log file by using the trace() method.

  • Write data services log messages to the local log file of the debugger version of Flash Player.

  • View run-time errors (RTEs).

  • Use the fdb command-line debugger.

  • Use the Flex Builder debugging tool.

  • Use the Flex Builder profiling tool.



If you have the Flex builder installed on your machine then you can get the Flash player debugger version from AdobeFlashBuilder\player\<osname> directory or you can download it from http://www.adobe.com/support/flashplayer/downloads.html.

Once installed you check if your using the flash debugger by right clicking on your flash player, it should show a context menu for Debugger like this

Handling WindowState change on client side

The Infocenter for WebSphere Portal has Changing portlet mode and window state on the client side document that describes how you can handle the portlet mode change on the client side

I tried creating a JavaScript function like this

function doWindowState( windowState, div ){
console.log("Entering namespace specific doWindowState ");
console.log("Exiting namespace specific doWindowState ");
return true;
}


but it does not get called when i click on either Maximize or Minimize button but then i tried using the same trick that i used for doPortletMode() blog and created a JavaScript code like this

function PC_<%=portletWindowID%>_doWindowState( windowState, div ){
console.log("Entering PC_<%=portletWindowID%>_ specific doWindowState ");
console.log("Window State " + windowState);
console.log("Window State " + div);
div.innerHTML="Content set on the client side"
console.log("Exiting PC_<%=portletWindowID%>_ specific doWindowState ");
return true;
}


The function gets control when i click on either minimize or maximize button but when i set the div.innerHTML it throws some wiered JavaScript exceptions like this

Using doPortletMode to handle portlet mode change on client side

In client-side aggregation, you can provide an event handler for changes of portlet mode and portlet window state. This handler gets called when a mode change or a window state change is triggered.

The Infocenter for WebSphere Portal has Changing portlet mode and window state on the client side document that describes how you can handle the portlet mode change on the client side

Note: This function is currently only supported in the portal CSA theme and the CSA skin. You can adapt the CSA theme and skin to write your own custom themes and skins to support this feature.

The return value of your handler determines whether or not the default action is executed:

  • A return value of true allows execution of the default action, in this case the portlet mode or window state change.

  • A return value of false cancels the default action.



This allows the portlet to handle these changes entirely on the client, with no server interaction.

I tried adding JavaScript function as described in the Infocenter in my portlet but that function never gets called.

function doPortletMode( portletMode, div ){
console.log("Entering namespace specific doPortletMode ");
var retVal = true;
if ( portletMode == ibm.portal.portlet.PortletMode.EDIT) {
div.innerHTML = "

Edit Mode from client side

";
retVal = false;
}
console.log("Exiting namespace specific doPortletMode ");
return retVal;
}


THen i looked the ibmCSA.js to find out what is it looking for and it appears that it is looking for PC_<%=portletWindowID%>_doPortletMode function name so i made change in my portlet to create function like this


function PC_<%=portletWindowID%>_doPortletMode( portletMode, div ){
console.log("Entering PC_WindowIdspecific doPortletMode ");
var retVal = true;
if ( portletMode == ibm.portal.portlet.PortletMode.EDIT) {
div.innerHTML = "

Edit Mode from client side

";
retVal = false;
}
console.log("Exiting PC_WindowIdspecific doPortletMode ");
return retVal;
}


After this change i could see that function was getting called when i clicked on personalized but the div which i am changing in my code is hidden so even though the setting of innerHTML works user does not see anything.

I made change to set div.style.display=""; after setting innerHTML and now i can see that the innerHTML that i am setting gets displayed to the user but problem is when is that when i exit back to view mode it does not hide the div

Getting Edit or Help mode markup from the Flex portlet

I made changes in the How to make action or render request from the Adobe Flex so that it makes request to get the markup for Edit and Help mode from the Flex. These are the changes in the source code


public class SPRInFlexPortlet extends javax.portlet.GenericPortlet {
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
// Set the MIME type for the render response
response.setContentType(request.getResponseContentType());
System.out.println("Entering SPRInFlexPortlet.doView()");
if(request.getParameter("caller") == null){
System.out.println("Request from portal, not flex");
SPRUtil sprUtil = new SPRUtil();
String editModeUrl = sprUtil.getPortletEditModeUrl(request, response);
String helpModeUrl = sprUtil.getPortletHelpModeUrl(request, response);
request.setAttribute("editModeUrl", editModeUrl);
request.setAttribute("helpModeUrl", helpModeUrl);

getPortletContext().getRequestDispatcher("/sprinflex.jsp").include(request, response);
return;
}
PrintWriter out = response.getWriter();
out.println("Listing out parameters in the SPRFlexPortlet.doView()");
Enumeration paramNames = request.getParameterNames();
while(paramNames.hasMoreElements()){
String paramName = paramNames.nextElement();
System.out.println(paramName +" " + request.getParameter(paramName));
out.println(paramName +" " + request.getParameter(paramName)+" ");
}
System.out.println("Got request for the first time");
}
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, java.io.IOException {
System.out.println("Entering SPRInFlexPortlet.processAction");
Enumeration paramNames = request.getParameterNames();
while(paramNames.hasMoreElements()){
String paramName = paramNames.nextElement();
System.out.println(paramName +" " + request.getParameter(paramName));
response.setRenderParameter(paramName, request.getParameter(paramName));
}
response.setRenderParameter("requestType", "action");
System.out.println("Exiting SPRInFlexPortlet.processAction");
}
protected void doEdit(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering SPRInFlexPortlet.doEdit");
response.setContentType(request.getResponseContentType());
response.getWriter().println("Returning response from SPRInFlexPortlet.doEdit()");
System.out.println("Exiting SPRInFlexPortlet.doEdit");
}
protected void doHelp(RenderRequest request, RenderResponse response)

throws PortletException, IOException {
System.out.println("Entering SPRInFlexPortlet.doHelp");
response.setContentType(request.getResponseContentType());
response.getWriter().println("Returning response from SPRInFlexPortlet.doHelp()");
System.out.println("Exiting SPRInFlexPortlet.doHelp");
}

}



<%@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://java.sun.com/portlet" prefix="portletx"%>
<portlet:defineObjects />
<script type="text/javascript">
function getEditModeUrl(){
console.log("Returning renderUrl");
return "<%=renderRequest.getAttribute("editModeUrl")%>";
}
function getHelpModeUrl(){
console.log("Returning processActionUrl");
return "<%=renderRequest.getAttribute("helpModeUrl")%>";
}

</script>
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
width="750" height="500" 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() + "/HelloFlex.swf") %>"
quality="high" bgcolor="#869ca7"
width="750" height="500" 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>


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="100%" >
<mx:Panel id="pnlMain" layout="absolute" title="Serve Resource Example">
<mx:Button label="Edit Mode request" y= "11" id="resourceUrl"
click="makeEditModeCall()" x="351"/>
<mx:Button label="Help Mode request" y= "54" id="resourceUrl0"
click="makeHelpModeCall()" x="351"/>
<mx:TextArea x="7" y="135" width="493" height="198" id="displayText"/>
<mx:TextInput x="142" y="10" id="httpMethod" text="GET"/>
<mx:Label x="10" y="12" text="Http Method" width="124"/>
<mx:Label x="10" y="54" text="Parameters"/>
<mx:TextInput x="142" y="54" id="httpParam"/>
<mx:Label x="10" y="106" text="Portlet Response"/>
</mx:Panel>
<mx:Script>
<![CDATA[
import flash.external.*;
import mx.controls.Alert;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
//Event handler for handling button click
public function makeHelpModeCall():void
{
if (ExternalInterface.available) {
var portletUrl:String = ExternalInterface.call("getHelpModeUrl");
useHttpService(portletUrl);
} else
displayText.text = "Unable to call JavaScript";
}
public function makeEditModeCall():void
{
if (ExternalInterface.available) {
var portletUrl:String = ExternalInterface.call("getEditModeUrl");
useHttpService(portletUrl);
} else
displayText.text = "Unable to call JavaScript";
}

//This method is used for making a URL call
private var service:HTTPService
public function useHttpService(url:String):void {
service = new HTTPService();
service.url = url;
service.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
service.method = httpMethod.text;
service.addEventListener("result", httpResult);
service.addEventListener("fault", httpFault);
service.send(parameters);
}
//This callback gets called in case if the http response is returned
public function httpResult(event:ResultEvent):void {
var result:Object = event.result;
displayText.text = event.result.toString();
}
//This call back gets called in case of error in http request
public function httpFault(event:FaultEvent):void {
var faultstring:String = event.fault.faultString;
displayText.text = event.fault.faultDetail;
Alert.show(faultstring + event.fault.faultDetail + event.fault.rootCause);
}
]]>
</mx:Script>
</mx:Application>

How to make action or render request from the Adobe Flex

I built a sample portlet using concept of Single portlet refresh url to demonstrate how to make either a render or action request from the Adobe Flex. You can download the sample code for portlet and flex code

The sample application has a SPRInFlexPortlet, which has doView() and processAction() method, inside the doView() method it is checking if the caller == flex parameter is set if not then it assumes that this request is for the first render and forwards control to sprinflex.jsp page, if this request is made by the flex then it returns the request parameters in the string format.

The processAction() method sets a render parameter requestType == action to let us know that this request was made from the action phase

public class SPRInFlexPortlet extends javax.portlet.GenericPortlet {
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
// Set the MIME type for the render response
response.setContentType(request.getResponseContentType());
System.out.println("Entering SPRInFlexPortlet.doView()");
if(request.getParameter("caller") == null){
System.out.println("Request from portal, not flex");
SPRUtil sprUtil = new SPRUtil();
String renderUrl = sprUtil.getPortletRefreshUrl(request, response);
String actionUrl = sprUtil.getPortletActionUrl(request, response);
request.setAttribute("renderUl", renderUrl);
request.setAttribute("actionUrl", actionUrl);
getPortletContext().getRequestDispatcher("/sprinflex.jsp").include(request, response);
return;
}
PrintWriter out = response.getWriter();
out.println("Listing out parameters in the SPRFlexPortlet.doView()");
Enumeration paramNames = request.getParameterNames();
while(paramNames.hasMoreElements()){
String paramName = paramNames.nextElement();
System.out.println(paramName +" " + request.getParameter(paramName));
out.println(paramName +" " + request.getParameter(paramName)+" ");
}
System.out.println("Got request for the first time");
}
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, java.io.IOException {
System.out.println("Entering SPRInFlexPortlet.processAction");
Enumeration paramNames = request.getParameterNames();
while(paramNames.hasMoreElements()){
String paramName = paramNames.nextElement();
System.out.println(paramName +" " + request.getParameter(paramName));
response.setRenderParameter(paramName, request.getParameter(paramName));
}
response.setRenderParameter("requestType", "action");
System.out.println("Exiting SPRInFlexPortlet.processAction");
}
}


The SPRInFlexPortlet method makes use of SPRUtil to calculate single window render and action url and forwards those values to the JSP. In the JSP i am returning those values from JavaScript method and also rendering HelloFlex object


<%@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 type="text/javascript">
function getRenderUrl(){
console.log("Returning renderUrl");
return "<%=renderRequest.getAttribute("renderUl")%>";
}
function getProcessActionUrl(){
console.log("Returning processActionUrl");
return "<%=renderRequest.getAttribute("actionUrl")%>";
}
</script>
<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() + "/HelloFlex.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>


The HelloFlex code is same as that of the Making resource request from portlet using Flex application, it demonstrates how to make a JavaScript method call to first get the URL and then to make a HTTP call to get response. After getting response it is setting that as value of TextArea


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="100%" >
<mx:Panel id="pnlMain" width="514" height="376" layout="absolute"
title="Serve Resource Example">
<mx:Button label="Render request" y= "11" id="resourceUrl"
click="makeRenderCall()" x="351"/>
<mx:Button label="Process Action request" y= "54" id="resourceUrl0"
click="makeProcessActionCall()" x="351"/>
<mx:TextArea x="7" y="135" width="493" height="198" id="displayText"/>
<mx:TextInput x="142" y="10" id="httpMethod" text="GET"/>
<mx:Label x="10" y="12" text="Http Method" width="124"/>
<mx:Label x="10" y="54" text="Parameters"/>
<mx:TextInput x="142" y="54" id="httpParam"/>
<mx:Label x="10" y="106" text="Portlet Response"/>
</mx:Panel>
<mx:Script>
<![CDATA[
import flash.external.*;
import mx.controls.Alert;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
//Event handler for handling button click
public function makeProcessActionCall():void
{
if (ExternalInterface.available) {
var portletUrl:String = ExternalInterface.call("getProcessActionUrl");
useHttpService(portletUrl);
} else
displayText.text = "Unable to call JavaScript";
}
public function makeRenderCall():void
{
if (ExternalInterface.available) {
var portletUrl:String = ExternalInterface.call("getRenderUrl");
useHttpService(portletUrl);
} else
displayText.text = "Unable to call JavaScript";
}
//This method is used for making a URL call
private var service:HTTPService
public function useHttpService(url:String):void {
service = new HTTPService();
service.url = url;
service.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
service.method = httpMethod.text;
service.addEventListener("result", httpResult);
service.addEventListener("fault", httpFault);
service.send(parameters);
}
//This callback gets called in case if the http response is returned
public function httpResult(event:ResultEvent):void {
var result:Object = event.result;
displayText.text = event.result.toString();
}
//This call back gets called in case of error in http request
public function httpFault(event:FaultEvent):void {
var faultstring:String = event.fault.faultString;
displayText.text = event.fault.faultDetail;
Alert.show(faultstring + event.fault.faultDetail + event.fault.rootCause);
}
]]>
</mx:Script>
</mx:Application>

Utility Class to generate single portlet url

I copied the sample code from Refreshing individual portlets and preferences using Single Portlet Refresh in WebSphere Portal V6.0.1 article into an self contained Utility class so that it can be used in other portlets.

I did create following 4 methods that let you handle the most common use cases of using the Single portlet refresh url


  • getPortletRefreshUrl: This method will give you render URL for the current portlet

  • getPortletActionUrl: This method will give you the action url pointing to the current portlet. Please note one important point that the action Url is valid for only one request so if you want to make action call using Ajax request you cant reuse the actionUrl, you will have to get a new action url from the server every time

  • getPortletEditModeUrl: You can use this method to get the markup for edit mode of the portlet. It will call the doEdit() method of your portlet and return markup generated by it

  • getPortletHelpModeUrl: You can use this method to get the markup for help mode of the portlet. It will call the doHelp() method of your portlet and return markup generated by it



Making a resource request is pretty straight forward, you dont have to do anything special for it.


package com.webspherenotes.ajax;

import java.io.IOException;
import java.io.StringWriter;
import java.util.Map;
import java.util.logging.Logger;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.portlet.PortletException;
import javax.portlet.PortletMode;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import javax.portlet.WindowState;

import com.ibm.portal.ModelException;
import com.ibm.portal.portlet.service.PortletServiceHome;
import com.ibm.portal.portlet.service.PortletServiceUnavailableException;
import com.ibm.portal.portlet.service.model.PortletModelProvider;
import com.ibm.portal.portletmodel.PortletWindow;
import com.ibm.portal.state.EngineURL;
import com.ibm.portal.state.PortletStateManager;
import com.ibm.portal.state.StateHolderController;
import com.ibm.portal.state.URLFactory;
import com.ibm.portal.state.accessors.portlet.PortletAccessorController;
import com.ibm.portal.state.accessors.portlet.PortletTargetAccessorController;
import com.ibm.portal.state.accessors.selection.SelectionAccessorController;
import com.ibm.portal.state.accessors.selection.SelectionAccessorFactory;
import com.ibm.portal.state.exceptions.StateException;
import com.ibm.portal.state.service.PortletStateManagerService;
import com.ibm.ws.install.configmanager.logging.LoggerFactory;

public class SPRUtil {

Logger logger = LoggerFactory.createLogger(SPRUtil.class);
/** JNDI name needed to lookup the state manager service */
private static final String JNDI_STATE_SERVICE =
"portletservice/com.ibm.portal.state.service.PortletStateManagerService";

/** JNDI name needed to lookup the portlet model provider */
private static final String JNDI_MODEL_PROVIDER =
"portletservice/com.ibm.portal.portlet.service.model.PortletModelProvider";

/** portlet state manager service */
protected PortletStateManagerService stateService;

/** portlet model provider */
protected PortletModelProvider modelProvider;

PortletWindow portletWindow;


public SPRUtil(){
init();
}


public void init() {
try {
final Context ctx = new InitialContext();
// lookup the portlet state manager service
PortletServiceHome psh = (PortletServiceHome) ctx.lookup(JNDI_STATE_SERVICE);
stateService = (PortletStateManagerService)
psh.getPortletService(PortletStateManagerService.class);
// lookup the portlet model provider
psh = (PortletServiceHome) ctx.lookup(JNDI_MODEL_PROVIDER);
modelProvider = (PortletModelProvider)
psh.getPortletService(PortletModelProvider.class);
} catch (NamingException e) {
e.printStackTrace();
} catch (PortletServiceUnavailableException e) {
e.printStackTrace();
}
}

public String getPortletRefreshUrl(PortletRequest request, PortletResponse response)throws PortletException{
logger.entering(SPRUtil.class.getName(),"getRenderUrl");
StringWriter writer = new StringWriter();
try {
// get the request-specific portlet state manager
final PortletStateManager stateManager =
stateService.getPortletStateManager(request, response);
// get the portlet window
final PortletWindow portletWindow =
modelProvider.getCurrentPortletWindow(request);

// get the URL factory
final URLFactory urlFactory = stateManager.getURLFactory();
final EngineURL url = urlFactory.newURL(null);
// get the URL-specific state
final StateHolderController state = url.getState();
// make the URL a single portlet refresh URL
setSelectionMapping(stateManager, portletWindow, state);

// if needed add other aspects to the URL
// ...
// write the URL to the markup

url.writeDispose(writer);
// dispose the objects that are no longer needed
urlFactory.dispose();
stateManager.dispose();
} catch (StateException e) {
throw new PortletException(e);
} catch (ModelException e) {
throw new PortletException(e);
} catch (IOException e) {
throw new PortletException(e);
}
logger.exiting(SPRUtil.class.getName(),"getRenderUrl");
return writer.toString();
}

public String getPortletActionUrl(PortletRequest request, PortletResponse response)throws PortletException{
logger.entering(SPRUtil.class.getName(),"getRenderUrl");
StringWriter writer = new StringWriter();


try {
// get the request-specific portlet state manager
final PortletStateManager stateManager =
stateService.getPortletStateManager(request, response);
// get the portlet window
final PortletWindow portletWindow =
modelProvider.getCurrentPortletWindow(request);

// get the URL factory
final URLFactory urlFactory = stateManager.getURLFactory();
final EngineURL url = urlFactory.newURL(null);
// get the URL-specific state
final StateHolderController state = url.getState();
// make the URL a single portlet refresh URL
setSelectionMapping(stateManager, portletWindow, state);
createActionURL(stateManager, portletWindow, null).writeDispose(writer);
// dispose the objects that are no longer needed
urlFactory.dispose();
stateManager.dispose();
} catch (StateException e) {
throw new PortletException(e);
} catch (ModelException e) {
throw new PortletException(e);
} catch (IOException e) {
throw new PortletException(e);
}
logger.exiting(SPRUtil.class.getName(),"getRenderUrl");
return writer.toString();
}

public String getPortletEditModeUrl(PortletRequest request, PortletResponse response)throws PortletException{
logger.entering(SPRUtil.class.getName(),"getRenderUrl");
StringWriter writer = new StringWriter();
try {
// get the request-specific portlet state manager
final PortletStateManager stateManager =
stateService.getPortletStateManager(request, response);
// get the portlet window
final PortletWindow portletWindow =
modelProvider.getCurrentPortletWindow(request);

// get the URL factory
final URLFactory urlFactory = stateManager.getURLFactory();
final EngineURL url = urlFactory.newURL(null);
// get the URL-specific state
final StateHolderController state = url.getState();
// make the URL a single portlet refresh URL
setSelectionMapping(stateManager, portletWindow, state);
setState(stateManager, portletWindow, state, PortletMode.EDIT, null, null);
// if needed add other aspects to the URL
// ...
// write the URL to the markup

url.writeDispose(writer);
// dispose the objects that are no longer needed
urlFactory.dispose();
stateManager.dispose();
} catch (StateException e) {
throw new PortletException(e);
} catch (ModelException e) {
throw new PortletException(e);
} catch (IOException e) {
throw new PortletException(e);
}
logger.exiting(SPRUtil.class.getName(),"getRenderUrl");
return writer.toString();
}


public String getPortletHelpModeUrl(PortletRequest request, PortletResponse response)throws PortletException{
logger.entering(SPRUtil.class.getName(),"getRenderUrl");
StringWriter writer = new StringWriter();
try {
// get the request-specific portlet state manager
final PortletStateManager stateManager =
stateService.getPortletStateManager(request, response);
// get the portlet window
final PortletWindow portletWindow =
modelProvider.getCurrentPortletWindow(request);

// get the URL factory
final URLFactory urlFactory = stateManager.getURLFactory();
final EngineURL url = urlFactory.newURL(null);
// get the URL-specific state
final StateHolderController state = url.getState();
// make the URL a single portlet refresh URL
setSelectionMapping(stateManager, portletWindow, state);
setState(stateManager, portletWindow, state, PortletMode.HELP, null, null);
// if needed add other aspects to the URL
// ...
// write the URL to the markup

url.writeDispose(writer);
// dispose the objects that are no longer needed
urlFactory.dispose();
stateManager.dispose();
} catch (StateException e) {
throw new PortletException(e);
} catch (ModelException e) {
throw new PortletException(e);
} catch (IOException e) {
throw new PortletException(e);
}
logger.exiting(SPRUtil.class.getName(),"getRenderUrl");
return writer.toString();
}

protected void setSelectionMapping(final PortletStateManager stateManager,
final PortletWindow window, final StateHolderController urlState)
throws StateException {
// get the selection accessor controller to set the selection mapping
final SelectionAccessorFactory factory = (SelectionAccessorFactory) stateManager
.getAccessorFactory(SelectionAccessorFactory.class);

final SelectionAccessorController selCtrl = factory
.getSelectionAccessorController(urlState);
try {
// set the mapping which maps the current state to our portlet window
selCtrl.setSelectionMapping(selCtrl.getSelection(), window.getObjectID());
} finally {
// release the selection controller
selCtrl.dispose();
}
}


protected EngineURL createRenderURL(final PortletStateManager stateManager,
final PortletWindow window, final Map renderParams, final PortletMode mode,
final WindowState winState) throws StateException {
// get the URL factory
final URLFactory urlFactory = stateManager.getURLFactory();
// get a new EngineURL object from the factory
final EngineURL url = urlFactory.newURL(null);
// get the URL-specific state
final StateHolderController state = url.getState();
// make the URL a single portlet refresh URL
setSelectionMapping(stateManager, window, state);
// set the render state
setState(stateManager, window, state, mode, winState, renderParams);
// release the URL factory
urlFactory.dispose();
// return the URL
return url;
}

protected void setState(final PortletStateManager stateManager,
final PortletWindow window, final StateHolderController urlState,
final PortletMode mode, final WindowState winState, final Map params)
throws StateException {
// get the portlet accessor controller
final PortletAccessorController portletCtrl =
stateManager.getPortletAccessorController(urlState);
try {
// set portlet mode (if any)
if (mode != null) {
portletCtrl.setPortletMode(mode);
}
// set window state (if any)
if (winState != null) {
portletCtrl.setWindowState(winState);
}
// set render /action parameters (if any)
if (params != null && !params.isEmpty()) {
portletCtrl.getParameters().putAll(params);
}
} finally {
// release portlet accessor controller
portletCtrl.dispose();
}
}

protected void setAction(final PortletStateManager stateManager,
final PortletWindow window, final StateHolderController urlState)
throws StateException {
// get the target accessor controller to set the action
final PortletTargetAccessorController targetCtrl = stateManager
.getPortletTargetAccessorController(urlState);
try {
// set the action to our portlet window
targetCtrl.setActionTarget(window.getObjectID());
} finally {
// release the target controller
targetCtrl.dispose();
}
}
protected EngineURL createActionURL(final PortletStateManager stateManager,
final PortletWindow window, final Map actionParams) throws StateException {
// get the URL factory
final URLFactory urlFactory = stateManager.getURLFactory();
// get a new EngineURL object from the factory
final EngineURL url = urlFactory.newURL(null);
// get the URL-specific state
final StateHolderController state = url.getState();
// make the URL a single portlet refresh URL
setSelectionMapping(stateManager, window, state);
// set the portlet action
setAction(stateManager, window, state);
// set the action parameters
setState(stateManager, window, state, null, null, actionParams);
// release the URL factory
urlFactory.dispose();
// return the URL
return url;
}

}

Nice article about Single Portlet Refresh URL

There is very nice article on developerworks about how to generate Single Portlet Refresh URL. You can find it here Refreshing individual portlets and preferences using Single Portlet Refresh in WebSphere Portal V6.0.1

In a portal environment, using Ajax to refresh the page is more problematic than in a simple Web application because of the fact that multiple portlets are on a page. You must also handle issues such as navigational state in the portal environment.

The Single Portlet Refresh feature, introduced in WebSphere Portal Version 6.0.1, enables you to use Ajax inside of portlets to refresh portlet content. In particular, it provides a method which you can use to create URLs that address a single portlet window only. Submitting such a "single portlet refresh URL" will cause the portal server to only invoke the rendering of the addressed portlet window (as implied by the name "Single Portlet Refresh"). Consequently, the response will only contain the markup produced by the portlet; that is, it will not contain the markup generated by the theme or skin or other artifacts.

You can use the Single Portlet Refresh in use cases such as:


  • Refreshing only a specific part of the portlet’s markup using an asynchronous render request.

  • Updating portlet preferences using an asynchronous (action) request.

Working with user profile from flex application

I built this sample portlet to demonstrate how you can retrieve the User Profile attribute from the Adobe Flex application. You can download the sample application from here


  1. UserProfileFlex.zip

  2. UserProfileFlexPortlet.war



This is how the User Profile Flex Portlet application looks like



When you click on the Print User Profile, the flex application will get the pre defined set of user preference attributes and print them in the Grid format. I followed these steps to build the sample application


  • First i did develop UserProfileFlexPortlet.java like this

    public class UserProfileFlexPortlet extends GenericPortlet{
    protected void doView(RenderRequest request, RenderResponse response)
    throws PortletException, IOException {
    System.out.println("Entering UserProfileFlexPortlet.doView()");
    response.setContentType("text/html");
    getPortletContext().getRequestDispatcher("/userprofile.jsp").include(request, response);
    System.out.println("Exiting UserProfileFlexPortlet.doView()");
    }
    }

    This portlet is pretty simple, only thing that it does is to forward control to userprofile.jsp

  • This is how my userprofile.jsp file looks like

    H<%@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 printUserProfile(){
    console.log("Entering printUserProfile()");
    var _portletWindow = new ibm.portal.portlet.PortletWindow("<%=portletWindowID%>");
    _portletWindow.getUserProfile(printUserProfileCallback);
    console.log("Exiting printUserProfile()");
    }
    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 userProfileList = new Array();
    for( var i = 0; i < attributeList.length; i++){
    userProfileList[i] = {"name":attributeList[i],"values":userProfile.getAttribute(attributeList[i])};
    }
    console.log("Returning User Profile array " + userProfileList);
    getFlexApp('UserProfile').displayUserProfile(userProfileList);
    }else{
    console.error("Error in getting user profile");
    }
    }
    function getFlexApp(appName){
    if (navigator.appName.indexOf ("Microsoft") !=-1){
    return window[appName];
    }
    else{
    return document[appName];
    }
    }

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

    Most of the code in the userprofile.jsp is same as that of Client Side User Profile manipulation with the difference that in the printUserProfileCallback method i am passing user profile attributes in the form of JavaScript Array to the displayUserProfile method in the Flex application.

  • This is how my UserProfileFlex.mxml file looks like

    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%" initialize="initApp()">
    <mx:Script>
    <![CDATA[
    import flash.external.*;
    import mx.controls.Alert;

    public function initApp():void
    {
    if (ExternalInterface.available){
    ExternalInterface.addCallback("displayUserProfile", displayUserProfile);
    }
    }
    //Event handler for handling button click
    public function getUserProfile():void
    {
    if (ExternalInterface.available) {
    // Make call to the printPreference method
    ExternalInterface.call("printUserProfile");
    } else
    Alert.show("Error sending data!");
    }
    public function displayUserProfile(obj:Object):void
    {
    dgUserProfile.dataProvider = obj;
    }
    ]]>
    </mx:Script>
    <mx:Panel id="pnlMain" width="600" height="324" layout="absolute" title="User Profile Example">
    <mx:Button label="Print User Profile" y= "37" id="resourceUrl" click="getUserProfile()" x="482"/>
    <mx:DataGrid id="dgUserProfile" x="10" y="10" width="464" height="271">
    <mx:columns>
    <mx:DataGridColumn headerText="Name" dataField="name"/>
    <mx:DataGridColumn headerText="Values" dataField="values"/>
    </mx:columns>
    </mx:DataGrid>
    </mx:Panel>
    </mx:Application>

    When user clicks on the Print User Profile button the control gets passed to the getUserProfile method which is using the ExternalInterface.call for calling printUserProfile JavaScript method.

    Once the UserProfile attributes are retrieved the JavaScript method is passing the control back to the displayUserProfile method, which is setting that Array as data source for the grid

Manipulating Portlet Preferences using Adobe Flex application

I built this PortletPreferencesFlex sample portlet to demonstrate how you can manipulate the Portlet Preferences from the Flex application.

You can download the sample application from here

  1. PortletPreferencesFlex portlet

  2. Portlet Preferences Flex code




The sample portlet has two things first, when you click on the Print Preferences button it will get all the preferences for the user and list them in the Grid. You can add new preference by entering value of preference Name and value and then clicking on the Add preference button.

In this sample application i am using the Client Side API for Preference Management and the JavaScript to Flex communication for getting as well as setting preferences.

I followed these steps to create the sample application

  • First i did develop a FlexPortletPreferences portlet like this

    public class FlexPortletPreferences extends GenericPortlet{
    protected void doView(RenderRequest request, RenderResponse response)
    throws PortletException, IOException {
    System.out.println("Entering FlexPortletPreferences.doView()");
    response.setContentType("text/html");
    getPortletContext().getRequestDispatcher("/preference.jsp").include(request, response);
    System.out.println("Exiting FlexPortletPreferences.doView()");
    }
    }

    This portlet is very simple only thing that it does is pass control to the preference.jsp in the doView() method

  • Then i did develop preference.jsp file 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>
    <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 prefList = new Array();
    for (var i=0; i<prefs.length; i++) {
    console.log(prefs[i].name);
    console.log(prefs[i].values[0]);
    prefList[i] = {"name":prefs[i].name,"values":prefs[i].values[0]};
    }
    getFlexApp('PortletPreferenceFlex').displayPreferences(prefList);
    }else{
    console.error("Error in getting preferences");
    }
    }
    function getFlexApp(appName){
    if (navigator.appName.indexOf ("Microsoft") !=-1){
    return window[appName];
    }
    else{
    return document[appName];
    }
    }
    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 addPreferences(prefName, prefValue){
    console.log("Entering changePreference()");
    console.log("Preference Name " + prefName);
    console.log("Preference Value " + prefValue);
    var _portletWindow = new ibm.portal.portlet.PortletWindow("<%=portletWindowID%>");
    _portletWindow.setAttribute("preferenceName",prefName );
    _portletWindow.setAttribute("preferenceValue",prefValue );
    _portletWindow.getPortletPreferences(loadPreferences);
    console.log("Exiting changePreference()");
    return false;
    }
    </script>
    <portlet:defineObjects />
    <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
    id="PortletPreferenceFlex" width="750" height="500"
    codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
    <param name="movie" value='<%=renderResponse.encodeURL(renderRequest.getContextPath()+ "/PortletPreferenceFlex.swf")%>' />
    <embed src="<%=renderResponse.encodeURL(renderRequest.getContextPath() + "/PortletPreferenceFlex.swf")%>"
    width="750" height="500"
    name="PortletPreferenceFlex" align="middle" play="true" loop="false" quality="high"
    type="application/x-shockwave-flash" pluginspage="http://www.adobe.com/go/getflashplayer">
    </embed>
    </object>

    Most of the JavaScript code in this file is same as Client Side User Preferences Manipulation with the difference that it has getFlexApp method which gives me the object of flex application. Inside the printPreferencesCallback method, first i am getting object of Flex application and then calling its displayPreferences() method with Array of the preferences. The displayPreferences() method is implemented in the flex application

  • Last and most important piece of the demo application is the PortletPreferenceFlex.mxml

    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    width="100%" height="100%" initialize="initApp()">
    <mx:Script>
    <![CDATA[
    import flash.external.*;
    import mx.controls.Alert;
    public function initApp():void
    {
    if (ExternalInterface.available){
    ExternalInterface.addCallback("displayPreferences", displayPreferences);
    }
    }
    //Event handler for handling button click
    public function getPortletPreference():void
    {
    if (ExternalInterface.available) {
    // Make call to the printPreference method
    ExternalInterface.call("printPreferences");
    } else
    Alert.show("Error sending data!");
    }
    public function displayPreferences(obj:Object):void
    {
    dgPreferences.dataProvider = obj;
    }
    protected function prefValue_clickHandler():void
    {
    if (ExternalInterface.available) {
    // Make call to the printPreference method
    ExternalInterface.call("addPreferences",prefName.text,prefValue.text);
    }
    }
    ]]>
    </mx:Script>
    <mx:Panel id="pnlMain" width="600" height="324" layout="absolute" title="Portlet Preferences Example">
    <mx:Button label="Print Preference" y= "37" id="resourceUrl" click="getPortletPreference()" x="482"/>
    <mx:DataGrid id="dgPreferences" x="10" y="10" width="464" height="100">
    <mx:columns>
    <mx:DataGridColumn headerText="Name" dataField="name"/>
    <mx:DataGridColumn headerText="Values" dataField="values"/>
    </mx:columns>
    </mx:DataGrid>
    <mx:Button x="321" y="123" label="Add Preference" click="prefValue_clickHandler()"/>
    <mx:Label x="13" y="150" text="Preference Value"/>
    <mx:TextInput x="121" y="121" id="prefName"/>
    <mx:Label text="Preference Name" x="10" y="121"/>
    <mx:TextInput x="120" y="148" id="prefValue"/>
    </mx:Panel>
    </mx:Application>


    When user clicks on the Print Preferences button the control get passed to getPortletPreference method inside this method i am using ExternalInterface to call displayPreferences method which is JavaScript method inside preference.jsp.

    The displayPreferences makes call to _portletWindow.getPortletPreferences attaching printPreferencesCallback as callback. Inside that method first i am getting the preferences then converting them into Array and then passing that Array to the displayPreferences method which is part of PortletPreferenceFlex.mxml, which is using that array as data source for the Grid.

    Same thing happens when you click on Add Preference button control gets passed to prefValue_clickHandler method which in turn is passing control to the addPreferences method for actual adding of the preferences

Flex Portlet development

I am trying to learn about, how to use the Adobe Flex for building UI for the portlets. I tried using the stand alone Adobe Flex Builder, which is nothing but bare minimum Eclipse with plug-ins for building Adobe Flex applications. But in that case i had to use WebSphere Portal, Rational Application Developer and Adobe Flex at the same time for development and i dont have enough memory for that.

So what i did is i did install "Eclipse Java EE IDE for Web Developers. - GaliLeo version" and in that i did install Adobe Flex Builder 4.0 Trail edition, i like the Adobe Flex Builder 4.0 edition much better than the 3.0, this one seems to be much faster.

Now i can use one version of Eclipse for developing both Flex and Portlet application.




I tried installing Flex Builder 4.0 in RAD 7.5 but it did not work, it looks like the FLex Builder 4.0 needs newer version of Eclipse. I tried installing FLex Builder 3.0 in my RAD 7.5 but that did not work either. Somehow i could not give sufficient time to debug the problems in that environment. But i like my Eclipse + Flex builder environment because it is not that memory hungry

Making resource request from portlet using Flex application

I built this sample portlet in which i am making a resource request to the portlet. My portlet is very simple the doView() method returns HTML that includes a Flex application and then the flex application makes HTTP call to the portlet.

The interesting part was since the portlets are not URL addressable i cant hard code the resource URL inside my flex application so what i did is i am generating the resource URLs on the HTML and then making use of Flex to JavaScript communication capability to first get the Resource URL and then the FLex application was making HTTP call using action script. This is how my demo portlet looks like, you can download the sample code from here

  1. HelloFlex.zip- Flex source code

  2. FlexPortlet.war - Portlet Source code





I followed these steps to build the demo application


  • First create a FlexPortlet.java file like this, as you can see in the doView() method of the portlet i am passing control to the flex.jsp and in the serveResource() method i am returning "Returning response from FlexPortlet.serveResource" message.


    package com.webspherenotes.flex;

    import java.io.IOException;
    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 javax.portlet.ResourceRequest;
    import javax.portlet.ResourceResponse;

    public class FlexPortlet extends GenericPortlet {
    protected void doView(RenderRequest request, RenderResponse response)
    throws PortletException, IOException {
    response.setContentType("text/html");
    getPortletContext().getRequestDispatcher("/flex.jsp").include(request, response);
    System.out.println("Exiting FlexPortlet.doView()");
    }
    public void serveResource(ResourceRequest request, ResourceResponse response)
    throws PortletException, IOException {
    System.out.println("Entering FlexPortlet.serveResource()");
    response.setContentType("text/html");
    response.getWriter().println("Returning response from FlexPortlet.serveResource");
    System.out.println("Exiting FlexPortlet.serveResource()");
    }
    }




  • This how the flex.jsp code looks like

    <%@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"%>
    <portletx:defineObjects />
    <script type="text/javascript">
    function getResourceURL(){
    console.log("Returning resourceURL");
    return "<%=renderResponse.createResourceURL().toString()%>";
    }
    </script>
    <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() + "/HelloFlex.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>

    The JSP code is pretty simple it has a getResourceURL() javascript method that will be called by Flex application and it is including HelloFlex.swf in the markup.


  • The biggest chunk of business logic is in the HelloFlex.mxml, this is how it looks like, please note that i am not Flex expert and i believe a Flex expert might be able to build much better code here. In my case both application layout and business logic is in the same file

    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    width="100%" height="100%" >
    <mx:Script>
    <![CDATA[
    import flash.external.*;
    import mx.controls.Alert;
    import mx.rpc.events.FaultEvent;
    import mx.rpc.events.ResultEvent;
    import mx.rpc.http.HTTPService;

    //Event handler for handling button click
    public function getPortletResponse():void
    {
    var javaScriptMethodName:String = "getResourceURL";
    if (ExternalInterface.available) {
    // Make call to the getResourceUrl javaScript function
    var portletUrl:String = ExternalInterface.call(javaScriptMethodName);
    useHttpService(portletUrl);
    } else
    displayText.text = "Error sending data!";
    }
    //This method is used for making a URL call
    private var service:HTTPService
    public function useHttpService(url:String):void {
    service = new HTTPService();
    service.url = url;
    service.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
    service.method = "GET";
    service.addEventListener("result", httpResult);
    service.addEventListener("fault", httpFault);
    service.send(parameters);
    }
    //This callback gets called in case if the http response is returned
    public function httpResult(event:ResultEvent):void {
    var result:Object = event.result;
    displayText.text = event.result.toString();
    }
    //This call back gets called in case of error in http request
    public function httpFault(event:FaultEvent):void {
    var faultstring:String = event.fault.faultString;
    displayText.text = event.fault.faultDetail;
    Alert.show(faultstring + event.fault.faultDetail + event.fault.rootCause);
    }
    ]]>
    </mx:Script>
    <mx:Panel id="pnlMain" x="333" y="10" width="514" height="144" layout="absolute" title="Serve Resource Example">
    <mx:Button label="Make Resource request" y= "75" id="resourceUrl" click="getPortletResponse()" x="10"/>
    <mx:TextArea x="10" y="10" width="493" height="61" id="displayText"/>
    </mx:Panel>
    </mx:Application>

    My application layout is pretty simple it has a button and a Text Area, when we click on button the control gets passed to getPortletResponse() method and that method will take the output generated by the portlet and write it in the text area.

    The getPortletResponse() method is calling the getResourceURL() javascript function by using ExternalInterface ExternalInterface.call(javaScriptMethodName); like this, the getResourceUrl() method will return the resourceUrl generated by the RenderResponse.createResourceURL() method and then it is passing the control to useHttpService() method.

    The useHttpService() method is making HTTP "GET" call to the resourceUrl and then it is attaching the httpResult() function as a callback function for the ajax request the httpFault() method gets called in case of error in HTTP call.