30.07.2013 Views

Methods for resetting IBM WebSphere Portal portlet views

Methods for resetting IBM WebSphere Portal portlet views

Methods for resetting IBM WebSphere Portal portlet views

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

<strong>Methods</strong> <strong>for</strong> <strong>resetting</strong> <strong>IBM</strong> <strong>WebSphere</strong> <strong>Portal</strong> <strong>portlet</strong><br />

<strong>views</strong><br />

James Barnes (jwbarnes@us.ibm.com), <strong>IBM</strong><br />

Ryan Wilson (rywilson@us.ibm.com), <strong>IBM</strong><br />

May 2012<br />

© Copyright International Business Machines Corporation 2012. All rights reserved.<br />

Abstract: This article explains how to reset <strong>portlet</strong> <strong>views</strong> with multiple frameworks (Java<br />

Specification Request, <strong>IBM</strong> Web Content Manager, and JavaServer Faces), along with<br />

examining how the theme impacts the process and can be used to reset the state. Also, we<br />

examine how the various themes (<strong>Portal</strong> standard, <strong>Portal</strong>web2, PB2) can be modified to<br />

achieve the desired results.<br />

Table of Contents<br />

1 Introduction.............................................................................................................................1<br />

1.1 <strong>WebSphere</strong> <strong>Portal</strong> settings and configurations................................................................2<br />

2 Modifying themes....................................................................................................................2<br />

2.1 <strong>Portal</strong> theme.....................................................................................................................3<br />

2.2 <strong>Portal</strong>Web2 theme............................................................................................................6<br />

2.3 PageBuilder2 theme.......................................................................................................10<br />

3 Framework modifications......................................................................................................13<br />

3.1 JavaServer Faces (JSF)..................................................................................................13<br />

3.2 Struts Portlet Frame (SPF).............................................................................................13<br />

3.3 WCM.............................................................................................................................16<br />

4 Sample <strong>portlet</strong>........................................................................................................................16<br />

5 Conclusion.............................................................................................................................17<br />

6 Resources...............................................................................................................................17<br />

7 About the authors...................................................................................................................17<br />

1 Introduction<br />

A common requirement <strong>for</strong> <strong>portlet</strong> developers or portal administrators is to have a <strong>portlet</strong><br />

reset to its default view. This is often desired when navigating to another portal page and<br />

then returning to the page the <strong>portlet</strong> is on. The default behavior of <strong>IBM</strong>® <strong>WebSphere</strong>®<br />

<strong>Portal</strong> is to maintain the previous state of the <strong>portlet</strong>; however, to understand why, we should<br />

first look at the different ways a <strong>portlet</strong> determines its current page.<br />

1


A <strong>portlet</strong> can either use renderParameters or a session object to determine the <strong>portlet</strong>'s<br />

pagination. Though some frameworks [Struts, JavaServer TM Pages (JSP), etc.] hide this, the<br />

underlying implementation uses one of the two.<br />

<strong>WebSphere</strong> <strong>Portal</strong> maintains a history of the renderParameters associated with the <strong>portlet</strong> <strong>for</strong><br />

X number of page visits. Since a <strong>portlet</strong> is a J2EE application, its session is not able to be<br />

altered by <strong>WebSphere</strong> <strong>Portal</strong> and is maintained until a user logout. <strong>WebSphere</strong> <strong>Portal</strong> cannot<br />

implictly clear the session attributes set in a <strong>portlet</strong>.<br />

There is nothing in the <strong>portlet</strong> lifecycle that can be used to identify when the <strong>portlet</strong> is being<br />

rendered when returning from another portal page. This means that you must make<br />

modications to either the configuration, theme, or the <strong>portlet</strong>s to accomplish the desired<br />

behavior.<br />

NOTE: The solutions described in this document rely on the functionality of<br />

renderParameters used during the render phase of the <strong>portlet</strong>. If you have <strong>portlet</strong> caching<br />

enabled <strong>for</strong> your <strong>portlet</strong>, then the solutions herein may not work.<br />

When caching is enabled on the <strong>portlet</strong>, the cached markup (that was generated during<br />

the last render) is returned when the <strong>portlet</strong> is rendered, and the code of the <strong>portlet</strong> is not<br />

executed. This means that the clearing of the render parameters will not effect the display<br />

of the <strong>portlet</strong>. To change this behavior, you would need to explicitly target an action of<br />

your <strong>portlet</strong>.<br />

1.1 <strong>WebSphere</strong> <strong>Portal</strong> settings and configurations<br />

As noted above, <strong>WebSphere</strong> <strong>Portal</strong> maintains a certain history level of the renderParameters<br />

associated with the <strong>portlet</strong>. The expiration limit is determined by use of the value of the<br />

property keymanager.lru.size in the StateManagerService. This value should not be set to<br />

zero, and negative numbers are not valid.<br />

Refer to the <strong>WebSphere</strong> <strong>Portal</strong> In<strong>for</strong>mation Center topic, "<strong>Portal</strong> configuration services," <strong>for</strong><br />

more in<strong>for</strong>mation on changing the keymanager.<br />

2 Modifying themes<br />

One approach to get the desired behavior is to make modifications to your theme.<br />

Throughout the last several releases of <strong>WebSphere</strong> <strong>Portal</strong> there have been several styles of<br />

theme. In this paper we cover some of the more popular themes and provide examples of how<br />

to clear the state. Note that all these approaches affect only the renderParameters on the<br />

<strong>portlet</strong> and not session attributes.<br />

If your <strong>portlet</strong> uses session attributes to determine the pagination, then you may need to<br />

combine some of the approaches below with custom code in your <strong>portlet</strong>.<br />

For example, rather than clearing the renderParameters in the theme, you could set a specific<br />

parameter and then check <strong>for</strong> this parameter in your <strong>portlet</strong>; if it exists, you could then clear<br />

your session attribute and display the default page of the <strong>portlet</strong>.<br />

2


2.1 <strong>Portal</strong> theme<br />

In the most basic theme, it is easy to erase the rich text that is included with page navigation<br />

within portal. When you remove the rich text, you also remove the state as stored <strong>for</strong> render<br />

parameters. This provides a way to reset <strong>portlet</strong>s that rely on render parameters, but you<br />

cannot affect <strong>portlet</strong>s that rely on the session <strong>for</strong> their state in<strong>for</strong>mation.<br />

Using the base <strong>WebSphere</strong> <strong>Portal</strong>/<strong>IBM</strong> theme<br />

All pages in which you do not want a rich URL must have a friendly URL. In the example<br />

shown in figure 1, the page feeds have been modified to have the friendly URL "feeds", and<br />

the Home page also contains the friendly URL of Home..<br />

Figure 1. Example friendly URL creation<br />

You can create your own theme by following the steps detailed in the <strong>WebSphere</strong> <strong>Portal</strong><br />

In<strong>for</strong>mation Center topic, "Creating a new theme." After creating the theme you would need<br />

to make the following changes:<br />

1. Edit the topnav.jspf file and find all occurrences of "navigationUrl".<br />

2. Add the following to the tag:<br />

keepNavigationalState="false"<br />

The final results should look like this:<br />

<br />

which will produce a URL that looks like the following in the address bar:<br />

http://test.ibm.com:10040/wps/myportal/Home/feeds<br />

3. Repeat these steps in the sidenav.jsp file, if you want to get the same behavior.<br />

NOTE: Only alter the "navigationUrl" tag that has the type of launch.<br />

Using the Helpers<br />

Another way to cause the <strong>WebSphere</strong> <strong>Portal</strong> navigation to drop the state is to code this into<br />

the URL generation. By taking advantage of the URL generation SPIs we can make URLs<br />

that have no state in<strong>for</strong>mation in them except <strong>for</strong> changing a page or targetting a <strong>portlet</strong>.<br />

3


You can find the Helpers in the <strong>IBM</strong> Support Technote, "<strong>Portal</strong> 6.1 and 7.0 Advanced URL<br />

Generation Helper classes," as well as more details on how to use them.<br />

To accomplish this task, we take advantage of the URL generation SPI to build URLs to<br />

pages with absolutely no state in<strong>for</strong>mation:<br />

1. Use the Helper classes to create the URL, using the method in listing 1.<br />

Listing 1. Code <strong>for</strong> generateUrlDropState method<br />

public static String generateUrlDropState(<br />

NavigationNode wpsNavNode,<br />

HttpServletRequest request,<br />

HttpServletResponse response)<br />

throws StateException, NamingException, IOException {<br />

final <strong>Portal</strong>StateManagerService mgr = getStateManager(request,<br />

response);<br />

// Get the URL factory<br />

final URLAccessorFactory urlFactory = (URLAccessorFactory)<br />

mgr.getAccessorFactory(URLAccessorFactory.class);<br />

final EngineURL url = urlFactory.newURL(request, response,<br />

mgr.newState(), Constants.EMPTY_COPY);<br />

// Set the page this URL should point to<br />

final SelectionAccessorFactory selectionFactory =<br />

(SelectionAccessorFactory)<br />

mgr.getAccessorFactory(SelectionAccessorFactory.class);<br />

// Request the selection controller to set the page; pass in the<br />

state associated with the created URL<br />

final SelectionAccessorController selectionCtrl =<br />

selectionFactory.getSelectionAccessorController(url.getState());<br />

// Set the page; you need the unique name (String) or the<br />

ObjectID of that page<br />

selectionCtrl.setSelection(wpsNavNode.getObjectID());<br />

// Dispose the accessor (avoids memory leak)<br />

selectionCtrl.dispose();<br />

}<br />

return url.writeDispose(new StringWriter()).toString();<br />

This method returns a string that is the URL to change the page selection.<br />

2. Next, modify the theme to properly call this method, first adding the import to the<br />

topnav.jspf file:<br />

<br />

3. In the topnav.jspf file, change two lines:<br />

a) The first line is <strong>for</strong> the current page URL (if you want the state kept on this page,<br />

then leave it as is). Change the following:<br />

4


<br />

<br />

to the code in listing 2.<br />

Listing 2. New code <strong>for</strong> first line<br />

<br />

<br />

<br />

<br />

b) The second line to change is <strong>for</strong> unselected pages. Change the following:<br />

<br />

<br />

<br />

to the code in listing 3.<br />

Listing 3. New code <strong>for</strong> second line<br />

<br />

<br />

<br />

<br />

4. In the sideNav.jspf file, change the following one line from:<br />

<br />

<br />

<br />

to the code in listing 4.<br />

Listing 4. New code <strong>for</strong> sideNav.jspf file<br />

<br />


(isNodeSelected) { %>onfocus="showPageAf<strong>for</strong>dance()" ><br />

<br />

<br />

The basics of this method are similar to using keepNavigationalState="false" when creating<br />

URLs using the URLGeneration tag. This is an easier way <strong>for</strong> your themes in that you don't<br />

have to hardcode any values.<br />

In the Download section on the landing page <strong>for</strong> this paper is a .zip attachment containing a<br />

sample theme and .jar file with Helper classes. Place the .jar file into the shared/app directory<br />

of the <strong>WebSphere</strong> <strong>Portal</strong> Server directory and deploy the theme, following the standard<br />

procedures in the Deploying the theme topic in the <strong>WebSphere</strong> <strong>Portal</strong> In<strong>for</strong>mation Center.<br />

2.2 <strong>Portal</strong>Web2 theme<br />

The <strong>Portal</strong>Web2 was the first theme to introduce client-side aggregation (CSA). Covering all<br />

the details of CSA is outside the scope of this document; however, we provide some<br />

in<strong>for</strong>mation that is needed to understand the changes in <strong>Portal</strong>Web2 theme and the<br />

PageBuilder theme.<br />

Though designed <strong>for</strong> CSA, the <strong>Portal</strong>Web2 can also be rendered in a <strong>for</strong>ce_SSA mode, which<br />

causes the theme to be rendered in a server-side aggregation (SSA) mode. The theme<br />

structure/code used in SSA is virtually the same as the <strong>Portal</strong> theme described above.<br />

Its important to note that all the in<strong>for</strong>mation in this section pertains to the pages/<strong>portlet</strong><br />

rendering in CSA mode. If any of the pages/<strong>portlet</strong>s are <strong>for</strong>cing SSA mode, you must also<br />

follow the instructions Section 2.1 above.<br />

Although the <strong>portlet</strong> cannot detect when there is a change to the portal navigation, the theme<br />

can. There are several events that occur within the processing of a CSA page. To implement<br />

the code to clear the navigation, we register a listener <strong>for</strong> the startPage event, which is called<br />

when the page is begining to render and is the best hook to clear the render parameters.<br />

The stateChanged event listenter may look like a promising place to put this code; however,<br />

it ends up being executed after the rendering is already started, so the changes to the state are<br />

not picked up.<br />

1. Create a file named clearNavigationState.js in the js directory of your theme and add the<br />

content in listing 5.<br />

Listing 5. Code to add to clearNavigationState.js<br />

dojo.declare( "com.ibm.portal.aggregation.ClearNavigationalStat<br />

e",<br />

null,<br />

{<br />

constructor: function(){<br />

// use the EVENT_BROKER.startPage event.<br />

this._stateChangedStartHandle =<br />

com.ibm.portal.EVENT_BROKER.startPage.register( this,<br />

this.start );<br />

},<br />

start: function(){<br />

6


var state =<br />

com.ibm.portal.navigation.controller.NAVIGATION_CONTROLLER.getS<br />

tate();<br />

var stateMgr = new<br />

com.ibm.portal.state.StateManager(ibm<strong>Portal</strong>Config.contentHandle<br />

rURI);<br />

stateMgr.reset( state.stateDOM );<br />

//get all the <strong>portlet</strong>s associated with this state<br />

var <strong>portlet</strong>ListAccessors =<br />

stateMgr.newPortletListAccessor();<br />

var <strong>portlet</strong>List = <strong>portlet</strong>ListAccessors.getPortlets();<br />

if ( <strong>portlet</strong>List ) {<br />

<strong>for</strong>(var i = 0; i < <strong>portlet</strong>List.length; i++ ){<br />

//get the <strong>portlet</strong>s and clear the renderParameters<br />

var pAcc =<br />

stateMgr.newPortletAccessor(<strong>portlet</strong>List[i]);<br />

pAcc.setPortletMode( ibm.portal.<strong>portlet</strong>.PortletMode.VIEW );<br />

pAcc.setWindowState( ibm.portal.<strong>portlet</strong>.WindowState.NORMAL );<br />

pAcc.getRenderParameters().clear();<br />

}<br />

}<br />

}<br />

});<br />

com.ibm.portal.aggregation.TEST_NAVIGATION = new<br />

com.ibm.portal.aggregation.ClearNavigationalState();<br />

2. Open the head_extras.jspf file and look <strong>for</strong> the following line:<br />

<br />

We include our script directly after this line by using the following code:<br />

<br />

3. Since the head_extras.jspf is included in the head.jspf by use of an include directive, you<br />

must touch both head.jspf and Default.jsp to <strong>for</strong>ce a recompile and have our changes<br />

picked up. If JSP reloading is not enabled, we will need to restart the server.<br />

Now that the code is in place we can explain what it does. The first step is registering a<br />

listener <strong>for</strong> the startPage event:<br />

this._stateChangedStartHandle =<br />

com.ibm.portal.EVENT_BROKER.startPage.register( this, this.start );<br />

Now let's look at the start function, as this handles the task of clearing the <strong>portlet</strong>s state. We<br />

first need to get the StateManager from the state:<br />

var state =<br />

com.ibm.portal.navigation.controller.NAVIGATION_CONTROLLER.getState();<br />

7


var stateMgr = new<br />

com.ibm.portal.state.StateManager(ibm<strong>Portal</strong>Config.contentHandlerURI);<br />

Then we reset the stateMgr be<strong>for</strong>e obtaining the <strong>portlet</strong> list on the page:<br />

stateMgr.reset( state.stateDOM );<br />

Next, we get a list of all the <strong>portlet</strong>s on the page and begin iterating through them:<br />

var <strong>portlet</strong>ListAccessors = stateMgr.newPortletListAccessor();<br />

var <strong>portlet</strong>List = <strong>portlet</strong>ListAccessors.getPortlets();<br />

The <strong>portlet</strong>List variable is an array of String elements represending the OID of the <strong>portlet</strong><br />

window. For example:<br />

["7_IP4IHB429GJSB0IL5O9DTN30G5", "7_CGAH47L008JD302B5MRL683G22"]<br />

To do anything useful we need to get the PortletAccessor, using the OID(Object ID) from the<br />

list. We do this by using the stateMgr object:<br />

var pAcc = stateMgr.newPortletAccessor(<strong>portlet</strong>List[i]);<br />

With the <strong>portlet</strong>Accessor we can now per<strong>for</strong>m the task of clearing the parameters and<br />

<strong>resetting</strong> the state. The following code also resets the <strong>portlet</strong> to view mode, sets the window<br />

state to normal and, finally, clears all the renderParameters:<br />

pAcc.setPortletMode( ibm.portal.<strong>portlet</strong>.PortletMode.VIEW );<br />

pAcc.setWindowState( ibm.portal.<strong>portlet</strong>.WindowState.NORMAL );<br />

pAcc.getRenderParameters().clear();<br />

As mentioned above this code only affects the pagination if the <strong>portlet</strong> was developed using<br />

renderParameters. If the <strong>portlet</strong> does use a session object, the above code will not work;<br />

however, there is a potential solution.<br />

We not only have the ability to remove renderParameters but also are able to add new ones.<br />

For example, after clearing all the parameters, you could then add a parameter like the<br />

following:<br />

pAcc.getRenderParameters().setValue("clearSession", "true");<br />

Now you can develop your <strong>portlet</strong>s to listen <strong>for</strong> this parameter and then manually remove the<br />

pagination object from the session.<br />

Another thing to note is that the above code will clear the parameters <strong>for</strong> all <strong>portlet</strong>s and<br />

pages. If you want only certain <strong>portlet</strong>s/page to be cleared, you could create a list of the<br />

pages or <strong>portlet</strong>s that should or should not be cleared. For example:<br />

In the constructor of the ClearNavigationState class above, add the following:<br />

this._pageList = ["ClearNavigationalState_1"]; //list of page<br />

titles to clear.<br />

8


In this example the list uses the page title, but we could also use the OID of the page. The<br />

above code assumes that you have a page titled "ClearNavigationalState_1" in your<br />

environment and you want all the <strong>portlet</strong>s on this page to be reset.<br />

After the start function (referenced in Step 1) you need to add a new function (see listing 6):<br />

Listing 6. Add a new function<br />

clearPage: function(id){<br />

<strong>for</strong>(i = 0; i < this._pageList.length; i++){<br />

if(id === this._pageList[i]){<br />

return true;<br />

}<br />

}<br />

}<br />

return false;<br />

Finally, change the start function to look like that shown in listing 7.<br />

NOTE: The start function is the first function in the<br />

com.ibm.portal.aggregation.ClearNavigationalState class. Functions must be separated by<br />

comma's <strong>for</strong> the Dojo declared object, so be sure to add a comma after the closing bracket of<br />

the start function when adding the new clearPage function.<br />

Listing 7. Code to change the start function<br />

start: function(){<br />

var state =<br />

com.ibm.portal.navigation.controller.NAVIGATION_CONTROLLER.getState(<br />

);<br />

var selectedNode =<br />

state.selectionModel.getSelectedNode();<br />

console.log(selectedNode.title);<br />

if(this.clearPage(selectedNode.title)){<br />

var stateMgr = new<br />

com.ibm.portal.state.StateManager(ibm<strong>Portal</strong>Config.contentHandlerURI)<br />

;<br />

stateMgr.reset( state.stateDOM );<br />

//get all the <strong>portlet</strong>s associated with this state<br />

var <strong>portlet</strong>ListAccessors =<br />

stateMgr.newPortletListAccessor();<br />

var <strong>portlet</strong>List = <strong>portlet</strong>ListAccessors.getPortlets();<br />

if ( <strong>portlet</strong>List ) {<br />

<strong>for</strong>(var i = 0; i < <strong>portlet</strong>List.length; i++ ){<br />

//get the <strong>portlet</strong>s and clear the renderParameters<br />

var pAcc =<br />

stateMgr.newPortletAccessor(<strong>portlet</strong>List[i]);<br />

pAcc.setPortletMode( ibm.portal.<strong>portlet</strong>.PortletMode.VIEW );<br />

pAcc.setWindowState( ibm.portal.<strong>portlet</strong>.WindowState.NORMAL );<br />

9


pAcc.getRenderParameters().clear();<br />

pAcc.getRenderParameters().setValue("clearSession", "true");<br />

},<br />

}<br />

}<br />

}<br />

Now the <strong>portlet</strong>s will be cleared only <strong>for</strong> pages that are in the list.<br />

2.3 PageBuilder2 theme<br />

As with the <strong>Portal</strong>Web2 theme, the PageBuilder2 theme supports a CSA and SSA mode of<br />

rendering. However instead of having a fallback behavior, the PageBuilder2 theme uses a<br />

similar code path <strong>for</strong> the navigation to support both modes. This means that the same code<br />

can be used <strong>for</strong> both rendering modes.<br />

With this model, <strong>portlet</strong>s are wrapped in an iWidget. We will reference iWidgets throughout<br />

the remainder of this paper and sample codes; however, <strong>for</strong> the context of this section the<br />

term iWidget also represents the underlying <strong>portlet</strong>.<br />

The PageBuilder2 theme uses a subscribe/publish event model. This provides a mechanism<br />

<strong>for</strong> components to be notified of events such as a navigational change to another portal page.<br />

The NavigationController is the core object used <strong>for</strong> handling the navigational changes <strong>for</strong><br />

the theme.<br />

The switchPageHandler function is a member of the NavigationController and is registered to<br />

listen <strong>for</strong> the SWITCH_PAGE event. In the provided sample code we extend this function to<br />

clear the state. We list the code snippet and then explain what the code is doing.<br />

For simplicity you can place the code in listing 8 into the theme_.html file of your<br />

theme, after the config DCS (Dynamic Content Spot):<br />

.<br />

Listing 8. Code to put in theme_.html<br />

<br />

(function(){<br />

var c = com.ibm.pb.control.NavigationController.getController();<br />

var origSwitchPageHandler = c.switchPageHandler;<br />

c.switchPageHandler = function(pageNode, status, semantics) {<br />

var navStateModel =<br />

com.ibm.mashups.enabler.model.state.NavigationStateModelFactory.getN<br />

avigationStateModel();<br />

// clear all the parameters <strong>for</strong> all the widgets and such<br />

// then commit the navigation state model<br />

// then call...<br />

//get the navigationModel<br />

10


var nModel =<br />

com.ibm.mashups.enabler.model.Factory.getNavigationModel();<br />

//var locator = nModel.getLocator();<br />

//var navNode = locator.find(pageNode.getID());<br />

var defferedNavNode = nModel.find(pageNode.getID());<br />

var navNode = defferedNavNode.start();<br />

var layoutModel = nModel.getLayoutModel(navNode);<br />

if(layoutModel){<br />

var iWidgetList = getiWidgets(layoutModel);<br />

}<br />

//clear WCM shareable render parameters<br />

//Look <strong>for</strong> the public-render-parameter scope parameter on<br />

the page.<br />

var scopeParam = navNode.getMetaData("param.sharing.scope");<br />

if(scopeParam){<br />

var publicRenderParameters =<br />

com.ibm.mashups.enabler.model.state.AccessorFactory.getShareablePara<br />

meterSetAccessor(navStateModel, "public-render-parameters",<br />

scopeParam);<br />

} else {<br />

//Public render parameters are not scoped to the page<br />

so get the global scope.<br />

var publicRenderParameters =<br />

com.ibm.mashups.enabler.model.state.AccessorFactory.getShareablePara<br />

meterSetAccessor(navStateModel, "public-render-parameters");<br />

}<br />

//publicRenderParamsNames =<br />

publicRenderParameters.getAllNames();<br />

//WCM specific public render parameters.<br />

var removePath =<br />

publicRenderParameters.removeItem("{http://www.ibm.com/xmlns/prod/we<br />

bsphere/portal/publicparams}path-info");<br />

var removeContext =<br />

publicRenderParameters.removeItem("{http://www.ibm.com/xmlns/prod/da<br />

tatype/content}context");<br />

var removePageDesign =<br />

publicRenderParameters.removeItem("{http://www.ibm.com/xmlns/prod/da<br />

tatype/content}page-design");<br />

if(iWidgetList){<br />

<strong>for</strong>(var i = 0; i < iWidgetList.length; i++){<br />

var widgetAccessor =<br />

com.ibm.mashups.enabler.model.state.AccessorFactory.getWidgetAccesso<br />

r(navStateModel, iWidgetList[i]);<br />

var stateNames = widgetAccessor.getWidgetStateNames();<br />

if(stateNames){<br />

<strong>for</strong>(var x = 0; x < stateNames.length; x++){<br />

//add error checking <strong>for</strong> null<br />

widgetAccessor.removeWidgetState(stateNames[x]);<br />

}<br />

}<br />

}<br />

11


};<br />

}<br />

var deferred = navStateModel.commit();<br />

//deferred.setFinishedCallback(cb);<br />

deferred.start();<br />

return origSwitchPageHandler.apply(this, arguments);<br />

var getiWidgets = function(layoutModel){<br />

var iWidgetsList = new Array();<br />

var iterateLayoutModel = function(node){<br />

//check to see if the node is of type<br />

if(node.getLayoutNodeType() ==<br />

com.ibm.mashups.enabler.layout.Constants.LAYOUT_CONTROL){<br />

//add the node ID to the array since this represents<br />

a widget on the page<br />

iWidgetsList.push(node.getID());<br />

} else {<br />

var children = layoutModel.getChildren(node);<br />

while(children.hasNext()){<br />

iterateLayoutModel(children.next());<br />

}<br />

}<br />

};<br />

iterateLayoutModel(layoutModel.getRoot().start());<br />

return iWidgetsList;<br />

}<br />

})();<br />

<br />

Since we need to call the original switchPageHandler function after our processing, we need<br />

to first save the original function to be called later. Once we have the original function saved,<br />

we can overwrite the switchPageHandler function in the NavigationController and, using the<br />

enabler APIs, we obtain the layout of the target page.<br />

The layout is needed to obtain a list of the <strong>portlet</strong>s on the page. The sample code provides a<br />

function named getiWidgets that iterates the layoutModel, looking <strong>for</strong> nodes that are of type<br />

com.ibm.mashups.enabler.layout.Constants.LAYOUT_CONTROL. These nodes represent<br />

the <strong>portlet</strong>s on the page.<br />

We can then iterate through the list of iWidget IDs and use the WidgetAccessor APIs to get<br />

the state names, which represent the renderParameters associated with the <strong>portlet</strong>. We can<br />

also add a renderParameter <strong>for</strong> use in the <strong>portlet</strong> code. For example, we can add the<br />

clearSession parameter that the <strong>portlet</strong> can detect and then clear an attribute from its session:<br />

widgetAccessor.setWidgetState("clearSession", ["false"]);<br />

We could also look <strong>for</strong> and remove public-render-parameters. The sample code contains a<br />

snippet <strong>for</strong> removing three public-render-parameters that are commonly used in <strong>IBM</strong> Web<br />

Content Manager (WCM) rendering <strong>portlet</strong>s.<br />

12


3 Framework modifications<br />

3.1 JavaServer Faces (JSF)<br />

The easiest way to reset the state is by using one of the above methods that clears the render<br />

methods (most often, setting the keymanager) and setting the JSF framework to save its state<br />

as render paramaters.<br />

Refer to <strong>IBM</strong> Support Technote #1424426, "JSF Portlet Development Migration topics: Tips<br />

and Suggestions" (the section under Per<strong>for</strong>mance tuning tips), <strong>for</strong> more in<strong>for</strong>mation about<br />

using the session vs. the render parameters. Be aware that using render parameters can lead to<br />

long URLs that some browsers cannot handle.<br />

The JSF <strong>portlet</strong> bridge, as provided by <strong>IBM</strong> previously, stored its state in the session. If you<br />

are storing the state in the session, the state of the <strong>portlet</strong> is maintained by the<br />

<strong>portlet</strong>/framework itself and would need to be cleared by direct action.<br />

The best way to do this is to target an action in the <strong>portlet</strong> that resets the state. This link<br />

would be from other pages that target this page, so that when the page is changed, the <strong>portlet</strong><br />

action is also fired, causing the action to reset.<br />

You could do this as well in your <strong>portlet</strong> by setting the following:<br />

<strong>portlet</strong>Session.setAttribute("com.ibm.faces.<strong>portlet</strong>.page.view",<br />

);<br />

3.2 Struts Portlet Frame (SPF)<br />

The SPF uses a session attribute to determine the pagination. The typical flow of an SPF<br />

<strong>portlet</strong> is to render a landing page and then, when an action is executed, an IViewCommand<br />

object is created and stored into the session.<br />

This IViewCommand object contains the in<strong>for</strong>mation needed to render the page. When the<br />

<strong>portlet</strong> is rendered again, the SPF will determine whether an IVewCommand object is already<br />

present in the session; if so, this is used to render the last view of the <strong>portlet</strong>.<br />

The Struts Portlet Framework does provide a flag interface named IstrutsSingleAction that<br />

allows the action to be executed once, and the resulting IviewCommand is created and stored<br />

into the session.<br />

The <strong>portlet</strong> is then rendered by use of the results of the created IviewCommand. However,<br />

when the <strong>portlet</strong> is rendered again, the IviewCommand is removed from the session, and the<br />

<strong>portlet</strong> is rendered with its default page.<br />

It is important to understand that this behavior is tied to the rendering of the <strong>portlet</strong> and not a<br />

portal navigation change. This means that the <strong>portlet</strong> will be "reset," if another <strong>portlet</strong> on the<br />

page causes the page to be updated, or the page is simply refreshed.<br />

The following example deomonstrates the IstrutsSingleAction:<br />

We first create a landing page with two links, one of which is to a "regular" Action, and the<br />

other is to an IstrutsSingleAction (see listing 9)<br />

13


Listing 9. Code <strong>for</strong> landing page<br />

<br />

<br />

You're on the landing page.<br />

Click to execute the<br />

IStrutsSingleAction<br />

Click to execute the<br />

Action<br />

The action mappings are set to the code shown in listing 10.<br />

Listing 10. Action mapping code<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

Let's first look at the ExecuteAction class and the regularAction.jsp. As you can see from<br />

listing 11, the ExecuteAction is a very simple class that returns the mapping to the jsp.<br />

Listing 11. ExecuteAction class<br />

public class ExecuteAction extends StrutsAction {<br />

public ActionForward execute(ActionMapping mapping, ActionForm<br />

<strong>for</strong>m,<br />

PortletRequest request, PortletResponse response)<br />

throws Exception {<br />

}<br />

}<br />

ActionForward <strong>for</strong>ward = mapping.findForward("success");<br />

return <strong>for</strong>ward;<br />

Then the regularAction.jsp prints a simple message, as shown in listing 12.<br />

Listing 12. regularAction.jsp<br />

<br />

14


<br />

<br />

This is the result of a regular StrutsAction.<br />

Next, place this <strong>portlet</strong> on a page and select the "click to execute Action" link. The content in<br />

listing 12 will be displayed in the <strong>portlet</strong>. If you navigate to another <strong>WebSphere</strong> <strong>Portal</strong> page<br />

and back, you will notice that the same content is displayed.<br />

Now let's examine the IstrutsSingleAction by first looking at the ExecuteSingleAction class<br />

(see listing 13).<br />

Listing 13. ExecuteSingleAction class<br />

public class ExecuteSingleAction extends StrutsAction implements<br />

IStrutsSingleAction {<br />

public ActionForward execute(ActionMapping mapping, ActionForm<br />

<strong>for</strong>m,<br />

PortletRequest request, PortletResponse response)<br />

throws Exception {<br />

}<br />

}<br />

ActionForward <strong>for</strong>ward = mapping.findForward("success");<br />

return <strong>for</strong>ward;<br />

As you can see, this action is quite similar to the previous one, except that this one<br />

implements the IstrutsSingleAction interface. As mentioned be<strong>for</strong>e, this is a flag interface, so<br />

there are no methods to implement. Like the regularAction.jsp, the IstrutsSingleAction.jsp<br />

displays a simple message (see figure 14).<br />

Listing 14. IstrutsSingleAction.jsp<br />

<br />

<br />

<br />

<br />

This is the result of an IStrutsSingleAction. <br />

This page should not be displayed when refreshing this portal page<br />

or when returning to this portal page from a different portal<br />

page.<br />

Log out of <strong>WebSphere</strong> <strong>Portal</strong> and then log back in. This clears the state since we have set the<br />

state in the session with the standard action. Navigate to the page where this <strong>portlet</strong> is<br />

deployed, and this time select the "Click to execute the IstrutsSingleAction" link. You will see<br />

the content from listing 14 displayed on the page.<br />

15


If you navigate to another <strong>WebSphere</strong> <strong>Portal</strong> page and back, this time you should see the<br />

landing page instead the above content.<br />

The IstrutsSingleAction interface provides the powerful ability to reset the <strong>portlet</strong> view while<br />

requiring minimal code changes. Specifically, it only requires adding the interface and does<br />

not require any other <strong>portlet</strong> or theme code changes.<br />

3.3 WCM<br />

When using previous versions of WCM you may want the rendering <strong>portlet</strong> to reset when you<br />

return. By default the older versions of the rendering <strong>portlet</strong> save these items to the session.<br />

In newer versions it will use render paramaters, so if you used one of the procedures in<br />

Sections 2.1, 2.2, or 2.3 above to target <strong>resetting</strong> render paramaters, it will clear its state.<br />

With the older version of the <strong>portlet</strong> you must modify your navigation to also pass a URL<br />

parameter that can be read by the <strong>portlet</strong> to reset its state.<br />

First you must configure the <strong>portlet</strong> to "receive links from other <strong>portlet</strong>s and this <strong>portlet</strong>". You<br />

can find this in the links configuration <strong>for</strong> the <strong>portlet</strong>, under Edit shared settings. Then in your<br />

theme navigation loops, you have your links created by the code in listing 15, assuming you<br />

are using the default page navigation that uses navigationLoop tags.<br />

Listing 15. Code to create links<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

4 Sample <strong>portlet</strong><br />

Included with the Download file is a sample <strong>portlet</strong> that uses the session object to control the<br />

current view. The <strong>portlet</strong> was coded such that, when you use the pagination links, the .jsp<br />

name is set into the session:<br />

session.setAttribute(SESSION_OBJECT, jsp)<br />

On each page view it is checked to determine whether a page change has been selected; if<br />

not, the current page is pulled from the session and used as the view:<br />

String state = (String)session.getAttribute(SESSION_OBJECT);<br />

jsp = state;<br />

16


To be able to reset the session, we have enabled the <strong>portlet</strong> to look <strong>for</strong> a request parameter<br />

and, if present, to remove the session object and reset the view back to the base view (see<br />

listing 16.<br />

Listing 16. Code to reset session<br />

String resetState = request.getParameter("clearSession");<br />

System.out.println("The resetState ={" + resetState +<br />

"}");<br />

PortletSession session = request.getPortletSession();<br />

if((resetState !=null) && (!resetState.equals(""))) {<br />

session.removeAttribute(SESSION_OBJECT);<br />

System.out.println("Resetting state now");<br />

jsp = VIEW_JSP;<br />

This works with the other download samples we have provided and is a good way of testing<br />

your basic theme changes that effect <strong>portlet</strong>s that rely on the state in the session.<br />

5 Conclusion<br />

We have presented several methods <strong>for</strong> <strong>resetting</strong> the state of your page that, taken together,<br />

can be used to customize your site and deliver the desired experience <strong>for</strong> your users. You can<br />

use this to reset <strong>portlet</strong>s on all pages or on only some page. Just be sure to use strategies like<br />

these only where they makes sense; otherwise, you may be dealing with upset users<br />

complaining that the design is not behaving as expected.<br />

You should also now have a good understanding of how the various frameworks manage their<br />

state. Many <strong>portlet</strong>s probably manage the state in the session and, as a J2EE application, other<br />

<strong>portlet</strong>s and the <strong>Portal</strong> application cannot modify those sessions.<br />

It is up to each application to modify their own session and state. Additionally, other factors<br />

like caching may impact how the <strong>portlet</strong> renders, so make sure to test your solution on a<br />

production look-alike, to ensure it behaves as expected.<br />

6 Resources<br />

Participate in the discussion <strong>for</strong>um.<br />

<strong>WebSphere</strong> <strong>Portal</strong> product documentation.<br />

developerWorks® <strong>WebSphere</strong> <strong>Portal</strong> zone.<br />

<strong>WebSphere</strong> <strong>Portal</strong> Family wiki.<br />

7 About the authors<br />

James Barnes is a Level 2 Engineer <strong>for</strong> the <strong>WebSphere</strong> Commerce team and is based at <strong>IBM</strong>'s<br />

RTP, North Carolina, facility. He joined <strong>IBM</strong> in 1999 and then moved to Level 2 Support in<br />

2003, focusing on development-related issues. He holds Certifications <strong>for</strong> <strong>WebSphere</strong> <strong>Portal</strong><br />

development (5.1, 6.0, 6.1, 7.0) and <strong>WebSphere</strong> <strong>Portal</strong> Administrator (5.0, 5.1, 6.0), and<br />

earned a B.Sc. in Agriculture and Applied Economics from Virginia Tech University.<br />

17


Ryan Wilson is the Technical Lead <strong>for</strong> the <strong>WebSphere</strong> <strong>Portal</strong> Level 2 API/Migration team in<br />

RTP, North Carolina. His areas of expertise include Java Plat<strong>for</strong>m, Enterprise Edtion,<br />

application development with <strong>IBM</strong> <strong>WebSphere</strong> Studio and <strong>IBM</strong> Rational® Application<br />

Developer. Ryan holds certificates in <strong>WebSphere</strong> <strong>Portal</strong> development (versions 5.1, 6.0, 7.0),<br />

and is a Java Certified Programmer and Java Certified Web Component Developer.<br />

Trademarks<br />

• developerWorks, <strong>IBM</strong>, Rational, and <strong>WebSphere</strong> are trademarks or registered trademarks<br />

of <strong>IBM</strong> Corporation in the United States, other countries, or both.<br />

• Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the<br />

United States, other countries, or both.<br />

• Other company, product, or service names may be trademarks or service marks of others.<br />

18

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!