IBM RAD Portal Development Guide
IBM RAD Portal Development Guide
IBM RAD Portal Development Guide
IBM Rational Application Developer V6 Portlet Application Development and Portal Tools
Learn how to develop MVC, Struts and JavaServer Faces portlet applications Learn about IBM Portlet API and the JSR 168 standard API Access Web Services and secure Web applications
Juan R. Rodriguez Cristiano Cesario Karla Galvan Belen Gonzalez George Kroner Gianfranco Rutigliano Ryan Wilson
ibm.com/redbooks
International Technical Support Organization IBM Rational Application Developer V6 Portlet Application Development and Portal Tools August 2005
SG24-6681-00
Note: Before using this information and the product it supports, read the information in Notices on page xvii.
First Edition (August 2005) This edition applies to Version 6 of IBM Rational Application Developer and Version 5.1 of IBM WebSphere Portal.
Copyright International Business Machines Corporation 2005. All rights reserved. Note to U.S. Government Users Restricted Rights -- Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
Contents
Notices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii Trademarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xviii Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix The team that wrote this redbook. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix Become a published author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxii Comments welcome. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxii Chapter 1. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1 Portal evolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.1.1 The generations of portal technology . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.2 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.2.1 What is a portal? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.2.2 Enablement for portals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.2.3 The WebSphere Portal framework . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.2.4 WebSphere Portal architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.2.5 WebSphere Portal tooling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 1.3 WebSphere Portal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 1.3.1 Portal concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 1.3.2 Portlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 1.3.3 The model-view-controller (MVC) design pattern . . . . . . . . . . . . . . . 23 1.3.4 Standard MVC architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 1.3.5 Portlet MVC architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 1.3.6 Portlet MVC sample . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 1.3.7 WebSphere Portal runtime: the portlet container . . . . . . . . . . . . . . . 26 1.3.8 Page aggregation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 1.4 Highlights in WebSphere Portal V5.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 1.4.1 Portal install. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 1.4.2 General infrastructure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 1.4.3 Event broker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 1.4.4 Member subsystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 1.4.5 Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 1.4.6 Authorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 1.4.7 URL generation, processing and mappings . . . . . . . . . . . . . . . . . . . 35 1.4.8 Search. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 1.4.9 Content management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 1.4.10 Transcoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 1.4.11 Struts Portlet Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
iii
1.4.12 JSF Portlet Runtime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 1.4.13 User interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 1.4.14 Cooperative portlets (Click-To-Action) . . . . . . . . . . . . . . . . . . . . . . 39 1.4.15 Portal Toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 1.5 Portlet solution patterns. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 1.6 Building a war file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Chapter 2. Developing Portal applications . . . . . . . . . . . . . . . . . . . . . . . . . 47 2.1 Portal overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 2.1.1 Portal concepts and definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 2.1.2 IBM WebSphere Portal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 2.1.3 IBM Rational Application Developer . . . . . . . . . . . . . . . . . . . . . . . . . 51 2.2 Developing applications for WebSphere Portal. . . . . . . . . . . . . . . . . . . . . 54 2.2.1 Portal samples and tutorials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 2.2.2 Development strategy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 2.2.3 Portal tools for developing portals. . . . . . . . . . . . . . . . . . . . . . . . . . . 58 2.2.4 Portal tools for developing portlets . . . . . . . . . . . . . . . . . . . . . . . . . . 65 2.2.5 Portal tools for testing and debugging portlets . . . . . . . . . . . . . . . . . 78 2.2.6 Portal tools for deploying and managing portlets . . . . . . . . . . . . . . . 82 2.2.7 Enterprise Application Integration Portal tools . . . . . . . . . . . . . . . . . 84 2.2.8 Coexistence and migration of tools and applications . . . . . . . . . . . . 85 2.3 Portal development scenario. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 2.3.1 Preparing for the sample . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 2.3.2 Creating a portal project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 2.3.3 Adding and modifying a portal page . . . . . . . . . . . . . . . . . . . . . . . . . 89 2.3.4 Creating and modifying two portlets . . . . . . . . . . . . . . . . . . . . . . . . . 92 2.3.5 Adding portlets to a portal page . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 2.3.6 Running the project in the test environment . . . . . . . . . . . . . . . . . . . 98 Chapter 3. Portlet development platform sample installation. . . . . . . . . 101 3.1 Prerequisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 3.1.1 Hardware requirements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 3.1.2 Software requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 3.2 Rational Application Developer and Portal Tools . . . . . . . . . . . . . . . . . . 102 3.3 WebSphere Portal V5.1 Test Environment . . . . . . . . . . . . . . . . . . . . . . . 106 3.4 Configuration of the Test Environment . . . . . . . . . . . . . . . . . . . . . . . . . . 110 3.5 WebSphere Test Environment V5.1 (optional) . . . . . . . . . . . . . . . . . . . . 113 Chapter 4. IBM Portlet API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 4.1 IBM portlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 4.2 IBM portlet application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 4.3 Servlets versus portlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 4.4 Portlet modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 4.5 Portlet states . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
iv
4.6 Core objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 4.6.1 Hierarchy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 4.6.2 Portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 4.6.3 PortletAdapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 4.6.4 PortletRequest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 4.6.5 PortletResponse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 4.6.6 PortletSession object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 4.6.7 Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 4.6.8 PortletConfig object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 4.6.9 PortletContext object. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 4.6.10 PortletSettings object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 4.6.11 PortletApplicationSettings object. . . . . . . . . . . . . . . . . . . . . . . . . . 129 4.6.12 PortletData object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 4.6.13 PortletLog object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 4.6.14 PortletException . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 4.6.15 UnavailableException . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 4.6.16 PortletWindow object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 4.6.17 User object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 4.6.18 PortletURI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 4.7 Portlet life cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 4.8 Listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 4.8.1 PortletTitleListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 4.8.2 PortletPageListener. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 4.8.3 PortletSessionListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 4.8.4 WindowListener. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 4.8.5 PortletSettingsAttributeListener. . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 4.8.6 PortletApplicationSettingsAttributesListener . . . . . . . . . . . . . . . . . . 141 4.9 Action event handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 4.9.1 ActionListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 4.9.2 ActionEvent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 4.9.3 PortletURI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 4.9.4 ModeModifier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 4.10 Attribute storage summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 4.11 Portlet JSPs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 4.11.1 Portlet tag library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 4.11.2 Portlet events and messaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 4.12 Portlet deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 4.12.1 web.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 4.12.2 portlet.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 4.12.3 Parameter summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 4.12.4 Descriptors relationship (web.xml and portlet.xml) . . . . . . . . . . . . 170 4.12.5 UID guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 4.13 Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
Contents
Chapter 5. A first portlet application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 5.1 Sample scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 5.2 Creating the portlet project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 5.2.1 Using the Portlet Project wizard . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 5.3 Configuring the test environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 5.4 Running the portlet project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 5.5 Modifying the portlet project and verifying changes . . . . . . . . . . . . . . . . 192 5.5.1 Changing the JSP used for the View mode . . . . . . . . . . . . . . . . . . 192 5.5.2 Adding a JavaBean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 Chapter 6. IBM Portlet API portlet development. . . . . . . . . . . . . . . . . . . . 203 6.1 About action events. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 6.2 Development scenario. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 6.3 Creating the portlet project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 6.4 Configuring your project in the test environment . . . . . . . . . . . . . . . . . . . 214 6.5 Examining and modifying the source code . . . . . . . . . . . . . . . . . . . . . . . 216 6.6 Running your project in the test environment . . . . . . . . . . . . . . . . . . . . . 219 Chapter 7. Portlet messaging. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 7.1 Portlet messaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226 7.2 MessageListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226 7.3 MessageEvent. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227 7.4 DefaultPortletMessage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227 7.5 PortletMessage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 7.6 Sample scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230 7.6.1 Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 7.6.2 Sending a message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 7.6.3 Creating the target portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 7.6.4 Running the portlet application . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240 7.7 Broadcasting messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242 Chapter 8. JSR 168 API. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251 8.1 JSR overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252 8.1.1 Number of portlet instances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252 8.1.2 Portlet windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252 8.1.3 Thread safety . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253 8.2 JSR 168 comparison to servlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254 8.3 JSR 168 portlet modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254 8.4 JSR 168 Portlet window states . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 8.5 Core JSR 168 objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 8.5.1 interface javax.portlet.Portlet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 8.5.2 class javax.portlet.GenericPortlet . . . . . . . . . . . . . . . . . . . . . . . . . . 259 8.5.3 interface javax.portlet.PortletURL . . . . . . . . . . . . . . . . . . . . . . . . . . 261 8.5.4 interface javax.portlet.PortletContext . . . . . . . . . . . . . . . . . . . . . . . 263
vi
8.5.5 interface javax.portlet.PortletRequest . . . . . . . . . . . . . . . . . . . . . . . 264 8.5.6 interface javax.portlet.ActionRequest . . . . . . . . . . . . . . . . . . . . . . . 267 8.5.7 interface javax.portlet.RenderRequest . . . . . . . . . . . . . . . . . . . . . . 270 8.5.8 interface javax.portlet.PortletResponse . . . . . . . . . . . . . . . . . . . . . 270 8.5.9 interface javax.portlet.ActionResponse . . . . . . . . . . . . . . . . . . . . . . 271 8.5.10 interface javax.portlet.RenderResponse . . . . . . . . . . . . . . . . . . . . 272 8.5.11 interface javax.portlet.PortalContext . . . . . . . . . . . . . . . . . . . . . . . 274 8.5.12 interface javax.portlet.PortletPreferences . . . . . . . . . . . . . . . . . . . 274 8.5.13 interface javax.portlet.PreferencesValidator . . . . . . . . . . . . . . . . . 276 8.5.14 interface javax.portlet.PortletConfig . . . . . . . . . . . . . . . . . . . . . . . 277 8.5.15 interface javax.portlet.PortletSession . . . . . . . . . . . . . . . . . . . . . . 278 8.6 JSR 168 Portlet life cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280 8.6.1 Instantiation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280 8.6.2 Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280 8.6.3 Request handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281 8.6.4 End of service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282 8.7 Portlet caching. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283 8.7.1 Remote cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283 8.8 Listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284 8.8.1 HttpSessionBindingListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284 8.8.2 ServletContextListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 8.8.3 ServletContextAttributeListener . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 8.8.4 HttpSessionListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 8.8.5 HttpSessionAttributeListener. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 8.9 Deployment descriptors. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 8.9.1 Portlet.xml declaration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288 8.9.2 portlet-app - required, can occur only once . . . . . . . . . . . . . . . . . . . 288 8.9.3 portlet - can occur zero or more times . . . . . . . . . . . . . . . . . . . . . . . 289 8.9.4 custom-portlet-mode - can occur zero or more times . . . . . . . . . . . 295 8.9.5 custom-window-state - can occur zero or more times . . . . . . . . . . . 296 8.9.6 user-attribute - can occur zero or more times . . . . . . . . . . . . . . . . . 296 8.9.7 security-constraint - can occur zero or more times . . . . . . . . . . . . . 298 8.10 JSR 168 limitations in WebSphere Portal . . . . . . . . . . . . . . . . . . . . . . . 299 Chapter 9. JSR 168 portlet development . . . . . . . . . . . . . . . . . . . . . . . . . 301 9.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302 9.2 Creating a JSR 168 portlet project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302 9.2.1 Creating a basic JSR 168 portlet . . . . . . . . . . . . . . . . . . . . . . . . . . 303 9.2.2 Examining the generated portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . 306 9.3 Updating the generated portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306 9.3.1 Modifying the session bean. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307 9.3.2 View mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310 9.3.3 Edit mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
Contents
vii
9.3.4 Configure mode. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326 9.3.5 Updating the portlet descriptor (portlet.xml) . . . . . . . . . . . . . . . . . . 329 9.3.6 Modifying the MySimplePortletPortletPreferenceValidator class . . 331 9.4 Running the portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333 9.4.1 Executing the portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333 Chapter 10. Migrating to JSR 168 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335 10.1 Modifying the deployment descriptor. . . . . . . . . . . . . . . . . . . . . . . . . . . 336 10.1.1 doctype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 10.1.2 portlet-app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 10.1.3 concrete-portlet-app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337 10.1.4 portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337 10.1.5 portlet-name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337 10.1.6 web.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337 10.1.7 cache. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337 10.1.8 supports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338 10.1.9 allows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339 10.1.10 config-param . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339 10.1.11 Locale settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339 10.2 Modifying the Java source. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340 10.2.1 Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340 10.2.2 Superclass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 10.2.3 doXXX methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 10.2.4 actionPerformed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 10.2.5 ActionEvent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 10.2.6 Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342 10.2.7 JSP includes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342 10.2.8 PortletData and PortletSettings . . . . . . . . . . . . . . . . . . . . . . . . . . . 343 10.2.9 namespace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343 10.2.10 portlet URLs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343 10.3 Modifying the JSP source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344 10.3.1 taglib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344 10.3.2 portletAPI:init. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 10.3.3 namespace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 10.3.4 Creating URLs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346 10.3.5 portletAPI:text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347 10.3.6 encodeURL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347 10.3.7 CSS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347 10.4 Struts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347 10.5 JSF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348 10.6 Portlet services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348 10.7 Messaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
viii
Chapter 11. Using JSPs and servlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353 11.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354 11.1.1 Generating output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354 11.2 RequestDispatcher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356 11.2.1 PortletContext.getRequestDispatcher . . . . . . . . . . . . . . . . . . . . . . 356 11.2.2 PortletContext.getNamedDispatcher. . . . . . . . . . . . . . . . . . . . . . . 356 11.2.3 PortletRequestDispatcher.include . . . . . . . . . . . . . . . . . . . . . . . . . 356 11.3 JSP tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360 11.3.1 defineObjects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361 11.3.2 renderURL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361 11.3.3 actionURL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362 11.3.4 namespace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363 11.3.5 param . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364 11.3.6 IBM tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364 11.3.7 JSTL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365 11.4 Cascading style sheets (CSS). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366 11.4.1 WSRP Styles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366 11.4.2 IBM styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370 Chapter 12. Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373 12.1 Resource bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374 12.1.1 Creating resource bundles in Rational Application Developer . . . 376 12.1.2 Translating resource bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 12.1.3 Accessing resource bundles in portlets. . . . . . . . . . . . . . . . . . . . . 381 12.1.4 Accessing resource bundles in JSPs . . . . . . . . . . . . . . . . . . . . . . 382 12.2 Translating whole resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383 12.3 JSR 168 API considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384 12.4 Dynamically changing the language . . . . . . . . . . . . . . . . . . . . . . . . . . . 385 12.5 NLS administration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386 12.5.1 Portlet NLS administration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386 12.5.2 Portal NLS administration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390 12.5.3 Setting NLS titles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390 12.5.4 Supporting a new language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391 12.6 Working with characters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392 12.7 NLS best practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392 12.8 Sample scenario: NLS bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393 12.8.1 NLS bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394 12.8.2 Accessing NLS bundles from JSPs. . . . . . . . . . . . . . . . . . . . . . . . 399 12.8.3 Running the NLS scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400 12.8.4 Accessing NLS bundles in Java portlets . . . . . . . . . . . . . . . . . . . . 404 12.9 Sample scenario: translating whole resources . . . . . . . . . . . . . . . . . . . 406 12.10 Dynamically changing the language . . . . . . . . . . . . . . . . . . . . . . . . . . 411
Contents
ix
Chapter 13. Struts portlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415 13.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416 13.2 The Struts portlet framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419 13.2.1 Struts applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420 13.2.2 Changes to Struts JSPs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423 13.2.3 Configuration files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426 13.2.4 Creating link tags in Struts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426 Chapter 14. Creating Struts portlets with the IBM Portlet API . . . . . . . . 429 14.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430 14.2 Creating Struts applications with IBM portlet API . . . . . . . . . . . . . . . . . 430 14.2.1 Creating a Portlet project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430 14.2.2 Inspecting the Struts portlet project. . . . . . . . . . . . . . . . . . . . . . . . 433 14.2.3 Designing the application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434 14.2.4 Realizing the application components. . . . . . . . . . . . . . . . . . . . . . 436 14.2.5 Adding logging support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444 14.2.6 Adding support to Edit mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446 14.2.7 Realizing the new application components . . . . . . . . . . . . . . . . . . 450 14.2.8 Adding internationalization support . . . . . . . . . . . . . . . . . . . . . . . . 460 14.3 Messaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462 14.4 Migration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463 Chapter 15. Struts portlet development using the JSR 168 API . . . . . . . 465 15.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466 15.2 Message flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467 15.3 Creating a Portlet project. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469 15.3.1 Inspecting the Struts portlet project. . . . . . . . . . . . . . . . . . . . . . . . 475 15.4 Designing the application (View mode) . . . . . . . . . . . . . . . . . . . . . . . . . 477 15.4.1 Realizing the application components. . . . . . . . . . . . . . . . . . . . . . 480 15.4.2 Realizing the index.jsp page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482 15.4.3 Realizing the notConfigured.jsp . . . . . . . . . . . . . . . . . . . . . . . . . . 486 15.4.4 Realizing the Form Bean. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486 15.4.5 Realizing configured.jsp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489 15.4.6 Editing the resources file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490 15.4.7 Realizing the welcome action mapping . . . . . . . . . . . . . . . . . . . . . 490 15.4.8 Running the Struts portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493 15.5 Designing the application (Edit mode). . . . . . . . . . . . . . . . . . . . . . . . . . 494 15.6 Realizing the Edit mode application components . . . . . . . . . . . . . . . . . 495 15.6.1 Realizing the editBean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495 15.6.2 Realizing index.jsp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498 15.6.3 Realizing the saveConfiguration action . . . . . . . . . . . . . . . . . . . . . 499 15.7 Adding new keys to the resources file. . . . . . . . . . . . . . . . . . . . . . . . . . 500 15.8 Running the portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
15.9 Adding internationalization support . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502 15.9.1 Portlet application View mode internationalization . . . . . . . . . . . . 503 15.9.2 Struts framework internationalization support . . . . . . . . . . . . . . . . 504 15.9.3 Editing the Portlet deployment descriptor . . . . . . . . . . . . . . . . . . . 505 15.9.4 Testing the new locale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505 15.10 Adding logging support to the application . . . . . . . . . . . . . . . . . . . . . . 506 Chapter 16. JavaServer Faces portlets . . . . . . . . . . . . . . . . . . . . . . . . . . . 511 16.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512 16.1.1 Life cycle of a JSF page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 514 16.2 A simple JSF application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518 16.2.1 Creating the pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519 16.2.2 Defining navigation rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521 16.2.3 Developing the beans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523 16.3 User interface component model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525 16.3.1 User interface component classes . . . . . . . . . . . . . . . . . . . . . . . . 525 16.3.2 Component rendering model. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527 16.3.3 Conversion model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531 16.3.4 Event and listener model. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535 16.3.5 Validation model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536 16.4 Navigation model. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539 16.5 Backing bean management. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540 16.6 JSF in portlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542 16.6.1 JSF portlet runtime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542 16.6.2 Mapping between portlet phases and JSF phases . . . . . . . . . . . . 544 16.6.3 Welcome page and navigation in JSF portlets . . . . . . . . . . . . . . . 545 16.6.4 Programming guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546 16.6.5 Limitations in JSF portlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548 16.7 Migration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548 Chapter 17. JavaServer Faces portlet development . . . . . . . . . . . . . . . . 551 17.1 The calculator application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552 17.2 Creating the project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552 17.2.1 Inspecting the JSF portlet project . . . . . . . . . . . . . . . . . . . . . . . . . 555 17.3 Creating the page layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558 17.4 Implementing component attributes and validation . . . . . . . . . . . . . . . . 563 17.4.1 Testing the validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 567 17.5 Binding the front end to the calculator. . . . . . . . . . . . . . . . . . . . . . . . . . 568 17.5.1 Testing the binding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 572 17.6 Invoking the business logic of the calculator . . . . . . . . . . . . . . . . . . . . . 572 17.6.1 Implementing an error page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573 17.7 Implementing page navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577 17.8 Implementing a validator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 580
Contents
xi
17.9 Implementing a value change event . . . . . . . . . . . . . . . . . . . . . . . . . . . 583 17.10 Implementing internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . 587 17.10.1 Internationalization of standard validator messages . . . . . . . . . . 590 Chapter 18. Additional Faces portlet sample scenarios . . . . . . . . . . . . . 593 18.1 The call center application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594 18.1.1 Creating the project. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594 18.1.2 Creating the page layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595 18.1.3 Defining a parameter for the list . . . . . . . . . . . . . . . . . . . . . . . . . . 599 18.1.4 Creating a detail portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601 18.1.5 Linking the portlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 602 18.1.6 Testing the application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605 18.2 The Web service client portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606 18.2.1 Creating the project. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606 18.2.2 Creating a new Web service client . . . . . . . . . . . . . . . . . . . . . . . . 607 18.2.3 Creating the page layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612 18.2.4 Testing the application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615 Chapter 19. Portlet services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617 19.1 Portlet services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618 19.1.1 ContentAccessService . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618 19.1.2 Custom services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620 19.2 Accessing portlet services. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 627 19.2.1 Accessing a portlet service in an IBM portlet . . . . . . . . . . . . . . . . 627 19.2.2 Accessing a portlet service in a JSR 168 portlet . . . . . . . . . . . . . . 627 Chapter 20. Credential Vault Service. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629 20.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 630 20.1.1 Credentials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 630 20.2 Credential Vault organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 631 20.2.1 Vault segments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 632 20.2.2 Credential slots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633 20.3 Working with the CredentialVaultService . . . . . . . . . . . . . . . . . . . . . . . 634 20.3.1 Acquiring a reference to the CredentialVaultService . . . . . . . . . . 634 20.3.2 Using the CredentialVaultService . . . . . . . . . . . . . . . . . . . . . . . . . 634 20.4 Credential objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635 20.4.1 Passive credential objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636 20.4.2 Active credential objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637 20.4.3 Storing credential objects in the PortletSession . . . . . . . . . . . . . . 640 Chapter 21. The Credential Vault . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 641 21.1 Sample scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642 21.2 Importing a secure servlet application. . . . . . . . . . . . . . . . . . . . . . . . . . 644 21.3 Using active credentials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 648
xii
21.3.1 Creating the Credential Vault portlet application . . . . . . . . . . . . . . 648 21.3.2 Reviewing the generated code . . . . . . . . . . . . . . . . . . . . . . . . . . . 651 21.3.3 Updating the generated portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . 653 21.3.4 Running the portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662 21.4 Using passive credentials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667 Chapter 22. Accessing JDBC databases from portlet applications . . . . 669 22.1 Creating a portlet project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 670 22.1.1 Creating HRPortlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 670 22.2 Creating a sample database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675 22.2.1 Creating the WSSAMPLE database . . . . . . . . . . . . . . . . . . . . . . . 675 22.2.2 Creating a connection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678 22.2.3 Creating an SQL statement. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683 22.2.4 Generating Java classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685 22.3 Sample scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689 22.3.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 690 22.3.2 Importing the WAR file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 692 22.3.3 Reviewing the portlet code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693 22.3.4 Running the HRPortlet application . . . . . . . . . . . . . . . . . . . . . . . . 698 Chapter 23. Accessing JDBC databases using Data Source in standard portlets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703 23.1 Data Source overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704 23.2 Creating a JSR 168 portlet project . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705 23.2.1 Creating HRPortlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705 23.3 Creating a sample database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710 23.3.1 Creating a connection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710 23.3.2 Creating an SQL statement. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716 23.3.3 Generating Java classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719 23.4 Sample scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 724 23.4.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725 23.4.2 Importing the WAR file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 727 23.4.3 Reviewing the portlet code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729 23.4.4 Creating the Data Source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733 23.4.5 Running the HRPortlet168 application . . . . . . . . . . . . . . . . . . . . . 736 Chapter 24. IBM API declarative cooperative portlets . . . . . . . . . . . . . . . 741 24.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 742 24.1.1 The WebSphere Portal property broker . . . . . . . . . . . . . . . . . . . . 743 24.1.2 Property broker runtime components . . . . . . . . . . . . . . . . . . . . . . 744 24.2 IBM Portlets for cooperation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 744 24.2.1 Registering and publishing properties . . . . . . . . . . . . . . . . . . . . . . 745 24.2.2 Struts integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 746 24.2.3 Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 746
Contents
xiii
24.3 Sample scenario (IBM portlets) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747 24.3.1 Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747 24.3.2 Source cooperative portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 750 24.3.3 Target cooperative portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761 24.3.4 Running the cooperative portlets . . . . . . . . . . . . . . . . . . . . . . . . . 765 Chapter 25. IBM API programmatic cooperative portlets . . . . . . . . . . . . 771 25.1 Publishing properties programmatically . . . . . . . . . . . . . . . . . . . . . . . . 772 25.2 Portlet event handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 773 25.2.1 PropertyListener interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 775 25.2.2 EventPhaseListener interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776 25.3 Broadcasting source data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 777 25.4 Wiring tool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 778 25.5 Sample scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 779 25.5.1 Declarative source cooperative portlet . . . . . . . . . . . . . . . . . . . . . 779 25.5.2 Enabling the portlet for target C2A programmatic . . . . . . . . . . . . . 781 25.5.3 Running the cooperative portlets . . . . . . . . . . . . . . . . . . . . . . . . . 789 25.5.4 Wire portlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 793 25.5.5 Enabling HRPortlet for programmatic source C2A . . . . . . . . . . . . 794 25.5.6 Running the programmatic source portlet . . . . . . . . . . . . . . . . . . . 798 Chapter 26. JSR 168 cooperative portlets . . . . . . . . . . . . . . . . . . . . . . . . 801 26.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 802 26.2 Source cooperative portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 803 26.2.1 Importing the HRPortlet168 portlet . . . . . . . . . . . . . . . . . . . . . . . . 804 26.2.2 Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805 26.2.3 Declaring exchange capabilities using WSDL. . . . . . . . . . . . . . . . 806 26.2.4 Updating the portlet deployment descriptor . . . . . . . . . . . . . . . . . 809 26.2.5 Updating the HRPortlet168 portlet code . . . . . . . . . . . . . . . . . . . . 811 26.2.6 Updating the JSP to generate a link . . . . . . . . . . . . . . . . . . . . . . . 815 26.3 Target cooperative portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816 26.3.1 Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 818 26.3.2 Declaring exchange capabilities using WSDL. . . . . . . . . . . . . . . . 819 26.3.3 Updating the portlet deployment descriptor . . . . . . . . . . . . . . . . . 821 26.4 Running the cooperative portlets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824 26.4.1 Populating the sample database. . . . . . . . . . . . . . . . . . . . . . . . . . 824 26.4.2 Creating a data source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825 26.4.3 Running and wiring the cooperative portlets . . . . . . . . . . . . . . . . . 828 Chapter 27. Struts cooperative portlets . . . . . . . . . . . . . . . . . . . . . . . . . . 835 27.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 836 27.2 Source cooperative portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 837 27.2.1 Importing the Echo portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 837 27.2.2 Declaring exchange capabilities using WSDL. . . . . . . . . . . . . . . . 838
xiv
27.2.3 Updating the portlet deployment descriptor . . . . . . . . . . . . . . . . . 841 27.2.4 Updating the EchoSource portlet code . . . . . . . . . . . . . . . . . . . . . 842 27.2.5 Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 844 27.3 Target cooperative portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 844 27.3.1 Importing the Echo portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 845 27.3.2 Declaring exchange capabilities using WSDL. . . . . . . . . . . . . . . . 846 27.3.3 Updating the EchoTarget portlet code . . . . . . . . . . . . . . . . . . . . . 849 27.3.4 Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 850 27.4 Running the cooperative portlets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 851 Chapter 28. Accessing Web Services from portlet applications . . . . . . 857 28.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 858 28.2 Sample scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 858 28.2.1 Creating a Web Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 859 28.2.2 Creating a Web Services client portlet . . . . . . . . . . . . . . . . . . . . . 874 Chapter 29. Web Services for Remote Portlets (WSRP) . . . . . . . . . . . . . 885 29.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886 29.2 Implementing WSRP in WebSphere Portal. . . . . . . . . . . . . . . . . . . . . . 888 29.2.1 Tasks for Producer portals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 889 29.2.2 Tasks for Consumer portals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 892 29.2.3 Testing the scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 898 29.3 Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 899 Chapter 30. Portlet debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 901 30.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 902 30.2 Sample scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 902 30.2.1 Fixing compile errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 902 30.2.2 Debugging a portlet application. . . . . . . . . . . . . . . . . . . . . . . . . . . 905 Chapter 31. Remote Server Attach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 915 31.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 916 31.2 Sample scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 917 31.2.1 Preparing Remote Portal server to debug. . . . . . . . . . . . . . . . . . . 917 31.2.2 Creating Remote Portal server users . . . . . . . . . . . . . . . . . . . . . . 920 31.2.3 Creating a WebSphere Portal Server Attach . . . . . . . . . . . . . . . . 921 31.2.4 Debugging a portlet on WebSphere Portal Server Attach. . . . . . . 924 31.3 Defining Web browsers and emulator devices . . . . . . . . . . . . . . . . . . . 932 Chapter 32. Updating a portal layout. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 935 32.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 936 32.2 Creating a Portal server connection . . . . . . . . . . . . . . . . . . . . . . . . . . . 937 32.3 Importing the WebSphere Portal server configuration . . . . . . . . . . . . . 943 32.4 Modifying the Portal Navigation and Layout . . . . . . . . . . . . . . . . . . . . . 945
Contents
xv
Adding portlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 950 Additional ways to add portlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 952 Testing the updated portal configuration . . . . . . . . . . . . . . . . . . . . . . . . 954 Applying themes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 957
Chapter 33. Creating new portal themes. . . . . . . . . . . . . . . . . . . . . . . . . . 961 33.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 962 33.2 Creating a new theme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 962 33.3 Editing a theme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 965 33.4 Editing Styles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 976 33.5 Applying MyCorp theme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 984 33.6 Applying a Skin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 985 33.7 Testing the new Portal Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 986 33.8 Publishing the Portal Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 990 33.9 Deploying the Portal Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 993 Appendix A. Additional material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1003 Locating the Web material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1003 Using the Web material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1003 System requirements for downloading the Web material . . . . . . . . . . . . 1004 How to use the Web material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1004 Related publications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1005 IBM Redbooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1005 Other publications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1006 Online resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1006 How to get IBM Redbooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1007 Help from IBM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1007 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1009
xvi
Notices
This information was developed for products and services offered in the U.S.A. IBM may not offer the products, services, or features discussed in this document in other countries. Consult your local IBM representative for information on the products and services currently available in your area. Any reference to an IBM product, program, or service is not intended to state or imply that only that IBM product, program, or service may be used. Any functionally equivalent product, program, or service that does not infringe any IBM intellectual property right may be used instead. However, it is the user's responsibility to evaluate and verify the operation of any non-IBM product, program, or service. IBM may have patents or pending patent applications covering subject matter described in this document. The furnishing of this document does not give you any license to these patents. You can send license inquiries, in writing, to: IBM Director of Licensing, IBM Corporation, North Castle Drive Armonk, NY 10504-1785 U.S.A. The following paragraph does not apply to the United Kingdom or any other country where such provisions are inconsistent with local law: INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THIS PUBLICATION "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some states do not allow disclaimer of express or implied warranties in certain transactions, therefore, this statement may not apply to you. This information could include technical inaccuracies or typographical errors. Changes are periodically made to the information herein; these changes will be incorporated in new editions of the publication. IBM may make improvements and/or changes in the product(s) and/or the program(s) described in this publication at any time without notice. Any references in this information to non-IBM Web sites are provided for convenience only and do not in any manner serve as an endorsement of those Web sites. The materials at those Web sites are not part of the materials for this IBM product and use of those Web sites is at your own risk. IBM may use or distribute any of the information you supply in any way it believes appropriate without incurring any obligation to you. Information concerning non-IBM products was obtained from the suppliers of those products, their published announcements or other publicly available sources. IBM has not tested those products and cannot confirm the accuracy of performance, compatibility or any other claims related to non-IBM products. Questions on the capabilities of non-IBM products should be addressed to the suppliers of those products. This information contains examples of data and reports used in daily business operations. To illustrate them as completely as possible, the examples include the names of individuals, companies, brands, and products. All of these names are fictitious and any similarity to the names and addresses used by an actual business enterprise is entirely coincidental. COPYRIGHT LICENSE: This information contains sample application programs in source language, which illustrates programming techniques on various operating platforms. You may copy, modify, and distribute these sample programs in any form without payment to IBM, for the purposes of developing, using, marketing or distributing application programs conforming to the application programming interface for the operating platform for which the sample programs are written. These examples have not been thoroughly tested under all conditions. IBM, therefore, cannot guarantee or imply reliability, serviceability, or function of these programs. You may copy, modify, and distribute these sample programs in any form without payment to IBM for the purposes of developing, using, marketing, or distributing application programs conforming to IBM's application programming interfaces.
xvii
Trademarks
The following terms are trademarks of the International Business Machines Corporation in the United States, other countries, or both: Eserver Eserver Redbooks (logo) ibm.com z/OS ClearCase Cloudscape CrossWorlds CICS DB2 DPI Everyplace Illustra Informix IBM IMS Lotus Notes Lotus Notes Rational Redbooks Roma SecureWay Tivoli TME WebSphere Workplace Workplace Web Content Management
The following terms are trademarks of other companies: Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both. Microsoft, Windows, Windows NT, and the Windows logo are trademarks of Microsoft Corporation in the United States, other countries, or both. Intel, Intel logo, Intel Inside, Intel Inside logo, Intel Centrino, Intel Centrino logo, Celeron, Intel Xeon, Intel SpeedStep, Itanium, and Pentium are trademarks or registered trademarks of Intel Corporation or its subsidiaries in the United States and other countries. Linux is a trademark of Linus Torvalds in the United States, other countries, or both. Other company, product, or service names may be trademarks or service marks of others.
xviii
Preface
This IBM Redbook provides an overview and hands-on scenarios to help you design, develop and implement portlet applications using the Rational Application Developer V6.0 and the provided Portal Tools. The sample scenarios included in this redbook target Business-to-Employee (B2E) enterprise applications, but most of the scenarios presented will also apply to Business-to-Consumer (B2C) applications. You will find step-by-step examples and scenarios showing ways to integrate your enterprise applications into an IBM WebSphere Portal environment using the WebSphere Portal APIs provided by the Portal Tools to develop portlets. You will also learn how to extend your portlet capabilities to use other advanced functions such as cooperative portlets, internationalization, action events, using the Credential Vault to enable Single Sign-On, Web Services, remote portlets, portal design and portlet debugging capabilities. Elements of the Portlet API and the standard JSR168 API are described and sample code is provided. The scenarios included in this redbook can be used to learn about portlet programming and as a basis to develop your own portlet applications. You will also find numerous scenarios describing recommended ways to develop portlets and portlet applications that follow the MVC design pattern, the Struts framework and JavaServer Faces technology. A basic knowledge of Java technologies such as servlets, JavaBeans, EJBs, JavaServer Pages (JSPs), as well as of XML applications and the terminology used in Web publishing, is assumed.
xix
Cristiano Cesario is an Application IT Architect at IBM Global Services in Brazil. He has experience in WebSphere products and Portal solutions. He holds a Bachelor's degree in Computer Systems Enginnering from the Universidade do Estado do Rio de Janeiro (UERJ) in Brazil and a Post-Graduate Degree in Information Systems Project and Management from Universidade Federal do Rio de Janeiro (UFRJ) in Brazil. Karla Galvan is a WebSphere IT Specialist at IBM Software Group in Monterrey, Mexico. She is responsible for Application Integration and Middleware (AIM) Technical Sales in the northeast region of Mexico. She holds a Bachelors degree in Computer Science from Universidad Autnoma de Nuevo Len, Mexico. Her areas of expertise include WebSphere Portal, Workplace Web Content Manager and Business Intregration Solutions. Belen Gonzalez is an IT Specialist in IBM Global Services in IBM Spain. She has seven years of experience in the e-business field and three years of experience in WebSphere Portal products. She holds a degree in Computer Science Engineering from Universidad Autonoma de Madrid. Her areas of expertise include J2EE application development with WebSphere Studio and Rational Application Developer and e-commerce projects. George Kroner is a recent graduate of Penn State University, having pursued a Bachelor of Science degree in Information Sciences and Technology. He has several years of experience in portal development and design. His interests include Web applications, wireless mobile applications and integration, portlet applications, and innovative business strategy. Gianfranco Rutigliano holds a degree in Systems Engineering from the University of Lima (Peru) and has been a member of IBM Global Services. He has experience designing and implementing pervasive and Java-based Web solutions. He is now a Software Architect for Avatar e-Business Solutions, an IBM Business Partner, and has participated in several ITSO residencies for pervasive computing and WebSphere technologies. Ryan Wilson is a WebSphere Portal support IT Specialist in RTP, North Carolina. He was the technical team lead for WebSphere Studio site developer support before joining Portal. His areas of expertise include J2EE application development with IBM WebSphere Studio and Rational Application Developer. He has participated in many projects, including internal tools development.
xx
Thanks to the following people for their contributions to this project: Masato Noguchi IBM Software Group, Japan Marshall Lamb, Ryan E. Smith, Shannon Pixley IBM Research Triangle Park, North Carolina, USA Stefan Hepper, Oliver Koeth IBM Software Group, Germany Cecilia Bardy International Technical Support Organization, Raleigh Center
Preface
xxi
Comments welcome
Your comments are important to us! We want our Redbooks to be as helpful as possible. Send us your comments about this or other Redbooks in one of the following ways: Use the online Contact us review redbook form found at:
ibm.com/redbooks
Mail your comments to: IBM Corporation, International Technical Support Organization Dept. HZ8 Building 662 P.O. Box 12195 Research Triangle Park, NC 27709-2195
xxii
Chapter 1.
Overview
WebSphere Portal provides a flexible framework based on open standards with the capability to integrate with a best of breed solution. This chapter provides an overview of the WebSphere Portal technology, IBMs portal tooling, and its use in developing integrated portal applications. A high-level overview of the WebSphere Portal concepts integral to development is presented here. In this chapter, we explore: The evolution of portals Fundamental Portal concepts and definitions Portal development patterns
Chapter 1. Overview
IBM WebSphere Portal is a fourth generation portal providing organizations with a portal framework that connects a wide range of enterprise content and applications. It provides a high degree of integration technologies based on the J2EE platform. Its extensible architecture provides a scalable framework allowing adaptation to the changing needs of business.
1.2 Overview
The primary purpose of implementing an enterprise portal is to enable a working environment that integrates people, their work, personal activities and supporting processes and technology. Investment in portal technology will remain high amidst economic adjustments. The reason for the sustained growth is that enterprise portals deliver immediate tangible cost savings, enhance productivity, increase efficiency and generate revenue for the clients. Most companies have developed their Business to Consumer (B2C), Business to Business (B2B) and Business to Employee (B2E) strategies. Many times, the challenge is to tie them together via a comprehensive strategy that is extendable to employees, business partners and customers. Customers are often faced with issues of integration with legacy systems. Companies are often faced with the decision of whether to build or buy.
Portal solutions such as IBM WebSphere Portal are proven and shorten the development time. Pre-built adapters and connectors are available so that customers can leverage on the company's existing investment by integrating with the existing legacy systems without re-inventing the wheel.
Authentication/authorization
Authentication provides different mechanisms that can be used to validate the identity of all portal users. Authorization determines whether a user has the necessary privilege to request a service.
Directory services
The Lightweight Directory Access Protocol (LDAP) infrastructure provides a foundation for deploying comprehensive identity management and advanced software architectures such as Web services.
Content management
Content management provides a way for the company to manage and leverage the enterprises intellectual assets. Knowledge assets may include business intelligence and competitive intelligence data.
Collaboration
Collaboration enables employees, customers and business partners to work with, interact with, and develop or maintain content with others who share activities or interests.
Chapter 1. Overview
Search
The portal offers a search service that supports distributed, heterogeneous searches across different data sources. Search and indexing allows users to solve problems quickly, since users often need to make ad hoc queries to gather new information.
Personalization
Personalization provides the user the ability to establish preferences and profiles. In addition, value-added services for users increase the stickiness of the portal.
e-learning
A portal can provide just-in-time training and development of skills or expertise for work. It allows the individual to select the time and place of learning activities in their own time.
Internationalization
There is an increasing need for providing globalization. As business is getting more global, workplaces are decentralized, often with thousands of individuals working in shifting locations.
Pervasive computing
Portal provides access to applications and systems to mobile, remote users at any time and any place. It provides personalized delivery of integrated content through multiple channels: portal, wireless, kiosk, etc.
e-commerce
Most of the time, the return on investment (ROI) of implementing a portal may accrue through direct savings in self-service as well as reduced transaction costs. Integrating the portal with e-commerce applications can generate revenue and add tangible value that contributes to enterprise competitiveness.
Host integration
These capabilities provide a single point of entry to applications including legacy systems. This allows processes and data from multiple applications through a single workspace. Most of the time, companies have invested substantially in the legacy systems and the investment can be leveraged.
Site usage
Site analytics provide comprehensive Web site analytics to improve the overall effectiveness of Web initiatives and campaigns and to ensure a high quality, high-availability, error-free Web experience for visitors and customers.
Directory Services
Site Usage
Portal
Search Internationalization
IBM WebSphere Portal provides a single, secure, interactive point of access to dynamic applications, information, people and processes to help build successful
Chapter 1. Overview
Business to Business (B2B), Business to Employee (B2E) and Business to Consumer (B2C) portals. WebSphere Portal: Consists of pre-integrated software which is customizable, extensible and scalable Is built on the award-winning WebSphere Application Server 5.1 platform, using J2EE standards to optimize performance Provides integrated Web services so you can quickly deploy portlets Gives users a content publishing and personalization interface that lets them create and target portal content in one step Offers numerous portlets for e-mail, calendars, syndicated news, industry applications and many other functions Provides award-winning collaborative technology within the portal, in addition to making it available for portlets WebSphere Portal is a framework that lets you plug in new features or extensions called portlets. In the same way that a servlet is an application within a Web server, a portlet is an application within WebSphere Portal. Developing portlets is the most important task in providing a portal that functions as the users window to information and tasks. Portlets are an encapsulation of content and functionality. They are reusable components that combine Web-based content, application functionality and access to resources. Portlets are assembled into portal pages which, in turn, make up a portal implementation. Portlets are similar to Windows applications in that they present their contents in a window-like display on a portal page. Like a Windows application, the portlet window has a title bar which contains controls, allowing the users to expand (maximize) and shrink (minimize) the application. Portlets function within the Portal framework where Windows applications function in the Windows framework. From the portal users perspective, a portlet is a window on a portal site which provides access to a specific service or resource. The portal also provides the runtime environment for the portlets that make up the portal implementation. This runtime environment is the portlet container. WebSphere Portal supports two different portlet containers, one for running IBM portlets and the other for running JSR 168 portlets. The portlet container, in the J2EE sense of a container, is responsible for instantiating, invoking and destroying portlets. The portlet container provides the life cycle infrastructure for the portlets. Portlets rely on their container to provide the necessary infrastructure to support a portal environment. The portal
infrastructure provides the core sets of services required by the portlets, including: Access to user profile information A framework for portlets to participate in events A framework to communicate with other portlets Access to remote content Access to credentials A framework for storing persistent data. Note: The IBM portlet API and JSR 168 portlets will interact with portlet services in different ways, including the Credential Vault and the Cooperative portlet features. Please see the respective sections for more information.
Typical topology
Building on the Jetspeed implementation, WebSphere Portal provides an architecture for building and running portal applications. The overall WebSphere Portal Architecture can be seen in Figure 1-5 on page 14. WebSphere Portal provides services for authentication and authorization though the WebSphere Member Services. The core of WebSphere Portal architecture is composed of the Presentation Services, the portal infrastructure, and the portal services.
Chapter 1. Overview
Distributed solution
WebSphere Portal can run in a single, two, three, or n - tier environment. This, combined with the delegation capabilities from WebSphere Portal, provides you with a structured management in a distributed environment. WebSphere Portal can be part of an open, architected, and extensible end-to-end geographically distributed solution. The scalable solution incorporates redundancy with high availability design and is proven for a geographically distributed infrastructure. Additional optional components in the Portal Server architecture include a load balancer (WebSphere Edge Server - Network Dispatcher), integration to intrusion detection, and translation (WebSphere Translation Server) within the demilitarized zone (DMZ).
10
SSL support
IBM WebSphere Portal supports SSL. SSL support for secure transactions is one of the main reasons to use the IBM HTTP Server as part of your Web development process. The SSL encryption system is used on servers to ensure privacy when information is sent across the Internet. An SSL-enabled server enables clients to verify a server's identity, and ensures that information transmitted between client and server remains private.
Load balancing
In this particular example of integrating with WebSEAL, you can configure WebSphere Application Server to use the LDAP user registry, which can be shared with WebSEAL and TAM. Replicated front end WebSEAL provides the portal site with load balancing during periods of heavy traffics and failover capability. The load balancing mechanism is handled by a Network Dispatcher such as an IBM WebSphere Edge Server. If the Network Dispatcher fails for some reason, the standby Network Dispatcher will continue to provide access to the portal. In our sample configuration, HTTP Servers and Portal Servers are clustered to provide additional redundancy.
Directory services
The Directory and Security Services provide support for a directory of users accessible through LDAP. These services are used for authentication and can also control and verify the resource access privileges or permissions granted to users. The Directory Server can be replicated to one or more replica LDAP servers to provide redundancy. WebSphere Application Server uses LDAP to perform authentication. The client ID and password are passed from WebSphere Application Server to the LDAP server.
Chapter 1. Overview
11
Database service
The database server component is not accessed directly by portal users or administrators. No application-specific tables are created. Database Server is used by WebSphere Application Server, WebSphere Portal, TAM and Directory Server to store the data they need for their operation. Replication can be turned on in the database server which is used by the portal.
Intranet clients
In this configuration, it is optional to use a separate WebSEAL for the internal users for better performance.
Open standards
IBM WebSphere Portal is based on open standards. IBM is leading efforts to standardize the application programming interfaces between portals and other applications. In particular, the Java Community Process (JCP) and the Organization for the Advancement of Structured Information Standards (OASIS) are working cooperatively to standardize the Java and XML technology needed to link portals to disparate applications. IBM along with Sun and several other companies have created the JSR 168 Portlet specification. This new portlet API is designed to provide a standard for portlet development. OASIS created the Web Services for Remote Portals (WSRP) Technical Committee. Chaired by IBM, the WSRP committee has created an XML and Web services standard that allows the interoperability of visual, user-facing services with portals or other Web applications.
Syndicated content
IBM WebSphere Portal provides a framework for pre-built, real-time news and syndicated content portlets from third-party vendors such as Financial Times, Pinnacor, YellowBrix, Factiva (Dow Jones and Reuters Company), Moreover, CoreMedia, divine, FatWire, Autonomy, ScreamingMedia, X-Fetch, Atomica, Knowmadic and Quiver, just to name a few. The integration is pre-built and seamless. End users and administrators can easily subscribe to the portlets and customize the preference personally to enhance the user experience. Companies are embracing syndication concepts and standards to automate the publishing of electronic catalogs and other internal information, and to make this information available to workers through enterprise portals. A popular and useful format for syndicated news and entertainment content is Rich Site Summary (RSS). Content can be published directly from the content management system into Rich Site Summary and Open Content Syndication
12
(OCS) channels, where it can easily be displayed by the Portal Server's built-in RSS portlet. This self-syndication concept defines a procedure for editing, managing, and publishing your own sources of content.
Internet
Production DMZ
Browser
Cluster IBM Web HTTP Web Server Network Dispatcher WebSEAL/ TAM Lotus QuickPlace
Browser
Cluster Portal Portal Server Portal Server Server LDAP LDAP Replicate
DB DB
Lotus Sametime
WebSEAL/ TAM
Firewall
Building on the Jetspeed implementation, WebSphere Portal provides an architecture for building and running portal applications. WebSphere Portal V5.1 provides a modular, easily extensible architecture. It is designed as a product that can run stand-alone if required, but allows plugging in alternative implementations for those components that may already be set in customer environments. The main components of the WebSphere Portal V5 architecture are shown in Figure 1-5 on page 14. The core of WebSphere Portal architecture is composed of the presentation services, the portal infrastructure, and the portal services.
Chapter 1. Overview
13
WebSphere Personalization
Translation Server
WTP
WebSphere Workflow
Click-2-Action Tags
Collaboration & Awareness Portal Content Access Click-2-Action Portal Content Access Search Corporate UDDI Directory Local Corporate Local Portlets Web Portlets Services Local Corporate Local Portlets RPWS Portlets Services Local Public Local Portlets RPWS Portlets
Search Tags
Portlet Tags
Dynamic Assembly
Click-2-Action Filter
Transcoding Filter
Translation Filter
Credential Vault
Personalization Fragments
WP Database
Netegrity Siteminder
Collaboration
Workflow Fragments
SOAP
Portlet Services
WAS Authentication
Browser Cache
AXIS Servlet (SOAP) Web Sphere Portal Engine Portal Servlet (HTTP) Object Caching WebSphere Member Manager LDAP DB WebSphere Portal Datastore
Local Local Portlets App.Portlets WBI/Struts Portlets Local Local Portlet Portlets Portlets Proxies
Portlet Invoker
Portlet Filters
Services
Netegrity TAI
Servlet Filter
URL Mapper
TAM TAI
J2EE and WebSphere APIs Workflow Engine (Dragonfly) Intelligent Notification (e.g. SMS) Personalization Rules
Services
VoiceXML
Java Mail
CHTML
HTML WML
JMS
WTP
DB
Presentation services
WebSphere Portal presentation services provide customized and personalized pages for users through aggregation. Page content is aggregated from a variety of sources via content and applications. The portal presentation framework simplifies the development and maintenance of the portal by defining the page structure independent the portlet definition. Portlets can be changed without impact to the overall portal page structure.
14
Portal Engine
Portal Servlet
Aggregation Modules
Portlets
Services
Access Control
The Authentication Server is a third-party authentication proxy server that sits in front of the Portal engine. Access to portlets is controlled by checking access rights during page aggregation, page customization, and other access points. The Portal Servlet is the main component of the Portal engine. The Portal Servlet handles the requests made to the portal. The portal requests are handled in two phases. The action phase is used for processing actions. This is also the phase where messaging is processed in the IBM portlet API. The other is the redner phase. In the render phase, the appropriate Aggregation Module for the requesting device renders the overall portal page by collecting information from all the portlets on the page and adding standard decorations such as title bars, edit buttons, etc.
Portlet container
Portal Services are components WebSphere Portal uses to extend the portal functionality. Key functionality is provided with WebSphere Portal for personalization, search, content management, site analysis, enterprise application integration collaboration and Web services. Portlets can access these services via their container or JNDI in the case of JSR 168 portlets.
Portal infrastructure
The WebSphere Portal infrastructure is the framework that provides the internal features of the portal. Functionality such as user and group management via self registration, as well as portal administration, are provided by the Portal infrastructure.
Chapter 1. Overview
15
Security services
Since WebSphere Portal runs within the WebSphere Application Server platform, it makes use of the standard Java Security APIs to provide authentication. The WebSphere Portal is configured so that incoming requests pass through an authentication component such as WebSphere Application Server, WebSEAL or other proxy servers. A users authorization for a particular resource such as page or a portlet is handled by the portal engine. User beans are provided to allow programmatic access to the User information for use within IBM portlets. JSR 168 portlets can take advantage of J2EE security methods or they can use the user attributes stated in the specification.
Page transformation
WebSphere Transcoding Technology is integrated with WebSphere Portal to transform the portal markup produced by WebSphere Portal to markup for additional devices such as mobile phones and PDAs.
Portal services
Portal services are built-in features the WebSphere Portal provides to extend and enhance the full portal solution. These services are provided via the Portlet container as seen in Figure 1-5 on page 14. Among the services are the following: Personalization The IBM WebSphere Personalization functionality enables advanced personalization capabilities. Base customization, such as choosing which portlets are desired on a page, is accomplished by the user via administration functionality. Advanced personalization via rules engines, user preferences and profiles is accomplished by the provided personalization services. Content management WebSphere Portal provides services to facilitate connections to content management sources. Built-in support is provided for several common content types such a as Rich Site Summary (RSS), News Markup Language (NewsML) and Open Content Syndication (OCS) along with most XML and Web browser markup. Search WebSphere Portal offers a simple search service. The Portal Search capability enables search across distributed HTML and text data sources.
16
The search can crawl a Web site and is configured so as to force it to follow several layers in a site or to extend beyond several links in a site. Furthermore, IBM Extended Search and Enterprise Information Portal can be fully incorporated into the portal environment. These search engines are industrial-strength tools that provide federated searches across numerous data sources. Site analysis You can take advantage of the underlying WebSphere Application Server technology and Site Analyzer to provide information about Web site visitor trends, usage and content. This detailed information can then be used to improve the overall effectiveness of the site. Collaboration Collaboration services are provided by WebSphere Portal through a set of pre-defined portlets. These portlets allow for team-room function, chat, e-mail, calendering and many other collaborative technologies. Web Services WebSphere Portal provides services for exposing and integrating portlets as remote portlets hosted on another portal platform via Web Services technology. The entire process of packaging and responding to a SOAP request is hidden from the developer and the administrator. WebSphere Portal V5.1 provides a set of portlets for providing and consuming remote portlets.
WebSphere Portal
WebSphere Portal contains built-in support for portlet deployment, configuration, administration and communication between portlets. WebSphere Portal provides the framework for building and deploying portals and the portal components, portlets. Portlet content is aggregated by the WebSphere Portal to provide the desired portal implementation. WebSphere Portal makes use of the WebSphere Application Server technology to provide a portal platform.
Chapter 1. Overview
17
Portlet
A portlet is an application that displays page content.
Portlet application
Portlet applications are collections of related portlets and resources that are packaged together. All portlets packaged together share the same context which contains all resources such as images, properties files and classes.
Page
A portal page displays content. A page can contain one or more portlets. For example, a World Market page might contain two portlets that displays stock tickers for popular stock exchanges and a third portlet that displays the current exchange rates for world currencies. To view a page in the portal, you select its page.
Layout
The page layout defines the number of content areas within the page and the portlets displayed within each content area. In many cases, the portal administrator defines the page layout. The administrator can permit specified users or user groups to change the page layout to reflect individual preferences.
18
If you have authority to change a page, use the configure icon (wrench icon) to alter the page layout.
Roles
Each portal page is subdivided into one or more content areas. Each content area can contain one or more portlets. The portal administrator or a user who has authority to manage a page can control whether others who have authority to edit the page can move, edit or delete the content areas and the portlets on the page. Portal V5.1 permission is role-based. A role is a set of permissions. Roles can be assigned (or mapped) to individual principals granting those principals the corresponding permissions. If you have authority to make changes to a portal page, use the Resource Permissions page in Access under Administration to set the permissions for the page. By default, there are seven roles: Administrators are allowed to have unrestricted access on all portal resources Security Administrators are allowed to grant access on a resource Delegators are allowed to grant access to other principals Managers are allowed to create, edit, and delete shared resources Editors are allowed to create and edit shared resources Privileged Users are allowed to create private resources Users are allowed to view portal resources
Chapter 1. Overview
19
V5.0 Roles Manager Security Administrator + Privileged User (Migration option: Security Administrator + Editor) Administrator Manager Security Administrator + Privileged User (Migration option: Security Administrator + Editor) Administrator Administrator No longer necessary. In WebSphere Portal V5.0, principals with the Administrator, Manager, Editor, or Privileged User roles on a resource are automatically allowed to create new resources underneath that resource in the resource hierarchy.
Themes
Themes represent the overall look and feel of the portal, including colors, images and fonts. There are several default themes provided with the standard installation of WebSphere Portal. Each page in the portal may have a different theme associated with it, thereby creating the appearance of virtual portals. Use the Themes and Skins under Portal User Interface to manage themes.
Skins
The term skin refers to the visual appearance of the area surrounding an individual portlet. Each portlet can have its own skin. The skins that are available for use with a portlet are defined by the portal theme that is associated with the page. The portal administrator or the designer determines the theme for pages and the available skins for the theme. The administrator can permit specified users to change the skins to reflect individual preferences. If you have authority to make changes to a portal page, use the Themes and Skins under Portal User Interface to manage themes.
20
1.3.2 Portlets
The base building blocks of a portal are the portlets. Portlets are complete applications following the Model-View-Controller design pattern. Portlets are developed, deployed, managed and displayed independent of all other portlets. Portlets may have multiple states and modes along with event. Based on the J2EE container model, portlets run inside the Portlet Container of WebSphere Portal analogous to the way servlets run inside the Servlet Container of WebSphere Application Server. To understand the portlet model used by WebSphere Portal, let us take a step back and examine the Flyweight pattern. This pattern is used by WebSphere Portal as the design pattern for the portlet model.
Extrinsic state
Chapter 1. Overview
21
This separation into extrinsic and intrinsic information allows great numbers of similar objects to exist, differing only in the context in which they exist. The different components involved in the Flyweight Pattern are the Flyweight, the ConcreteFlyweight, the UnsharedConcreteFlyweight, the FlyweightFactory and the Client. The flyweight: the shared object with intrinsic state. The flyweight declares an interface through which flyweights can receive and act on intrinsic data. ConcreteFlyweight: implements the flyweight interface and adds storage for the intrinsic state. UnsharedConcreteFlyweight: the flyweight interface enables sharing but does not enforce it. Not all flyweights are shared. It is common for UnsharedConcreteFlyweight objects to have ConcreteFlyweight objects as children at some level in the hierarchy. FlyweightFactory: serves to dispense particular flyweights that are requested. When a Flyweight with certain properties is requested, it checks to see if one already exists, and if so, returns that flyweight. If the requested flyweight does not exist, it creates the requisite flyweight, stores and returns it. Client: when creating an object, a client must assign a flyweight to it, so it asks the FlyweightFactory for a particular flyweight, receives that flyweight, and creates a reference to it in the object it is creating. The parameterization of portlets is based on the flyweight pattern, the Portlet Container being the Flyweight Factory.
Portlets
Portlets are invoked by the portlet container. A portlet is a Web application that runs within a portlet container. Portlets receive and respond to requests from the portlet container. There is only ever one portlet object instance per portlet configuration in the deployment descriptor. IBM portlets use the Web deployment descriptor while JSR portlets are defined in the portlet deployment descriptor.
State
This is the current state of the portlet window. Valid states in both portlet APIs are Normal, Minimized and Maximized. IBM portlet API will also support SOLO while JSR 168 does not.
22
Mode
This defines the current condition of the portlet. The modes that are available for any particular user depend on the permissions for that user, the device used to access the portlet and the configuration and implementation of the portlet. IBM portlet API supports View, Edit, Config and Help modes, while JSR 168 portlets supported View, Edit and Help modes, as well as custom modes. The Config mode for JSR 168 portlets is supported as a custom mode. Note: All portlets must support the default mode, View.
Control
Servlet Bean
Client
JSP
Model
View
Chapter 1. Overview
23
This section is technology-independent, but will use the servlet technology to demonstrate the application of MVC.
Model
This component is responsible for encapsulating all the business logic required by the system. It must be independent of the other components in the system. To achieve this objective, it must be able to retrieve the data required to complete the business rules data by itself or accept very generic receive parameters. Furthermore, it must be able to return the results in a generic form that any potential View component could use. In a typical servlet environment, the Model is represented by one or more Java beans, EJBs or other Java classes.
View
This component is responsible for creating a presentation resource for the results of the Model component. Like all MVC components, the View must be independent of the other components in the system. Its success or failure must not depend on the success or failure of the Model component. In practice, several different View components may be developed in order to create a dynamic, complete and possibly multi-purpose application. In a typical servlet environment, the View is created using Java Server Pages.
Control
At the heart of the MVC architecture is the Controller component. Each client request of the system is routed through the Controller class. Its responsibility is threefold. First, it should evaluate the validity of the request, including the users state and any parameter information passed as part of the request. The Controller then decides which Model component has the requisite functionality to satisfy the business requirements of the request. Once the Model component has completed its work, the Controller is responsible for deciding on the appropriate View component to present the results back to the client. If either one of the Model or View components fails, the Controller is responsible for either attempting to satisfy the request in another fashion or deciding on an appropriate View component capable of presenting an error message. In a typical servlet environment, the servlet itself plays the role of the Controller.
24
Model
The Model in a portlet application is not necessarily different from the Model in any other Java server side application.The Model represents business logic and should not be concerned with the Controller or the View. The Controller could be a servlet, portlet, or a simple Java class. The View could be a JSP or even simple HTML. In theory, then, provided that existing applications employ solid MVC practices, porting the functionality WebSphere Portal should not require any changes to the logic. However, in practice, there are always applications that lack this foresight. The rich API covered later in this chapter will arm you with the tools to tackle this situation. Implementing a rigid commitment to the MVC architecture now will conserve an enormous amount of effort in later migrations or maintenance duties.
View
Like the servlet MVC implementation, the View is traditionally implemented using JSPs or simple HTML. However, because the HTML the View returns will be aggregated, it must not contain page-level tags and must be very mindful of the environment in which it is executing. Furthermore, the Portlet API provides tag libraries which aid in creating dynamic view resources for the portlet environment.
Control
The Controller is responsible for determining the requested mode, executing an appropriate Model and selecting the correct View. The portlet class itself acts as the Controller. Instead of determining the request method as in servlets, portlets need to determine the mode the user has requested. In a normal presentation, where a page is built with several portlets on it, the mode is View. The user, with appropriate permissions, may click the Edit button in order to perform some edit functionality. In this case, the mode is Edit.
Chapter 1. Overview
25
q Re
ue
st
WebSphere Portal
Portlet Bean
Client
Re
JSP
sp
on se
Aggregated JSP
26
Page aggregation is the process that collects information about the users choices, the device being used and the selected portlets, then takes that information and combines it to create a display that is appropriate for the device. The aggregation process involves three basic steps: Collecting user information Selecting the active applications Aggregating the output Once the active page is determined, the layout of this page is used to aggregate the content of the defined applications, arrange the output and integrate everything into a complete page. Basic Portal Page Layout can be seen in Figure 1-9 on page 28. Rendering of page components is done using JSPs, images, style sheets, and other resources. These resources are located in the file system in a path-naming convention that the portal server uses to locate the correct resources for the client. WebSphere Portal provides dynamic aggregation of pages from page descriptors held in the portal database.
Markup
Markup version
Language
Chapter 1. Overview
27
If no browser language is available, the portal UI is displayed in the default language set for the portal. Portlets not supporting any of the above scenarios display their UI in the portlets default language. Page Theme Screen The access control list determines which pages and labels a user has access to. The name of the active theme is taken from the currently active page. Depending on the interactions of the user with the portal, different screens are presented. The screen holds the output of the portlets on a page.
28
The content inside the displayed window. It is made up of the banner and the portal page. The top area of the window that holds the company information, the greeting, a page selection box, tabs to select the current page in the page group being displayed and some additional controls for interacting with the portal such as logging in, logging out and help. Hold the output of the portlets on the currently selected page. The layout is determined by its row and column containers. Node is a level of hierarchy in the portal. Nodes include pages, labels, or URLs, and are used to navigate the portal structure. The portal has a tree structure that is used to organize the portal into branch nodes, which belong to other nodes that are higher in the tree. The single highest node in the portal is called the content root. Nodes are represented and accessed from the portal navigation menu. Page is a type of node that provides portal content, similar to a page on any Web site. However, portal pages display content in the form of portlets, which are arranged on the page by row and column containers. Each page displays content that has been customized for the portal user. Label is a type of node that has a name and is used to hold other nodes. URL is a type of node that can open locations within the portal or external Web sites. A container is an area on a page that contains content. A container can be structured as a row, column, or cell in a table. That is, when you are arranging content on the page, the content can be placed in a container that spans the width of the page (row) or the height of a page (column). A container inside a page that allows portlets to be arranged in a horizontal format. A container inside a page that allows portlets to be arranged in a vertical format. The frame around the portlet is constructed by the frame. It builds the bar above the portlet output including buttons to control the state and view of the portlet.
Screen
Node
Page
Chapter 1. Overview
29
Portlet
A portlet is a type of application that can be accessed through a small box or window in a portal page. Portlets provide access to specific services or information, for example, a calendar or news feed.
Templates
Aggregation uses the concept of templates to perform window, screen and component layout. When a corresponding part needs to be rendered, a template loader will load the requested template. If the requested template cannot be found, the default template will be used. A template consists of the template class that controls the rendering, the localization and the launch of the template JSP. The template JSP performs the actual rendering. There are three types of templates: Window templates The Window template is responsible for the layout of the parts of the banner area and the placement of the screen. You can change, for example, the navigation tab location via the window template. Screen templates The Screen template is responsible for the layout and the content of the screen, the portion of the portal page containing the output of the portlets. Component templates Component templates are responsible for rendering the component itself and for starting the rendering of its children components. The children of container components (row and column) may be other containers or controls. The child of a control will always be a single portlet.
30
The Root Container holds all the containers and controls for this page. The pageRender tag tells the Root Container to invoke the rendering of all its children. Since the Root Container is used only as a starting point for the aggregation, it does not render itself and therefore is not visible on the screen. Each child of the Root Container invokes its template which is responsible for rendering all the content of its child. If the child contains further child components the componentLoop and componentRender tags execute the rendering of all these components one at a time. Each component invokes its own template which in turn invokes more components until the leaf of each branch is reached. Thus, control moves between component classes and their respective JSPs. Each component writes its content to the servlet output stream. When a control is reached, it invokes the rendering of the portlet, which adds its output to the output stream via its rendering. When the entire tree has been traversed, the output stream is closed and the output is sent back to the requesting client.
Portlet
Portlet
Root
Portlet Portlet
Portlet Rendering
RowContainer.jsp
ColumnContainer.jsp
Control.jsp
Chapter 1. Overview
31
32
EventBroker Event
EventRegistry EventhandlerZ
1.4.5 Authentication
Authentication in this version consists of the following elements.
J2EE Security
The authentication function in WebSphere Portal Server 5.x uses the J2EE Security calls to authenticate users instead of the SSO Authenticator calls that had been used in WebSphere Portal 4.x.
1.4.6 Authorization
Authorization in this version consists of the following elements.
Chapter 1. Overview
33
34
URL mappings
To translate user-friendly URLs (which in general have an arbitrary structure and do not contain GUIDS or unique names that can be understood by the portal's URL processing) into canonical portal URLs (which contain the correct GUIDs or unique names for portal resources), URL mappings are required. WebSphere Portal allows administrators to define URL mappings for the parts of URL spaces for which they have the appropriate access rights in two ways: They select a node in the URL spaces and may map it to a URL addressable portal resources they start by selecting a URL addressable portal resource and then selecting the node(s) in the user-friendly URL space which should map to the resource. Administrators may only define URL mappings for those friendly URL contexts for which they have been granted the appropriate access rights.
1.4.8 Search
WebSphere Portal 5.1 introduces major improvements in its search functionality, adding additional content sources and a new Search Center Portlet. New in WebSphere Portal 5.1: You can make intranet sites available for searches. WebSphere Portal site are now also available for searches. Only portlets for which you have access to will be indexed. Furthermore only the main panels of portlets are indexed.
Chapter 1. Overview
35
Lotus Workplace Web Content Management is now available for indexing as well. You can configure the use of external search engine such as Google. The new Search Center portlet provides many new useful features. See the WebSphere Portal InfoCenter for more details:
http://publib.boulder.ibm.com/infocenter/wp51help/topic/com.ibm.wp.ent.doc/wps/ admsrch.html
36
documents or attach PDM documents to e-mail. ODC document conversion services are used when needed to change PDM document formats. Versioning: The user can create new versions of documents. The user can view and retrieve document versions. PDM provides built-in versioning support but can be configured to support CVS, IBM CM, and ClearCase.
1.4.10 Transcoding
Transcoding technology was incorporated into WebSphere Portal 4.1. As transcoding technology serves different market through various IBM offerings, including WebSphere Portal, a number of markup language transformations were not enabled in WebSphere Portal. Starting with WebSphere Portal V5, plug-ins for WML and cHTML markup transformation are enabled. WebSphere Transcoding Publisher will be bundled as part of the portal server core install package. This will alleviate the need to have a separate installation for WebSphere Transcoding Publisher and Portal. The aim is to simplify the installation process and reduce the chance of an error during installation of portal and later during migration. In an effort to do this, now transcoding is installed inside the portal directory; this includes moving transcoding classes to the shared app directory. Configuration steps are also simplified by pre-configuring portal property files with transcoding information.
Chapter 1. Overview
37
For WebSphere Portal 4.2, the Struts Portlet Framework was updated to include the Struts 1.1 Beta 3 release and support for Tiles and File upload was added. The Struts Portlet Framework in the WebSphere Portal 4.2 implementation is closely tied to Portal Core API, so changes there will effect the Struts Portlet framework and require changes to function in the WebSphere Portal version 5 product. There are also WebSphere Application Server V5 dependencies that need to be addressed. The new functions that end users will see again are supported for newer releases of Struts. WebSphere portal version 5.1 provide a Struts portlet framework to be used with JSR 168 portlets.
Information supply
The containment model provides the information needed to perform tasks such as content aggregation or building navigation to browse the aggregated content. The information supplied can be dependent on a specific user; it would be a user-specific view on the containment model.
Administration
The information in the containment model must be changeable, of course. This can only be done via the Command API. The commands can manipulate information about the content, navigation, derivation and any other information
38
stored in the containment model. Elements can be managed via PAC to enable the permission concept of the portal, this means each element of the containment model is a resource which can be protected in regards to what action can be performed on it for a specific user. The administration of the containment model allows you to: Add and delete root nodes Add, move, reorder and delete child nodes of a node Modify a node, including: Changing associated information Implicitly or explicitly deriving a content node (a page)
Chapter 1. Overview
39
existing rules, it invokes getMatchRules on the PropertyBrokerServiceInternal interface. This allows the user to pairwise choose compatible properties on two portlet instances and wire them up, or to specify that type-based matching be used for a specified property on a portlet. After the user has created the matching rules using this tool, the tool will invoke setMatchRules on the PropertyBrokerServiceInternal interface. The property broker service will store the rules persistently and cause the property match broker to update its in-memory data structures to add the new rules.
40
Chapter 1. Overview
41
JCA also defines a standard Service Provider Interface (SPI) for integrating the transaction, security and connection management facilities of an application server with those of a transactional resource manager. Thus, JCA is a standards-based approach to managing connections, transactions, and secure access to enterprise application systems. IBMs JCA connectors provide access to systems such as SAP, People Soft, CICS, and IMS. Leveraging its CrossWorlds acquisition, IBM will also develop and integrate JCA connectors to many other systems. Another way to look at portlets for application integration is from a topology point of view.
Hand-held computer
Workstations
Servers
Workstation
Server
Mainframe
42
Hand-held computer Application server Web application Portal server Workstations Internet/Intranet HTTP server Portlet 1 Portlet 2 Web application
Servers
Workstation
Server
Mainframe
Hand-held computer Application server Web application Portal server Workstations Internet/Intranet HTTP server Portlet 1 Portlet 2 Web application
Servers
Workstation
Server
Mainframe
Chapter 1. Overview
43
The source folder is optional and you may choose what source to include for distribution. The webApplication folder is optional and may be used to contain the WEB-INF folder. Alternatively, the WEB-INF folder can be placed directly under the root without modification. Generally, it will also contain a JSP folder to hold all JSPs used throughout the entire portlet application. The JSP folder will organize the contained JSPs in folders representing the markup and languages they are intended to support. For example, if a portlet supported HTML, WML and cHTML and provided limited National Language support for HTML requests, the JSP folder would be organized as in Figure 1-16.
Figure 1-16 WAR structure for a portlet with NLS and multi-device support
44
The WEB-INF folder must contain at a minimum the two required deployment descriptors. The web.xml and portlet.xml must be placed directly under the WEB-INF folder. The classes that make up the portlet application must be stored in one of two locations. Those classes that have been packaged into jar files should be stored in the lib directory. Classes that are not packaged in a jar file are stored in the classes folder with the complete package structure. Both approaches are illustrated in Figure 1-17.
Once the contents have been organized correctly, you can use the jar command line utility to create the war file. There is no compression requirement for the war file so you may choose to compress the file or not without affecting deployment. For a complete discussion regarding the jar utility, refer to:
http://java.sun.com/docs/books/tutorial/jar/basics/index.html
Chapter 1. Overview
45
46
Chapter 2.
47
Portal page
A portal page is a single Web page that can be used to display content aggregated from multiple sources. The content that appears on a portal page is displayed by an arrangement of one or more portlets. For example, a World Stock Market portal page might contain two portlets that display stock tickers for popular stock exchanges and a third portlet that displays the current exchange rates for world currencies.
48
Portlet
A portlet is an individual application that displays content on a portal page. To a user, a portlet is a single window or panel on the portal page that provides information or Web application functionality. To a developer, portlets are Java-based pluggable modules that can access content from a source such as another Web site, an XML feed, or a database, and display this content to the user as part of the portal page.
Portlet
Portlet
Portlet
Portlet
Portal page
Figure 2-1 Portal page and portlets
Portlet application
Portlet applications are collections of related portlets and resources that are packaged together. Portlets within the same portlet application can exchange and share data and act as a unit. All portlets packaged together share the same context which contains all resources such as images, properties files and classes.
49
Portlet states
Portlet states determine how individual portlets look when a user accesses them on the portal page. These states are very similar to minimize, restore, and maximize window states of applications run on any popular operating system just in a Web-based environment. The state of the portlet is stored in the PortletWindow.State object and can be queried for changing the way a portlet looks or behaves based on its current state. The IBM portlet API defines three possible states for a portlet: Normal: The portlet is displayed in its initial state as defined when it was installed. Minimized: Only the portlet title bar is visible on the portal page. Maximized: The portlet fills the entire body of the portal page hiding all other portlets.
Portlet modes
Portlet modes allow the portlet to display a different face depending on how it is being used. This allows different content to be displayed within the same portlet depending on its mode. Modes are most commonly used to allow users and administrators to configure portlets or to offer help to the users. There are four modes in the IBM Portlet API: View: Initial face of the portlet when created. The portlet normally functions in this mode. Edit: This mode allows the user to configure the portlet for their personal use (for example, specifying a city for a localized weather forecast). Help: If the portlet supports the Help mode, this mode displays a help page to the user. Configure: If provided, this mode displays a face that allows the portal administrator to configure the portlet for a group of users or a single user.
Portlet events
Some portlets only display static content in independent windows. To allow users to interact with portlets and to allow portlets to interact with each other, portlet events are used. Portlet events contain information to which a portlet might need to respond. For example, when a user clicks a link or button, this generates an action event. To receive notification of a given event, the portlet must also have the appropriate event listener implemented within the portlet class. There are three commonly used types of portlet events: Action events: Generated when an HTTP request is received by the portlet that is associated with an action, such as when a user clicks a link.
50
Message events: Generated when one portlet within a portlet application sends a message to another portlet. Window events: Generated when the user changes the state of the portlet window.
51
Portal Tools
Unlike WebSphere Studio Application Developer where the tools where installed as a separate toolkit, Portal Tools can now be installed as a feature when installing IBM Rational Application Developer V6.0. For this reason, there is no longer a separate Portal Toolkit or separate installation procedure.
52
CDs to install the WebSphere Portal V5.1 Test Environment are also included with the IBM Rational Application Developer V6.0 distribution. To install the WebSphere Portal V5.1 Test Environment, you must run the WebSphere Portal V5.1 setup program and select Test Environment as the setup type as seen in Figure 2-4 on page 54. Follow the instructions in the InfoCenter to configure this test environment so that it works from within Rational Application Developer. The WebSphere Portal V5.0.2.2 and V5.1 Test Environments can co-exist within the same product installation.
53
54
The Tutorials Gallery provides detailed tutorials to illustrate portlet development. These are accessible by selecting Help Tutorials Gallery. Then expand Do and Learn. You can select Create a portal application or Examine the differences between portlet APIs to view the content of these tutorials.
JSR 168 is a Java specification from the Java Community Process Program that addresses the requirements of content aggregation, personalization, presentation, and security for portlets running in a portal environment. It was finalized in October of 2003. Portlets conforming to JSR 168 are more portable and reusable because they can be deployed to any JSR 168 compliant portal. Rational Application Developer supports Faces portlet development based on the JSR 168 specification. Unlike the IBM portlet API, the JSR 168 API does not extend the servlet API. It does, however, share many of the same characteristics such as servlet specification, session management, and request dispatching. The JSR 168 API as implemented in WebSphere Portal V5.0.2.2 does not support Click-to-Action cooperative portlets or portlet messaging. However, in WebSphere Portal V5.1, the JSR 168 container has been enhanced with Property Broker support which can act as a messaging broker for either portlet messaging or wired (automatic cooperating) portlets. At the time of writing, the support for Click-to-Action (user-initiated cooperating portlets) was still under development. For new portlets, consider using JSR 168 when the functionality it provides is sufficient for the portlets needs or when the portlet is expected to be published as a Web Service for Remote Portlets (WSRP) service.
55
IBM will further support JSR 168 in follow-on versions to make the JSR 168 portlet API as robust as the current IBM counterpart and offer tooling to support JSR 168 development. IBM is committed to wider adoption of open standards in WebSphere Portal. More information can be found on JSR 168 at:
http://www.jcp.org/en/jsr/detail?id=168
56
<rad_home>\runtimes\portal_v50\shared\app\config\services The services.properties file is located by default in the following directory: <rad_home>\runtimes\portal_v50\shared\app\config
Struts
Struts-based application development can be applied to portlets, similar to the way that Struts development is implemented in Web applications. The Struts Portal Framework (SPF) was developed to merge these two technologies. SPF support in Rational Application Developer simplifies the process of writing Struts portlet applications and eliminates the need to manage many of the underlying requirements of portlet applications. In addition, multiple wizards are present to help you create Struts portlet-related artifacts. These are the same wizards used in Struts development. These wizards include: Action Class, Action Mapping, ActionForm, Form-Bean Mapping, Struts Configuration, Struts Module, Struts Exception, and Web Diagram. Refer to the Rational Application Developer Struts documentation for usage details. In WebSphere Portal V5.0.2.2, Struts is only supported using the IBM portlet API. Struts is fully supported in both the IBM and JSR 168 APIs in WebSphere Portal V5.1; however, there is no tooling support in Rational Application Developer for this configuration. More information about Struts can be found at:
http://struts.apache.org/
57
should be limited in a production environment. File upload and binary download are not supported for Faces components. Finally, document root-relative URLs are not supported for images. Refer to the Rational Application Developer Faces documentation in the InfoCenter for usage details. Alternatively you can refer to the following Web site:
http://www.jcp.org/en/jsr/detail?id=127
Beginning development
After making these decisions, you can now begin development using the Portal Tools included with Rational Application Developer.
58
You can access the Portal Import Wizard by selecting File Import, then selecting Portal. You will need to specify the server and options for importing the project into Rational Application Developer. Follow the instructions in the Help Topics on Developing Portal Applications to ensure that the configuration in the development environment accurately reflects that of the staging or runtime environment. If you do not do this, you may experience compilation errors after the product is imported or unexpected portal behaviors.
59
Portal Designer
Rational Application Developer allows for editing both the graphic design and portlet layout within your portal. Portal Designer is the workbench interface that you see upon opening a portal project. When using Portal Designer, the portal page layout can be altered. The layout refers to the number of content areas within a page and the number of portlets within those content areas. Page content includes rows, columns, URLs and portlets. Once the project is published to the Portal server, Portal administrators can use the administration portlets to give site users permission to edit the page layout. In terms of portal layout and appearance, you can think of Portal Designer as a What-You-See-Is-What-You-Get (WYSIWYG) editor. It will render graphic interface items such as themes, skins, and page layouts. Portal Designer will also render the initial pages of JSF and Struts portlets within your portal pages, but not anything else with regard to portlet content.
60
Portal Designer provides the capability of altering the layout of the content within a portal page with respect to navigation (the hierarchy of your labels, pages, and URLs) and content (the arrangement of portlets via rows and columns on the portal pages). Portal Configuration is the name of the layout source file that resides in the root of the portal project folder (see Figure 2-6). To open Portal Designer, double-click the file in the Project Explorer.
61
portal administration. In addition to these wizards for creating new skins and themes, there are tools that can be used to change or edit these. Once created, skins and themes will be displayed in the Project Explorer view. Double-click a skin or theme to manually edit it.
62
customized themes for your project. Right-click the portal project in the Project Explorer view and select New Theme (see Figure 2-8).
63
To export, select File Export, then select Portal Project Deploy Set. You will need to specify the Portal EAR file and specify a target Portal server. The wizard examines the Portal server so that it can generate specific deploy information. It then generates a set of files for manually deploying the portal project to the Portal server. These files include: WPS.ear XmlAccess for deployment portal configuration (contained in the EAR) Readme file with instructions for deploying to a server WAR files for each portlet project used in the portal project XMLAccess script for deploying portlets
The wizard also created a file named DeployInstructions.txt, which is a set of instructions that will guide you through the process of manually deploying your exported project to the server. Note: Do not attempt to manually deploy the exported files to a portal server other than the one you specify. The export operation contains information from this portal server, and it will not work with other servers. Deploy: This method automatically publishes a configuration from a portal project to a Portal Server. The deploy method is recommended for publishing to a test, integration or staging server. If you are also transferring the theme and skin files during the deployment, you must also have FTP or copy access to the portal server. To deploy a portal, right-click the portal project and select Deploy Portal. From here, a wizard will guide you through the deployment process. This will include specifying the portal server to where you are deploying. Once you start the deploy process, do not interrupt it. Errors in a project or an unfinished deploy may cause a portal server to become inoperable. As such, you should not deploy directly to a production server. Before running deploy, it is recommended that you back up or image your server. Note: If a transfer interruption (for example, network failure) occurs during deployment, there is a slight chance that the portal server will become inoperable. Since the portal project does not have any access control information, use administration portlets in the published Portal site to set appropriate access control.
64
65
4. In the To zip file field, enter the full path, including the ZIP file name, where you want to export the selected projects. 5. Click Finish.
66
4. To import a WAR file into an existing Web project, select the project from the Web project drop-down list. If this method is used, the overwrite existing resources without warning option can be selected. 5. Click Finish to populate the Web project. Note: When a portlet project is exported to a WAR file, the source files must be included. This procedure is detailed in Exporting WAR files on page 82.
67
1. Select File New Project. 2. Select Portlet Project and click Next. 3. On the first screen in the wizard, enter a project name. You can also specify an alternate project location by clicking the Browse button. 4. If you do not want to create the initial portlet definitions in the project, clear the Create a portlet checkbox. Typically, a portlet does not need to be created when importing a WAR file. 5. Click the Show Advanced button to see more options (see Figure 2-10 on page 69): The advanced options allow changes to be made to the projects J2EE settings and runtime server environment. The Servlet version specifies the version of Servlet and JSP specifications to be included in your portlet. Tip: Use the 2.2 Servlet version if importing a WebSphere Portal V4.x project WAR file. Note that features such as Servlet filters and life cycle event listeners are not supported if this level is chosen. Choosing a Servlet version also determines the choice of target servers that appear in the drop-down list. When choosing a server, do not accidentally select any WebSphere Application Server options. a. Deselect the Add module to an EAR project option only if you do not intent to deploy the portlet. Name an EAR project according to the name of the enterprise application project (EAR) that the portlet project should be associated with during deployment. All portlet applications associated with a single EAR project will run on a single session on a WebSphere Portal Server. You should use the same EAR project for portlet projects that are related. The context root is used as the top-level directory in the portlet project when the portlet is deployed. It must not be the same as ones used by other projects. b. Ensure that the Add support for annotated Java classes checkbox is selected if using model annotations to generate code in portlet projects. c. Click Next to continue with the Portlet Project wizard or Finish to generate a portlet project based on the defaults associated with a Basic IBM API portlet project.
68
d. On the following screen, select a portlet type that is appropriate for the portlet project. There are four types of portlets (see Figure 2-11 on page 70): Empty portlet: Creates a portlet application that extends the PortletAdapter class with minimum code included. This option is selected if importing a project from a WAR file or when customizing empty portlet projects from scratch. Basic portlet: Creates a basic portlet application that extends the PortletAdapter class comprised of a complete concrete portlet and concrete portlet application. It contains a portlet class, sample JSP files that are used when rendering the portlet, and a sample Java bean. Faces portlet: Creates a Faces portlet application based on Java Faces technology.
69
Struts portlet: Creates a Struts portlet application based on Java Struts technology.
When creating a Faces portlet, you will be presented with the following window.
70
When creating a Struts portlet, you will be presented with the following window.
71
When creating a Basic portlet, you will be presented with the following window. It allows you to select features that provide additional functionality in the portlet application. Select features as necessary. Deselect the Web Diagram check box if you are creating a Basic or Empty portlet. Select JSP Tag Libraries to include the functionality of this technology in the portlet project.
72
In the Portlet Settings window, update or add any general portlet settings. The application name is the name of the portlet application as used to manage it by the portal administrator. To update this name after generating your portlet project, use the deployment descriptor editor to modify portlet.xml. Modify the Display name of each concrete portlet application. The portlet name is the name of the portlet. It is also used by the portal administrator. It also can be updated used the deployment descriptor editor and modifying the Display name of each concrete portlet. The default locale specifies a default locale to use if the client locale cannot be determined. You can add supported locale using the deployment descriptor editor and adding a locale to each concrete portlet. The portlet title appears in the portlet title bar. To update this in the future, you can use the deployment descriptor editor to modify the title of each concrete portlet. Change code generation options can be used to change the package and class prefixes. Click Next to continue. If creating an empty portlet, the Miscellaneous screen as seen in Figure 2-18 on page 77 will be shown. If creating a Basic portlet, the Event Handling screen will be shown.
73
On this screen, you have the ability to optionally add event handling capabilities to the portlet application. An action event is sent when an HTTP request is received that is associated with a portlet action. The Add action listener option implements the ActionListener interface to handle action events. The Add form sample option generates code to demonstrate action event handling with a simple form example. Cooperative portlets provide a model for declaring, publishing, and sharing information with each other using the WebSphere Portal property broker. Cooperative portlets are only available when the Servlet level is 2.3. Cooperative portlets can run on WebSphere Portal V5.x servers. Create a portlet application that extends the PortletAdapter class. Enable cooperative target adds a sample WSDL file so that the Click-to-Action target can receive input properties. If you select this option with the Add form sample option in the Action Event handling section of this screen, the generated portlet project will be enabled as a Click-to-Action receiver. It is also possible to create an action handler and form and customize the WSDL file as required. The Add Click-to-Action sender portlet sample adds a simple Click-to-Action sender portlet that is useful to test receiver function and provides sample code. The Enable cooperative source
74
adds the Click-to-Action tag library directive for JSP files of the Click-to-Action source portlet. Message events can be sent from one portlet to others if the recipient portlets are placed on the same portal page as the sending portlet. To get a Java class that implements the MessageListener interface, select the Add message listener option. The Add message sender portlet sample generates a sample message sender portlet. To add a function showing events received by listeners in View mode, select Add event log viewer. To select this option, you need to add at least one of the event listeners. The option to Add edit panel allows you to change the default maximum event count while in Edit mode. Click Next to continue with the Portlet Project wizard.
75
Use the Single Sign-On screen to add sample code to support credential vault handling which is used to safely store credentials that are used in portlet authentication. Portlets written to extract users credentials from the credential vault can hide the login challenge from the user. A portlet private credential vault clot stores user credentials that are not shared among portlets. A shared credential vault slot shares user credentials among all a users portlets. The administrative credential vault slot allows each user to store their confidential information for accessing administrator-defined resources such as Lotus Notes databases. A system credential vault slot stores system credentials where the actual confidential information is shared among all users and portlets. The slot name defines the name of the credential vault slot to store and retrieve the credentials. The Show password option allows a password to be displayed on the screen while in View mode. Click Next to continue with the Portlet Project wizard.
The Miscellaneous window allows other supported markup languages and portlet modes to be selected. For more information about markup languages, see Choosing markup languages on page 56. For more information about modes, see Portlet modes on page 50.
76
Click Finish to generate the new portlet project. You may be presented with a an option to switch to the Web perspective to work on this project. Click Yes if the Confirm Perspective Switch is shown.
Web perspective
The Web perspective combines views and editors that assist you with Web application development. This perspective is used to edit the project resources, such as HTML and JSP files, and deployment descriptors that make up the portlet project.
Page Designer
Page Designer is an editor for HTML, XHTML, JSP, and Faces JSP files. It provides three representations of each file: Design, Source, and Preview. Each of these provides a different way to work with a file while it is being edited. You can switch between these by clicking the tabs at the bottom of the editor (see Figure 2-19 on page 78): Design: The design page provides a visual environment to create and work with a file while viewing its elements on the page. Source: The source page enables you to view and directly work with a file's source code.
77
Preview: The preview page shows you how the current page is likely to look when viewed in an external Web browser. Previewing dynamic content requires running the portlet or portal page on a local or remote test server.
78
79
Note: An XML exception occurs and the server attach fails to start if the project name, the file name, the file directory structure or the user ID for WebSphere Portal login name is excessively long. To correct this, shorten the length of the filename, the file directory structure or the User ID for WebSphere Portal login at the WebSphere Portal Server Attach server configuration.
80
4. On the WebSphere Server Configuration Settings page, select one of the following values: Select Use default port numbers and set the HTTP port number to use the default HTTP port (9081). Select Use consecutive port numbers and set the First port number to use port numbers other than the default numbers used by WebSphere Application Server. This setting causes the Test Environment to use sequential port numbers, starting with the number you specify. You must specify a port number that begins a range of port numbers that are not being used by another application. This option allows you to have an external Portal server or WebSphere Application Server running on your system, and allows the Test Environment to use different port numbers. You can also configure the Test Environment server's HTTP port numbers by editing the server configuration, as explained below. 5. Click Finish. Additional options for local servers can be viewed and changed by double-clicking the server in the Servers view. This opens the server configuration editor. You can change any of the settings that were defined previously. In addition, the Portal tab has several additional settings that can be changed to suit your individual configuration.
81
When you test a portlet on the local test environment server, the default theme and skin are used. The test environment does not support features that rely on WebSphere Enterprise Edition (personalization and asynchronous rendering of portlets) or LDAP. Transcoding is also not enabled by default. It must be enabled to use a WML device emulator when developing portlets for mobile phones and other devices. See Choosing markup languages on page 56 for instructions on how to do this. When testing or debugging, you may experience the following limitations: Help mode does not function correctly in the test environment while using the internal browser. Using an external browser corrects this issue. Single sign-on using LDAP is not supported when using the local test environment. LDAP is supported when testing portlets by remotely attaching to another WebSphere Portal server. You cannot create new portal users while in debug mode. Use the normal mode to create users. Portlet modifications are not previewed correctly when the portlet has been cached by the browser. Logging out and back in to the portal server corrects this. If using Linux, you may not be able to start the test environment server without the appropriate user permissions. Users need full permissions on <STUDIO_HOME>/runtimes/portal_v50/cloudscape/wps50.
82
4. Select the Overwrite existing file option to replace an existing WAR file with the same name. 5. Click Finish. Install the WAR file on the WebSphere Portal server by using the WebSphere Portal administrative tools.
83
Note: An XML Exception occurs and the server attach fails to start if the project name, the filename, the file directory structure or the User ID for WebSphere Portal login name is excessively long. To correct this, shorten the length of the filename, the file directory structure or the User ID for WebSphere Portal login at the WebSphere Portal Server Attach server configuration.
Portal administration
Administrative portlets can be enabled in the server configuration by using the Portal Server Configuration editor described in Portal Server configuration on page 78. You can use the administrative portlets to configure advanced options when running portal and portlet projects. There are several limitations to using the administrative portlets. You cannot install portlets using the administration portlets. In addition, any changes that are made are reset to the default values the next time the Test Environment is started. It is recommended that you only use this option when necessary. It affects the performance of the Test Environment. To debug portlets in a particular layout, use the test and debug options of a portal project, not the administration portlets in the test environment.
84
85
Manually migrate your portlet projects by following these directions: 1. Export the existing project to a WAR file, and include its source files. a. Right-click the project and select Export. b. Select WAR file and Export source files and click Finish. 2. Import the portlet WAR file into a new portlet project: a. In the Portal Tools for Rational Application Developer V6.0, create a new empty portlet project. i. Select File New Project Portal Portlet Project or Portlet Project (JSR 168). ii. Deselect Create a portlet. iii. Click Show Advanced. iv. If you are importing a WebSphere Portal V4.2 portlet, select 2.2 as the servlet version. v. Select WebSphere Portal V5.0 as the target server, and click Finish. b. Import the WAR file to this new empty portlet project. i. Select File Import. ii. Select WAR file and specify the WAR file from the portlet project that you exported. iii. Select the newly created empty portlet project. iv. Select Overwrite existing resources without warning. v. Do not select Delete project on overwrite. vi. Delete the TLD file. It is recommended that you delete the portlet TLD file from the project if it exists. Otherwise, you will get a warning message when you rebuild the project. Leaving it may cause a problem when the portlet project is deployed to WebSphere Portal and the TLD file of the portlet is different from the file in the server. Note: If you are migrating a WebSphere Portal V4.2 portlet, you will need to migrate this migrated portlet project to WebSphere Portal V5.x. Backward compatibility of portlet projects is not supported.s
86
The portal development scenario is organized into the following tasks: Preparing for the sample Adding and modifying a portal page Creating and modifying two portlets Adding portlets to a portal page Running the project in the test environment Note: The sample code described in this chapter can be completed by following along in the procedures documented. Alternatively, you can import the sample Portal code provided in the c:\6449code\portal\Portal.zip Project Interchange file. For details refer to Appendix A, Additional material on page 1003. When importing the Project Interchange file, we found some errors when using the IBM WebSphere Portal V5.0.2 Test Environment due to issues related files outside of the scope of the Project Interchange packaging (specifically, themes and related JSPs).
87
4. When the Portal Project dialog appears, enter MyPortal in the Name field, click Show Advanced to see more options (we accepted the defaults), and then click Next. 5. When the Select Theme dialog appears, select the desired theme. For example, we select the Corporate theme and then clicked Next. 6. When the Select Skin dialog appears, select the desired skin. For example, we selected the Outline skin and then clicked Finish. This will generate the framework for the portal site.
88
7. If prompted to change to the Web perspective as seen in Figure 2-22, click Yes.
89
5. Add a label to the page (see Figure 2-24 on page 91). a. Drag and drop the Label button from the Palette view, and place it to the right of the existing Label1. b. Drag and drop the Page button from the Palette view onto the New Label to add a new page on which to place portlets.
90
6. Change the label names (see Figure 2-25). In the same way that the titles of pages are modified, change the names of the two labels on the portal site. Name the right label Right Label and the left label Left Label.
7. Click File Save All to save all the changes you have made. 8. By adding labels and pages, you are able to alter the navigational structure of the portal. You can also view an outline view of this structure by looking at the
91
Outline view which appears in the lower left corner of the workbench (see Figure 2-26).
92
9. When the Miscellaneous dialog appears, check Add Edit mode on the miscellaneous settings page, and click Finish to generate your portlet code. The portlets View mode JSP is now displayed in the workbench to be edited. 10.Expand Dynamic Web Projects MyPortal in the Project Explorer view (see Figure 2-27). Under this directory are all the resources associated with the portlet including the supporting JSP files, Java classes, and the portlets deployment descriptor. You can double-click any resource to edit it in its default editor.
93
4. When the Portlet Type dialog appears, select Basic portlet and click Next. 5. When the Features dialog appears, uncheck Web Diagram, check the JSP Tag Libraries, and then click Next. 6. When the Portlet Settings dialog appears, we accepted the default portlet settings, and clicked Next. 7. When the Event Handling dialog appears, accept the defaults on the event handling screen. The Add action listener and Add form sample options should be selected. Click Next. 8. When the Single Sign-on dialog appears, accept the default values for credential vault handling and click Next. 9. When the Miscellaneous dialog appears, accept the default and click Finish to generate your portlet code. 10.In the FormPortletPortletView.jsp file that is displayed on your screen, delete Welcome! 11.Figure 2-28 displays a sample View mode page. You have to edit this page to customize it for your own use. The source file for this page is as follows, leaving only the form sample to be displayed on this page:
/Web Content/form_portlet/jsp/html/FormPortletPortletView.jsp
12.Click File Save All to save all the changes made to the portlet projects.
94
c. Right-click the left column and click Insert Portlet As Child. d. Select the Basic Portlet portlet as seen in Figure 2-30 on page 96, and click OK.
95
4. Add portlets to the Left Label of the Bottom Page. a. Select the Left Label of the Bottom Page. b. Drag and drop the Column button from the Palette View into the area of the Bottom Page that says Place portlet here. c. Right-click the right column and click Insert Portlet As Child. d. Select the Form Portlet portlet and click OK. 5. Perform the same action to insert the Basic Portlet portlet to the Left Label of the Bottom Page (see Figure 2-31 on page 97).
96
Figure 2-31 Basic portlet on the bottom page of the left label
6. Now click the Right Label. 7. Insert the Form Portlet onto this page (see Figure 2-32).
Figure 2-32 Inserting the form portlet into the right label new page
8. Click File Save All to save all the changes made to your portal site.
97
4. When the Define a New Server dialog appears, select Manually define a server, and select the desired WebSphere Portal Test Environment (V5.0 or V5.1). For example, we selected WebSphere Portal V5.0 Test Environment and clicked Next. 5. When the WebSphere Server Configuration Settings dialog appears, we accepted the default port (9081) and clicked Next.
98
6. When the Add and Remove Projects dialog appears, select each of the following projects and click Add: Form PortletEAR Basic PortletEAR MyPortalEAR 7. When done adding the projects to the Configured projects column, you should have four projects (MyPortalEAR, Form PortletEAR, Basic PortletEAR, MyPortalPortletsEAR) associated with your MyPortal project so that they can run on the server. Click Finish. 8. Click OK if you receive the Repair Server Configuration dialog window (see Figure 2-34). This indicates that your portlets will be added to the server so that they run in your portal project.
9. The server will now start, and your portal site will load in the Web browser as seen in Figure 2-35 on page 100. Test the portal site. a. Navigate the portal site using the labels and page links. b. Enter Edit mode on the Basic Portlet by clicking Edit Page. c. Submit a value using the form.
99
Figure 2-35 My Portal project in Web browser within IBM Rational Application Developer
100
Chapter 3.
101
3.1 Prerequisites
The following software and hardware requirements must be met in order to successfully install and use Rational Application Developer V6 for portlet application development.
102
Follow these steps to install this product: 1. Insert installation disk 1. If autorun is enabled on your system, the installation launchpad program automatically opens. If autorun is disabled on your system, run launchpad.exe from the CD to display the Rational Software Development Platform Launchpad.
2. Select Install Rational Application Developer V6.0. 3. Click Next to continue once the installation program opens. 4. Accept the license agreement, and click Next to continue. 5. Accept the default installation path: C:\Program Files\IBM\Rational\SDP\6.0\.
103
6. In the features window, you may deselect IBM WebSphere Application Server V6.0 Integrated Test Environment. It is not required for portlet application development and is not a prerequisite for the WebSphere Portal V5.1 Test Environment. You may wish to leave this feature selected if you will be developing applications for WebSphere Application Server V6.0. You can run the installation program again in the future to add this functionality. WebSphere Application Server V5.1 will be automatically installed when installing the WebSphere Portal V5.1 Test Environment as shown in 3.3, WebSphere Portal V5.1 Test Environment on page 106. Also select Portal Tools under the Additional Features heading. This option is required for portlet development. Click Next to continue.
104
7. In the summary information window, click Next to continue with the installation. 8. When prompted, insert disks 2, 3, and 4 and click OK after each to continue the installation. 9. Click Next to continue after this installation has been completed. 10.You may deselect the Launch Agent Controller Install option. Agent Controller is a daemon that allows client applications to launch and manage local or remote applications and provides information about running applications to other applications. You must install Agent Controller before you can use profiling tools to profile your applications, logging tools to import remote log files, component testing tool to run test cases, runtime analysis tools for probe insertion, code coverage and leak analysis, and tools for remote application testing on WebSphere Application Server version 5.0 or 5.1. It is not a required component for portlet development. Note: Agent Controller can be installed after the installation of Rational Application Developer by selecting Install Agent Controller from the launchpad, as shown in Figure 3-1. More information about this tool can be found online at:
http://www.ibm.com/developerworks/rational
105
106
6. Click Next to continue after ensuring that no instances of WebSphere Application Server or WebSphere Portal are running on the local machine. 7. Accept the default installation directory for WebSphere Application Server. Click Next to continue.
107
8. Accept the default installation directory for WebSphere Portal. Click Next to continue.
108
9. Enter information for a WebSphere Portal administrative user and password. For example, enter a username and password of wpsadmin. Click Next to continue.
109
10.Click Next to begin the installation. 11.When prompted, insert CDs 1-2, 1-1, 1-15, 2, and 3. Click Next after each to continue the installation. 12.Click Finish in the final installation window to complete the installation.
110
3. Expand the Server category and select Installed Runtimes. Select the WebSphere Portal V5.1 stub. Then click the Edit button.
111
4. Change the following information: Name: WebSphere Portal V5.1 WebSphere Portal Location: C:\Program Files\Portal51UTE\PortalServer WebSphere Application Server Location: C:\Program Files\Portal51UTE\AppServer
112
113
4. Accept the license agreement and click Next. 5. Select WebSphere Application Server 5.1 from the Integrated Test Environments category and click Next to continue.
6. Click Next to begin installation of the test environment. 7. Click Finish to complete installation of the test environment. No additional configuration is needed within Rational Application Developer for the WebSphere V5.1 Test Environment to function.
114
Chapter 4.
115
116
Real estate
Portlets can access a variety of information through the API to help it understand its current condition in the portal. The PortletState informs the portlet if the user has requested the portlet to be minimized, maximized or restored (normal). A portlet should attempt to tailor the content it returns in accordance with the requested state. For example, if the user has maximized the portlet window, the content returned should adequately fill the portal page. However, if the user has requested that the portlet be minimized, you can return some content. To allow a portlet to return
117
content in a minimized state you will need to update the skin to allow it. It is not possible to dynamically change the state of the portlet except during event handling.
Page aggregation
Although a servlet may be a single piece of a much larger Web application, at any given point in time only a single servlet is fulfilling a users request. This provides a great deal of predictability in that as the master controller, it can guarantee what is executed and returned to the client. This is not true of portlets. Each portal page is potentially the aggregation of several portlets. Furthermore, when a servlet executes and returns content to the user, it can be sure that the content it returns will not be affected by any other servlet in the system. This is not true of portlets. A portlet has the ability to write markup to the top of the page even though its normal content is placed inside a cell in a table. This provides a mechanism to include JavaScript functionality the portlet may need. Be aware, however, that as one portlet has that ability, so do all. As such, you must properly encode variable names and functions. This functionality must be used with care as there is no inherent mechanism for one portlet to control the presence or absence of another portlet on a page, and as such it cannot reasonably predict what other page-level code may be present.
Inter-portlet communication
Servlets have the ability to share data through a variety of scopes but since they are executed serially by the client, they cannot interact with each other during a single request. Because portlets are pieces of a larger portal, they have the ability to communicate with other portlets and to be affected by other portlets in a single request. This inter-portlet communication provides a way to create a dynamic portlet application crossing multiple portlets on the same page. For example, one portlet can inform other portlets in the same portlet application or the same page that a user has performed some action. The listening portlets can then alter their presentation, perform alternative logic or otherwise change their behavior.
Event handling
In the servlet architecture, events are represented via HTTP methods. For example, when a user submits a form, the doPost method is called. The portlet event model, however, closely mirrors the traditional Java event model in that portlets implement appropriate interfaces and are notified by the Portal Server when these events are fired. For example, when a user clicks a button, an action event is generated and sent to the registered listener. The Portlet API also provides MessageEvents.
118
Security
Servlets execute in a neutral environment and are inherently responsible for validating the users authenticity and/or authority to make a specific request. This is traditionally a function of the controller role. A portlet, on the other hand, operates only in the context of the portal server and cannot be called directly. The Portal Server is responsible for authentication and authorizing all user access. Therefore, portlets can be reasonably assured that authentication and authorization has been performed prior to their execution. They may, however, perform some authorization in order to tailor content to a specific user or role. Where in servlets, authentication is a daily concern of developers, it is an option for portlet developers.
119
Maximized Minimized
The portlet view is maximized and takes over the entire body of the portal replacing all the other portal views. By default only the portlet title bar is visible inside the portlet page. You can modify the skin do display the portlets content in a minimized state. When doing this you test for the state before generating content. If the state is minimized you should only generate minimal content.
4.6.1 Hierarchy
The abstract class Portlet descends from the HttpServlet interface as illustrated in Figure 4-2. Note that the package structure indicates the portlet belongs to the org.apache.jetspeed.portlet package. It is important to understand that the IBM Portlet API and the Jetspeed API are not the same, or even compatible at this time.
120
4.6.2 Portlet
The abstract class Portlet defines the abstract methods that comprise the base functionality of each portlet. All life cycle methods such as init, service and destroy are defined in this class. For convenience, these abstract methods have been implemented in the PortletAdapter class. The PortletAdapter implements the service method with the basic functionality to determine the type of request and delegate the request to the appropriate do method. As such, it also defines the doView, doConfigure, doHelp and doEdit methods. Most portlet development will extend from the PortletAdapter class.
4.6.3 PortletAdapter
This class is provided as a default implementation of the Portlet class. It is recommended that your portlet extends from this abstract class rather than from the Portlet class. The adapter only provides implementations of the portlet-specific methods. It does not provide an implementation for the doXXX methods of the servlet parent (for example, doPost, doGet, etc.). The service method in PortletAdapter will not call doGet or doPost and therefore should not be used in your portlet. In addition to the methods of the Portlet class, this class defines several additional methods. The methods getVariable, setVariable and removeVariable provide access to the variables you can set on the concrete portlet. It is important to remember that these variables are at the concrete level and therefore will not be shared with other concrete portlets even though they may be based upon the same abstract portlet. These variables are available only in code and are not presented in portal administration, nor are they configurable in the portlet.xml deployment descriptor. Example 4-1 illustrates the usage of these methods.
Example 4-1 Setting and Accessing the concrete portlet variable setVariable("var", Some Value); String var = (String) getVariable("var");
4.6.4 PortletRequest
The PortletRequest interface inherits from the HttpServletRequest and ServletRequest interfaces. It represents the users request and like ServletRequest, encapsulates information about the user and the client. An implementation of PortletRequest is passed to the service method and subsequently to the delegated do methods (doView, doEdit and so on). In
121
addition to client and user information, the PortletRequest object can be used as a short term bucket for storing information, such as JavaBeans. JSPs then have access to the information stored in the PortletRequest to create dynamic presentations. Some of the more frequently used methods of this object are listed below. Example 4-2 illustrates some common usage of the PortletRequest object. getAttribute/setAttribute/removeAttribute These methods allow you to store data in a short term bucket. The PortletRequest is portlet-specific and therefore data stored in this object is not available to other portlets. The storage is only valid during the single request. All objects placed in this scope should be serializable. getParameter This method provides access to the parameters passed as part of the HttpServletRequest. There is no need to distinguish whether the parameter is passed via an HTTP get or post method. This method is often used in event-handling. getCookies This method provides access to the cookies stored by the current domain on the clients machine. An array of cookie objects is returned and the portlet is responsible for iterating through the collection. getHeader This method provides access to the headers supplied by the client. Some of the more common headers you may want to access include accept, accept-encoding and cache-control. getLocale This method returns the preferred locale for the user. The Portal Server determines the locale by first retrieving the users preferred language set during registration. If the preferred language is not set, the locale is retrieved from the accept-language header supplied by the client. getPreviousMode This method returns the previous mode visited by the user.
Example 4-2 Working with the PortletRequest request.setAttribute("uri", uri); String fNmame = request.getParameter("f_name"); java.util.Locale locale = request.getLocale();
122
4.6.5 PortletResponse
The PortletResponse interface extends from the HttpServletResponse and ServletResponse interfaces. This object encapsulates the response sent to the Portal Server for aggregation. Unlike the ServletResponse, the response is sent to the Portal Server, not the client machine directly. Therefore, attempting to influence the overall request, such as setting a status code, will have no effect. Some of the most commonly used methods of this object are listed below: getWriter This method returns a java.io.PrintWriter object that can be used to return markup to the Portal Server. The content returned by the PrintWriter is aggregated into the entire portal page. While it is possible to use a PrintWriter as well as include a JSP, it is generally considered bad practice to do so. encodeNamespace This method takes a String and attaches the name of the portlet application as a prefix. For example, the value variable_one when encoded would be returned as PC_175_variable_one. Any variables that will become part of the aggregated portal page should be encoded. JavaScript functions and variables are good examples of values that should be encoded to prevent name collisions. addCookie This method allows you to add a cookie to the ultimate HTTP response that is sent by the Portal Server to the client. In order to ensure the name of cookie is unique throughout the portal, it is recommended that you use the encodeNameSpace method. addHeader/setHeader/containsHeader This method provides access to the headers sent back to the client via the portal server. encodeURL This method will append the passed string to the complete URL of the Portal Server. For example, the string example.gif becomes http://www.yourco.com/wps/WPS_PA_351/example.gif when passed to the encodeURL method. createURI/createReturnURI These methods will create URI object that contains a URL pointing the portlet in particular mode. For more information see 4.6.18, PortletURI on page 133.
123
Example 4-3 Working with the PortletResponse java.io.PrintWriter out = response.getWriter(); out.println("Hello World"); PortletURI uri = response.createURI(); String functionName = response.encodeNamespace("myFunction");
Portlet
Portlet Settings
Portlet
Portlet Settings
Portlet
Portlet Settings
Concrete Portlet
PortletData
Portlet
Portlet Settings
Portlet
Portlet Settings
PortletData
PortletData PortletSession
Portlet
Portlet Settings
Portlet
Portlet Settings
PortletData PortletSession
PortletData PortletSession
The PortletSession object extends from HttpSession and serves much the same purpose. The PortletSession is intended to represent an ongoing conversation between the client and the portlet. To this end, the PortletSession can be used to store information needed between requests. The PortletSession is intended to store data between requests, not between portlets. As such, data stored in the session by one portlet is not accessible by another. The PortletSession is retrieved from the request object as illustrated in Example 4-4 on page 125.
124
Since a PortletSession object is created when a user logs in, there is no need to create one. However, the getPortletSession(boolean) can be used to create a session for an anonymous user.
Example 4-4 Retrieving a PortletSession PortletSession session = request.getPortletSession();
The most important methods of the PortletSession are getAttribute/setAttribute/removeAttribute: these methods allow you to store, retrieve and delete objects in the PortletSession. Objects stored in the PortletSession should be serializable. To be serializable, the class needs to implement the Serializable interface. Object being placed in the session should be serializable in case the session needs to sent to another JVM in the case of failover or load balancing. This will prevent a NotSerializableException.
4.6.7 Client
The Client interface represents the device making the request, not the user. The Client object can be retrieved from the PortletRequest object as illustrated in Example 4-5. Figure 4-4 on page 126 illustrates the result of most of the methods of the client object when requested via Internet Explorer and a Nokia WAP emulator.
Example 4-5 Working with the client object Client client = request.getClient(); out.print("<P>Manufacturer: " + client.getManufacturer() + "<br/>"); out.print("MarkupName:" + client.getMarkupName() + "<br/>"); out.print("MimeType " + client.getMimeType() + "<br/>"); out.print("Model: " + client.getModel() + "<br/>"); out.print("UserAgent: " + client.getUserAgent() + "<br/>"); out.print("Version: " + client.getVersion() + "</P>");
Generally, the client object is used to determine the markup language to which the device is mapped. Based on that information, device-specific markup can be generated.
125
126
Example 4-6 Working with PortletConfig boolean maxSup = getPortletConfig().supports(PortletWindow.State.MAXIMIZED); boolean minSup = getPortletConfig().supports(PortletWindow.State.MINIMIZED); boolean viewSup = getPortletConfig().supports(Portlet.Mode.VIEW, request.getClient()); boolean editSup = getPortletConfig().supports(Portlet.Mode.EDIT, request.getClient()); boolean configureSup = getPortletConfig().supports(Portlet.Mode.CONFIGURE, request.getClient()); boolean helpSup = getPortletConfig().supports(Portlet.Mode.HELP, request.getClient()); PortletContext context = getPortletConfig().getContext();
The PortletContext can also be used to store attributes that will be shared by all portlets deployed via the same web.xml regardless of concrete portlet application. These attributes are not distributed in a clustered environment. include This is the most commonly used method of the PortletContext object. In a well-designed MVC architecture, the portlet executes one or more business objects to satisfy the logic of the request. Once the logic has completed, the include method generally calls a JSP to produce the output. Unlike Servlets, there is no ability to forward to a JSP. Example 4-8 on page 128 illustrates this approach.
127
getContainerInfo This method indicates the Portal Server version the portlet is executing. It only indicates the major version, not the minor one. In WebSphere Portal Server V4.1.2, this method returns the String IBM WebSphere Portal Server/4.1. getText This method provides access to Resource Bundles to use in providing National Language Support (NLS). For more information about NLS, see Chapter 12, Internationalization on page 373.
Example 4-8 Including a JSP public void doView(PortletRequest request, PortletResponse response) throws PortletException, IOException { //Business logic completed getPortletConfig().getContext().include("/jsp/View.jsp", request, response); }
Portlet Settings
PortletData
Persistent Data
PortletSession
Transient Data
This object is best thought of as wrapping the information defined in the <concrete-portlet> section of the portlet.xml deployment descriptor. The PortletSettings object encapsulates the configuration information of the concrete portlet instance. The parameter information is retrieved from the portlet.xml but can be modified at runtime while the portlet is in Configure mode. Therefore, the PortletSettings object can be used as a storage for attributes to be shared by all
128
the concrete portlet instances. When attributes are adjusted or added, be sure to call the store method to persist the new values. The administrator can add new parameters and alter existing parameter values via the Manage Portlets portlet in Administration place. The PortletSettings object also provides access to configuration information such as the title of the concrete portlet and the default locale. This object can be retrieved from the PortletRequest object or is passed as a parameter to the initConcrete and destroyConcrete methods of the portlet. The main methods are: getAttribute/setAttribute/removeAttribute: these methods provide access to attributes. getTitle: this returns a string indicating the title of the portlet for the current client and the specified locale. Note that this method returns the active title, not necessarily the title specified in the deployment descriptor. If the administrator has changed the title at runtime for example, that value is returned. getDefaultLocale: this method returns a Locale object specifying the default locale as determined by the portlet.xml. getPortletApplicationSettings: this method will return the PortletApplicationSettings object discussed in 4.6.11, PortletApplicationSettings object on page 129.
Example 4-9 Working with PortletSettings String title = request.getPortletSettings().getTitle( request.getLocale(), request.getClient())); java.util.Locale locale = request.getPortletSettings().getDefaultLocale()); PortletApplicationSettings portletAppSettings = request.getPortletSettings().getApplicationSettings(); String attribute = request.getSettings().getAttribute(attName); //Only available in doConfigure: request.getSettings().setAttribute("attribute", "Some Value"); request.getSettings().store();
129
this objects getAttribute method. These parameters can be adjusted and new ones added only while a portlet is in configure mode. getAttribute/setAttribute/removeAttribute: these methods provide access to attributes of the concrete portlet application.
Example 4-10 Working with PortletApplicationSettings PortletApplicationSettings portletAppSettings = request.getPortletSettings().getApplicationSettings(); String attribute = portletAppSettings.getAttribute("attribute"): //Only available in doConfigure: portletAppSettings.setAttribute("attribute", "Some Value"); portletAppSettings.store();
130
Example 4-11 Working with PortletData PortletData data = request.getData(); String greeting = (String) data.getAttribute("greeting"); String moniker = (String ) data.getAttribute("moniker"); //Only available in doEdit or possibly actionPerformed: PortletData data = request.getData(); data.setAttribute("greeting", greeting); data.setAttribute("moniker", moniker);
If the portlet you are writing extends PortletAdapter, a convenience method has been provided for you as illustrated in Example 4-13 on page 132.
131
4.6.14 PortletException
The Portlet Exception inherits from the ServletException and is used as the basis for most exceptions thrown in the Portal environment, including UnavailableException
4.6.15 UnavailableException
This exception is thrown if the portlet fails to initialize. Generally, your portlets will include an init method which calls the super.init. Since this call may produce an UnavailableException, the functionality is provided to evaluate what to do if the initialization fails. getUnavailableSeconds: this method returns an int (integer) indicating how long this portlet is unavailable for. isPermament: this method returns a boolean indicating this portlet is not permanently unavailable. The length of time the portlet is unavailable is determined when the exception is first created. UnavailableException(String msg): this constructor indicates the portlet is permanently unavailable. UnavailableException(String msg, int time): this constructor will reflect the length of time for which this portlet is unavailable.
132
Example 4-14 Determining portlet window state PortletWindow.State state = request.getWindow().getWindowState(); if (state.equals(PortletWindow.State.NORMAL)){ getPortletConfig().getContext().include("/jsp/View.jsp", req, resp); } else if (state.equals(PortletWindow.State.MAXIMIZED)){ getPortletConfig().getContext().include("/jsp/MaxView.jsp", req, resp); } else { //Window is minimized, no need to generate content. }
The getID returns as a String the complete DN of the user. For example, wpsadmin in a typical SecureWay environment would return uid=wpsadmin, cn=users,dc=<domain>,dc>=<com> There are two User interfaces defined in the Portlet API. The org.apache.jetspeed.portlet.User class represents the logged in user and is the User object you will use day-to-day. The com.ibm.wps.puma.beans.User interface is an EJB and is not used to access individual user information
4.6.18 PortletURI
The PortletURI is used in organizing navigation through the portal as a user moves from mode to mode in a portlet. When a user is on a normal page (for example when the portlets are presented in View mode), the page is an aggregation of all the portlets. In order for any one portlet to be able to navigate
133
back to that aggregated state, the PortletURI can store the URL. The PortletURI is then placed in a bucket such as the request or session object.
Much like the Servlet Container, the Portlet Container manages the portlet life cycle along with providing services to the portlets running in the container. The portlet container loads and instantiates the portlet class. This can happen during startup of the portal server or later, but no later then when the first request to the portlet has to be serviced. Also, if a portlet is taken out of service temporarily, for example while administrating it, the portlet container may finish the life cycle before taking the portlet out of service. When the administration is done, the portlet will be newly initialized. During the portlet life cycle, the portlet container invokes the following methods on the Portlet class (subclass of a the Portlet Adapter class) on behalf of user requests as seen in Figure 4-7 on page 135. init() initConcrete()
134
Portlet initailized
User login
Portlet login()
init(PortletConfig config)
This method is called by the portlet container on the abstract portlet when the portlet is first loaded. As with servlets, portlets are loaded when they are first requested. Any subsequent calls to the portlet will not execute this method. Generally, initialization that is applicable to every concrete portlet based on this abstract portlet is placed in this method. If you choose to override this method, at a minimum it should make a call to its parent via super.init(portletConfig). At this point in the portlet life cycle, no portlet-specific storage objects are available. This includes PortletSession, PortletData, PortletApplicationSettings and PortletSettings.
initConcrete(PortletSettings settings)
This method is called by the portlet container on the concrete portlet. The initialization code performed in this method is not shared by other concrete portlets even though they may be based upon the same abstract portlet. It is in this method that the PortletSettings object is first available. The PortletSettings
Portlet
135
encapsulates the concrete portlet configuration parameter information. From the PortletSettings object, the PortletApplicationSettings object is available. The PortletApplicationSettings object encapsulates concrete portlet application context parameters. In this method, no user-specific objects are yet available.
login(PortletRequest request)
If the concrete portlet has been placed on a page that requires authorization, the login method is called by the portlet container to associate a user with the portlet. It is at this point that the PortletData object is first available. The PortletSession is created by the container for the registered user at this point and is available in this method via the request object. If the request for the portlet is made by an anonymous user, this method is not called. If this method is not called, a default session object can still be created with no user association, though it may be of little practical use. This method is actually defined in the PortletSessionListener interface which is implemented by the abstract class Portlet. Since your custom portlets will extend from Portlet, it is included in this discussion even though other oft-used listeners are not.
logout(PortletSession session)
Only when a user specifically selects the Log Off button on the portal is this method called. This method provides you with the opportunity to manage any user-specific information once the user has logged out and to clean up user-related resources. If the user removes the portlet from their page, the logout method is not called until the user actually logs out of the portal, even though they no longer are accessing the portlet. When the portlet is taken out of service by the Portal server or the administrator, this method will not be called. The PortletSettings object is still available in this method, although the PortletRequest is not. This method is actually defined in the PortletSessionListener interface which is implemented by the abstract class Portlet. Since your custom portlets will extend from Portlet, it is included in this discussion even though other oft-used listeners are not.
destroyConcrete(PortletSettings settings)
This method is called when the concrete portlet is taken out of service either because of the portal server stopping or the application being uninstalled from
136
the portal server. The portlet container will call each running concrete portlet in the application individually when the application is deleted. In this method, the PortletSettings object is passed in as a parameter and cannot be retrieved from the normal getPortletSettings method.
destroy(PortletConfig config)
The portlet container executes this method on the abstract portlet when the portlet is taken out of service. Since it is executed on the abstract portlet and not the concrete portlets, it is executed only once. This method provides an opportunity to execute clean-up code on each and every concrete portlet in the application derived from this abstract portlet.
4.8 Listeners
The event model of the IBM Portal API is very similar to the traditional Java event model. However, there are two main points of distinction. First, there is no need to register listeners. When a portlet in installed, the Portal Server determines the listeners it implements and registers them on behalf of the portlet. Secondly, since the registration is taken care of by the Portal Server, it is not possible to specify which portlets a particular portlet wishes to register for. Therefore, portlets implementing listeners need to carefully plan for unsolicited and unexpected events. There are several listeners defined in the IBM Portal API. The ActionListener is covered in the Event handling section and the MessageListener is covered in the Messaging section.
4.8.1 PortletTitleListener
This listener allows you to dynamically set the title of the portlet. This listener requires the single method as shown in Example 4-16. This interface is particularly useful when tailoring the title to certain modes or devices. To return a title, simply use a PrintWriter object or include a JSP using the PortletContext object. While the second approach allows you to create a more dynamic title including images and so forth, you must remain mindful of the limited space in the title bar.
Example 4-16 PortletTitleListener example public void doTitle(PortletRequest request, PortletResponse response) throws PortletException, IOException { PrintWriter out = response.getWriter(); String title = getPortletSettings().getTitle( request.getLocale(), request.getClient());
137
4.8.2 PortletPageListener
This interface provides the opportunity to add content to the top and bottom of the aggregated page. Example 4-17 illustrates a simple implementation of the PortletPageListener interface. It is important to note that content returned from the beginPage method is not placed at the top of the page but rather at the top of the aggregated content as displayed in Figure 4-8 on page 139.
Example 4-17 PortletPageListener implementation public class AgendaPortlet extends PortletAdapter implements PortletPageListener { ........ public void beginPage(PortletRequest request, PortletResponse response) throws PortletException, IOException { PrintWriter out = response.getWriter(); out.println("This page contains my agenda."); } public void endPage(PortletRequest request, PortletResponse response) throws PortletException, IOException { PrintWriter out = response.getWriter(); out.println("End of my agenda."); } }
The resulting page including the top and bottom messages is illustrated in Figure 4-8 on page 139.
138
The beginPage is a convenient method when you need to include JavaScript functions needed by your portlet. However. be very conscious of any content you decide to display in the beginPage method as it may adversely affect the overall aggregation of the page. Furthermore, because the page is aggregated, be sure that any functions or global variables you declare have properly encoded the namespace of the portlet to ensure there are no naming collisions. Use the response.encodeNamespace to do this. Restriction: The Home.jsp can choose to cancel calls to the PortletPageListener via the <wps:pageRender includeBeginPage="no" includeEndPage="no"> tag. In this case, your beginPage and endPage methods will not be called.
4.8.3 PortletSessionListener
This interface requests the Portal Server to notify the portlet if an authenticated user has accessed the portlet. This interface is already implemented by the PortletAdapter class which is traditionally the parent of most custom portlets. This interface defines the two methods shown in Example 4-18 on page 140. Figure on page 134 illustrates where in the life cycle of the portlet these methods are called. The functionality of the login and logout methods is detailed in 4.7, Portlet life cycle on page 134.
139
Example 4-18 PortletSessionListener methods public void login(PortletRequest request) throws PortletException{ ... } public void logout(PortletSession session) throws PortletException{ ... }
4.8.4 WindowListener
The WindowListener is no longer supported in WebSphere Portal as of version 5.1. Note: The WindowEvent interface has also been removed in WebSphere Portal V5.1; you should use the PortletWindow.getWindowState() method instead. It is included here for information and as a reference for portlets developed using previous releases. This interface will notify the portlet that the user has changed the window state. Presently, there are only three supported window states, despite the javadoc. NORMAL, MAXIMIZED and MINIMIZED states are supported. The portlet is notified of these three states through windowMaximized, windowMinimized, and windowRestored, respectively. Though only three states are currently supported, the WindowListener defines methods for windowClosing, windowOpening, windowDetached and windowClosed. This methods are never called. However, in order to implement this interface, all methods must be implemented even though several will contain empty bodies. Note: You will need to make sure you implement the interface org.apache.jetspeed.portlet.event.WindowListener and not the AWT counterpart since some development environments will offer both.
140
Example 4-19 Implementing the WindowListener public void windowMaximized(WindowEvent arg0) throws PortletException { // Some action can be performed } public void windowMinimized(WindowEvent arg0) throws PortletException { // Some action can be performed } public void windowRestored(WindowEvent arg0) throws PortletException { // Some action can be performed } public void windowClosing(WindowEvent arg0) throws PortletException { } public void windowClosed(WindowEvent arg0) throws PortletException { } public void windowDetached(WindowEvent arg0) throws PortletException { }
4.8.5 PortletSettingsAttributeListener
The PortletSettings object encapsulates the concrete portlet defined in the portlet.xml. Part of that definition includes configuration parameters that may be declared at deployment time. These parameters can be altered and new ones can be added at runtime. The PortletSettingsAttributeListener notifies your portlet if the configuration parameters are changed at runtime.
4.8.6 PortletApplicationSettingsAttributesListener
Similar to the PortletSettingsAttributeListener, this listener provides notification when the context parameters of the concrete application have changed, been added or removed.
141
MessageEvents are generated, delivered and processed in this phase. Once this phase is complete, the content generation phase begins. Once content generation has begun, no events can be generated. Attempting to generate events during the content generation phase, for example doView, doEdit, etc., will cause an exception.
4.9.1 ActionListener
The org.apache.jetspeed.portlet.event.ActionListener interface defines a single method to be implemented as illustrated in Example 4-20.
Example 4-20 ActionListener Interface org.apache.jetspeed.portlet.event.ActionListener public void actionPerformed(org.apache.jetspeed.portlet.event.ActionEvent event) throws PortletException;
4.9.2 ActionEvent
An implementation of the org.apache.jetspeed.portlet.event.ActionEvent interface is passed to the actionPerformed method by the PortalServer when a PortletURI with an action is executed. The ActionEvent object provides access to the PortletRequest and the action. Note: The DefaultPortletAction class and the PortletAction interfaces are deprecated in this release and you should use the Simple Action string instead, as illustrated in Example 4-21.
Example 4-21 Working with the ActionEvent public void actionPerformed(ActionEvent event) throws PortletException { PortletRequest request = event.getRequest(); String action = event.getActionString();
4.9.3 PortletURI
The portletURI represents a URL that can be used to navigate between modes. The PortletURI can be used to navigate to a previous mode, such as from Edit to View, or to navigate back to the same mode, such as a multi-part form in View or Edit. There is no ability to create a PortletURI object pointing to a mode not yet visited by the user. PortletRequest.createURI returns a portletURI object pointing to the portlet in its current mode. For example, if the portletURI is created in the doView mode, the
142
URL points to the portlet in View. The createReturnURI method returns a PortletURI object pointing to the last mode the portlet was in. This mode is commonly used in the doEdit method when the URI needs to point back to the View mode. The edit.jsp would use the PortletURI to bring the user back to the View mode when they have completed the edit or configure process. In order for a portlet to be notified of an event, such as the user clicking a button, the portletURI must contain an associated PortletAction. Typical PortletURI construction and usage is shown in Example 4-22. The process of adding actions to PortletURI objects has been simplified. The addAction(PortletAction) method has been deprecated and replaced with addAction(String). Since the vast majority of work with PortletActions involves no more than setting a name, this new implementation is much more convenient. Developers are advised to use simple action string instead. Note: Deprecated classes and interfaces are still supported in the current release but are not recommended for use because they might not be supported in future releases.
Since the DefaultPortletAction class and the PortletAction interfaces are deprecated in this release, we show the use of the Simple Action string instead, as illustrated in Example 4-22.
Example 4-22 Working with PortletURI PortletURI uri = response.createReturnURI(); uri.addAction("save"); request.setAttribute("uri", uri.toString());
It is possible to add parameters to the PortletURI object. Parameters added to the PortletURI via code or through a form are accessed the same way via the portlet request object. This provides a mechanism to pass default values or to pass parameters not displayed in the form. Example 4-23 displays the code for adding a parameter. Be aware that parameters set via the PortletURI are not passed in the traditional HTML syntax. Note: The DefaultPortletAction class and the PortletAction interfaces are deprecated and you should use the Simple Action string instead, as illustrated in Example 4-23.
Example 4-23 Add URI public void doView(PortletRequest request, PortletResponse response) throws
143
PortletException, IOException { PortletURI viewURI = response.createReturnURI(); viewURI.addAction("save"); viewURI.addParameter("Param1", "Param1Value"); request.setAttribute("viewURI", viewURI.toString()); getPortletConfig().getContext().include("/jsp/View.jsp", request, response); }
4.9.4 ModeModifier
When a PortletURI is created, it points to a portlet in particular mode. When that PortletURI is executed and it contains a PortletAction, it will notify the appropriate listener. If, in the actionPerformed method, you need to redirect the user to a mode other the one specified, the request.setModeModifier method can be used to redirect the user to another mode. The ModeModifier can only be set during event processing. Calling this method in doView or doEdit, etc., will have no effect. There are three possible modes the user can be redirected to: REQUESTED This ModeModifier will navigate the user to whatever mode was originally set by the PortletURI. Essentially, this is the default. If the ModeModifier is changed, it cannot be changed back to REQUESTED. CURRENT This ModeModifier will keep the user in the current mode. For example, if the user tries to save some information and the actionPerformed determines it is incorrect, setting ModeModifier to CURRENT will return them to the Edit screen. PREVIOUS This ModeModifier will return the user to the mode the user was in prior to the CURRENT regardless of previous ModeModification. Therefore, setting ModeModifier to CURRENT in one event process will not make that mode PREVIOUS in the next event process.
144
PortletSession
object
PortletSettings
String
PortletApplication Settings
String
PortletData
PortletURI
145
Object PortletMessage
Scope Only available to registered message listeners in the event processing phase
Programmatic Access Since each custom portlet message can be implemented uniquely, access is not pre-defined
Best Practice Use to adequately capture all the information necessary to complete the message. There is no predictably regarding order of execution for listeners so do depend on this. This vale can only be set during development or deployment. Since its scope is very broad, use carefully. It is not recommended that you store objects such as PortletResponse etc. Use sparingly. Use this object to store attributes that are not unique to any one user, and can be lost if the server shuts down
PortletConfig
Same config object is available to every concrete portlet instance derived from the same abstract portlet Available as long as the PortletURI it is attached to is available. Available to all instances of the concrete portlet. Value is not unique between users.
String
getInitParameter()
DefaultPortlet Action
object
setParameter() getParameters()
PortletAdapter
object
getVariable() setVariable()
146
extend.tld This tag library is only supplied if the installation type is extend or experience. These tags are not available with the enable installation. content.tld This tag is used in JSPs working with the PortletContent Organizer. menu.tld This tag library provides access to Collaborative functionality in the themes. person.tld This tag library provides access to Collaborative functionality inside your portlets.
This section will cover the tags available in the portlet.tld tag library and some of their most common uses. init <portletAPI:init /> This tag must be called if you wish to access the PortletRequest, PortletResponse or PortletConfig objects in the JSP. This tag simply initializes three variables for you: portletRequest, portletResponse, and portletConfig. Attempting to access these variables without calling the init tag will cause the page compilation of the JSP to fail. However, you still have full access to the javax.servlet.http.HttpServlet objects via the normal variable names. createReturnURI <portletAPI:ecreateReturnURI /> This tag returns a String pointing to the portlet in the previous mode. The resulting URI could be used to create a Back button or to specify and an action on a FORM. If you wish to add a PortletAction to the URI object in order to notify any applicable listeners, you can include the URIAction tag in the body of the createReturnURI tags. Example 4-25 on page 148 illustrates this approach.
147
Example 4-25 Adding a PortletAction to the PortletURI <portletAPI:createReturnURI > <portletAPI:URIAction name="submit" /> </portletAPI:createReturnURI>
You can also add a parameter to the PortletURI object using a similar approach to the one used with the PortletAction.
Example 4-26 Adding a Parameter to the PortletURI and the resulting URI <portletAPI:createReturnURI > <portletAPI:URIParameter name="fname" value="john" /> </portletAPI:createReturnURI>
Result:
/wps/myportal/.cmd/ActionDispatcher/_pagr/104/_pa.104/113/.md/-/.piid/188/.ciid /223/.reqid/-1/PC_188_fname/john#223
createURI <portletAPI:createURI /> This tag returns a String pointing to the portlet in the current mode. As with the createReturnURI tag, PortletActions and parameters can be added to the resulting URI. Though the documentation indicates that the state can be controlled by passing a string attribute, this functionality is not implemented. URIAction <portletAPI:URIAction name=sting/> This tag is only used when creating a PortletURI object. Example 4-25 illustrates the use of this tag. This tag requires that a name attribute be specified. URIParameter <portletAPI:URIParamter name=string value=string/> This tag is only used when creating a PortletURI object. Example 4-26 illustrates the use of this tag. This tag requires that name and value attributes be specified. dataAttribute <portletAPI:dataAttribute name=string /> This tag will retrieve from the PortletData object the attribute specified by the name attribute. If the attribute does not exist in the PortletData, nothing is returned. When the dataAttribute tag is used in the body of the a dataLoop tag, it does need to specify the name of the attribute.
Example 4-27 Retrieving a single PortletData attribute Welcome <portletAPI:dataAttribute name = "pref.nick_name" /> to your page.
148
dataLoop <portletAPI:dataLoop pattern="string"> </portletAPI:dataLoop> This tag provides a loop through all the attributes stored in the PortletData object. Though by default, it will iterate through all attributes, it is possible to specify a pattern to limit the attributes it locates. Omitting the pattern attribute will return all attributes. Example 4-28 illustrates the usage of this tag. Notice the loop simply iterates through the collection of attributes; it does not retrieve the value. To retrieve a PortletData value, use the dataAttribute tag.
Example 4-28 Looping through the attributes in the PortletData object <portletAPI:dataLoop pattern="pref.*"> <portletAPI:dataAttribute/><br> </portletAPI:dataLoop>
Though using an asterisk in the pattern is helpful for readability and reliability, the pattern attribute in fact does not need to use an asterisk at all. The tag will attempt to find the value specified by the pattern attribute anywhere in the name of the attribute. For example, if an attribute is stored in the PortletData with the name pref.greeting, the code in Example 4-29 would successfully locate the attribute. However, it is important to note that the pattern is case-sensitive. Therefore, the pattern name would not locate the attribute Name.
Example 4-29 Using the Pattern attribute <portletAPI:dataLoop pattern="eet">
settingsAttribute <portletAPI:settingAttribute name=string /> This tag provides access to the parameters set in the <config-param> blocks in the portlet.xmls concrete portlet section. When the dataAttribute tag is used in the body of the settingsLoop tag, it does need to specify the name of the attribute.
Example 4-30 Accessing the PortletSettings attributes For support contact <portletAPI:settingsAttribute name = "author" />
settingsLoop <portletAPI:settingsLoop pattern="string"> </portletAPI:settingsLoop> If several configuration parameters have been set in the portlet.xml, they can all be retrieved with this tag. The pattern tag is optional.
149
Example 4-31 Looping through the PortletSettings attributes <portletAPI:settingsLoop pattern="info."> <portletAPI:settingsAttribute/><BR> </portletAPI:settingsLoop>
If you do not include the pattern attribute or enter an empty string, it will return all attributes in the PortletSettings object. As with the dataLoop tag, the settingsLoop tag will attempt to locate the specified pattern anywhere in the attributes name. For example, if a <config-param> were set in the portlet.xml with a name of info.author, the code in Example 4-32 would successfully retrieve the attribute. However, it is important to note that the pattern is case-sensitive. Therefore the pattern author would not locate the attribute Author.
Example 4-32 Using pattern to locate an attribute <portletAPI:settingsLoop pattern="thor">
encodeNameSpace <portletAPI:encodeNamespace value="string" /> When including JavaScript functions or other variables that will be returned to the aggregated portal page, it is important to ensure the values are unique in order to avoid name collisions. This tag prefixes the namespace of the portlet to the string it is passed. This tag should be used when creating the variable and when accessing it. Example 4-33 illustrates the usage and result of this tag.
Example 4-33 Encoding the name space <portletAPI:encodeNamespace value="function1" />
Result:
PC_189_function1
encodeURI This tag will prefix the full URL of the portal to the passed path value. For example, if the image yourco.jpg is in the images folder directly under the root of the deployed portlet application, the code shown in Example 4-34 on page 151 would successfully locate the image and create a fully qualified URL.
150
Example 4-34 Creating a fully qualified URL <img src= <portletAPI:encodeURI path="/images/yourco.jpg" /> >
Result:
http://ka0kkhc.sg246897.com/wps/WPS_PA_206/images/yourco.jpg
if <portletAPI:if attribute= "string"> </portletAPI:if> This tag allows you test some of the more common conditions a portlet may face. When the attribute evaluates to true, the body of the if tag is executed. There are several attributes you can evaluate. mode state locale mime type markup capabilities
Example 4-35 Executing If tags individually <portletAPI:if state = "Normal"> state is normal </portletAPI:if> <portletAPI:if state = "Maximized"> state is maximized </portletAPI:if> <portletAPI:if locale = "en"> Locale is english </portletAPI:if> <portletAPI:if markup = "html"> Markup is html </portletAPI:if>< <portletAPI:if mimetype = "text/html"> mime type is text html </portletAPI:if><BR> <portletAPI:if mode="view"> Mode is View </portletAPI:if ><BR>
You can evaluate more than one condition on a single attribute. In this case, if any of the conditions are true, that attribute will evaluate to true. Example 4-36 illustrates this.
Example 4-36 Evaluating multiple conditions on a single attribute <portletAPI:if state="Normal, Maximized" >
You may also evaluate multiple attributes in the same tag as illustrated in Example 4-37. All conditions must evaluate to true for the if tag to return true.
Example 4-37 Evaluating multiple attributes <portletAPI:if state="Normal" mode="view" locale="en"> Displaying the normal English view
151
</portletAPI:if>
log <portletAPI:log text="string" level="string"/> This tag will write the value passed to the log file located in the <WP-ROOT>\log directory. The text attribute contains the string you wish to write to the log file. The level attribute indicates which level this message should be written under. This tag does not evaluate whether the requested level is enabled before it attempts to write the message. For more information about writing to the log, see 4.6.13, PortletLog object on page 131. Example 4-38 illustrates the usage of this tag. The valid values for the level attribute are error, warn, debug and info. If you omit the level tag, the default level is error.
Example 4-38 Using the log tag <portletAPI:log text="There was an error" level="warn"/>
text <portletAPI:text key="sting" bundle="string"> Note: The text tag has been deprecated in this release. You should now use the fmt tag from the JSP Standard Tag Library (JSTL). This tag was used to provide access to key-value pairs in resource bundles. bidi <porteltAPI:bidi locale=string dir=ltr | rtl /> This tag is used to support text for bidirectional languages. Bidirectional languages are read from right to left or from bottom to top. The attributes are not required. For example, if the request indicates that the client is Hebrew or Arabic, it will execute the tag contents if dir is set to rtl.
152
In portlet messaging, one portlet typically detects a condition and formats a message as a result of that condition, then sends the message to the receiver. The receiving portlet receives the message from the event handler and processes the message as you would expect. Portlets can both send and receive messages. Portlets communicate using portlet actions and portlet messages. For example, an account portlet creates a portlet action and encodes it into the URL that is rendered for displaying transactions. When the link is clicked, the action listener is called, which then sends a portlet message to send the necessary data to the transaction detail portlet. There are some basic rules to portlet messaging: Portlets in different applications can only communicate through default portlet message objects. Default portlet message objects can only carry strings. In order for portlets to communicate through custom messages, they must be part of the same portlet application. WebSphere Portal uses a unique class loader for each portlet application to provide security between applications. The message is typically a custom Java object unique to the application. Since messaging portlets must share this message object, they must share the same class loader and therefore they must be part of the same portlet application. For performance reasons, portlets that communicate through messaging must reside on the same page. Since only one page is displayed at a time, there is little need to send messages to portlets not currently displayed. Portlet events contain information about an event to which a portlet might need to respond. For example, when a user clicks a link or button, this generates an action event. To receive notification of the event, the portlet must have the appropriate event listener implemented within the portlet class. Action events: generated when an HTTP request is received by the portlet container that is associated with an action, such as when the user clicks a link. Message events: generated when another portlet within the portlet application sends a message. Window events: generated when the user changes the state of the portlet window. The portlet container delivers all events to the respective event listeners (and therefore the portlets) before generating any content to be returned to the portal page. Should a listener, while processing the event, find that another event needs to be generated, that event will be queued by the portlet container and delivered at a time point determined by the portlet container. It is only guaranteed
153
that it will be delivered and that this will happen before the content generation phase. There is no guarantee for event ordering. Once the content generation phase has started, no further events will be delivered. For example, messages cannot be sent from within the service, doView or other content generation methods. Attempts to send a message during the content generation phase will result in an org.apache.jetspeed. portlet.AccessDeniedException. The event listener is implemented directly in the portlet class. The listener can access the PortletRequest. It is important to understand the underlying event handling and message processing to ensure delivery of all send messages. The portal event handling and message processing sees four steps executed in the following order. 1. Processing all action events The user makes a request of the portal, the portal receives the request and decodes the action URI sent by the client and propagates an action event to the appropriate portlet. The receiving portlets action listener is called to process an action event. An appropriate time to send messages to other portlets is during the processing of the action event. 2. Processing all message events If a message is sent to a portlet, the portlets message listener is called to process the message. Since portlets can send multiple messages and send messages as a result of receiving a message, this process continues until there are no more messaging events pending. Cyclical messaging is prevented by the WebSphere Portal architecture. 3. Processing all window events Sizing operations such as maximize, minimize and restore, along with the portlets ability to request a specific size, causes multiple window events to be sent to all portlets affected by the sizing activity. This processing of window events continues until there are no more window events pending. 4. Portlet rendering process Upon completing the event processing in the order specified above, the portal aggregator begins calling each container on the page being displayed, causing its contents to be rendered. When aggregation is complete, the page is returned to the user.
154
Important: It is important to note that events are not processed in the last step of the process, page rendering. If a message is sent by a portlet during rendering, the message will not be delivered or processed. This is a result of the fact that the event queue is help in the portlet request and at the time of rendering, the portlet request is no longer available. Therefore, if portlet interaction is desired, portlet messages must be sent during the first three steps of the event and aggregation process.
not an enterprise application (ear). When the war file is deployed, the portal
server actually creates an associated ear folder in the WebSphere\AppServer\installedApps directory. The new ear folder will contain the original name of the war file with _WPS_PA_191.ear appended to the end of the name of the war file. The last number indicates the order in which the portlet application was installed. The war file that was deployed is placed into the ear file with a META-INF folder. This folder contains the application.xml deployment descriptor, the ibm-application-ext.xmi and the Manifest.MF files. All these files are generated by the portal server when the application is installed.
155
allows the same application to be deployed repeatedly with different parameterization, effectively creating multiple applications.
Abstract Portlet 1
Concrete Portlet 2
Abstract Portlet 2
Abstract Portlet 3
Concrete Portlet 3
Certain configuration parameters are set at the abstract level while others are set at the concrete level. As expected, parameters set at the abstract level affect each concrete application. The parameters and configuration information unique to each concrete application are represented in a PortletApplicationSettings object. Parameter and configuration information unique to each portlet are represented in the PortletSettings object. When the portlet is actually placed on a page, it is parameterized by a PortletData object. When a user logs on and accesses a portlet, it is associated with a session object. This sequence is represented in Figure 4-10 on page 157.
156
PortletSettings
Portlet
PortletSettings
ata tD rtle Po
4.12.1 web.xml
The web.xml defines the Web application being deployed. This section will detail the required elements of the web.xml when deploying a portlet application. For details on all of the elements of the web.xml deployment descriptor, refer to http://java.sun.com/j2ee/tutorial. Example 4-39 provides an example web.xml document. To keep this section simple, the deployment descriptors will define only a single portlet in the application.
Example 4-39 Simplest web.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app id="WebApp"> <display-name>HelloWorld</display-name> <context-param> <param-name>webmaster</param-name> <param-value>the webmaster</param-value> </context-param> <servlet id="Servlet_1"> <servlet-name>HelloWorldPortlet</servlet-name> <servlet-class> com.ibm.itso.ral.portlets.HelloWorldPortlet </servlet-class> <init-param> <param-name> init_param1 </param-name>
157
<param-value> An initialization parameter </param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>HelloWorldPortlet</servlet-name> <url-pattern>/HelloWorldPortlet/*</url-pattern> </servlet-mapping> </web-app>
DOCTYPE required This will be the same for each and every web.xml deployed. This tag defines the DTD that is to be used when this document is parsed. Only one is allowed. web-app required This is the top-level tag wrapping the entity of the web.xml. Only one is allowed. id required This attribute identifies the web-app in the application server but is not seen in the portal environment. display-name required Though this name is never seen in the portal environment, it will be seen in the WebSphere Administrators console Event Message screen when the Web module is loaded. Only one is allowed. context-param optional This element provides an opportunity to set context parameters that will be shared by all portlets deployed via this Web application. Every portlet subsequently based on this web.xml will share access to the context parameters via the PortletContext object. There is no limit on the number of context parameters that may be set. These parameters cannot be changed at runtime by the administrator. For more information about parameters see Parameter summary on page 168. param-name required This indicates the name of the parameter. This name is used in code to retrieve the parameter value. For a summary on the various parameters set in the deployment descriptors, see Parameter summary on page 168. param-value required The String value held by the parameter.
158
servlet required This tag wraps the definition of the servlet class. There must be one or more of these tags. id required This provides an identifier for the defined servlet. This id will be used in the portlet.xml to refer to the defined class. The id must be unique within the Web application only. servlet-name required This name is not used by the portlet environment. servlet-class required The full class name must be provided. This class must be accessible either in the deployed war, via the application server library or through the classpath. init-param optional This element provides an opportunity to set parameters that will be shared by all portlets based on this servlet. These parameters are available in code through the PortletConfig object. There is no limit on the number of init parameters that may be set. These parameters cannot be changed at runtime by the administrator. For a summary on the various parameters set in the deployment descriptors, see Parameter summary on page 168. param-name required Indicates the name of the parameter. This name is used in code to retrieve the parameter value. param-value required The value held by the parameter. servlet-mapping required Though this element is not used by the portal environment it is a required element of the web-app element and therefore must be included. servlet-name required This is required and must be the same as the servlet name defined in the servlet element. url-pattern required This tag is required and may not be empty. The URL pattern is not used in the portal environment. There are numerous other elements in traditional web.xml but they are not required in the portal environment.
159
4.12.2 portlet.xml
The portlet.xml elements are defined by the portlet_1.1.dtd which can be found in the WebSphere\PortalServer\app\wps.ear\wps.war\dtd directory. Figure 4-40 displays a simple portlet.xml.
Example 4-40 portlet.xml deployment descriptor <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE portlet-app-def PUBLIC "-//IBM//DTD Portlet Application 1.1//EN" "portlet_1.1.dtd"> <portlet-app-def> <portlet-app uid="DCE:d798f9c6c:1" major-version="2" minor-version="1"> <portlet-app-name>HelloWorld application</portlet-app-name> <portlet id="HWPortlet_1" href="WEB-INF/web.xml#Servlet_1" major-version="3" minor-version="2"> <portlet-name>HelloWorld portlet</portlet-name> <cache> <expires>30</expires> <shared>yes</shared> </cache> <allows> <maximized/> <minimized/> </allows> <supports> <markup name="html"> <view output="fragment"/> <configure/> <edit/> <help output="document"/> </markup> <markup name="wml"> <view/> </markup> </supports> </portlet> </portlet-app> <concrete-portlet-app uid="DCE:d798f9c6c:1.1"> <portlet-app-name>HelloWorld application</portlet-app-name> <context-param> <param-name>context_param1</param-name> <param-value>A context parameter</param-value> </context-param> <concrete-portlet href="#HWPortlet_1"> <portlet-name>HelloWorld portlet</portlet-name> <default-locale>en</default-locale> <language locale="en"> <title>HelloWorld</title>
160
<title-short>Hello</title-short> <description>A simple hello world portlet.</description> <keywords>Hello World simple</keywords> </language> <language locale="it"> <title>ciao mondo</title> <title-short>ciao</title-short> <description>Un portlet semplice del mondo di ciao. </description> <keywords>Ciao mondo semplice.</keywords> </language> <language locale="es"> <title>hola mundo</title> </language> <config-param> <param-name>config_param1</param-name> <param-value>A configuration parameter</param-value> </config-param> </concrete-portlet> </concrete-portlet-app> </portlet-app-def>
DOCTYPE required This will be the same for each and every portlet.xml deployed. This tag defines the DTD that is to be used when this document is parsed. Only one is allowed. portlet-app-def required This is the top-level tag which encapsulates all abstract and concrete portlet application definitions. It is required and not more than one is allowed. portlet-app required This tag is used to define the abstract portlet application. This abstract application will be used as a basis for the concrete portlet applications defined later in the descriptor. Only one portlet-app tag is allowed per portlet.xml and only one portlet.xml is allowed per war file. Therefore, each war file may only deploy a single abstract portlet application. uid required This ID uniquely identifies this abstract application in the portal server. This ID must unique throughout the entire portal environment. For guidelines on ensuring the ID is unique, refer to UID guidelines on page 171. This ID will be used if the portlet application is updated. Once the ID is determined, it should not be changed between iterations. Doing so will cause updates to fail. The ID may contain any combination of letters and characters to maximum length of 255.
161
major-version optional An optional tag only used by administrators to track releases; it is not used in the portal. It must be a number and only one is allowed. If this attribute is not supplied, the major-version will always be 0. If this attribute is supplied, the minor-version must also be included or the default value of 0 will be assumed. minor-version optional An optional tag only used by the administrators to track releases and not used in the portal. Must be a number and only one is allowed. If this attribute is not supplied, the minor version will be 0. If this attribute is supplied, the major-version must also be included or the default value of 0 will be assumed. portlet-app-name required Only one is allowed. Since only concrete portlet applications are visible in the portal, this name is visible in the portal environment when the full information for the portal application is requested on the Manage Portlet Applications portlet. This name need not be unique. portlet required One or more of these tags is required. This tag defines the abstract portlets contained in the abstract portlet application. Each portlet tag maps to a single servlet defined in the web.xml. There is a one-to-one relationship between the servlets defined in the web.xml and the abstract portlets defined in the portlet.xml. You may not map two abstract portlets to the same servlet. Therefore, if there are two servlets defined in the web.xml, there will be two abstract portlets defined in the portlet.xml id required This ID must be unique within the abstract portlet application only. This ID will be used by the concrete portlets to create a link to the abstract definition. It may be any combination of letters and numbers to a maximum of 64 characters. href required This tag creates the link between the abstract portlet and the servlet defined in the web.xml. The link is formatted as in Example 4-41 where Servlet_1 is the ID defined in the <servlet id> tag of the web.xml.
Example 4-41 href syntax href="WEB-INF/web.xml#Servlet_1"
162
major-version optional An optional tag only used by the administrators to track releases. Not used in the portal. Must be a number and only one is allowed. If this attribute is supplied, the minor-version must also be included or the default value of 0 will be assumed. minor-version optional An optional tag only used by the administrators to track releases. Not used in the portal. Must be a number and only one is allowed. If this attribute is supplied, the major-version must also be included or the default value of 0 will be assumed. portlet-name required Defines the name of the abstract portlet. This name will only be seen in the show info screen of the Manage Portlet Application portlet, not during normal portlet administration or execution. Must be unique within the abstract portlet application only. cache optional This tag indicates the type and level of caching this portlet will perform. If this tag is included, it must contain the expires and shared elements. If the cache element is not included, the default values for expires and shared are 0 and no respectively. expires required Indicates in seconds the when the cached content should be considered expired. 0 indicates the content immediately expires and should always be refreshed -1 indicates the content does not expire.The content will not be refreshed once the portlet is initialized. Any other value measures the cache in seconds. shared required Indicates if the content is to shared among all users or if a cache must be maintained for each user. Use the NO option carefully as a large cache will result. Valid values are yes or no. allows required This tag indicates the portlet states that are supported by this portlet. The normal state is assumed and may not be unsupported. The other valid values are:
163
maximized optional When selected, the portlet will take ownership of the portal screen and other portlets will not be able to return content for inclusion in the portal page. Each portlet on the page will state have the opportunity to execute any listeners it implements, such as PortletPageListener. However, the service method, and by extension doView method of the other portlets will not be executed. minimized optional When selected, the portlet will be displayed as a title bar only. Any listeners implemented by the portlet will be executed but the portlets service, and by extension doView method will not. detached, moving, resizing, closed optional Though these are valid values according to the DTD, they have no corresponding support in the portal server. As such, including or omitting them will have no effect at this point. supports required This element indicate which markup languages this portlet can render its content. It is required and at least one markup may be supported. markup required This tag provides a definition for a single markup that this portlet will support. Each markup that is to be supported is defined in a markup element. name required This attribute identifies the name of the markup defined in this element. Valid strings are html, wml, chtml. If custom markups have been defined, they too would be valid. view required Indicates that at a minimum, this portlet supports View mode. This is required for all markup types. output optional This attribute indicates the type of output the portal server should expect from this portlet. Valid values are document and fragment
Document: Not used in V4.1.2 Fragment: All HTML portlets should use this value.
configure optional Indicates this portlet supports the Configure mode. As with the View mode, it may specify as output fragment or document. This tag has no effect on non-html markup types. The developer is required to implement configure
164
support by including a doConfigure method in the portlet. This tag simply instructs the portal server to include the appropriate link on the portlet title bar. edit optional Indicates this portlet supports the Edit mode. As with the View mode, it may specify as output fragment or document. This tag has no effect on non-html markup types. The developer is required to implement edit support by including a doEdit method in the portlet. This tag simply instructs the portal server to include the appropriate link on the portlet title bar. help optional Indicates this portlet supports the Help mode. As with the View mode, it may specify as output fragment or document. This tag has no effect on non-html markup types. The developer is required to implement help support by including a doHelp method in the portlet. This tag simply instructs the portal server to include the appropriate link on the portlet title bar. concrete-portlet-app required This element defines the concrete portlet application to be deployed into the portal server. One or more of these elements are required. This concrete application is based upon the abstract portlet application defined earlier in the portlet.xml. A concrete portlet application is not required to contain all of the portlets defined in the abstract application. However, it may not define more portlets than the abstract application. Each concrete portlet contained in the concrete application maps to one and only one abstract portlet. An abstract portlet may not be mapped twice in the same concrete application. uid required This uid must be unique throughout the entire portal environment. Refer to 4.12.5, UID guidelines on page 171 for more information about ensuring UIDs are unique. This UID will be used by the portal server when the portlet is updated or deleted. If the ID changes between iterations, the original concrete application will not be update. Instead, a new concrete application will be installed, resulting in multiple concrete applications. Generally, once the ID has been determined, it should not be changed. The ID may contain any combination of letters and characters to a maximum length of 255. portlet-app-name required This is the application name that will be used in the portal server. When the war file is deployed, each of the concrete applications will be listed. This is the name that will appear in that list. This name need not be unique in the portlet.xml or the portal server. However, deploying more than one concrete portlet application with the same name may cause some administrative confusion. If two or more applications are deployed with the exact same name, only one will be initially active. The other application must be manually
165
activated. In practice, when there is a one-to-one relationship between the abstract and concrete portlet applications, the application names are often the same. This name may contain any combination of letters and numbers to a maximum length of 255 and only one is allowed per concrete application. context-param optional This element provides an opportunity to set parameters that will be shared by all concrete portlets defined in the concrete portlet application. These parameters are available in code through the PortletApplicationSettings object. There is no limit on the number of context parameters that may be set. Be aware that these parameters may be changed at runtime by the administrator via the Manage Portlet Applications portlet. For a summary on the various parameters set in the deployment descriptors, see 4.12.3, Parameter summary on page 168. param-name required Indicates the name of the parameter. This name will be seen by the administrator if they decide to work with these parameters at runtime. This is also the name used in code to retrieve the parameter value. The name has a maximum length of 255. param-value required The value intended to be held by the parameter. This value can be changed at runtime by the administrator. The maximum length of the parameter value is 1048576. concrete-portlet required This element wraps the definition of the concrete portlet being deployed in this concrete application. Any number of concrete portlets may be deployed, up to the number of abstract portlets defined in the abstract portlet application. href required This attribute creates a link between the concrete portlet and the abstract portlet. The syntax dictates that the portlet id defined in the abstract application be prefixed with a # symbol as illustrated in Example 4-42.
Example 4-42 Concrete portlet href <concrete-portlet href="#HWPortlet_1">
portlet-name required This tag indicates the administrative name of the portlet. This name is not the title of the portlet and will not be seen by the average end user. This name need not be unique in the portlet.xml or the portal server. However, take care
166
to properly name your portlets to prevent confusion. If two or more portlets are deployed in the same portlet.xml with the exact same name, only one will be initially active. The name may be any combination of characters to a maximum length of 255. default-locale required This element indicates which language is the default language for this concrete portlet alone. This setting will not override the users preferred locale or locale settings provided by the client browser. If the clients locale cannot be determined, this value is used. Also, if the portlet does not support the locale requested by the user, this default locale is used instead. The value must be a recognized country code including, if appropriate, any variants. This value cannot be changed at runtime by the administrator. language required At least one language block must be included. Though not required, it is a best practice to ensure that at a minimum, the default locale is implemented in a language block. In practice, a language block should be provided for each language this portlet intends to support. ideally this includes all ten group 1 languages. Only the languages defined in the portlet.xml will be available. Though the strings can be changed, there is no mechanism to add new languages at runtime. locale required This attribute indicates the locale being defined by this language block. The value must a recognized country code, including any applicable variants. title required This tag specifies the language specific title of this portlet. This title will be seen in the title bar of the portlet at runtime.This value may be changed at runtime by the administrator. The maximum length of the title is 255 characters. title-short optional This tag specifies the language specific short title of the portlet. The maximum length of the short title is 128. description optional This description is used in several places in the portal. For example, in the Edit Layout and Content portlet, the description will display in a hover box over the portlet. The maximum length for the description is 1024 characters.
167
keywords optional This tag specifies the language specific keywords of the portlet. The maximum length of the keywords is 1024 characters. config-param optional This element allows parameters to be passed to the concrete portlet. Unlike context and servlet-config parameters, these parameters are not shared between portlets. Any number of portlet-config parameters may be supplied. The values can be changed at runtime by the administrator via the Manage Portlets portlet. These parameters are accessed in code via the PortletSettings object. For a summary on the various parameters set in the deployment descriptors, see Parameter summary on page 168. param-name required Indicates the name of the parameter. This name will be seen by the administrator if they decide to work with these parameters. This is also the name used in code to retrieve the parameter value. The name has a maximum length of 255. param-value required The value intended to be held by the parameter. This value can be changed at runtime by the administrator. The maximum length of the parameter value is 1048576.
ContextParam
Read/Write
168
169
170
4.13 Resources
For the most up-to-date information about WebSphere Portal, refer to the Portal zone at:
http://www7b.boulder.ibm.com/wsdd/zones/portal/
For help via a news group, visit http://new.software.ibm.com/ and locate the ibm.software.websphere.portal-server news group. For other Redbooks discussing installation and administration, refer to:
http://www.redbooks.ibm.com
171
172
Chapter 5.
173
Development
IBM Rational Application Developer V6.0 Portal Tools V6.0 for Rational Application Developer Sample portlets
Runtime
IBM WebSphere Portal V5.1 Test Environment IBM WebSphere Application Server V5.1 via Portal V5.1 Test Environment Cloudscape V5.1 via Portal V5.1
Run on Server
Figure 5-1 Development and runtime environments
174
Control
HelloWorldPortlet java
set
t ge
Include
Client
Model View
You will start by creating a portlet application using IBM Rational Application Developer. This sample portlet uses the basic portlet type. In this section, you will create a portlet application and then test the application in the WebSphere Portal V5.1 Test Environment. The sample portlet HelloWorld displays a Hello World message and a message indicating the portlet current mode. Portlet modes allow a portlet to display different content to the user, depending on the task required by the portlet. The WebSphere Portal API provides the modes View, Help, Edit and Configure.
175
176
4. If you receive a dialog to confirm the enablement of the Portal Development Tools, click OK.
177
5. In the Portlet Project window, name the project HelloWorld. Click the Show Advanced button, and select the WebSphere Portal V5.1 target server. Click Next to continue the wizard.
178
7. On the Features window, deselect Web Diagram and select JSP Tag Libraries to add these to your project. Click Next to continue.
179
8. Accept the default settings on the Portlet Settings window. Click Next to continue.
180
9. Deselect Add action listener and Add form sample from the Event Handling window. Click Next to continue.
181
10.Since the portlet project does not require credential vault handling, accept the default settings on the Single Sign-On window. Click Next to continue.
182
11.On the Miscellaneous window, select Add Edit mode, Add Help mode, and Add configure mode. Then click Finish to generate your project.
183
12.If you are prompted to switch to the Web perspective to work on this project, answer Yes.
13.The workbench will now show your newly created portlet project in the Web perspective.
184
14.The files for your project can be seen in the Project Explorer view on the left-hand side of the workbench.
185
2. In the Define a New Server window, choose the WebSphere Portal V5.1 Test Environment and click Next.
186
4. On the Portal Server Settings window, accept the defaults and click Next.
5. On the Projects window, add HelloWorld to this server. Then click Finish to configure your new test environment.
187
188
2. Select the existing WebSphere Portal V5.1 Test Environment and click Next.
189
3. Confirm that HelloWorld is configured to run on this server. Click Finish to run the project.
190
4. Your portlet will load in the Web browser configured in the workbench. By default, it will run in View mode, click the icons in the upper right of the portlet to enter configure mode, Edit mode, and Help mode. Configure mode is only available to Portal administrators to change portlet settings.
Configure mode
191
2. Click the Source tab at the bottom of the editor to see the source code.
192
4. Save all of your changes by clicking File Save All. 5. Again, right-click your project in the Project Explorer view. Then click Run Run on Server.
193
6. Choose your existing WebSphere Portal V5.1 Test Environment. You can click Finish to run the project in the Web browser since you have already configured this server to work with this project.
194
195
196
197
198
4. The HelloWorldPortletViewBean.java file will now appear under the /Java Resources/JavaSource/helloworld/ folder. Double-click this file for editing. Modify the source code according to the following example. When you are finished, select File Save All to save your changes.
Example 5-2 JavaBean code public class HelloWorldPortletViewBean { private String myName = ""; public String getMyName() { return myName; } public void setMyName(String myName) {
199
this.myName = myName; } }
5. Next, you will need to modify the HelloWorldPortlet.java file.You will add code that will use the set information in the bean you just created. This code is inserted in the doView() method of the HelloWorldPortlet.java file.
Example 5-3 doView() method code modification public void doView(PortletRequest request, PortletResponse response) throws PortletException, IOException { //Make a bean HelloWorldPortletViewBean viewBean = new HelloWorldPortletViewBean(); //Set your name viewBean.setMyName("John Doe"); //Save bean in the request so the view jsp can read it request.setAttribute("HelloWorldPortletViewBean", viewBean); // Invoke the JSP to render getPortletConfig().getContext().include(VIEW_JSP+getJspExtension(request), request, response); }
6. Now that the bean is created and the portlet can successfully store a value in this bean, it is necessary to modify the code to the HelloWorldPortletView.jsp file so that the value can be extracted from the bean and shown on the screen. Double-click the HelloWorldPortletView.jsp file and make the following changes. The useBean tag tells the JSP file that it will be accessing values stored in a JavaBean.
Example 5-4 View JSP code modification <jsp:useBean id="HelloWorldPortletViewBean" class="helloworld.HelloWorldPortletViewBean" scope="request"></jsp:useBean> <%@ page session="false" contentType="text/html" import="java.util.*, helloworld.*"%> <%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %> <portletAPI:init/> <DIV style="margin: 6px"> <H3 style="margin-bottom: 3px">Welcome!</H3>
200
This is a sample <B>view mode</B> page. You have to edit this page to customize it for your own use.<BR> The source file for this page is "/Web Content/helloworld/jsp/html/HelloWorldPortletView.jsp". <br> Current time: <%=new java.util.Date() %> <br> Hostname: <%= request.getRemoteHost() %> <br> Updated by <%= HelloWorldPortletViewBean.getMyName() %> </DIV>
7. Again save your changes by clicking File Save All. 8. Again, right-click the HelloWorld project, and select Run Run on Server. Click Finish on the Server Selection window to run it on the existing test environment server. Your changes will be shown in the Web browser.
201
202
Chapter 6.
203
When a portlet wishes to be notified that a user has performed an action, it has to implement the ActionListener interface and a portlet action. Only the portlet generating the event may listen for that event. There will always be only a single listener for any particular action event. The ActionListener interface provides the actionPerformed() method, to which an ActionEvent is passed. This event listener is implemented directly in the portlet class. The listener can access the PortletRequest from the event and respond using the PortletRequest or PortletSession attributes. In order to notify other portlets of an event, the listening portlet may choose to send messages.
204
A portlet has two phases of processing and rendering sequences. The first phase is the action processing phase. All events are generated, delivered and processed in this phase. Once this phase is complete, the service phase begins, in which portlets outputs are rendered. Once this phase has begun, no events can be generated without causing an exception. The service method is also called when a portal page is refreshed. The objects you will need to work with when managing event handling in action events are described below.
ActionListener
The org.apache.jetspeed.portlet.event.ActionListener interface defines a single method to be implemented as illustrated in Example 6-2.
Example 6-2 Implementing ActionListener interface org.apache.jetspeed.portlet.event.ActionListener public void actionPerformed(org.apache.jetspeed.portlet.event.ActionEvent event) throws PortletException;
ActionEvent
An ActionEvent is sent by the portlet container when an HTTP request is received that is associated with a portlet action. Note: The getAction() method returns the action that this action event carries but it is deprecated in favor of a getActionString() method. The getActionString() method returns the action string that this event carries. Simple portlet actions use a single string as portlet action which can be executed multiple times and does not require a session.
Example 6-3 Working with the ActionEvent. public void actionPerformed(ActionEvent event) throws PortletException { String actionString = event.getActionString(); PortletRequest request = event.getRequest(); }
PortletURI
The PortletURI represents a URL that can be used to create navigation between modes. The PortletURI can be used to navigate to a previous mode, such as from Edit to View, or to navigate back to the same mode, such as for a multi-part
205
form in View or Edit. There is no ability to create a PortletURI object pointing to a mode not yet visited by the user. PortletResponse.createURI returns a portletURI object pointing to the portlet in its current mode. For example, if the portletURI is created in the doView mode, the URL points to the portlet in View. The createReturnURI method returns a PortletURI object pointing to the last mode the portlet was in. This mode is commonly used in the doEdit method when the URI needs to point back to the View mode. The edit.jsp would use the PortletURI to bring the user back to the View mode when the edit or configure process has been completed. In order for a portlet to be notified of an event, such as the user clicking a button, the portletURI must contain an associated PortletAction. Typical PortletURI construction and usage is shown in Example 6-4.
Example 6-4 Working with PortletURI PortletURI uri = response.createReturnURI(); uri.addAction(save); request.setAttribute("uri", uri.toString());
It is possible to add parameters to the PortletURI object. Parameters added to the PortletURI via code or through a form are accessed in the same way via the portlet request object. This provides a mechanism to pass default values or to pass parameters not displayed in the form. Example 6-5 displays the code for adding a parameter. Be aware that parameters set via the PortletURI are not passed in the traditional HTML syntax. Example 6-5 shows how parameters are added to the URI.
Example 6-5 Adding a parameter to the PortletURI public void doView(PortletRequest request, PortletResponse response) throws PortletException, IOException { PortletURI viewURI = response.createReturnURI(); viewURI.addAction(save); viewURI.addParameter("Param1", "Param1Value"); request.setAttribute("viewURI", viewURI.toString()); getPortletConfig().getContext().include("/jsp/View.jsp", request, response); }
Portlet.ModeModifier
When a PortletURI is created, it points to a portlet in a particular mode. When that PortletURI is executed and if it contains a PortletAction, it will notify the appropriate listener. If, in the actionPerformed method, you need to redirect the user to a mode other than the one specified, the request.setModeModifier
206
method can be used to redirect the user to another mode. The ModeModifier can only be set during event processing. Calling this method in doView or doEdit will have no effect. There are three possible modes to which the user can be redirected. REQUESTED: this ModeModifier will navigate the user to whatever mode was originally set by the PortletURI. Essentially, this is the default. If the ModeModified is changed, it cannot be changed back to REQUESTED. CURRENT: this ModeModifier will keep the user in the current mode. For example, if the user tries to save some information and the actionPerformed determines it is incorrect, setting ModeModifer to CURRENT will return the user to the edit screen. PREVIOUS: this ModeModifier will return the user to the mode the user was in prior to CURRENT regardless of previous ModeModification. Therefore, setting ModeModifer to CURRENT in one event process will not make that mode PREVIOUS in the next event process.
Development
IBM Rational Application Developer V6.0 Portal Tools V6.0 for Rational Application Developer Sample portlets
Runtime
IBM WebSphere Portal V5.1 Test Environment IBM WebSphere Application Server V5.1 via Portal V5.1 Test Environment Cloudscape V5.1 via Portal V5.1
Run on Server
Figure 6-1 Development environment
207
We will start by creating a portlet application using IBM Rational Application Developer. This portlet is again based on the Model-View-Controller (MVC) design pattern. In this scenario, you will create a portlet with support for action events using the Portlet Project wizard. To send an ActionEvent you must associate a PortletAction with the HTTP request. The PortletAction is normally linked with URLs or buttons in an HTML form to provide a way for portlet programmers to implement different processing actions for different user input or interaction.
Portal
1
ActionPortlet doEdit
7
action Performed
doView
2 4
Browser
5 6
View Mode
Edit Mode
The sequence flow for this scenario is as follows: 1. Initially, the doView method is executed. 2. A JSP is called to render an initial screen. A message is obtained from the request object. 3. The Portlet View mode screen is shown in the browser window. 4. The user clicks Edit to go into Edit mode. 5. The Edit mode screen is displayed (the doEdit method is executed). 6. The user selects the desired action button (red or blue). 7. The actionPerformed method is executed to process the action. A resulting message is stored in the request object.
208
8. The doView method is executed to complete the cycle and a message is obtained from the request object.Starting your portlet development project
3. Name the project ActionEvent. Click the Show Advanced button and select WebSphere Portal V5.1 as the target server. Then click Next.
209
4. Choose the Basic Portlet type. Click Next to continue. 5. On the Features window, deselect Web Diagram and select JSP Tag Libraries. Then click Next to continue.
210
6. Accept the default settings on the Portlet Settings window. Click Next to continue. 7. On the Event Handling window, deselect Add form sample. Since you will be using the action event listener, leave this option selected. Click Next to continue.
211
8. Since you will not be using the credential vault in this project, accept the defaults on the Single Sign-On window and click Next. 9. Click Add Edit mode on the Miscellaneous window. You will be using it in this scenario. Click Finish to generate your portlet project.
212
10.Your project will display in the Project Explorer view on the left-hand side of the workbench.
213
214
2. Use the Add and Remove buttons to remove HelloWorldEAR from the test environment and to add ActionEventEAR to the test environment. Click Finish to finish this configuration.
215
216
2. After making these updates, click File Save All to save your changes. You may see errors listed in the Problems view. The changes you will make in other files will correct these errors. 3. Next, you will make changes to the ActionEventPortletView.jsp file. Open it by double-clicking the file which is located in the /WebContent/actionevent/jsp/html/ folder. Make the changes highlighted in the following example.
Example 6-7 ActionEventPortletView.jsp <%@ page session="false" contentType="text/html" import="java.util.*, actionevent.*"%> <%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %> <portletAPI:init/> <DIV style="margin: 6px"> <H3 style="margin-bottom: 3px">Welcome!</H3> This is a sample <B>view mode</B> page. You have to edit this page to customize it for your own use.<BR> The source file for this page is "/Web Content/actionevent/jsp/html/ActionEventPortletView.jsp". <br> <% if (request.getAttribute("value") == null) { %> <B>No action performed, select your action in Edit Mode</B> <% } else { %> <B><%= request.getAttribute("value") %> ...was selected !</B> <% } %> </DIV>
4. Click File Save All to save your changes. 5. Next, you will make the changes to ActionEventPortlet.java. Open this file for editing by navigating to the /Java Resources/JavaSource/actionevent/ folder and double-clicking it. 6. Several changes need to be made to ActionEventPortlet.java. Refer to the following examples to make these: a. First, you will add the variables ACTION_RED and ACTION_BLUE to hold the values for each of the two possible user actions.
Example 6-8 ActionEventPortlet.java ... public class ActionEventPortlet extends PortletAdapter implements ActionListener {
217
public static final String VIEW_JSP = "/actionevent/jsp/ActionEventPortletView."; // JSP file name to be rendered on the view mode ... // Add strings corresponding to the actions public static final String ACTION_RED = "ACTION.RED"; public static final String ACTION_BLUE = "ACTION.BLUE"; ... public void init(PortletConfig portletConfig) throws UnavailableException { super.init(portletConfig); ... }
b. Next, you will edit the doView method to send content to the JSP to render.
Example 6-9 ActionEventPortlet.java ... public void doView(PortletRequest request, PortletResponse response) throws PortletException, IOException { // Create an instance of portlet data to store values PortletData portData = request.getData(); // Extract value in portlet data into variable String value = (String) portData.getAttribute("value"); // Store the extracted value in the request request.setAttribute("value", value); // Invoke the JSP to render getPortletConfig().getContext().include(VIEW_JSP+getJspExtension(request), request, response); } ...
c. Next, you will edit the actionPerformed method to process the action. Remove the existing action string handler, and add the code from the following example.
Example 6-10 ActionEventPortlet.java public void actionPerformed(ActionEvent event) throws PortletException { if( getPortletLog().isDebugEnabled() )
218
getPortletLog().debug("ActionListener - actionPerformed called"); // ActionEvent handler String actionString = event.getActionString(); PortletRequest request = event.getRequest(); // Add action string handler here if(actionString.equalsIgnoreCase(ACTION_RED)){ // Create the string of HTML to be rendered String value = "Action <FONT color=\"#ff0000\">RED</FONT>"; // Create an instance of portlet data to store values PortletData portData = request.getData(); try{ // Save value into portlet data portData.setAttribute("value", value); portData.store(); } catch (AccessDeniedException ade){ }catch (IOException ioe){} } if(actionString.equalsIgnoreCase(ACTION_BLUE)){ // Create the string of HTML to be rendered String value = "Action <FONT color=\"#0000ff\">BLUE</FONT>"; // Create an instance of portlet data to store values PortletData portData = request.getData(); try{ // Save value into portlet data portData.setAttribute("value", value); portData.store(); } catch (AccessDeniedException ade){ }catch (IOException ioe){} } }
219
1. Right-click the ActionEvent project in the Project Explorer view. Then select Run Run on Server.
2. Select your existing WebSphere Portal V5.1 Test Environment server and click Next.
220
3. Confirm that the ActionEventEAR project is the only one configured to run in the test environment. Click Finish to run your project.
221
4. Your project will run in the Web browser. Since no action has been made, a message is displayed to indicate this. Enter Edit mode to choose an action by click the Edit mode icon.
222
6. A message will be displayed according to which button you select. You may enter Edit mode repeatedly to select an action.
223
224
Chapter 7.
Portlet messaging
Portlet messaging is provided for portlet applications using the IBM Portlet API. This chapter describes what portlet messaging is and the objects you will need to work with when messaging between portlets. MessageListener MessageEvents DefaultPortletMessage PortletMessage
225
7.2 MessageListener
The org.apache.jetspeed.portlet.event.MessageListener interface must be implemented by the portlets receiving a message. The interface defines the single method listed in Example 7-1 on page 227. Since the portlet may be notified by more than one other portlet and therefore may receive different types of messages, it should validate the type of message received prior to working with the object. This is illustrated in Example 7-1 on page 227.
226
Example 7-1 Implementing the MessageListener interface public void messageReceived(MessageEvent event) throws PortletException { PortletMessage msg = event.getMessage(); if( msg instanceof DefaultPortletMessage ) { String messageText = ((DefaultPortletMessage)msg).getMessage(); // Add DefaultPortletMessage handler here } ..... }
Be aware that when a portlet receives a message, it is not in Edit or Configure mode and therefore faces certain restrictions. For instance, portlets do not have write access to the PortletData object when they are not in Edit mode. Also, they cannot adjust the attributes stored in the PortletSettings object unless they are in configure mode. Attempts to store attributes in these object when not in the appropriate mode result in an AccessDeniedException. Therefore, when attempting to share configuration or settings information between portlets, you need to choose your scope carefully or decide to persist to an outside resource.
7.3 MessageEvent
This object is sent to registered MessageListeners by the portlet container when a portlet executes the send method of the PortletContext object. There are two important methods available in this object getMessage: returns the message object sent with this event. Since this method returns a PortletMessage, the result must be cast to the appropriate type as illustrated in Example 7-1. getRequest: returns the current PortletRequest. The request can be used to access the PortletSession object or to store data to be used in the doView method.
7.4 DefaultPortletMessage
This object implements the PortletMessage interface and provides the basic functionality needed for sending string messages between portlets on the same page regardless of the portlet application.
227
Note: Since portlet messaging can be accomplished across portlets in different applications, this is the recommended way to implement portlet messaging. If you broadcast a DefaultPortletMessage to null, it will be sent to all portlets on the page implementing the MessageListener interface. Example 7-2 illustrates sending a simple broadcast message to all portlets on the same page regardless of application affiliation.
Example 7-2 Broadcasting a message to all portlets on a page PortletMessage msg = new DefaultPortletMessage(Some Message); getPortletConfig().getContext().send(null, msg);
If you specify the portlet name, the message will be sent to all portlets and all their instances on the same page. The portlets with that name receive the message if they have implemented the appropriate listener. If the source and target portlet have the same name, the message will not be sent to avoid cyclic calls.
Example 7-3 Sending a message to a given portlet name on a page PortletMessage msg = new DefaultPortletMessage(Some Message); getPortletConfig().getContext().send(Portlet name, msg);
7.5 PortletMessage
This interface defines the message object that will be sent between portlets inside the same portlet application on the same page. Since it is a flag interface, it does not define any methods to be implemented. Therefore, you are free to create message objects that can store a wide variety of information. Example 7-4 illustrates a simple custom message used to carry a detail information about an entry of an agenda.
Example 7-4 Creating a custom message import org.apache.jetspeed.portlet.PortletMessage; public class AgendaMessage implements PortletMessage { private AgendaBean entry; public AgendaBean getEntry() { return entry; }
228
If you simply need to send a string message between portlets, the DefaultPortletMessage provides this basic functionality. It is not possible to send a broadcast message using custom messages. Sending a custom message to null will only send the message to portlets implementing the MessageListener interface on the same page and deployed as part of the same portlet application. This is illustrated in Example 7-5; a message is sent with the information of an entry selected in other portlet in the same application.
Example 7-5 Sending a custom message public void actionPerformed(ActionEvent event) throws PortletException { if( getPortletLog().isDebugEnabled() ) getPortletLog().debug("ActionListener - actionPerformed called"); // ActionEvent handler String actionString = event.getActionString(); PortletRequest request = event.getRequest(); // Add action string handler here if ( actionString != null && actionString.startsWith(ACTION_DETAILS)) { //get the entry selected from the actionString String opc = actionString.substring(actionString.indexOf("=")+1); int elem = Integer.valueOf(opc).intValue(); Vector list = getSessionAgenda(request); AgendaBean entry = (AgendaBean)list.elementAt(elem); //send a message with this object AgendaMessage msg = new AgendaMessage(); msg.setEntry(entry); getPortletConfig().getContext().send(null,msg); } } .....
If a portlet wants to receive this message, it has to implement the messageListener interface.
Example 7-6 Receiving a custom message public void messageReceived(MessageEvent event) throws PortletException { if( getPortletLog().isDebugEnabled() ) getPortletLog().debug("MessageListener - messageReceived called"); // MessageEvent handler PortletMessage msg = event.getMessage(); // Add PortletMessage handler here if( msg instanceof AgendaMessage ) {
229
AgendaBean detailEntry = ((AgendaMessage)msg).getEntry(); // Add DefaultPortletMessage handler here PortletRequest request = event.getRequest(); request.setAttribute("detailEntry", detailEntry); } else { // Add general PortletMessage handler here } }
Now you can see all the entries in one portlet and detailed information about an entry you selected previously in the other portlet. Figure 7-1 shows the result after selecting the third entry of the agenda.
230
For this sample scenario, the action event sample portlet application (see Chapter 6, IBM Portlet API portlet development on page 203) will be modified to include message events so that you will see an example of how these can work together within portlet applications.
7.6.1 Description
In this scenario, you will enhance the ActionEvent portlet application to send messages to a new message receiver portlet as illustrated in Figure 7-2.
MessageReceiver.java
ActionEventPortlet.java
Portal
action Performed Bean JSP
doView
3
doView
2
Action Red Action Blue
4 JSP
Browser
View mode
Edit mode
5 View
mode
Figure 7-2 shows the flow for this scenario, as follows: 1. The actionPerformed() method in the ActionEventPortlet.java portlet will be extended to send a broadcast message event (DefaultPortletMessage) upon arrival of action events. 2. The MessageReceiver portlet, which will implement the MessageListener interface, receives the message in the new messageReceived method. 3. The received message is saved in the PortletRequest object. 4. The Portal invokes the doView method which in turn invokes the JSP (select). 5. The JSP retrieves the message from the request object and displays the message.
231
This scenario will be implemented using a broadcast style of message event rather than point-to-point messaging. In addition, the DefaultPortletMessage object will be used. Note: While the DefaultPortletMessage object allows you to send messages to portlets in different applications, you can only send a String type message.
232
// Save value into portlet data portData.setAttribute("value",value); portData.store(); } catch (AccessDeniedException ade) { }catch (IOException ioe) { } // Send a portlet message PortletMessage message = new DefaultPortletMessage(value); try { this.getPortletConfig().getContext().send(null, message); }catch (AccessDeniedException ade){} } if( actionString.equalsIgnoreCase(ACTION_BLUE) ) { // Create the string of HTML to be rendered String value = "Action <FONT color=\"#0000ff\">BLUE</FONT>"; // Create an instance of portlet data to store values PortletData portData = request.getData(); try { // Save value into portlet data portData.setAttribute("value",value); portData.store(); } catch (AccessDeniedException ade) { }catch (IOException ioe) { } // Send a portlet message PortletMessage message = new DefaultPortletMessage(value); try { this.getPortletConfig().getContext().send(null, message); }catch (AccessDeniedException ade){} } } ...
4. Save and close the ActionEventPortlet.java file. 5. Next, you will slightly update the ActionEventPortletView.jsp page to notify that you are now sending a message. Double-click the ActionEventPortletView.jsp under the /WebContent/actionevent/jsp/html/ directory.
233
234
</DIV>
7. Save and close the ActionEventPortletView.jsp file. Note: At this point, you have implemented all the required logic in ActionEventPortlet to be able to send a broadcast message from within its actionPerformed() method.
235
3. Click Next. 4. Examine and accept the default values in the portlet settings window and click Next. 5. Uncheck the Add form sample and the Add action listener boxes. Check the Add message listener box to add the messageReceived method. Click Next.
236
6. Do not check the Add credential vault handling box (not required in this sample scenario). Click Next. 7. Leave the options for markups and modes unchecked. Click Finish to add the portlet to your project. 8. You will now see the new portlet files in the Project Explorer panel.
237
9. Open the MessageReceiverPortlet.java file located in the /Java Resources/JavaSource/messagereceiver/ folder by double-clicking it. 10.Add the following highlighted code to this file to receive the broadcast Portlet Message in the messageReceived() method. Note: The messageReceived() method implements the logic to receive the PortletMessage. In this example, you only need to check for messages of type DefaultPortletMessage, which is the type of message sent by ActionEventPortlet. Then the message is extracted via the getMessage() method, and you set the text of this message into a portlet request as an attribute with name MyMessage.
Example 7-9 MessageReceiverPortlet.java ... public void messageReceived(MessageEvent event) throws PortletException { if( getPortletLog().isDebugEnabled() )
238
getPortletLog().debug("MessageListener - messageReceived called"); // MessageEvent handler PortletMessage msg = event.getMessage(); // Add PortletMessage handler here if( msg instanceof DefaultPortletMessage ) { String messageText = ((DefaultPortletMessage)msg).getMessage(); // Add DefaultPortletMessage handler here PortletRequest request = event.getRequest(); request.setAttribute("MyMessage", messageText); } else { // Add general PortletMessage handler here } } ...
11.When you are done, save the file and exit. 12.Now that you can receive the message, you will need to modify the MessageReceiverPortletView.jsp to display the message to the user. To edit this file, open the /WebContent/messagereceiver/jsp/html/ folder and double-click the file. Note: The Java code inside the scriptlet checks the value of the portlet request attribute MyMessage. If null, no message has been received yet, and it displays that it is ready to receive a message. If not null, the message is displayed with HTML markup. Make the following changes.
Example 7-10 MessageReceiverPortletView.jsp file (message receiver portlet) <%@ page session="false" contentType="text/html" import="java.util.*, messagereceiver.*"%> <%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %> <portletAPI:init/> <DIV style="margin: 6px"> <H3 style="margin-bottom: 3px">Welcome!</H3> This is a sample <B>view mode</B> page. You have to edit this page to customize it for your own use.<BR> The source file for this page is "/Web Content/messagereceiver/jsp/html/MessageReceiverPortletView.jsp". <br> <% if (request.getAttribute("MyMessage") == null) { %> <B>Ready to receive message ... </B> <% } else { %> <B>Received a message:</B> <B><%= request.getAttribute("MyMessage") %></B> <% } %>
239
</DIV>
13.When you are done, save the file and exit. You have now implemented the code to receive and display a broadcast portlet message to the user.
Figure 7-7 Running the messaging project in the Portal Server Test Environment
240
6. Choose the Edit mode of ActionPortlet. Click the Red Action button. This will both (a) create an action that you will see the action in ActionPortlet, and (b) broadcast a message which will be sent and shown in MessageReceiver.
7. You will see the value shown in both the ActionEvent portlet and the MessageReceiver portlet. Note: In summary, you have seen how the IBM portlet API implements message events which can be useful for passing data between portlets that need to be notified of other portletss actions and events. This is a very useful feature of the API when building portlet applications that contain multiple portlets.
241
8. You can enter Edit mode again and select the Blue Action button.The results will again be displayed accordingly.
242
Example 7-11 MessagePortlet.java public void messageReceived(MessageEvent event) throws PortletException { if( getPortletLog().isDebugEnabled() ) getPortletLog().debug("MessageListener - messageReceived called"); // MessageEvent handler PortletMessage msg = event.getMessage(); // Add PortletMessage handler here if( msg instanceof DefaultPortletMessage ) { String messageText = ((DefaultPortletMessage)msg).getMessage(); // Add DefaultPortletMessage handler here PortletRequest request = event.getRequest(); request.setAttribute("message", messageText); } else { // Add general PortletMessage handler here } }
4. You need to configure the new project to run in test environment. Right-click WebSphere Portal V5.1 Test Environment server and select Add and remove projects. Select MessageEAR in the available projects panel and click Add.
243
5. Click Finish. 6. Before starting the server open the server properties by double-clicking in WebSphere Portal 5.1 Test environment server. Click Portal panel and check enable base portlets for portal administration and customization option. Note: Portlets that send and receive message must be on the same page. By default, when you configure more than one project on the server they will appear in different pages. You will need to customize the ActionEvent page adding the new Message portlet.
244
7. Save and close the server options window. 8. Right-click the ActionEvent project or Message project and choose Run Run on Server. 9. When the Web browser appears, you will see the applications in different pages.
245
10.To customize your page, select Administration in the portal theme. You need to log in as an administrator user to see this option. 11.In Administration window, select on the left panel Manage Pages under Portal User Interfaces. On the right panel, you will see options to work with your pages. Select My Portal and then Test Environment. You will see a page as show in Figure 7-14.
246
13.Click Add portlet button. A new window appears with all portlets that have been installed. Fill the search file with the name of the portlet you are looking for, Message in our example, and click Search
14.In the results of your search you will see the Message portlet, check it and click OK.
247
15.Now you will see the Message portlet in ActionEvent page layout. 16.Click DONE. 17.Click My Portal to test the application. 18.You will see the tree portlets on ActionEvent page.
19.Go to the Edit mode of the ActionEvent portlet and click the Red action button; now both portlets (the portlet in the same application of ActionEventPortlet and the portlet in the other application) display the message.
248
2. Save the change and run the application. Now it is not necessary to restart the server; you only have to close the browser and run the project. Go to the Edit mode of the ActionEvent portlet and click the Red action button; now only the portlet called Message portlet receives the message.
249
250
Chapter 8.
251
WebSphere Portal starting with version 5.0.2.1 provides a runtime environment for both the IBM Portlet API, which is based on org.apache.jetspeed.portlet interfaces and JSR 168 compliant portlets. The basic concepts are similar between the two APIs; however, several differences do exist. Note: Portlets of either API can be placed on the same portal page. However, a portlet cannot mix classes and methods from both packages although the JSR 168 API can take advantage of some of the IBM specific features, including Cooperative portlets and portlet services like the Credential Vault.
252
The user will use the administrators portletPreferences object until they set their own; after that, changes made by the administrator will not be reflected in the users portletPreferences. As an example, a portlet is placed on a page that displays your name. The administrator has access to the Edit and Configuration modes. The user will only have access to the Edit mode. The administrator modifies the name using the Configuration mode to enter his name. The user then logs in and sees the administrator name. The user changes the name in Edit mode, adding their own name. Now the user will always see their own name even if the administrator updates it in Config mode. This user has their own portlet window representing this portlet.
Portlet Definition
portlet preferences
portlet.xml
PortletPreferences object
Portlet Entity
PortletPreferences object
PortletPreferences object
Portlet Window
Figure 8-1 Portlet window
Portlet Window
253
and render methods. You should not reference the PortletRequest or PortletResponse in any object outside these methods. WebSphere Portal server will handle multiple concurrent requests to the same portlet request handling methods on multiple threads. If the request handling methods modify class variables, these variables will also be modified for all instances of the portlet. This can cause unexpected problems in your portlets that are difficult to debug. Use method local variables to avoid this problem.
254
Portlet can change the mode programmatically in the processAction method. You can also specify the mode when creating an action or render link. See interface javax.portlet.PortletURL on page 261.
View
The View mode is used for displaying content reflecting the current state of the portlet. View mode may included multiple screens the user can navigate. The doView() method of the GenericPortlet class is invoked for this mode. All portlets are required to support the View mode.
Edit
The Edit mode is used for customizing the behavior of the portlet by modifying the PortletPreferences object. As with View mode the Edit mode may contain multiple screens for navigation. The doEdit() method of the GenericPortlet class is invoked for this mode. Portlets are not required to support the Edit mode.
Help
Help should be used to provide the user with information about the portlet. This could be generic or it could provide context-sensitive help. The doHelp() method of the GenericPortlet class is invoked for this mode. Portlets are not required to support the Help mode
Custom-portlet-mode
Note: WebSphere Portal will only support the custom portlet mode Configuration. Configuration mode is used to globally update a portlet configuration. In Configuration mode an administrator can update the read-only PortletPreferences. Changes are reflected in all occurrences of the portlet on all pages. The doDispatch() method of the GenericPortlet class needs to be overridden to handle this custom mode. The code in Example 8-1 was generated from Ration Application Developer when custom mode was selected in the portlet creation process.
Example 8-1 Overridden doDispatch() method to handle the custom Config mode protected void doDispatch(RenderRequest request, RenderResponse response) throws PortletException, IOException { if( !WindowState.MINIMIZED.equals(request.getWindowState()) ){ PortletMode mode = request.getPortletMode(); if( CUSTOM_CONFIG_MODE.equals(mode) ) { doCustomConfigure(request, response); return;
255
} } super.doDispatch(request, response); }
The doDispatch() method calls the doCustomConfigure() method of the portlet class if the current mode is represented by the CUSTOM_CONFIG_MODE variable, else it calls the doDispatch of GenericPortlet class to handle all other modes.
NORMAL
Normal indicates that the portlet will be provided equivalent space on the page as other portlets. This portlet could also be the only portlet on the page. The Show layout tools in WebSphere Portal could be used to further customize the layout of the portlet on the page.
MAXIMIZED
Maximized state indicates that the portlet will be the only portlet displayed on the page.
MINIMIZED
The default implementation of the GenericPortlet class and the IBM supplied skins, are to show only the portlet title bar when in the minimized state. The skins will not invoke the render method and therefore will not display any of the portlets output. You can modify the control.jsp for the skin as seen in Example 8-2 on page 257. You will also need to override the doDispatch() method to get different results as seen in Example 8-3 on page 257.
256
Note: Example 8-2 shows the portletState solo. This is to support portlets written using the IBM Portlet API. Solo is not a supported window state in the JSR 168 API.
Example 8-2 Modifying control.jsp in the skin <wps:if portletState="Normal,Maximized,Minimized,Solo"> Example 8-3 Overriding doDispatch protected void doDispatch(RenderRequest request, RenderResponse response) throws PortletException, IOException { if(request.getWindowState() == WindowState.MINIMIZED){ response.setContentType(request.getResponseContentType()); response.getWriter().println("Portlet is minimized!<br />"); } else { super.doDispatch(request, response); } }
257
void destroy()
This method is called when the portlet is being removed from service. Use this method to release any resources or write persistent state. Once the destroy method has been called no request can be sent to this portlet. If needed, a new portlet object will be instantiated to serve new requests. If there is a RuntimeException thrown during the destroy method, the destroy is considered to have completed successfully. The portlet object is eligible for garbage collection after the destroy method has completed.
258
It should not be assumed that the portlet will change mode or window state as WebSphere Portal server may override the change. Changes to the window state and mode are made through the ActionResponse object. Code in this method should be written to handle concurrent execution from multiple threads.
java.lang.Enumeration getInitParameterNames()
Returns all the initialization parameters names in an Enumeration of Strings. If there are no initialization parameters defined then an empty Enumeration is returned.
259
javax.portlet.PortletContext getPortletContext()
Returns the PortletContext for the portlets portlet application. Please see interface javax.portlet.PortletContext on page 263 for more information about the PortletContext class.
java.lang.String getName()
Returns the name of the portlet defined by either the administrator or the application deployment descriptor.
java.util.ResourceBundle getResourceBundle(java.util.Localelocale)
Portlets may define basic information to be used in the title bar. This information may include title, short-title and keywords. This information can be added directly in the portlet deployment descriptor or it may be added in an external ResourceBundle. If this information is located in an external ResourceBundle, the portlet deployment descriptor must provide the name of the ResourceBundle. Portal will search the values in the ResourcesBundle first and then it will look for the resources inline. If the resource cannot be found, an empty String will be returned. The render method of the GenericPortlet uses this method to get the portlet title.
260
PortletConfig getPortletConfig()
This method returns the PortletConfig object for this portlet. Please see interface javax.portlet.PortletConfig on page 277 for more information about the PortletConfig class.
java.lang.String getPortletName()
This returns the name of the portlet.
261
java.lang.String toString()
This method will return a URL in the correct form for the portal server. See Example 8-4.
Example 8-4 Using an actionURL in a link <% PortletURL actionurl = renderResponse.createActionURL(); %> <a href="<%= actionurl.toString() %>">ActionURL</a><BR>
262
int getMajorVersion()
Returns the major version of the JSR 168 Portlet API that is supported by the Portal server.
int getMinorVersion()
Returns the minor version of the JSR 168 Portlet API that is suported by the Portal server.
263
java.lang.String getPortletContextName()
This returns the display-name element defined in the web.xml. This represents the name of the portlet application for this PortletContext.
Example 8-5 getPortletContextName() web.xml <web-app id="WebApp_ID"> <display-name>MyPortlet</display-name> .... </web-app> Code in portlet String portletDisplayName = getPortletContext().getPortletContextName(); System.out.println("Display name: " + portletDisplayName); Output in console Display name: MyPortlet
264
while(enum.hasMoreElements()){ String name=(String)enum.nextElement(); Enumeration values = request.getProperties(name); StringBuffer valueBuffer = new StringBuffer(); while(values.hasMoreElements()){ //seperate values with a ';' valueBuffer.append((String)values.nextElement() + "; "); } System.out.println("Name: " + name + ", Value: " + valueBuffer.toString()); } Example 8-7 Output from the above code while running a sample portlet Name: accept-encoding, Value: deflate; gzip; Name: connection, Value: Keep-Alive; Name: referer, Value: http://localhost:9081/wps/myportal/!ut/p/kcxml/04_Sj9SPykssy0xPLMnMz0vM0Y_QjzKL N4g39DEDSUGYpvqRaGLGmEKOCBFvfV-P_NxU_QD9gtzQ0IhyR0UAATozOQ!!/delta/base64xml/L0 lJWWtpaWxDbEEhIS9JRGpBQUFUQUFNSkFBTXdzaXNwc1lBISEvNElVR1JZUWxHamdJLzZfMF8xTDYvN 18wXzFSRQ!!; Name: host, Value: localhost:9081; Name: accept-language, Value: en-us; Name: user-agent, Value: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0); Name: cookie, Value: JSESSIONID=00004jB0KukifHc_3h_7QBn9D-z:-1; Name: wps.markup, Value: html; Name: accept, Value: */*;
The following methods should be used for getting and setting request parameters:
java.util.Enumeration getParameterNames()
Returns all the parameter names in the form of an Enumeration of Strings or null if no parameters exist.
java.lang.Map getParameterMap()
Returns a Map of all the parameters. The keys are Stings and the values are stored in String [] (String arrays). If there are no parameters, an empty Map will be returned.
265
The following methods should be used for getting and setting request attributes.
java.util.Enumeration getAttributeNames()
Returns an Enumeration of the names of all the attribute.
java.lang.String getContextPath()
The path returned starts with / character but it does not end with a /. This method should be used when including resources such as images
Example 8-8 Code to get the context path of the portlet <%= portletResponse.encodeURL(renderRequest.getContextPath() + "/images/myImage.jpg") %>
PortletMode getPortletMode()
This method will return the current portlet mode.
WindowState getWindowState()
Returns the current window state of the portlet.
266
java.util.Enumeration getPropertyNames()
Returns an Enumeration of Strings containing all the properties for this request. An empty Enumeration is returned if there are no properties.
java.lang.String getRemoteUser()
This method will return the user who is logged in. If the user is not logged in null will be returned. Even with security disabled this method will return the user information, although with security disabled the returned name may not be human-readable. With security enabled a more readable user name will be returned.
javax.security.Principal getUserPrincipal()
Returns the Principal object of the current user who is logged in. If security is not enabled, this method will always returns null.
267
Example 8-9 Form is a JSP using multipart encoding to upload a file. <form method="POST" action="<portlet:actionURL />" enctype="multipart/form-data"> <input type="file" name="<%= UploadPortlet.FILE_NAME %>"><br /> <INPUT name="<%=UploadPortlet.FILE_SUBMIT%>" value="Upload" type="submit"/> </form>
java.io.BufferedReader getReader()
This returns a BufferedReader containing the body of the HTTP request as character data. The character data returned will be of the same type as the character encoding of the body. If the character encoding is not supported, an UnsupportedEncodingException will be thrown. When the data is of type application/x-www-form-urlencoded, portal has already processed the form and the parameters will be available via request parameters. An IllegalStateException will be thrown if the data is of type application/x-www-form-urlencoded. Either the getReader or the getPortletInputStream may be called for the ActionRequest object but not both. If the getPortletInputStream has been called, an ensuing call to getReader will throw an IllegalStateException.
java.io.InputStream getPortletInputStream()
An InputStream containing the HTTP request body in the form of binary data will be returned. When the data is of type application/x-www-form-urlencoded, portal has already processed the form and the parameters will be available via request parameters. An IllegalStateException will be thrown if the data is of type application/x-www-form-urlencoded.Either the getPortletInputStream or the getReader may be called for the ActionRequest object but not both. If the getreader has been called, an ensuing call to getPortletInputStream will throw an IllegalStateException.
java.lang.String getContentType()
Returns the MIME type of the HTTP request body. If the type is not known, null is returned.
java.lang.String getCharacterEncoding()
Returns the character encoding used in the body of the HTTP request or null if one was not specified.
268
int getContentLength()
Returns the length of the input stream bytes. If the length is unknown a -1 will be returned.
Example 8-10 Parsing a file using the getReader() method if(request.getContentType().lastIndexOf("multipart/form-data") != -1) { BufferedReader in = request.getReader(); String line; while ((line = in.readLine()) != null) { if((line.indexOf("Content-Disposition") != -1) || (line.indexOf("Content-Type") != -1)){ /* Place your parsing code here. The first few lines will contain the filename and content type Example: -----------------------------7d5352d210102 Content-Disposition: form-data; name="FileName"; filename="C:\MyFile.txt" Content-Type: text/plain <This would be a blank line> <The rest of the file> All request parameters would then be printed Example: -----------------------------7d5352d210102 Content-Disposition: form-data; name="UserName" Hailey */ } else if(line.length() > 0) //Line is part of the file } out.println(request.getPortletSession().getId()); in.close(); }
269
270
271
java.io.PrintWriter getWriter()
This method should be used for writing content to the response. The setContentType should be called before this method. An IllegalStateException will be thrown if the getPortletOutputStream has already been called.
java.io.OutputStream getPortletOutputStream()
This method is also used for generating content to the response. Like the getWriter, the setContent should be called before this method. An IllegalStateException will be thrown if the getWriter has already been called.
int getBufferSize()
The buffer size for the response is returned. A zero will be returned if no buffer is used.
272
void reset()
This will clear all the content on the buffer and the properties that were set. This will throw an IllegalStateException if the response has already been committed. You can use the isCommitted() method to determine if any bytes have been written to the client.
void resetBuffer()
This method is the same as the reset, however this will not clear the properties. This will also throw an IllegalStateException if the response is committed. You can use the isCommitted() method to determine if any bytes have been written to the client.
void flushBuffer()
Invoking this method will force the content in the buffer to be written to the client. The response is considered to be commited after a call to this method.
boolean isCommited()
Returns true if the response has been committed else it returns false.
java.lang.String getNamespace()
This method returns the namespace of the portlet. This String should be prefixed to elements to make them unique on a page. JavaScript variable, functions, and form fields should be prefix with the String returned by this method.
PortletURL createRenderURL()
Creates a URL pointing to the portlet. This URL will cause a render request. Please see interface javax.portlet.PortletURL on page 261 for information about the PortletURL object.
PortletURL createActionURL()
Creates a URL pointing to the portlet. This URL will cause an action request. Please see 8.5.3, interface javax.portlet.PortletURL on page 261 for information about the PortletURL object.
273
java.lang.String getPortalInfo()
This method returns information about the server
Example 8-12 String returned from the getPortalInfo() method IBM WebSphere Portal/5.1
java.util.Enumeration getPropertyNames()
Returns an Enumeration of all the portal property names. An empty Enumeration is returned if there are no portal properties.
java.util.Enumeration getSupportedPortletModes()
Returns an Enumeration of PortletMode objects that the portal server supports.
java.util.Enumeration getSupportedWindowStates()
Returns an Enumeration of WindowState objects that are supported by the portal server.
274
java.util.Map getMap()
This method returns a Map of all the preferences. The keys will be of type String and the Values will be of type String[] (String array). An empty Map will be returned if there are no preferences.
java.util.Enumeration getNames()
Returns an Enumeration of all the preference keys that have a value. An empty Enumeration will be returned if there are no keys.
275
the key is null, the key is too long, or the value is too long. A ReadOnlyException will be thrown if the user does not have the right to modify this preference for this request.
void store()
This method must be called for new or update preferences to be stored in persistence. This needs to only be called once for all of the values to be stored. Store does not have to be called for each preference. If store() does not throw an exception, it is assumed that the save completed. If the save cannot be completed in the backend, an IOException will be thrown. This method can also throw a ValidtorException if the parameter cannot be validated by the PreferenceValidator class specified in the deployment descriptor. See interface javax.portlet.PreferencesValidator for more information. An IllegalStateException is thrown if this method is called inside the render method.
The validate method of this class will be called by the store method. If the validate class throws a Validator exception the store will not complete and it will propagate the exception to the portlet.
276
Example 8-15 Validate method generated by Rational Application Developer public void validate(PortletPreferences preferences) throws ValidatorException { Collection failedKeys = new ArrayList(); for( Enumeration names=preferences.getNames(); names.hasMoreElements(); ) { String name = names.nextElement().toString(); if( name.startsWith(".") ) continue; String value = preferences.getValue(name, ""); //validates that the preferences do not start or end with white space //validates that the preferences start with http: or https: if( !value.equalsIgnoreCase(value.trim()) || !(value.startsWith("http:")||value.startsWith("https:")) ) { failedKeys.add(name); } } if( !failedKeys.isEmpty() ) { throw new ValidatorException("One or more preferences do not comply + with the validation criteria",failedKeys); } }
277
</init-param>
If the named parameter does not exist then null is returned. An IllegalArgumentException is thrown if the named parameter is null.
java.util.Enumeration getInitParameters()
Returns an Enumeration of Strings for all the initialization parameters defined by the portlet. An empty Enumeration is returned if there are no initialization parameters defined.
PortletContext getPortletContext()
Returns the PortletContext object. Please see interface javax.portlet.PortletContext on page 263 for more information about the PortletContext interface.
java.lang.String getPortletName()
This returns the name of the portlet as defined by the administrator or the name defined in the portlet deployment descriptor.
Example 8-17 Portlet name in the portlet deployment descriptor <portlet-name>myPortlet</portlet-name>
java.lang.ResourceBundle getResourceBundle(java.util.Locale)
Returns the resource bundle for the locale specified. The resource bundle can be defined as external or it can be inline text in the portlet.xml.
278
namespaced to the specific portlet. The PortletSessionUtil class should be used to decode portlet attributes stored in the PORTLET_SCOPE when called through the HttpSession of the HttpSessionBindingListener. Please see Listeners on page 284 for more information. The PortletSession and the HttpSession can share attributes. This means that servlets and JSPs will have access to attributes stored by the PortletSession at APPLICATION_SCOPE. The PortletSession will also have access to attributes stored by servlets and JSPs.
Example 8-18 Sharing session attributes Code in the portlet request.getPortletSession().setAttribute("name", "Hailey Anne", PortletSession.APPLICATION_SCOPE); PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher("/MyServlet"); rd.include(request,response); Code in MyServlet String name = request.getSession().getAttribute("name");
The following methods of the PortletSession are based on the HttpSession: getCreationTime getId getLastAccessedTime getMaxInactiveInterval invalidate isNew setMaxInactiveInterval The following methods of PortletSession are also based on HttpSession methods: getAttribute setAttribute removeAttribute getAttributeNames PortletSession does overload the previous methods to provide access for the different storage scopes of the portlet session (PORTLET_SCOPE and APPLICATION_SCOPE).
279
8.6.1 Instantiation
The portlet will be instantiated during startup. The portlet will be loaded using the same classloading that is used for loading the Web application.
8.6.2 Initialization
The init() method of the Portlet class is called when the portlet is first requested. Portal will pass a unique Portletconfig object to the init method. During initialization any one time setup for the portlet should be completed. This includes expensive operations such as database connections. There are two exceptions that may be thrown during initialization. If either exception is thrown, the portlet will not be placed into service and the destroy() method will not be called.
280
UnavailableException
The UnavalableException may indicate a specific time out value. If WebSphere portal server wishes to attempt to place this portlet back into service then it must at least wait for the specified time to expire.
PortletException
All RuntimeException thrown during initialization are treated as PortletExceptions. If PortletException is thrown the portal server does not need to wait any amount of time before attempting to place the portlet into service.
PortletException
This exception indicates that error in processing has occurred. All RuntimeExceptions are treated as a PortletException. If this is thrown from the processAction() method, all actions on the ActionResponse object will be ignored and the render method for this portlet will not be call during this request. The render method for all other portlets on the page will be invoked. If the portlet is cached, the cached version may be returned instead of invoking the render method.
PortletSecurityException
This exception indicates that the user does not have the proper rights for the portlet.
UnavailableException
This exception indicates that the portlet is unavailable to process the request. Like in the init method, the UnavailableException may indicate a time value that the portlet will be unavailable for.
Render phase
A RenderURL will cause the portlet to enter the render phase, this is also known as a render request. The render phase is used for displaying content in the
281
portlet. During a render request, the render method for all portlets on the page will be invoked, unless the portlet is cached. The render method may not be call and the cached content of the portlet could be returned. The processAction method will not be invoked during a render request. The render phase is also entered when a request is made for the page that the portlet resides.
Action phase
An actionURL will cause the portlet to enter the action phase, also known as action request. The action phase is typically used to modify the state of the portlet or to process a task. During the action phase the processAction() method of the portlet is called. After the process action completes, the render method is invoked for all portlets on the page, unless the portlet is cached. The render method may not be called and the cached content of the portlet could be returned. Action parameters are passed into the processAction method via the ActionRequest object. These parameters can represent a field in an HTML form or they can be parameters explicitly set by the programmer. While in the action phase, the portlet mode and the window state may be changed. Although they can be programmatically changed, the portal server can override the change for any reason including the user may not have the required rights. The user may also be redirected to another URL during the action phase by using a request dispatcher. Action parameter are not passed to the render phase unless they are explicitly specified. You should use the setRenderParameter or setRenderParameters method of the ActionResponse to pass parameters to the render phase. The parameters will be out of scope after the render method completes.
282
The expiration cache is defined in seconds. In the example above, the contents of the portlet should be cached for 10 minutes. A value of zero indicates that the portlet caching is disabled. A -1 value will cause the portlet cache to never expire. Any value above 0 will be interpreted as the number of seconds the content should be cached for. If the expiration-cache is defined in the portlet.xml, the portlet may programmatically change the expiration time. The request property expiration-cache should be used to modify the caching values.
Example 8-20 Programmatically changing the expiration time-out value RenderResponse.setProperty( PortletResponse.EXPIRATION_CACHE, (new Integer(600)).toString() );
While the portlet content is cached and the portlet is not a target of a request, the cached content will be used during the render phase. If there is a request for the portlet, the cached content will be disregarded and the render method of the portlet will be invoked.
283
version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ibm.com/xml/ns/portlet/portlet-app_1_0_ext.xsd http://www.ibm.com/xml/ns/portlet/portlet-app_1_0_ext.xsd" > <anonymous-session>true</anonymous-session> <!-- Use the portlet name in the portlet.xml for the href --> <portlet href="PORTLET_NAME_FROM_PORTLET_XML"> <remote-cache-scope>SHARED</remote-cache-scope> <remote-cache-dynamic>true</remote-cache-dynamic> </portlet> </portlet-app>
Portlets can get a persistent HttpSession object on an unauthenticated page by specifying true in the <anonymous-session> element. With a value of false, the HttpSession is lost after each request. The default value for the anonymous-session element if false if the element is not present. You can also specify the setting <remote-cache-dynamic>. This element determines if the portlet will publish remote cache information. A setting of false will improve performance by indicating that portal does not need to wait for the remote cache to be published. Like local cache, you can also programmatically modify the setting for remote cache.
Example 8-22 Programmatically modifying remote cache settings RenderResponse.setProperty( RemoteCacheInfo.KEY_SCOPE, SHARED); RenderResponse.setProperty( RenderResponse.EXPIRATION_CACHE, (new Integer(600)).toString());
8.8 Listeners
JSR 168 portlets do not define any portlet listeners, instead they use servlet listeners. Servlet listeners should be implemented in the portlet environment like they are in the servlet environment. The listeners should be added in the web.xml or to any object that needs to be informed when being added or removed from the session should implement HttpSessionBindingListener. Detailed description of servlet listeners are outside the scope of this book, but we will briefly describe them here.
8.8.1 HttpSessionBindingListener
Object that require notification when being added or removed from the portlet session should implement this interface. This interface defines two methods,
284
valueBound and valueUnbound. Both of these methods are passed a HttpSessionBindingEvent object. From the HttpSessionBindingEvent object you can get access to the name and value of the object that has been added or removed from the session. You can also get the session object. When using attribute that are store in the portlet scope, you should use the PortletSessionUtil class to decode the attribute names.
8.8.2 ServletContextListener
The ServletContextListener can be used for the PortletContext. This listener contains contextInitialized and contextDestroyed methods. An object of type ServletContextEvent is passed into the initialized and destroyed methods. From this object you can get access to the ServletContext. The contextInitialized method will be called when the PortletContext is created and contextDestroyed will be call when the PortletContext is destroyed.
8.8.3 ServletContextAttributeListener
This interface defines three methods, added, removed and replaced. These methods are to be used when attributes have been added, removed, and replaced. A ServletcontextAttributeEvent is passed to these three methods. From this object you can get the name and value of the attribute that has been added, updated or removed.
8.8.4 HttpSessionListener
The HttpSessionListener provides two methods sessionCreate and sessionDestroyed. These are invoked when the session has been created and destroyed. A HttpSessionEvent object is passed into these method. From the HttpSessionEvent you can get the HttpSession.
8.8.5 HttpSessionAttributeListener
Like the ServletContextAttributeListener, the HttpSessionAttributeListener defines the same three methods attributeAdded, attributeRemoved, and attributeReplaced. These methods are pass a HttpSessionBindingEvent object. You can get the name and value of the attribute that has been added, removed or replaced. The following examples will show the web.xml, the listener class and a bean implementing the HttpSessionBindingListener.
Example 8-23 web.xml <web-app id="WebApp_ID">
285
... <listener> <listener-class>listener.MyListeners</listener-class> </listener> ... </web-app> Example 8-24 Class implementing ServletContextListener, ServletContextAttributeListener,HttpSessionListener, and HttpSessionAttributeListener public class MyListeners implements ServletContextListener, ServletContextAttributeListener, HttpSessionListener, HttpSessionAttributeListener { //ServletContextListener methods. public void contextInitialized(ServletContextEvent sce) { System.out.println("Servlet Context initialized."); } public void contextDestroyed(ServletContextEvent sce) { System.out.println("Servlet Context destroyed"); } //ServletContextAttributeListener methods. public void attributeAdded(ServletContextAttributeEvent scae) { System.out.println("Servlet context attribute added: " + scae.getName()); } public void attributeRemoved(ServletContextAttributeEvent scae) { System.out.println("Servlet context attribute removed: " + scae.getName()); } public void attributeReplaced(ServletContextAttributeEvent scae) { System.out.println("Servlet context attribute replaced: " + scae.getName()); } //HttpSessionListener methods. public void sessionCreated(HttpSessionEvent hse) { System.out.println("HttpSession created"); } public void sessionDestroyed(HttpSessionEvent hse) { System.out.println("Httpsession destroyed."); } //HttpSessionAttributeListener methods. public void attributeAdded(HttpSessionBindingEvent hsbe) { System.out.println("HttpSession added: " + hsbe.getName()); } public void attributeRemoved(HttpSessionBindingEvent tbia) { System.out.println("HttpSession attribute removed: " + tbia.getName()); } public void attributeReplaced(HttpSessionBindingEvent hsbe) { System.out.println("HttpSession attribute replaced: " + hsbe.getName());
286
} } Example 8-25 Bean implementing HttpSessionBindingListener public class SessionBean implements HttpSessionBindingListener { public void valueBound(HttpSessionBindingEvent hsb) { System.out.println("SessionBean added to HttpSession"); } public void valueUnbound(HttpSessionBindingEvent hsb) { System.out.println("SessionBean removed from HttpSession"); } }
287
</welcome-file-list> </web-app>
Please see the servlet specification for complete details on the Web deployment descriptor. The portlet.xml is used to define all portlet related resources. You should use Rational Application Developer to create and modify your portlet.xml. Rational Application Developer contains tools for inserting and removing elements and also to verify that the portlet.xml is valid. We will discuss each of the elements in the portlet.xml to give a complete understanding.
Attributes
version This attribute is required. id This attribute is optional.
Elements
(The elements are required to be listed in the order they are presented) portlet Can occur zero or more times. This element defines the portlet. The portlet name must be unique within the portlet application.
288
custom-portlet-mode Can occur zero or more times. This element defines any custom portlet modes for the portlet application. The name of the portlet-mode defined must be unique for the portlet application. custom-window-state Can occur zero or more times. This element defines any custom window states for this application. The name of the window-state must be unique within the portlet application. Note: WebSphere Portal does not provide support for any custom window states at this time. user-attributes Can occur zero or more times. security-constraint Can occur zero or more times. Note: WebSphere portlet does not currently support the security-constrain tag.
289
<supported-locale>en</supported-locale> <resource-bundle>myportlet.nl.MyPortletPortletResource</resource-bundle> <portlet-info> <title>MyPortlet portlet</title> </portlet-info> <portlet-preferences> <preference> <name>.MyPortletPortletEditKey</name> <value>Read-Write Value</value> </preference> <preference> <name>.MyPortletPortletConfigKey</name> <value>Read-Only Value</value> <read-only>true</read-only> </preference> </portlet-preferences> </portlet>
Attributes
id The id attribute is optional.
Elements
description - can occur zero or more times Attribute: xml:lang This attribute indicates the language used in the description This element is used in describing the portlet. This should include information about the portlet. portlet-name - must occur only once The portlet name must be unique within the portlet application. The portlet-name can only occur once for each portlet element. portlet-class - must occur only once The portlet class must be a fully qualified class or interface. The portlet-class can only occur once for each portlet element. init-param - can occur zero or more times Attribute: id The id attribute is optional.
290
Elements: description - can occur zero or more times Attributes: xml:lang This attribute indicates the language used in the description This element is used to describing the initialization parameters. name - must occur only once The name element specifies the name of the initialization parameter value - must occur only once The value element specifies the value for this initialization parameter expiration-cache - can occur either zero or one time This elements defines caching for this portlet. The parameter indicates the time in seconds after which the portlet output expires. -1 indicates that the output never expires. supports - must occur one or many times All portlets are required to support View mode. The supports element must occur at least once, containing the View mode. WebSphere portlet will only support the following modes View, Edit, Help, and Configuration. Configuration is the only custom mode supported by WebSphere Portal. Attribute: id The id attribute is optional. Elements: mime-type - must occur only once This element is used to define the MIME type for the portlet mode. The MIME type may contain wildcard character '*', like "text/*" or "*/*, or it can explicitly define the type like "text/html". portlet-mode - can occur zero or more times Since all portlets must support View mode it is not required to add it. However, it is recommended that it is added for readability. The portlet-mode must be a valid mode. supported-locale - can occur zero or more times Indicates the locale that this portlet supports.
291
You can choose to have a resource bundle with zero or one portlet-info elements. You can only define one resource-bundle for each portlet application. If you do have a resource bundle with the inline portlet-info element, portal will look for the value in the resource bundle first. If it does not find the value there but finds it inline, it will add the inline element to the resource bundle. resource-bundle - must occur only once This element will contain the filename for the resource bundle. The filename should be the fully qualified class name and it should be located in /WEB-INF/classes/nls subdirectory of the WAR. All of the locale-specific resource bundles append the locale to the filename (for example, MyPortlet_es.properties for Spanish). To correctly display the portlet title, there must be a corresponding supported-locale element for each resource bundle locale. If the locale is missing from the supported-locale element, the default locale will be used for the portlet title. There are three constrains for the resource bundle javax.portlet.title This is the title of the portlet to be displayed in the title bar. This may be overridden programmatically of by the administrator. javax.portlet.short-title This is a short title for the portlet. This is used in cases where device cannot display the full title. javax.portlet.keywords Keywords may be used by portal when searching for portlets. The keywords must be separated by commas (,).
Example 8-29 Resource bundle in portlet.xml <resource-bundle>nls.SamplePortlet</resource-bundle> Example 8-30 Resource bundle examples # English Resource Bundle # # Filename: SamplePortlet.properties javax.portlet.title=Sample Portlet javax.portlet.short-title=sample javax.portlet.keywords=sample,test # Spanish Resource Bundle #
292
portlet-info - can occur only once The portlet info is used to provide the title, short-title, and keywords for the default locale. Attribute: id The id attribute is optional. Elements: title - must occur only once This is the title of the portlet to be displayed in the title bar. This may be overridden programmatically of by the administrator. short-title - can occur zero or one time This is a short title for the portlet. This is used in cases where device cannot display the full title. keywords - can occur zero or one time Keywords may be used by portal when searching for portlets. The keywords must be separated by commas (,). If the resource bundle does not exist, the portlet-info must occur only once. portlet-info - can occur only once The portlet info is used to provide the title, short-title, and keywords for the default locale. Attribute: id The id attribute is optional. Elements: title - must occur only once This is the title of the portlet to be displayed in the title bar. This may be overridden programmatically of by the administrator. short-title - can occur zero or one time This is a short title for the portlet. This is used in cases where device cannot display the full title.
293
keywords - can occur zero or one time Keywords may be used by portal when searching for portlets. The keywords must be separated by commas (,). portlet-preferences - can occur zero or more times The portlet-preferences element will store the portlet preferences. Portlet preferences are used to store persistent data for the user. Attribute: id The id attribute is optional. Elements: preference - can occur zero or more times The preference element will store the name and value pairs for the preferences. Please refer to interface javax.portlet.PortletPreferences on page 274 for more information. Attribute: id The id attribute is optional. Elements: name - must occur once The name element represents the name of the preference.
Example 8-31 Using the preferences in the portlet.xml and code <portlet-preferences> <preference> <name>test</name> <value>value for test preference</value> <read-only>true</read-only> </preference> </portlet-preferences> The following code will retrieve the value for test. PortletPreferences preferences = RenderRequest.getPreferences(); String value = preferences.getValue("test", "No value found");
value - can occur zero or more times You can have multiple values for the preference. Multiple values will be returned as a String[] (String array).
294
read-only - can occur zero or one time Read only will take a boolean value of either true or false. True indicates that this preference can only be updated by the administrator during Configuration mode.
preference-validator - can occur zero or one time The preference-validator class must use the fully qualified class name. This class must implement javax.portlet.PortletValidator. The validate() method will be invoked during a call to the store() method of the PortletPreference class. security-role-ref - can occur zero or more times Is used in conjunction with the web.xml. Note: WebSphere portal version 5.1 will ignore all security-role-ref tags as they are currently not supported. Attribute: id This id attribute is optional. Elements: description - can occur zero or more times Attribute: xml:lang This attribute indicates the language used in the description. This element is used to describing the security role references. role-name - must only occur once This defines the name for the security role. role-link - can occur zero or one time The role-link element refers to a defined security role. The role-link element must contain the name of one of the security roles defined in the security-role elements of the web.xml.
295
Attribute
id The id attribute is optional.
Elements
description - can occur zero or more times Attributes: xml:lang This attribute indicates the language used in the description. This element is used to describing the custom portlet mode. portlet-mode - must occur only once The portlet-mode names are not case sensitive. They must be unique within the portal application.
Attribute
id The id attribute is optional.
Elements
description - can occur zero or more times Attribute: xml:lang This attribute indicates the language used in the description This element is used to describing the custom window. window-state- must occur only once The window-state names are not case sensitive. They must be unique within the portal application.
296
supported by WebSphere portlet. The second column shows the corresponding attribute name in Member Manager (wmmAttributeName).
Table 8-1 Supported user attributes
User attribute name user.gender user.employer user.department user.jobtitle user.name.prefix user.name.given user.name.family user.name.middle user.home-info.telecom.telephone.number ser.home-info.online.email user.business-info.postal.street user.business-info.postal.stateprov user.business-info.postal.postalcode user.business-info.postal.country user.business-info.telecom.telephone.number user.business-info.telecom.fax.number user.business-info.telecom.mobile.number user.business-info.telecom.pager.number user.business-info.telecom.online.email
ou ibm-jobTitle ibm-personalTitle givenName sn ibm-middleName homePhone u ibm-otherEmail street stateOrProvinceName postalCode countryName telephoneNumber facsimileTelephoneNumber mobile pager ibm-primaryEmail
A map containing all of the user attribute information can be retrieved using a call to the getAttribute of the request object, see Example 8-32 on page 298. If the user is unauthenticated then a null will be returned. If, however, the user is authenticated and the there are no user attributes defined, an empty Map will be returned. The Map will contain the user attributes mapped during deployment and they will be in the form of a String name value pair. Security must be enabled to retrieve most of the user information.
297
Example 8-32 Getting user attribute information. Map userinfo = (Map)request.getAttribute(RenderRequest.USER_INFO); if(userinfo != null){ for (Iterator it=userinfo.entrySet().iterator(); it.hasNext(); ) { Map.Entry entry = (Map.Entry)it.next(); String key = (String)entry.getKey(); String value = (String)entry.getValue(); System.out.println("Name: " + key + ", Value: " + value); } }
Attribute
id The id attribute is optional.
Elements
description - can occur zero or more times Attribute: xml:lang This attribute indicates the language used in the description This element is used to describing the user attributes. name - must occur only once Defines the name of the user attribute.
Attribute
id The id attribute is optional.
Elements
display-name - can occur zero or more times Rational Application Developer will use the display-name to name this security constraint in the IDE. This name does not have to be unique.
298
Attribute: xml:lang This attribute indicates the language used in the description portlet-collection - must occur only once portlet-name - can occur once or many times The portlet name must be a valid portlet defined in the portal application. user-data-constraint - must occur only once Attribute: id The id attribute is optional. Elements: description - can occur zero or more times Attribute: xml:lang This attribute indicates the language used in the description. This element is used to describe the user data constraints. transport-guarantee - must occur only once The transport-guaranteeType specifies the type of communication between client and portlet. This value can only be one of the following, NONE, INTEGRAL, or CONFIDENTIAL. NONE indicates the portlet does not require guarantees for the transport. INTEGRAL indicates that the data between the portlet and client cannot be changed while in transit. CONFIDENTIAL indicates the data cannot be observed while in transit. This ensures that the data cannot be observed by other parties. INTEGRAL or CONFIDENTIAL usually indicate SSL is required.
299
The setsecure() method of the PortletURL is part of the PortletURL interface, However, it is not supported by WebSphere Portal. The security level will always be same as the current request. The isUserInRole() method of the portlet request at the time of this writing this is unsupported and will always return false. All <security-role-ref> elements in the portlet.xml are ignored. The getUserPrincipal() method of the portlet request always returns null if application server security is not enabled. The getRemoteUser method will return the user who is logged in. If the user is not logged in, null will be returned. Even with security disabled this method will return the user information, although with security disabled the returned name may not be human-readable. With security enabled a more readable user name will be returned. The <security-constraint/> element in the portlet.xml is not supported at the time of this writing. User information supported a limited number of user attributes. Please see user-attribute - can occur zero or more times on page 296 for more information. RenderResponse.setTitle(java.lang.String title) method is currently not supported.
300
Chapter 9.
301
9.1 Overview
The JSR 168 sample portlet shown in this chapter will allow you to implement the following: You will enter your name and select the color that it is displayed in. You will also be able to add new colors to use. A user with administration rights will be able to change the greeting that appears before your name. You will use the PortletPreferences object to store the colors and greeting. A JavaBean, stored in the PortletSession, will store the currently selected color, name, and greeting. The PortletPreferencesValidator will be used to validate the color before adding it to the portletpreferences object. The portlet will show the render and action phases of the JSR 168 portlet API. It will also display the View, Edit, Config, and Help modes.
doView()
doEdit()
doCustomConfigure()
doHelp()
processAction()
sessionBean
View.jsp Help.jsp
Edit.jsp
Configure.jsp
302
Figure 9-2 Enter the name and target server of your portlet
303
9. Examine and take default values in the features window. Click Next. 10.Examine and take default values in the portlet settings window. Click Next. 11.In the Actions and Preferences window, select all four check boxes to add support for action handling and preferences handling. Notice that the Add Preferences validator box will be available when the Add preferences handling box is selected.
304
12.Single Sign-On with Credential Vault will not be used. Click Next. 13.Select the check boxes to support Edit Mode, Help Mode, and Configure Mode. Select Finish to complete the creation of the portlet project.
305
2. In a similar way, select Filters. The Filters window will now be displayed. 3. Select the Types of content tab. 4. Deselect WebService, Databases, and Database Servers since these extensions will not be used in this project. Click OK.
306
307
4. Open the class by double-clicking MySimplePortletPortletSessionBean.java. 5. Delete all the class variables and methods from the SessionBean class. 6. Create private Strings for userName, color, and greeting. The bean class should now look as illustrated in Example 9-1.
Example 9-1 MySimplePortletPortletSessionBean class package mysimpleportlet; public class MySimplePortletPortletSessionBean { private String userName = ""; private String color = ""; private String greeting = ""; }
7. Right-click in the MySimplePortletPortletSessionBean window and select Source then Generate Getters and Setters.
308
8. Check the boxes for color, userName, and greetings so that getters and setters will be generated for all three variables. Click OK.
9. Save the file by using Ctrl-s or File Save. Note: You may see errors in other classes after saving this file. You will correct these errors in a later step.
309
6. You will now get the userName, color, and greeting variables out of the sessionBean. You should consider the following issues for this sample scenario:
310
Notice that you have previously defined the greeting and userName variable with an empty String in the sessionBean. The first time running the portlet, both the name and the greeting will be empty and therefore will not appear on the page. You will set a CSS style in the header to display the user name and greeting in the currently selected color. If the length of the color String is not longer than zero indicating that no color has been selected, we will use black for the color. The first time the portlet is called, there will not be a color selected so color black will be used as default. 7. Add the code highlighted in Example 9-3 to get the variables from the session bean. 8. Add also a heading element (H3) to display the variables obtained from the session bean. Note: You do not have to test for null for these variable because you set them as an empty String by default. However, for a production application, it is recommended testing for null.
Example 9-3 Getting variables from sessionBean <%@ page session="false" contentType="text/html" import="java.util.*,javax.portlet.*,mysimpleportlet.*" %> <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <portlet:defineObjects/> <% MySimplePortletPortletSessionBean sessionBean = (MySimplePortletPortletSessionBean)renderRequest.getPortletSession().getAttribu te(MySimplePortletPortlet.SESSION_BEAN); String name = sessionBean.getUserName(); String greeting = sessionBean.getGreeting(); String color = sessionBean.getColor(); if((color == null) || (color.length() == 0)){ color = "black"; } %> <H3 style="margin-bottom: 3px; color:<%= color %>"><%= greeting + " " + name %></H3>
9. Modify the form to get the users name and the desired color. You will now change the label and the input for the form to use the variable FORM_NAME.
311
Next, you will add a select tag and use SimplePortletPortlet.FORM_COLOR variable as the name. Then add code to get the preferences. Once you have the preferences, you can look through all the preferences to fill the drop-down list for all the colors. In a later step you will be adding the three primary colors into the portlet descriptor (portlet.xml) preferences along with a greeting preference. Because of this, you do not need to test the preferences to determine if there are any colors. You do, however, have to test for the .greeting preference. You do this by testing if the preference first character starts with a . character. If the color from the sessionBean matches the preference color then you mark the option tag as selected. It will be the selected color the next time the user views the page. 10.Replace the form in MySimplePortletPortletView.jsp with the form shown in Example 9-4.
Example 9-4 New updated form in the view JSP <FORM method="POST" action="<portlet:actionURL/>"> <LABEL for="<%=MySimplePortletPortlet.FORM_NAME%>">Enter your name:</LABEL><BR> <INPUT name="<%=MySimplePortletPortlet.FORM_NAME%>" value="<%= name %>" type="text"/> <SELECT name="<%= MySimplePortletPortlet.FORM_COLOR %>"> <% PortletPreferences prefs = renderRequest.getPreferences(); // loop through all the preferences to get the colors. // The three primary colors have been added to the portlet.xml so we do not // need to test for no colors. Enumeration prefNames = prefs.getNames(); while(prefNames.hasMoreElements()) { String thisColor = (String)prefNames.nextElement(); if((thisColor != null) && (!thisColor.startsWith("."))){ %> <option <% if(color.equalsIgnoreCase(thisColor)) { %> selected="selected" <% } %> value="<%= thisColor %>" > <%= thisColor %></option> <% } }%> </SELECT> <INPUT name="<%=MySimplePortletPortlet.FORM_SUBMIT%>" type="submit" value="Submit"/> </FORM>
11.Save the file by using Ctrl-s or File Save. Once again, there will be errors in the JSP and they will be fixed in the next step.
312
313
2. Right-click the FORM_TEXT field and select Refactor Rename. 3. A dialog box will pop up allowing you to change the field name. Change the name to FORM_NAME. Click OK. Note: Be sure the check box for Update References is selected.
4. If you get a warning, select Continue. This warning is a result of compilation errors in the portlet class. These errors will be corrected in a later step.
314
5. All the references in the portlet class will now be updated to reflect the new name. We will now modify the value for this variable. 6. Open the portlet class by double-clicking the file. 7. Modify the FORM_NAME variable to look like Example 9-5. This should be all be on the same line. You changed the value to reflect the new name of the variable and you also modified the comment line.
Example 9-5 FORM_NAME variable public static final String FORM_NAME = "MySimplePortletPortletFormName"; // Parameter for the name of the user
8. Now add a new variable for the FORM_COLOR. Enter the line shown in Example 9-6. It should be all in one line and under the FORM_NAME line.
Example 9-6 New FORM_COLOR variable public static final String FORM_COLOR = "MySimplePortletPortletFormColor"; // Parameter for the selected color
9. Locate the processAction() method. You will be using a session bean to store the user name and the selected color from the form. The processAction() method needs to be updated to handle the new fields of the form from MySimplePortletPortletView.jsp. Add the methods to set the variables in the sessionBean using the setter methods you created earlier. The code is shown in Example 9-7 on page 316.
315
Example 9-7 Submit action process if( request.getParameter(FORM_SUBMIT) != null ) { // Set form text in the session bean MySimplePortletPortletSessionBean sessionBean = getSessionBean(request); if( sessionBean != null ) { sessionBean.setUserName(request.getParameter(FORM_NAME)); sessionBean.setColor(request.getParameter(FORM_COLOR)); } }
10.Also, in the processAction() method, remove the complete if statements for PREF_RESET and PREF_SET. You will not be using these parameters. The processAction() method should now look as illustrated in Example 9-8.
Example 9-8 Processing actions in the processAction() method public void processAction(ActionRequest request, ActionResponse response) throws PortletException, java.io.IOException { if( request.getParameter(FORM_SUBMIT) != null ) { // Set form text in the session bean MySimplePortletPortletSessionBean sessionBean = getSessionBean(request); if( sessionBean != null ) { sessionBean.setUserName(request.getParameter(FORM_NAME)); sessionBean.setColor(request.getParameter(FORM_COLOR)); } } if( request.getParameter(EDIT_SUBMIT) != null ) { PortletPreferences prefs = request.getPreferences(); try { prefs.setValue(EDIT_KEY,request.getParameter(EDIT_TEXT)); prefs.store(); } catch( ReadOnlyException roe ) { } catch( ValidatorException ve ) { } } if( request.getParameter(CONFIG_SUBMIT) != null ) { PortletPreferences prefs = request.getPreferences(); try { prefs.setValue(CONFIG_KEY,request.getParameter(CONFIG_TEXT)); prefs.store(); } catch( ReadOnlyException roe ) { } catch( ValidatorException ve ) {
316
} } }
11.Save the file by using Ctrl-s or File Save There should not be any errors in the portlet class at this point. You should also not have any errors in the view JSP. You may need to open the view JSP make a small change like adding a new blank line and save the file again to cause it to recompile. Note: You may see an error for the renderRequest object not being resolved. This object will be created during runtime by the <portlet:defineObjects/> tag. This error should be ignored at this time.
317
<FORM ACTION="<portlet:renderURL portletMode="view"/>" METHOD="POST"> <INPUT NAME="back" TYPE="submit" VALUE="Back to view mode"> </FORM> </DIV>
3. First, you will edit the field names of the label and input to show that these fields will hold the new color value as follows: a. Change the name of the label and input from MySimplePortletPortlet.EDIT_TEXT to MySimplePortletPortlet.EDIT_COLOR. b. Also change the value of the label tag from New Value to New Color. c. Delete the value=<%=value%> from the input tag. d. The form should now look as shown in Example 9-10.
Example 9-10 Updated form in edit JSP <FORM ACTION="<portlet:actionURL/>" METHOD="POST"> <LABEL for="<%=MySimplePortletPortlet.EDIT_COLOR%>">New Color</LABEL><BR> <INPUT name="<%=MySimplePortletPortlet.EDIT_COLOR%>" type="text"/><BR> <INPUT name="<%=MySimplePortletPortlet.EDIT_SUBMIT%>" value="Save" type="submit"/> </FORM>
4. Display colors using an unordered list. a. Create a new line under the first division tag (<DIV style=margin: 6px>) and add the following heading element:
<H3 style="margin-bottom: 3px">Available colors:</H3>
b. The colors will be displayed in an unordered list. They will be displayed by their name and in the color they represent. For example, blue will be displayed in blue. Create a new line under line you just created and create an opening and closing list tag:
<ul> </ul>
<ul> </ul>
<FORM ACTION="<portlet:actionURL/>" METHOD="POST">
318
<LABEL for="<%=MySimplePortletPortlet.EDIT_COLOR%>">New Color</LABEL><BR> <INPUT name="<%=MySimplePortletPortlet.EDIT_COLOR%>" type="text"/><BR> <INPUT name="<%=MySimplePortletPortlet.EDIT_SUBMIT%>" value="Save" type="submit"/> </FORM> <FORM ACTION="<portlet:renderURL portletMode="view"/>" METHOD="POST"> <INPUT NAME="back" TYPE="submit" VALUE="Back to view mode"> </FORM> </DIV>
5. You have already created the code to loop through the preferences in the View mode JSP (MySimplePortletPortletView.jsp). You will now use the code in that JSP to create a snippet that you can easily paste into other files rather that retyping the code each time. a. Open MySimplePortletPortletView.jsp. b. In the View mode JSP, highlight and copy to the clipboard the code shown in Example 9-12. To copy select Ctrl-c or use Edit Copy.
Example 9-12 Selected code from View mode page (MySimplePortletPortletView.jsp) PortletPreferences prefs = renderRequest.getPreferences(); // loop through all the preferences to get the colors. // The three primary colors have been added to the portlet.xml so we do not // need to test for no colors. Enumeration prefNames = prefs.getNames(); while(prefNames.hasMoreElements()) { String thisColor = (String)prefNames.nextElement(); if((thisColor != null) && (!thisColor.startsWith("."))){ %> <option <% if(color.equalsIgnoreCase(thisColor)) { %> selected="selected" <% } %> value="<%= thisColor %>" > <%= thisColor %></option> <%} }
c. Notice that you did not select the opening and closing JSP scriplet tags (<% and %>). d. You will also use this code snippet in MySimplePortletPortlet class and these tags will not be needed. e. When you create the snippet you will also remove the other scriplet tags and replace the line with a variable. 6. Create a snippet from the previous code. Note: Snippets are sections of code that can be reused throughout your application.
319
a. Be sure you are in the Web Perspective and the Snippets view is selected as illustrated in Figure 9-15. b. In the gray area below the Web Services button, right-click and select Customize....
7. The Customize Palette window will now be displayed. a. In the Customize Palette Window select New then New Category. b. For the name, type MySnippets and then select Apply.
320
8. You should now see your new snippet on the left window. a. Highlight the snippet by clicking it and then select New New Item. b. For the name, type Color Preferences and for the description, type Loop through the colors in the preferences object. c. You will now paste in the code you copied to the clipboard from the view JSP. Click in the Template Pattern window and select Ctrl-V to paste the code.
9. You now need to modify the selected code to add the variable. a. Delete the following section in the template window:
321
%><option <% if(color.equalsIgnoreCase(thisColor)) { %> selected="selected" <% } %> value="<%= thisColor %>" ><%= thisColor %></option> <%
b. Under the variable section, select New and enter a name of loopAction and a description of Loop Action.
c. Place your cursor on the line that you deleted in step a., Delete the following section in the template window: on page 321 and select the Insert Variable PlaceHolder button. d. Double-click loopAction then select Apply. e. When your Customize Palette window looks as shown in Figure 9-19 on page 323, select OK.
322
10.Place your cursor in between the <ul> and </ul> tags in MySimplePortletPortletEdit.jsp and enter <% %> as highlighted in Example 9-13.
Example 9-13 Updated tags in edit page <%@ page session="false" contentType="text/html" import="java.util.*,javax.portlet.*,mysimpleportlet.*"%> <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <portlet:defineObjects/> <DIV style="margin: 6px"> <H3 style="margin-bottom: 3px">Available colors:</H3> <ul><% %> </ul>
11.Place you cursor in between the two <% %> tags you just created.
323
12.In the snippets view, double-click the Color Preferences snippet you just created. 13.Enter the following for the loopAction variable by placing the cursor in the value box next to the loopAction then select Insert.
%><li style="color:<%= thisColor %>"><%= thisColor %></li><%
Note: This will create a list items for each color in the preferences. The style tag will display the color in the color it depicts. 14.You will now add a note in MySimplePortletPortletEdit.jsp informing the user that there are only 16 supported colors for this portlet. In a later step, you will modify the PortletPreferencesValidator to only accept these colors. a. Add a new line directly under the ending </ul> tag. b. Enter the following code:
<br /> <div style="color:maroon"> (Only the following colors are supported:<br> aqua, black, blue, fuchsia, gray, green, lime, maroon, <BR> navy, olive, purple, red, silver, teal, white, yellow) </div>
324
Note: Once again, you will see errors in your JSP. These will be fixed in a later step. 16.Optionally, use the click the Preview tab to see the Edit mode JSP as illustrated in Figure 9-21.
3. Find the processAction() method and locate the if statement for the process of the edit form submit.
if( request.getParameter(EDIT_SUBMIT) != null ) { PortletPreferences prefs = request.getPreferences(); try {
325
4. You will need to make sure that the new color is not null. First, you will create a new String named newColor to store the color retrieved from the EDIT_COLOR field of the form. You will then test to see is the newColor String is null. If the newColor String is not null trim any leading and trailing spaces but using the trim() method and you also set it to all lower case. 5. Modify the try block in the if statement to look like Example 9-15.
Example 9-15 Modified the if statement for the edit form if( request.getParameter(EDIT_SUBMIT) != null ) { PortletPreferences prefs = request.getPreferences(); try { String newColor = request.getParameter(EDIT_COLOR); if(newColor != null){ newColor.trim().toLowerCase(); } prefs.setValue(newColor,newColor); prefs.store(); } catch( ReadOnlyException roe ) { } catch( ValidatorException ve ) { } }
6. Save the file by using Ctrl-s or File Save. Note: You should not see any errors in the portlet class after saving. If MySimplePortletPortletEdit.jsp still contains errors, open the file add a blank line then save the file to force it to recompile. You should no longer see the errors.
326
Example 9-16 Configuration mode JSP <%@ page session="false" contentType="text/html" import="javax.portlet.*,mysimpleportlet.*" %> <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <portlet:defineObjects/> <DIV style="margin: 6px"> <FORM ACTION="<portlet:actionURL/>" METHOD="POST"> <LABEL for="<%=MySimplePortletPortlet.CONFIG_TEXT%>">New Value</LABEL><BR> <INPUT name="<%=MySimplePortletPortlet.CONFIG_TEXT%>" value="<%=value%>" type="text"/><BR> <INPUT name="<%=MySimplePortletPortlet.CONFIG_SUBMIT%>" value="Save" type="submit"/> </FORM> </DIV>
2. You will add the code to get the current greeting from the sessionBean. Add the following lines to the Configure mode JSP above the opening division tag.
<% //This should be on one line MySimplePortletPortletSessionBean sessionBean = (MySimplePortletPortletSessionBean)renderRequest.getPortletSession().getAtt ribute(MySimplePortletPortlet.SESSION_BEAN); //The following code will be on new lines. String greeting = ""; if(sessionBean != null) greeting = sessionBean.getGreeting(); %>
3. Change the label and input parameters of the form from MySimplePortletPortlet.CONFIG_TEXT to MySimplePortletPortlet.CONFIG_GREETING. Also change the value of the label from New Value to New greeting. 4. Change the value of the input from <%= value %> to <%= greeting %> 5. Save the file by using Ctrl-s or File Save. 6. Optionally, preview the Configuration mode JSP.
327
Note: There might be some errors in your JSP. These errors will be fixed in a later step.
3. Change the value of the CONFIG_KEY from .MySimplePortletPortletConfigKey to .greeting. 4. Locate the processAction() method and find the if statement for CONFIG_SUBMIT.
request.getParameter(CONFIG_SUBMIT) != null ) { PortletPreferences prefs = request.getPreferences(); try {
5. Process the greeting message as follows: a. You will first create a String named greeting and store the value of the CONFIG_GREETING field from the form. b. You will then test if the greeting String is not null and if its length is greater than zero. If it is, you will store it in the preferences using the CONFIG_KEY c. You will then store the greeting in the session bean.
328
6. Save the file by using Ctrl-s or File Save Note: There should not be any errors in this file. If you still have errors in MySimplePortletPortletConfig.jsp, open the JSP add a blank line and then save. This will force the JSP to recompile and remove the errors.
329
3. Scroll down until you see the Persistent Preference Store box as shown in Figure 9-24. 4. Clear all preferences as follows: a. Select all the preferences by holding down Ctrl and selecting each preference. b. Select Remove.
5. You will now add the preferences for the .greeting, red, yellow, and blue. For the .greeting, you will mark it as read-only. Note: Read only means that only an administrator in Configuration mode will be able to update this preference during runtime.
330
Execute the following steps: a. Select Add to open the New Preference window. b. Enter .greeting for the name. c. Select Add to enter a value of Hello. d. For the .greeting preference only: check the box for read-only. e. Select OK. f. Repeat steps a through e for the three colors (red, yellow, and blue). Use lower case for the color names and values. Do not select the read-only option for these preferences.
g. Save and close the portlet.xml by using Ctrl-s or File Save. Note: There should not be any errors in any of the files. 6. Examine the portlet descriptor (portlet.xml) source and review the preferences you just entered.
331
Example 9-19 Validate method public void validate(PortletPreferences preferences) throws ValidatorException { Collection failedKeys = new ArrayList(); if( !failedKeys.isEmpty() ) { throw new ValidatorException("One or more preferences do not comply with the validation criteria",failedKeys); } }
3. In the validate method, you will do the following: a. Create a String array to store all the valid colors. b. You will then loop through all the preferences (excluding the .greeting). c. If there are any colors that do not match a supported color, a ValidatorException will be thrown and the new color will not be stored. d. The code in your validate method should look like Example 9-20.
Example 9-20 New validate method public void validate(PortletPreferences preferences) throws ValidatorException { String[] HTMLColors = new String[] { "aqua", "black", "blue", "fuchsia", "gray", "green", "lime", "maroon", "navy", "olive", "purple", "red", "silver", "teal", "white", "yellow" }; Collection failedKeys = new ArrayList(); PREFERENCES_LOOP: for( Enumeration names=preferences.getNames(); names.hasMoreElements(); ) { String name = names.nextElement().toString(); if( name.startsWith(".") ) continue; String value = preferences.getValue(name, ""); for(int i = 0; i < HTMLColors.length; i++){ if(value.equalsIgnoreCase(HTMLColors[i])) continue PREFERENCES_LOOP; } failedKeys.add(name); } if( !failedKeys.isEmpty() ) { throw new ValidatorException("One or more preferences do not comply with the validation criteria",failedKeys); } }
4. Save the file by using Ctrl-s or File Save. There should not be any errors in this file.
332
1. Enter your name then select Submit. 2. You should now see your name displayed in red. 3. Use the drop-down box to select blue and then click Submit. 4. Your name will now appear in blue. Try again, this time selecting yellow. 5. Lets add a greeting now. Select the Config mode icon (the wrench icon in the title bar). 6. Enter Hello in the New Greeting box and click Save. 7. Select the back button on the title bar.
333
8. Your name will now be displayed with the Hello greeting. 9. Lets give your portlet more color. Select the edit icon on the title bar, the edit icon is the pencil. 10.Add as many of the supported colors as you would like by entering the color in the new color text box and then selecting Save. 11.If you entered a supported color, you will now be able to see it in the list.
12.Select the back button in the title bar. 13.You can now select your new colors.
334
10
Chapter 10.
335
10.1.1 doctype
The JSR 168 portlet deployment descriptor uses XML schema for validation and does not require the use of the doctype element. The following line can be removed from the portlet.xml:
<!DOCTYPE portlet-app-def PUBLIC "-//IBM//DTD Portlet Application 1.1//EN" "portlet_1.1.dtd">
10.1.2 portlet-app
The root element for the portlet.xml for JSR 168 portlets is the <portlet-app> element. This element has attributes to specify the schema definition, version, and id. Remove the following elements:
<portlet-app-def> ..... </portlet-app-def>
The <portlet-app-name> element can also be removed; it is not used in JSR 168 portlets. The following example shows the portlet-app element for MyJSRPortlet portlet. Update the <portlet-app> element to look like the following. You will want to change the ID for your portlet project.
Example 10-1 portlet-app element <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" id="myjsrportlet.MyJSRPortletPortlet.422c1f0220">
336
10.1.3 concrete-portlet-app
The concept of concrete portlets in JSR 168 is nonexistent. The portlet element in the JSR portlet.xml is used to define the portlet. You will define the portlet title and any initialization parameters there. Save any configuration information along with language definitions and the remove all <concrete-portlet-app> elements.
10.1.4 portlet
JSR also uses the portlet elements like the IBM portlet API. However, there are many different attributes and sub-elements. You can remove the href attribute because, once again JSR portlets do not need to reference the web.xml. JSR portlets also do not contain the version numbers in the portlet.xml. If you need to maintain the version number, it is recommended that you use the manifest file under the meta-inf directory. Your new <portlet> element should look like the following:
<portlet id="myportlet.MyPortletPortlet">
10.1.5 portlet-name
The portlet-name element needs to be added to represents the portlets name:
<portlet-name>MyPortlet</portlet-name>
10.1.6 web.xml
The IBM portlets required that the servlet be declared in the web.xml. We will not be extending HttpServlet for JSR 168 portlets so the servlet and the servlet-mapping elements can be removed from the web.xml. Copy the servlet class; we will use this for the portlet-class element.
<portlet-class>myportlet.MyPortletPortlet</portlet-class>
10.1.7 cache
JSR 168 uses the <expiration-cache> for determining the cache. The interpretation of the cache value is the same for both portlet APIs. Copy the value of the portlet cache and then delete the cache elements. JSR 168 does not define shared cache so the expiration-cache element will just accept the cache value as shown in the following line.
<expiration-cache>0</expiration-cache>
337
10.1.8 supports
Instead of supporting markup types, JSR 168 supports MIME types. Both APIs use the supports element, however JSR 168 uses the portlet-mode element to define the supported modes. Change the portlet.xml from:
Example 10-2 IBM API supports element <supports> <markup name="html"> <view /> <configure /> <edit /> <help /> </markup> </supports>
to:
Example 10-3 JSR 168 supports element <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> <portlet-mode>help</portlet-mode> <portlet-mode>config</portlet-mode> </supports>
JSR 168 only defines View, Help, and Edit modes. CONFIG is provided as a custom mode. If using Config mode you will have to define a custom mode. You will also have to override the doDispatch method in the portlet class but we will discuss that later. The required order of elements under portlet-app is as follows: 1. 2. 3. 4. 5. portlet custom-portlet-mode custom-window-state user-attributes security-constraints
You must create the custom-portlet-mode definition after the last portlet element. Use the following code to define the CONFIG mode.
338
10.1.9 allows
JSR 168 portlets only support MINIMIZED, MAXIMIZED, and NORMAL window states. By default, all of these modes are supported by the portlet and the <allows> element should be removed form the portlet.xml
10.1.10 config-param
JSR 168 portlets use a PortletPreference object instead of the PortletData and PortletSettings objects. If you have defined any config-param elements in your portlet.xml, you should convert these to portlet-preference elements. Change:
Example 10-5 config-param in portlet.xml <config-param> <param-name>favoriteFood</param-name> <param-value>Pizza</param-value> </config-param>
to:
Example 10-6 portlet-preference <portlet-preferences> <preference> <name>favoriteFood</name> <value>Pizza</value> <read-only>true</read-only> </preference> </portlet-preferences>
The preference element contains a read-only element. If the preference is read-only, only the administrator can update this preference.
339
default-locale
The default-locale element can be removed, JSR 168 will use the first locale as the default.
supported-locale
All supported locales need to be declared using the supported-locale element.
<supported-locale>en</supported-locale>
resource-bundles
JSR 168 uses resource bundles to contain the portlet title, short title, and keywords for each supported locale. The locale for each resource should also be added to the supported-locale element. The resource-bundle element is used to define the resource bundle.
<resource-bundle>nls.MyPortlet</resource-bundle>
Resource bundles should contain the following three elements javax.portlet.title This key is used for the portlet title. javax.portlet.short-title The short-title is used for devices that cannot display the full title. javax.portlet.keywords Keywords are used when searching portlets. Note: You do have an option of using the portlet-info tag if you choose not to use the resource bundle.
10.2.1 Package
The package declarations need to be changed from the IBM portlet API to JSR 168 packages. You can manually change the imports or you can modify the class names and then perform an Organize Imports in Rational Application Developer.
340
Change:
import org.apache.jetspeed.portlet.*; import org.apache.jetspeed.portlet.event.*;
to:
import javax.portlet.*;
For JSR 168 portlets, you do not have to implement and action listener. There is no equivalent package for org.apache.jetspeed.portlet.event. If you are leveraging any other IBM specific classes, you may need to modify them for JSR 168.
10.2.2 Superclass
The superclass will need to be changed from PortletAdapter to GenericPortlet. Again, if you are implementing IBM portlet API listeners, you can remove the interfaces.
public class MyPortletPortlet extends GenericPortlet
10.2.4 actionPerformed
JSR 168 portlets use the processAction method instead of the actionPerformed method. The processAction method accepts an ActionRequest and ActionResponse object. Change your actionPerformed method to:
public void processAction(ActionRequest request, ActionResponse response) throws PortletException
10.2.5 ActionEvent
In the IBM portlet API, the ActionEvent object is used to retrieve an ActionString. The ActionString is used to determine which form made the request. With JSR 168 you should test the request parameters for the Submit button value. Example 10-7 on page 342 Illustrates a JSR form and the processAction method. As you can see, the submit button in the form has the name of the FORM_SUBMIT static variable. Then, in the processAction method, we test if
341
this parameter exists. If so, we know that the form associated with this submit button was submitted and we can process accordingly.
Example 10-7 JRS 168 Action Handling JSP Form <FORM method="POST" action="<portlet:actionURL/>"> <LABEL for="<%=MyJSRPortletPortlet.FORM_TEXT%>">Enter order id:</LABEL><BR> <INPUT name="<%=MyJSRPortletPortlet.FORM_TEXT%>" type="text"/> <INPUT name="<%=MyJSRPortletPortlet.FORM_SUBMIT%>" type="submit" value="Submit"/> </FORM> Portlet Code public void processAction(ActionRequest request, ActionResponse response) throws PortletException, java.io.IOException { if( request.getParameter(FORM_SUBMIT) != null ) { // FORM_SUBMIT form was submitted //Do something! }
Create new unique static variables for each of the submit buttons (or any other field that will result in a request parameter) used to create an action request. Then modify your processAction method to test for these parameters and process the data as need.
10.2.6 Logging
Unlike the IBM portlet API, JSR 168 portlets do not provide a way to determine if logging is enabled (ie. getPortletLog().isErrorEnabled()). You should remove all if statements testing for logging enabled. Logging for JSR 168 portlets should use getPortletContext().log() instead of getPortletLog().error().
342
The getJSPExtension() method in IBM portlets is used to get the file extension. For JSR 168 portlets, you should change this method to just return a String with the value jsp.
with:
Example 10-10 PortletPreferences PortletPreferences preferences = request.getPreferences(); preferences.seValue(name, new String(Hailey)); preferences.store();
10.2.9 namespace
The namespace concept for both portlet APIs are used in the same way. They do, however, use different methods. Change:
response.encodeNamespace();
to:
response.getNamespace();
343
10.3.1 taglib
The tag library needs to be changed to point to the JSR 168 tld and not the IBM tld. For all JSP using portlet tag, change:
<%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %>
to:
<%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
You can leave the prefix as portletAPI if you choose. You will then need to define the tld in the web.xml. The tagib element needs to be placed after the error-page element. If you are not using an error-page element, you can place the taglib element after the welcome-file-list. Example 10-12 shows the taglib element in the web.xml.
Example 10-12 taglib element <welcome-file-list> ..... <welcome-file>default.jsp</welcome-file> </welcome-file-list> <taglib id="PortletTLD"> <taglib-uri>http://java.sun.com/portlet</taglib-uri> <taglib-location>/WEB-INF/tld/std-portlet.tld</taglib-location> </taglib>
344
10.3.2 portletAPI:init
The IBM portlet API uses the init tag to defines the following objects. portletRequest of type PortletRequest portletResponse of type PortletResponse portletConfig of type PortletConfig JSR 168 uses the portlet:defineObjects tag to generate object for use in the JSP. JSP are called during the render phase. The defineObjects tag defines the following objects. renderRequest of type RenderRequest renderResponse of type RenderResponse portletConfig of type PortletConfig. Although both tags define a portletConfig variable, they are different types of PortletConfigs. All JSP scriptlets containing portletRequest and portletResponse should be changed to use renderRequest and renderResponse.
10.3.3 namespace
Both APIs use namespacing to uniquely identify portlets on a page. The portletAPI:encodeNameSpace tag and the portlet:namespace tag have the same function, but they are implemented quite differently. While the IBM API tag take a value attribute used to concant to the end of the namespace, the JSR tag will just return the namespace without accepting an attribute.
Example 10-13 Namespace using tags IBM API namespace <portletAPI:encodeNamespace value=name /> JSR 168 namespace <portlet:namespace/>
You can also use the method the RenderResponse class to get the namespace.
Example 10-14 Namespace using PortletResponse objects. IBM API namespace method response.encodeNamespace("name"); JSR 168 API response.getNamespace();
345
In Example 10-16 we will create a RenderURL, setting the portlet mode to Edit and the window state to maximized. We will also set some render parameters.
Example 10-16 RenderURL Creating a RenderURL using the JSP tags: <a href="<portlet:renderURL portletMode="EDIT" windowState="MAXIMIZED"> <portlet:param name="myParam" value="myValue"/> </portlet:renderURL>">My RenderURL</a> Creating a RenderURL using the RenderResponse object: <% PortletURL myRenderURL = renderResponse.createRenderURL();
346
10.3.5 portletAPI:text
The text tag of the IBM API is deprecated in favor of using JSTL.
10.3.6 encodeURL
The encodeURL method is used in both APIs. However, in JSR 168, the encodeURL must be passed a root relative path. You should use the RenderRequest.getContextPath() method before the path of the included file. In Example 10-17, we will include the myimage.gif image under the images directory.
Example 10-17 encodeURL <%= renderResponse.encodeURL(renderRequest.getContextPath() + "/images/myimage.gif") %>
10.3.7 CSS
We recommend using WSRP styles whenever possible when developing JSR 168 portlets. In WebSphere Portal, all consumed remote portlet are treated as JSR 168 portlets. By using WSRP styles, this help ensure that your portlet will display correctly if provided as a remote portlet on another portal server. If your portlet is consumed on a different vendors portal server, the IBM specific classes specified on your JSP/HTML would not be picked up. This could cause appearance problems in the new environment.
10.4 Struts
A Struts portlet configuration consists of a specific Portlet class to extend from, some init-params to configure it, as well as an specific RequestProcessor class in struts-config.xml. This configuration is slightly different from the IBM Portlet API to the JSR 168 API. In Chapter 15, Struts portlet development using the JSR 168 API on page 465 you can find more details that will help you to migrate a Struts Portlet application based on the IBM Portlet API to a Struts Portlet application based on the JSR 168 API.
347
10.5 JSF
JSF portlet applications have a portlet.xml with a FacesPortlet defined, as well as some init-params to specify the initial page for each portlet mode. The faces-config.xml file for JSF portlet applications must define a FacesContextFactory, as well as some variable and property resolvers. In Chapter 16, JavaServer Faces portlets on page 511, you can find more details that will help you to migrate JSF applications to JSF portlet applications that use the JSR 168 portlet API
You can use the same portlet service implementation with a few modifications. You will need to verify that the service implementation implements com.ibm.portal.portlet.service.spi.PortletServiceProvider. This interface provides the methods for the life cycle of the portlet service. You will also need to implement your new JSR portlet service interface. If you still need to use this service for IBM portlets, you can also implement you IBM Portlet API portlet service interface. WebSphere portal provides a class for converting IBM Portlet API object into JSR 168 objects. You can use this class to wrap your JSR 168 objects and pass them into the IBM methods of the portlet service
348
implementation. This allows only having to maintain code in only one method, be sure that you method behaves the same using this approach.
Example 10-19 JSR Portlet Service implementation package example.portletservice; import java.io.IOException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import import import import import org.apache.jetspeed.portlet.PortletRequest; org.apache.jetspeed.portlet.PortletResponse; org.apache.jetspeed.portlet.service.PortletServiceUnavailableException; org.apache.jetspeed.portlet.service.spi.PortletServiceConfig; org.apache.jetspeed.portlet.service.spi.PortletServiceProvider;
import com.ibm.portal.portlet.apiconvert.APIConverterFactory; public class MyPortletServiceImpl implements MyPortletService, PortletServiceProvider, MyJSRPortletService { public void HelloService(PortletRequest request, PortletResponse response) { //moved the code from here to HelloService for JSR 168 portlets //call the JSR 168 method using the APIConverterFactory class HelloService(APIConverterFactory.getInstance().getRenderRequest(request), APIConverterFactory.getInstance().getRenderResponse(response)); } public void HelloService(RenderRequest request, RenderResponse response){ try { response.getWriter().println("Hello World from a Portlet Service!"); } catch (IOException e) { e.printStackTrace(); } } public void init(PortletServiceConfig arg0) throws PortletServiceUnavailableException { } public void destroy() { }
349
JSR 168 portlets will register portlet services using JNDI. As with IBM Portlet API portlet services, you should place your JAR file under the <WebSphere Portal>/shared/app directory. You will then need to modify the PortletServiceRegistryService.properties file in <WebSphere Portal>/shared/app/config/services directory. If you are still using the service for IBM portlets you can leave the current entry and create a new entry for use with JSR 168 portlets. The main difference is that when defining the portlet service, you need to prefix the service interface with jndi:.
Example 10-20 Registering JSR 168 portlet service #My portlet service example.portletservice.MyPortletService = example.portletservice.MyPortletServiceImpl jndi:example.portletservice.MyJSRPortletService = example.portletservice.MyPortletServiceImpl
Note: In Example 10-20, we use the same implementation class for both portlets. We can do this because we defined methods for both portlet APIs in our implementation class. You will need to modify your portlet to use a JNDI lookup to get the portlet service. Remove all code from your portlet that retrieves portlet services. The object returned from the JNDI lookup will be of type PortletServiceHome. From the PortletServiceHome object you will be able to access your service. You should perform the lookup in the init method of your portlet. This may take time and only needs to be performed once.
Example 10-21 Retreiving a portlet service from a JSR 168 portlet package myjsrportlet; import java.io.IOException; import import import import import import import import import javax.naming.Context; javax.naming.InitialContext; javax.naming.NameNotFoundException; javax.naming.NamingException; javax.portlet.GenericPortlet; javax.portlet.PortletConfig; javax.portlet.PortletException; javax.portlet.RenderRequest; javax.portlet.RenderResponse;
import com.ibm.portal.portlet.service.PortletServiceHome;
350
import example.portletservice.MyJSRPortletService; public class MyJSRPortletPortlet extends GenericPortlet { private PortletServiceHome myServiceHome = null; public void init(PortletConfig config) throws PortletException { super.init(config); try{ Context ctx = new InitialContext(); Object home = ctx.lookup("portletservice/example.portletservice.MyJSRPortletService"); if(home != null){ myServiceHome = (PortletServiceHome)home; } } catch(NameNotFoundException nnf){ config.getPortletContext().log("MyJSRPortletService is not available"); nnf.printStackTrace(); } catch(NamingException ne){ ne.printStackTrace(); } } public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { response.setContentType("text/html"); MyJSRPortletService myService = (MyJSRPortletService)myServiceHome.getPortletService(MyJSRPortletService.class) ; myService.HelloService(request, response); } }
10.7 Messaging
Portlets using the Messaging interfaces should change to use a cooperative portlet. The JSR specification does not define a messaging listener and therefore the only option is to use cooperative portlets. See Chapter 26, JSR 168 cooperative portlets on page 801 for more information about the cooperative portlet.
351
352
11
Chapter 11.
353
11.1 Overview
Portlets follow the Model View Controller (MVC) design pattern. In this design pattern, portlets will usually use JSPs to generate the output. A portal page can be composed of many portlets along with the theme and skins.
354
Portlets should be designed to be accessible to users with disabilities. the following coding practices should be applied: Do not use only color or sound alone to convey information. Use labels with forms to match the description with the information requested. Example 11-2 uses labels within a form. The label goes with the OrderID form field.
Example 11-2 Using labels with a form <FORM method="POST" action="<portlet:actionURL/>"> <LABEL for="OrderID">Enter order id:</LABEL><BR> <INPUT name="OrderID" type="text"/> <INPUT name="SubmitButton" type="submit" value="Submit"/> </FORM>
Use the ALT attribute with images to provide a description of the image for those who cannot see it. JSR 168 provides a set of JSP tags to use while developing pages. These tags along with JSTL will reduce the amount of JSP scripting elements in the page. This also enables developers who are not familiar with JSP scripting elements to still create dynamic JSPs All JavaScript variable and function names should be namespaced to avoid conflict with other portlets on the same page. The namespace should be added before element name. You can either use the JSP tag <portlet:namespace/> or RenderResponse.getNamesapace() method. Portlets must use the methods of the portlet API to create links to other resources within the portlet (images, JSPs, applets, etc.). The getEncodeURL method of the RenderResponse object will create the correct link. You must pass a root relative path to this method. To obtain the correct path use the RenderRequest.getContextPath() method to prepend your path.
Example 11-3 Creating a link to an image from a jsp <img src="<%= renderResponse.encodeURL(renderRequest.getContextPath() + "/images/smile.jpg")%>alt=Smile/>
Although your portlets can also generate output for WML and cHTML, we will not discuss these topics here.
355
11.2 RequestDispatcher
JSR 168 portlets use the PortletRequestDispatcher to delegate responsibility to servlets and JSPs. The PortletRequestDispatcher can only be used while the portlet is processing a render action. The request dispatcher is obtained from the portlet context by using either the getRequestDispatcher or the getNamedDispatcher method.
11.2.1 PortletContext.getRequestDispatcher
The getRequestDispatcher method takes a String representing the context root relative path of the included file. This path must begin with a /. This method will return null if the path is not found. While using the getRequestDispatcher method, you may include parameters in the path. For example, if your path is /mypath/myjsp.jsp, you can include parameters by adding them to the end of the path /mypath/myjsps.jsp?name=Hailey. Parameters in the URL take precedence over any other render parameters of the same name. These parameters are only in scope for the current request.
Example 11-4 getRequestDispatcher Code in the portlet String path = "/jsptest/jsp/html/testCase.jsp?name=Hailey"; PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(path); rd.include(request,response); Code in JSP Hello <%= renderRequest.getParameter("name") %>
11.2.2 PortletContext.getNamedDispatcher
The getNamedDispatcher will take the name of a servlet rather than a path. This servlet must be a known servlet and registered in the web deployment descriptor. This method will return null if the servlet is not found.
11.2.3 PortletRequestDispatcher.include
The include() method of the PortletRequestDispatcher class is used to include JSPs and servlets. The RenderRequest and RenderResponse objects passed into the render method must be passed into the include() method. The include() method can be called at any time during the render method of the portlet.
356
Note: The included JSPs and Servlets should not use the Servlet RequestDispatcher to do a forward as this may cause unpredictable behavior. JSPs and Servlets called from the include method will have access to the following request parameters. However, if the RequestDispatcher for a servlet was obtained using the getNamedDispatcher the following attributes will not be available. All of the following attribute and parameter can be obtained from the requests getAttribute method. The following five request parameters have corresponding methods in the HttpServletRequest to get the information.
javax.servlet.include.request_uri
The request_uri is the context root relative path of the included JSP or servlet
javax.servlet.include.context_path
The context_path in the context root for the portlet.
javax.servlet.include.servlet_path
The servlet path will be the request_uri minus the context_path
javax.servlet.inclue.path_info
path_info contains information about the path.
javax.servlet.include.query_string
The query_string will contain any parameters that have been added to the path. For example if you had a path of /mypath/myjsps.jsp?name=Hailey, the query_string would contain name=Hailey. The following three attributes will store portlet objects. When using JSPs it is recommended that you use the defineObjects portlet tags rather than getting the objects from the request.
Example 11-5 Request attributes RenderRequest renderRequest = (RenderRequest)request.getAttribute("javax.portlet.request"); RenderResponse renderResponse = (RenderResponse)request.getAttribute("javax.portlet.response"); PortletConfig portletConfig = (PortletConfig)request.getAttribute("javax.portlet.config");
357
javax.portlet.config
Is an object of type PortletConfig
javax.portlet.request
Is an object of type RenderRequest
javax.portlet.response
Is an object of type RenderResponse
HttpServletRequest
The following methods will perform no actions and or return null getProtocol getRemoteAddr getRemoteHost getRealPath getRequestURL getCharacterEncoding setCharacterEncoding getContentType getInputStream getreader getContentLength will always return zero The following methods will be used to return the request parameters listed above getPathInfo javax.servlet.include.path_info getQueryString javax.servlet.include.query_string getRequestURI javax.servlet.include.request_uri getServletPath javax.servlet.include.servlet_path The methods of the HttpServletrequest method for handling parameters will be the same as the methods of PortletRequest.
358
getParameter getParameterNames getParameterValues getParameterMap The properties defined in PortletRequest will be used for the following HttpServletRequest methods. getHeader getHeaders getHeaderNames getCookies getDateHeader getIntHeader The following list of HttpServletRequest methods will be the as the methods of the PortletRequest with the same or similar name. getScheme getServerName getServerPort getAttribute getAttributeNames setAttribute removeAttribute getLocale getLocales isSecure getAuthType getcontextPath getRequestedSessionId isRequestedSessionIdValid getRequestDispatcher getMethod - (this will always return GET) getSession isRequestedSessionIdFromCookie isRequestedSessionIdFromURL isRequestedSessionIdFromUrl
HttpServletResponse
The following methods in HttpServletResponse will return null. encodeRedirectURL encodeRedirectUrl The following methods in HttpServletResponse will not perform any actions:
359
setContentType setContentLength setLocale addCookie sendError sendRedirect setDataHeader addDataHeader setHeader addHeader setIntHeader addIntHeader setStatus containsHeader - (will always return false) The following list of HttpServletResponse methods will be the as the methods of the PortletResponse with the same or similar name. getCharacterEncoding setBufferSize flushBuffer resetBuffer reset getBufferSize isCommited getOutputStream getWriter encodeURL endoceUrl
360
11.3.1 defineObjects
The defineObjects tag will create the following objects. renderResponse of type javax.portlet.RenderResponse renderRequest of type javax.portlet.RenderRequest portletConfig of type javax.portlet.PortletConfig After invoking the defineObjects tag on a page, you can use any of the defined variables in Java scriplets. In Example 11-6, after the defineObjects tag has been called we use the renderResponse object to create a render URL. We also set the portlet mode to EDIT, the window state to maximized, and we added a RenderRequest parameter named edit_param.
Example 11-6 Using renderResponse to create a render action <%@ page session="false" contentType="text/html" import="java.util.*,javax.portlet.*,jsptest.*" %> <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <portlet:defineObjects/> <% PortletURL url = renderResponse.createRenderURL(); url.setPortletMode(PortletMode.EDIT); url.setParameter("edit_param","This is an edit param!"); url.setWindowState(WindowState.MAXIMIZED); %> <a href="<%= url.toString() %>">Render URL</a>
11.3.2 renderURL
The renderURL tag will generate a URL that triggers a render action pointing to this portlet. You can set the portlet mode, window state and pass parameters using this tag. The following attributes are optional.
windowState
This attribute will set the window state. For WebSphere portal the only supported states are MINIMIZED, MAXIMIZED, and NORMAL. A JSPException will be thrown if the window state is not valid or the portlet does not specify the window state as supported. If no windowState is specified, the windowState will remain the same as the current request. This attributes does accept a runtime expression for the value (windowState=<%= newWindowState %>).
361
portletMode
This indicates the mode of the portlet after executing the render action. WebSphere Portal support VIEW, EDIT, HELP, and the custom mode CONFIG. A JSPException will be thrown if the mode specified is not a mode supported by WebSphere portal or the mode is not supported by the current portlet. If the portlet mode is not specified, the portlet mode will remain the same as the same as the current request. This attributes does accept a runtime expression for the value (portletMode=<%= newPortletMode %>).
var
The var attribute will create a variable with the provide name. This variable will be stored in the page scope and provided the value of the created URL. This attributes does accept a runtime expression for the value (var=<%= varName %>). Note: When in the source view in Rational Application Developer, when using this variable in a scriplet (<%= myVar %>) you may see a red box on the right side of the screen stating that myVar variable cannot be resolved. This will be resolved during runtime and you should not be concerned about the warning.
secure
The secure attribute will make use of the setSecure method and is currently not supported in WebSphere Portal. Example 11-7 will create a URL using the renderURL tag. The URL generated is the same as the URL generated in Example 11-6 on page 361.
Example 11-7 Using the renderURL tag to generated a render action <%@ page session="false" contentType="text/html" import="java.util.*,javax.portlet.*,jsptest.*" %> <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <portlet:defineObjects/> <portlet:renderURL portletMode="EDIT" windowState="maximized" var="myURL"> <portlet:param name="edit_param" value="This is an edit parameter"/> </portlet:renderURL> <a href="<%= myURL %>">Render URL</a>
11.3.3 actionURL
The actionURL tag will create a URL that triggers the action phase for this portlet. You can set the portlet mode, window state, and pass parameters. The following attributes are optional.
362
windowState
This attribute will set the window state. For WebSphere portal the only supported states are MINIMIZED, MAXIMIZED, and NORMAL. A JSPException will be thrown if the window state is not valid or the portlet does not specify the window state as supported. If no windowState is specified, the windowState will remain the same as the current request. This attributes does accept a runtime expression for the value (windowState=<%= newWindowState %>).
portletMode
This indicates the mode of the portlet after executing the render action. WebSphere Portal support VIEW, EDIT, HELP, and the custom mode CONFIG. A JSPException will be thrown if the mode specified is not a mode supported by WebSphere portal or the mode is not supported by the current portlet. If the portlet mode is not specified, the portlet mode will remain the same as the same as the current request.This attributes does accept a runtime expression for the value (portletMode =<%= newPortletMode %>).
var
The var attribute will create a variable with the provide name. This variable will be stored in the page scope and provided the value of the create URL. This attributes does accept a runtime expression for the value (var=<%= varName %>). Note: When in the source view in Rational Application Developer, when using this variable in a scriplet (<%= myVar %>) you may see a red box on the right side of the screen stating that myVar variable cannot be resolved. This will be resolved during runtime and you should not be concerned about the warning
secure
The secure attribute will make use of the setSecure method and is currently not supported in WebSphere Portal.
11.3.4 namespace
The namespace tag should be used for named elements to ensure that they are associated with the current portlet. Typically you would namespace JavaScript functions, variables. Namespacing uniquely identifies that the element belongs to the portlet and avoids conflicts with other portlets on the page. The namespace tag does not contain a body and it does not have any attributes.
Example 11-8 Using namespace with JavaScript elements <script language="JavaScript"> var <portlet:namespace/>userName = "Hailey";
363
function <portlet:namespace />helloUser(){ alert(<portlet:namespace/>userName); } </script> <INPUT type="button" value="Click Me" onclick="<portlet:namespace/>helloUser()"/>
11.3.5 param
The param tag defines a renderURL or an actionURL parameter. Example 11-7 on page 362 shows the param tag used in the body of a renderURL tag. The param tag has two required attributes. The following attributes are required.
name
The name attribute will be used to name the parameter. If this parameter is null or empty, the parameter will not be added. This attribute can be expressed by a runtime value (name=<%= myParameterName %>).
value
The value attribute will represent the String value for the named parameter. If this value is null or empty it will be treated as an empty String. This attribute can also be expressed by a runtime value (value=<%= myParameterValue %>).
bidi
Bidirectional languages are read from right to left. The bidi tag is used to support the bidirectional language. The following attributes are not required.
dir
The dir attribute specifies the direction for the language. The only two supported values are rtl for right to left and ltr for left to right. The default for this attribute is rtl.
364
locale
The locale must be specified in the LocalizerService.properties. This attribute can be expressed by a runtime value (for example locale=<%= myLocale %>).
11.3.7 JSTL
Java Standard Tag Library (JSTL) is a set of custom tags included in the Servlet specification. These tags provide common functionality used when developing JSPs. While JSTL is not specific to portlets, you can use them while developing the JSPs in your portlet. Although the complete JSTL specification is outside the scope if this book, we will try to give an overview and some examples. JSTL tags allows portlet developers to use less JSP scripting elements in their JSPs. This is cleaner and less prone to errors. It also enables developers who are not Java programmers to add dynamic content. Although JSTL does provide allot of functionality, there still may be times that scripting cannot be avoided. The JSTL is composed of four libraries.
core
The core is used for basic functionality such as conditional statements. You can also perform looping and input/output operations from this library. To use the core library you must declare the following taglib directive in your JSP
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c"%>
xml
The xml library is used for xml processing. You must include the correct taglib directive to use the xml library.
<%@ taglib uri="http://java.sun.com/jstl/xml" prefix="x"%>
fmt
The format library is used for formatting currency, dates, and other values. This library is also used for internationalization. The following taglib directive is used to declare the format library.
<%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt"%> Example 11-9 fmt <%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %> <fmt:setBundle basename="nls.NLSExample"/> <fmt:message key="message"/>
365
sql
The sql library is used for queries and database access. The following taglib directive is used for specifying the sgl library.
<%@ taglib uri="http://java.sun.com/jstl/sql" prefix="sql"%>
Example 11-10 show a JSTL loop. The number is printed along with a line stating if the number is even or odd.
Example 11-10 JSTL example <c:forEach var="i" begin="1" end="10" step="1"> <c:if test="${ (i % 2) == 0}"> <c:out value="${i} is an even number!" /> </c:if> <c:if test="${ (i % 2) != 0}"> <c:out value="${i} is an even number!" /> </c:if> <br /> </c:forEach>
366
Links
Table 11-1 Anchors Tag <a> Style There are no styles defined for an anchor.
Fonts
Table 11-2 Fonts Style Description This should be used for normal fonts. This will be lighter than the portlet-font
portlet-font
portlet-font-dim Example 11-11 Font styles <div class="portlet-font"> Hello World!<br /> </DIV> <div class="portlet-font-dim"> Hello dimmer World!<br /> </div>
Messages
Message style are using to modify the appearance of paragraphs.
Table 11-3 Messages Style portlet-msg-status portlet-msg-info portlet-msg-error portlet-msg-alert portlet-msg-success Description Displaying current status Additional information Error messages Warning messages Task completed successfully
367
Sections
Section style should be used with division (div) and span HTML tags.
Table 11-4 Sections Style portlet-section-header portlet-section-body Description Section header Normal text Text for alternating rows Selected text Subheading text Section footer All other text that does not fall into one of the previous styles. This could be used for additional information.
portlet-section-alternate
portlet-section-selected portlet-section-subheader portlet-section-footer portlet-section-text
Tables
Table styles should be used for HTML table tags.
Table 11-5 Table Style portlet-table-header portlet-table-body portlet-table-alternate portlet-table-selected portlet-table-subheader portlet-table-footer portlet-table-text Description Table header Text in the cell of a table Alternate rows of a table Selected text in a cell Subheading text Table footer All other text that does not fall into one of the previous styles. This could be used for additional information.
Example 11-12 Table styles <div class="portlet-table-text"> This is my Table. </div> <TABLE border="1" width="456">
368
<TBODY class="portlet-table-body"> <TR class="portlet-table-header"> <TD width="173">First Name</TD> <TD width="275">Last Name</TD> </TR> <TR > <TD width="173">Bob</TD> <TD width="275">Smith</TD> </TR> <TR class="portlet-table-alternate"> <TD width="173">Ed</TD> <TD width="275">Jones</TD> </TR> <TR> <TD width="173">Sue</TD> <TD width="275">Johnson</TD> </TR> </TBODY> </TABLE>
Forms
Table 11-6 Forms Style portlet-form-label portet-form-input-field portlet-form-button portlet-icon-label portlet-dlg-icon-label portlet-form-field-label portlet-form-field Description Label for the entire form and not a label specific to a field. Text of an input field Text of a button Text surrounding an action icon Text surrounding a standard button such as OK or cancel. Text for a field label Text for a field other than an input field such as a checkbox or radio button
Example 11-13 WSRP form styles <DIV style="margin: 6px"> <p class="portlet-form-label">Please fill out your information:</p> <form name="<portlet:namespace />" action="<portlet:actionURL/>"> Please enter your gender: <br /> <label class="portlet-form-field-label" for="<%= WSRPTestPortlet.GENDER %>">Male</label>
369
<input class="portlet-form-field" type="radio" name="<%= WSRPTestPortlet.GENDER %>" value="Male"><br /> <label class="portlet-form-field-label" for="<%= WSRPTestPortlet.GENDER %>">Female</label> <input class="portlet-form-field" type="radio" name="<%= WSRPTestPortlet.GENDER %>" value="Female"><br /> <label class="portlet-form-field-label" for="<%= WSRPTestPortlet.FIRST_NAME %>">First name:</label> <input class="portlet-form-input-field" type="text" name="<%= WSRPTestPortlet.FIRST_NAME %>" /><br /> <label class="portlet-form-field-label" for="<%= WSRPTestPortlet.LAST_NAME %>">Last name:</label> <input class="portlet-form-input-field" type="text" name="<%= WSRPTestPortlet.LAST_NAME %>" /><br /> <input class="portlet-form-button" type="submit" name="<%= WSRPTestPortlet.SUBMIT %>" value="Enter" /> </form> </DIV>
Menus
Table 11-7 Menus Style portlet-menu portlet-menu-item portlet-menu-item-selected portlet-menu-item-hover portlet-menu-item-hover-selected portlet-menu-cascade-item portlet-menu-cascade-item-selected portlet-menu-description portlet-menu-cation Description Menu settings Deselected menu item Selected menu item Mouse over on an deselected menu item Mouse over on a selected menu item Deselected menu item with submenus Selected submenu item Description of the menu Menu caption
370
The Style.css will be located in was_root/installedApps/hostname/wps.ear/wps.war/themes/html. In this Style.css you will be able to find both IBM and WSRP styles and a description about the style. WebSphere portlet also provides a HelpStyle.css to provide a constant look and feel to the Help mode of portlets. During aggregation, the most specific directory is searched first. If a match is not found there then the search gets more general until a match is found. This allows the creation of style for different scenarios. Example 11-14 shows the order of the search.
Example 11-14 Order of precedence during aggregation
1. 2. 3. 4. 5.
For example, if you use a style that behaves differently in Internet Explorer, you could change that Style under the ie directory while leaving the style in the directory below untouched. Now when an Internet Explorer client access the page they will see the correct style. Lets say we are using the Science theme and need to create a new class and a special value for clients using Internet Explorer. We could update the class under the ie directory. WebSphere portlat will search the following directories for the styles. You would need to update the Style.css in the most specific directory under ie in order to see the changes for Internet Explorer. All other browser will pick up the Style.css from the most specific directory not containing ie. themes/html/Science/ie/en/en_US/Style.css themes/html/Science/ie/en/Style.css themes/html/Science/ie/Style.css themes/html/Science/Style.css themes/html/ie/en/en_US/Style.css themes/html/ie/en/Style.css themes/html/ie/Style.css themes/html/en/en_US/Style.css themes/html/en/Style.css themes/html/Style.css themes/Style.css
371
372
12
Chapter 12.
Internationalization
In order to make a portal accessible and attractive to a wider audience, it is necessary to provide the portal and its components in multiple languages. WebSphere Portal has been translated into the languages listed on Table 12-1 on page 375. The WebSphere Portal architecture makes it easy and efficient to provide internationalization support to portals. The enablement can be performed during development, deployment or runtime with the proper design decisions up front. This chapter will guide you through several approaches to enable internationalization: Using resource bundles Translating whole resources JSR 168 API considerations Dynamically changing the language during the session Administration Working with characters Best practices
373
The file name of the resource bundle is very important. The file must of type properties. All translated copies of the default resource bundle must include the locale in their title. This is illustrated in Figure 12-1.
The name is important because the Portal will locate the appropriate bundle for you based on the locale you provide. The naming convention for resource bundles is [bundle]_[language]_[country]_[variant].properties. The ISO standard ISO-639 is used for the language codes of most languages.For Hebrew the old
374
language code iw is used. The ISO standard ISO-3166 is used for the country/region codes. WebSphere portal supports the use of [variant], although resource bundles supplied with the portal do not use it. When you use properties files in your code you need only provide the base name of the bundle and it will append the appropriate locale. The locales are listed in Table 12-1.
Table 12-1 Languages supported by WebSphere Portal Locale Code ar cs da de el en ru sv es tr fi fr zh zh_TW hu it iw ja ko nl no pl Language Arabic Czech Danish German Greek English Russian Swedish Spanish Turkish Finnish French Simplified Chinese Traditional Chinese Hungarian Italian Hebrew Japanese Korean Dutch Norwegian Polish
375
376
Enter the name of the new folder, typically nls as shown in Figure 12-4 on page 378.
377
In the nls folder, you need to create the default properties files. Select the nls folder and right-click. From the context menu, select New Other Simple File. Be sure the correct directory is selected and enter the name of the default properties file as illustrated in Figure 12-5 on page 379. Do not include any language codes in the name, or include any spaces in the name of the resource bundle.
378
When you are done, double-click the properties file in the navigator to open the simple text editor. Using the text editor, define your keys and the default values, such as those shown in Example 12-1 on page 374. Use CTRL-S to save the file.
379
Open the directory containing the application and use the normal system copy/paste and rename functionality to create the new resource bundles. Each new bundle should have a unique locale appended. In practice, you may at development time only have the default and English properties files. This same approach can later be used to import translated files received from an outside source. Once you have created the bundles you want, you need to make them available in the RAD environment. To do this, simply select the portlet project, right-click and select Refresh as shown in Figure 12-7 on page 381.
380
When you are done, the folder should appear as in Figure 12-1 on page 374, depending of course on the number of languages you choose to support.
Bundle Base Name: the first parameter indicates the base name of the resource bundle. The name includes the path relative to the classes directory as shown in Example 12-4 on page 382. The name does not specify the locale suffix or the properties file type. If the base file name cannot be found, or the key is not present in the properties file, a PortletException will be thrown. Key: this parameter maps to a key value in the properties file. If the key is not found, a PortletException is thrown.
381
Locale: this is used by the Portal to create the complete resource bundle name. You are free to use any locale you like but to ensure the users locale is returned, the code in Example 12-4 works well. The getLocale method returns the preferred locale for the user. The Portal Server determines the locale by first retrieving the users preferred language set during registration. If the preferred language is not set, the locale is retrieved from the accept-language header supplied by the client.
Example 12-4 Accessing resource bundles via the API getPortletConfig().getContext().getText("nls.NLSExample", "welcome", request.getLocale());
As with specifying bundles in portlet code, the bundle name here must include the package name relative to the classes directory.
382
If the key cannot be located in the properties file, you will see the key written between question marks.
383
Each locale you support must have its own folder and contain whatever fully translated resources you want the portal to serve. If the portal cannot find the requested resource in the appropriate directory, it will attempt to locate the default by searching one level higher. If no default resource is located up the directory tree, an exception is thrown. To retrieve the translated resource, simply use the include method of the PortletContext object. Do not specify the NLS directory structure. This code is illustrated in Example 12-7. You should notice that there is in fact nothing unique about calling a translated JSP and calling the simple JSPs. All the work is done by the portal.
Example 12-7 Including translated JSP files getPortletConfig().getContext().include("/messagereceiver/jsp/MessageReceiverVi ew.jsp", request, response);
The name of the resource bundles where the information is stored is provided by the portlet deployment descriptor, as shown in Figure 12-10 on page 385.
384
Unlike IBM Portlets API, in Portlets JSR 168 you can set the portlet title using resource bundles. If you want to set the portlet title using resource bundles, create a resource bundles for each language you want to be supported. Translate the javax.portlet.title key.
Example 12-9 Translate portlet title using resource bundles # Portlet Info resource bundle example javax.portlet.title=portlet con soporte NLS javax.portlet.short-title=Prueba NLS javax.portlet.keywords=
You also have to add the supported locales in portlet.xml file. In Figure 12-10 English, Spanish and Brazilian Portuguese are the supported locales for that portlet.
385
Example 12-10 Changing the language during the session <wps:url command=ChangeLanguage> <wps:urlParam name=locale value=language/> </wps:url>
where language is the code representation of the language that you want to be available in your portal. The 12.10, Dynamically changing the language on page 411 shows how to add links to the portal theme to change to different languages in one click.
386
Locate the portlet you want to work with and click the Configure portlets icon. At the bottom of the next window, select I want to set titles and descriptions. By default, the resulting window, shown in Figure 12-12 on page 388, will only display the languages listed in Table 12-1 on page 375 indicated for support by the portlet.xml.
387
To change the title and/or description, select the edit icon of the language for which you want to set a title and/or description. Enter the new title in the resulting window shown in Figure 12-13.
You may have noticed that only the title and description can be adjusted via the Administration window.
388
To add support for a locale in the portlet.xml in RAD, open the portlet.xml editor and select the concrete portlet you wish to work with. This is illustrated in Figure 12-14.
To add a new locale, select the Add button in the locale section as shown in Figure 12-14. In the resulting dialog, you can select the locale from the drop-down list or enter the country code if you know it. This is illustrated in Figure 12-15. If the locale you choose is already defined in the portlet.xml, you will be prevented from adding it again.
389
All portlets must have a default language specified in the deployment description, otherwise the portlet cannot be installed.
Display the advanced options and select I want to set titles and descriptions. In the resulting window, select the Edit icon of the language for which you want to set a title and/or description. Enter the new title and/or description and select OK.
390
Now you will see the new title and description in the list.
391
Note: If your portal configuration includes Lotus Collaborative Services, add a new CSRes_language.properties file for the new language to the /wp_root/shared/app/nls directory
392
Do not rely on an administration implementation of NLS. The NLS enablement facility for portlets is limited and there is no guarantee or check system. To implement dynamic NLS titles, consider implementing the PortletTitleListener interface and generating the title content via JSP or HTML files. Leave translations to language experts. With proper design, planning and construction of your portlet applications, there should be little to no effort involved in incorporating support for new languages.
ActionEventPortlet.java
MessageReceiver.java
Portal
action Performed Bean JSP receive Message Set Request Get object NLS bundles
Action Red Action Blue
doView
doView
Browser
View mode
Edit mode
View mode
393
The resource bundle is accessed via the PortletContext objects getText method and you will need to provide the following: Bundle Base Name: the first parameter indicates the base name of the resource bundle. The name includes the path relative to the classes directory. The name does not specify the locale suffix or the properties file type. If the base file name cannot be found, or the key is not present in the properties file, a PortletException is thrown. Key: this parameter maps to a key value in the properties file. If the key is not found, a PortletException is thrown. In addition, the locale is used by the Portal to select the proper language bundle. However, you cannot set this value when invoking NLS bundles from JSPs.
394
4. Right-click it and select New Other. 5. Select Simple Folder, and click Next.
395
6. In the next window enter the following values: Parent folder: ActionEvent/JavaSource Folder name: nls Click Finish. 7. Your directory structure should now look as shown in Figure 12-21 on page 397.
396
8. Select the new ActionEvent/Java Resources/JavaSource/nls folder. 9. Click File Import. 10.Select File System and click Next. Browse to C:\LabFiles\NLSLab\Bundles. Note: The sample scenario included in this chapter requires that you download the sample code available as additional materials. See Appendix A, Additional material on page 1003. 11.Select all four properties files and click Finish to import.
397
12.View the files in the nls folder by double-clicking them. Notice how they are structured.
Example 12-11 NLSLab.properties (default) readystatus=Ready to receive message receivedstatus=Received a message viewmode=Operating in View mode redColor=RED blueColor=BLUE Example 12-12 NLSLab_en.properties (English) readystatus=Ready to receive message
398
receivedstatus=Received a message viewmode=Operating in View mode redColor=RED blueColor=BLUE Example 12-13 NLSLab_es.properties (Spanish) readystatus=Listo para recibir mensaje receivedstatus=Mensaje recibido viewmode=Operando en modo de visualizaci\u00F3n redColor=ROJO blueColor=AZUL Example 12-14 NLSLab_pt_BR.properties (Brazilian Portuguese) readystatus=Pronto para receber mensagem receivedstatus=Mensagem recebida viewmode=Operando em modo de Visualizao redColor=VERMELHA blueColor=AZUL
399
Example 12-15 MessageReceiverPortletView.jsp supporting NLS with bundles <%@ page session="false" contentType="text/html" import="java.util.*, messagereceiver.*"%> <%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %> <%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %> <portletAPI:init/> <DIV style="margin: 6px"> <H3 style="margin-bottom: 3px">Welcome!</H3> This is a sample <B>view mode</B> page. You have to edit this page to customize it for your own use.<BR> The source file for this page is "/Web Content/messagereceiver/jsp/html/MessageReceiverPortletView.jsp". <fmt:setBundle basename="nls.NLSLab"/> <br> <% if (request.getAttribute("MyMessage") == null) { %> <B><fmt:message key="readystatus"/> ... </B> <% } else { %> <B><fmt:message key="receivedstatus"/>:</B> <B><%= request.getAttribute("MyMessage") %></B> <% } %> </DIV>
5. Select File Save All to save all your changes to the project.
400
6. Select a new locale value by clicking the Edit my profile icon to select a preferred language, as shown in Figure 12-24.
For example, select Brazilian Portuguese as the preferred language for the wpsadmin user (default user for portlet development environment). It may be necessary to enter a first and last name before you can continue. Enter wps for both if this happens.
401
7. Click OK. Now you will view the information displayed in the MessageReceiver portlet in Brazilian Portuguese. Note: You should also notice that when the language locale changes, the text of the WebSphere Portal menu at the top of the page also changes.
402
8. Edit the user profile again (Editar meu perfil in Brazilian Portuguese) and try Spanish (Espanhol) as the new locale. The content you specified will display in Spanish.
9. Edit the user profile again and try French (Frances in Spanish) as the new locale. 10.Since French has not been enabled, Portal will use the default bundle. Your message will display in English (as is specified in the default bundle), but the WebSphere Portal menu at the top will display in French.
403
11.To change your language back to English before you exit, click Edit my profile (Editer mon profil) and select English (Anglais in French) as your language.
404
if( getPortletLog().isDebugEnabled() ) getPortletLog().debug("ActionListener - actionPerformed called"); //get the preferred locale for the user Locale loc = event.getRequest().getLocale(); // ActionEvent handler String actionString = event.getActionString(); PortletRequest request = event.getRequest(); // Add action string handler here if( actionString.equalsIgnoreCase(ACTION_RED) ) { //access the resource bundle via the PortletContext object's getText method String red = getPortletConfig().getContext().getText("nls.NLSLab", "redColor", loc); //create the string of HTML to be rendered String value = "Action <FONT color=\"#ff0000\">" + red + "</FONT>"; // Create an instance of portlet data to store values PortletData portData = request.getData(); ........................ if( actionString.equalsIgnoreCase(ACTION_BLUE) ) { // access the resource bundle via the PortletContext object's getText method String blue = getPortletConfig().getContext().getText("nls.NLSLab", "blueColor", loc); //create the string of HTML to be rendered String value = "Action <FONT color=\"#0000ff\">" + blue + "</FONT>"; // Create an instance of portlet data to store values PortletData portData = request.getData(); .......
3. Open the resource bundles located in the ActionEvent/Java Resources/JavaSource/nls folder and make sure the following key-value pairs required for this scenario have been included in the properties files.
Example 12-17 NLSLab.properties (default) redColor=RED blueColor=BLUE
405
Example 12-18 NLSLab_en.properties (English) redColor=RED blueColor=BLUE Example 12-19 NLSLab_es.properties (Spanish) redColor=ROJO blueColor=AZUL Example 12-20 NLSLab_pt_BR.properties (Brazilian Portuguese) redColor=VERMELHA blueColor=AZUL
4. Select File Save All to save all your changes to the project. 5. Close the browser. 6. Click Run on Server to test your changes. 7. Click Edit my profile to change the preferred languages and execute the application again to check that portlets display the word BLUE or RED in the language you have selected. Note: For simplicity, not all text in this sample scenario has been enabled for NLS.
406
ActionEventPortlet.java
MessageReceiver.java
Portal
action Performed Bean JSP receive Message Set html Request Get JSP JSP JSP object (en) (es) (pt)
doView
doView
Browser
View mode
Edit mode
View mode
1. Open the WebContent/messagereceiver/jsp/html/MessageReceiverPortletView.jsp page to delete fmt tags and return to static information. Your code should look as shown in Example 12-21.
Example 12-21 MessageReceiverPortletView.jsp <%@ page session="false" contentType="text/html" import="java.util.*, messagereceiver.*"%> <%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %> <portletAPI:init/> <DIV style="margin: 6px"> <H3 style="margin-bottom: 3px">Welcome!</H3> This is a sample <B>view mode</B> page. You have to edit this page to customize it for your own use.<BR> The source file for this page is "/Web Content/messagereceiver/jsp/html/MessageReceiverPortletView.jsp". <br> <% if (request.getAttribute("MyMessage") == null) { %>
407
<B>Ready to receive message ... </B> <% } else { %> <B>Received a message:</B> <B><%= request.getAttribute("MyMessage") %></B> <% } %> </DIV>
2. Select your Web Content/messagereceiver/jsp/html folder, right-click it and select New Folder. 3. Type en for the Folder name field and click Finish. 4. Right-click WebContent/messagereceiver/jsp/html/MessageReceiverView.jsp and select Copy.
5. Now select the WebContent/messagereceiver/jsp/html/en folder, right-click it and select Paste. 6. Open the page WebContent/messagereceiver/jsp/html/en/MessageReceiverView.jsp to update the text indicating the location of the source file page. This is not required but it is recommended for clarity.
408
Example 12-22 MessageReceiverPortletView.jsp (English) <%@ page session="false" contentType="text/html" import="java.util.*, messagereceiver.*"%> <%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %> <portletAPI:init/> <DIV style="margin: 6px"> <H3 style="margin-bottom: 3px">Welcome!</H3> This is a sample <B>view mode</B> page. You have to edit this page to customize it for your own use.<BR> The source file for this page is "/Web Content/messagereceiver/jsp/html/en/MessageReceiverPortletView.jsp". <br> <% if (request.getAttribute("MyMessage") == null) { %> <B>Ready to receive message ... </B> <% } else { %> <B>Received a message:</B> <B><%= request.getAttribute("MyMessage") %></B> <% } %> </DIV>
7. Optionally, repeat the steps to create the folders and JSPs for other languages such as Spanish (es) and Brazilian Portuguese (pt_BR). Your directory structure should be as illustrated in Figure 12-31 on page 410.
409
8. Modify the JSP pages to display a message in the proper language. Also change the directory of the source file pages. For example, create a folder with a JSP for Spanish (es).
Example 12-23 MessageReceiverPortletView.jsp (Spanish) <%@ page session="false" contentType="text/html" import="java.util.*, messagereceiver.*"%> <%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %> <portletAPI:init/> <DIV style="margin: 6px"> <H3 style="margin-bottom: 3px">Welcome!</H3> This is a sample <B>view mode</B> page. You have to edit this page to customize it for your own use.<BR> The source file for this page is "/Web Content/messagereceiver/jsp/html/es/MessageReceiverPortletView.jsp". <br> <% if (request.getAttribute("MyMessage") == null) { %> <B>Listo para recibir mensaje ... </B> <% } else { %> <B>Mensaje recibido:</B> <B><%= request.getAttribute("MyMessage") %></B>
410
<% } %> </DIV> Example 12-24 MessageReceiverPortletView.jsp (Br Portuguese) <%@ page session="false" contentType="text/html" import="java.util.*, messagereceiver.*"%> <%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %> <portletAPI:init/> <DIV style="margin: 6px"> <H3 style="margin-bottom: 3px">Welcome!</H3> This is a sample <B>view mode</B> page. You have to edit this page to customize it for your own use.<BR> The source file for this page is "/Web Content/messagereceiver/jsp/html/pt_BR/MessageReceiverPortletView.jsp". <br> <% if (request.getAttribute("MyMessage") == null) { %> <B>Pronto para receber mensagem ... </B> <% } else { %> <B>Mensagem recebida:</B> <B><%= request.getAttribute("MyMessage") %></B> <% } %> </DIV>
9. Run the scenario (click Run Run on Server) and verify your results in multiple languages. 10.Change the locale in the user profile as before and try other supported languages.
411
That is where the default files for WebSphere theme are, if you are using another theme select its corresponding directory. 2. To create links for English, Spanish and Brazilian Portuguese add the following code
Example 12-25 Adding links to the portal theme ............ <%-- Admin link bar - New Page, Edit Page, Assign Permissions Don't show these links in solo state --%> <wps:if portletSolo="no"> <%@ include file="./AdminLinkBarInclude.jsp" %> </wps:if> </td> </tr> <%-- supported languages --%> <tr> <td colspan="2"> <table border="0" cellspacing="5" cellpadding"0"> <tr> <td> <a href="<wps:url command='ChangeLanguage'><wps:urlParam name='locale' value='en'/></wps:url>"><wps:text key="lang_en" bundle="nls.Text"/></a> </td> <td> <a href="<wps:url command='ChangeLanguage'><wps:urlParam name='locale' value='es'/></wps:url>"><wps:text key="lang_es" bundle="nls.Text"/></a> </td> <td> <a href="<wps:url command='ChangeLanguage'><wps:urlParam name='locale' value='pt_BR'/></wps:url>"><wps:text key="lang_pt_BR" bundle="nls.Text"/></a> </td> </tr> </table> </td> </tr> <tr> <td colspan="2"> <%-- Show navigation bars for first two levels of page navigation Don't show navigation in solo state --%> <wps:if portletSolo="no"> <%@ include file="./PlaceBarInclude.jsp" %> <%@ include file="./PageBarInclude.jsp" %> </wps:if> </td> </tr> .............
412
3. In Example 12-25 on page 412 we also translate the links for the different language using resource bundles. Create four new files in wp_root/shared/app/nls/ directory and call them: Text_en.properties, Text_es.properties, Text_pt_BR.properties and Text.properties.
Example 12-26 Text.properties (default) lang_en=English lang_es=Spanish lang_pt_BR=Brazilian Portuguese Example 12-27 Text_en.properties (English) lang_en=English lang_es=Spanish lang_pt_BR=Brazilian Portuguese Example 12-28 Text_es.properties (Spanish) lang_en=Ingl\u00E9s lang_es=Espa\u00F1ol lang_pt_BR=Portugu\u00E9s de Brasil Example 12-29 Text_pt_BR.properties (Brazilian Portuguese) lang_en=Ingl\u00EAs lang_es=Espanhol lang_pt_BR=Portugu\u00EAs (Brasil)
4. Re-start the server and run the application. You will see a link for each language supported as show in Figure 12-32. Click them to test the scenario.
Note: Since the preferred language selected by the user does not change, no text translated using the preferred locale for the user will display in the proper language.
413
414
13
Chapter 13.
Struts portlets
Struts is a very popular open source framework that embodies the Model-View-Controller (MVC) pattern. The Struts framework is an open source project of The Apache Software Foundation and can be used to effectively design Web applications as well as portlet applications. The Struts portlet framework was implemented by IBM so that applications written based on the Struts framework could run in WebSphere Portal environment, as well as to enable the development of portlets using the Struts framework. In this chapter we provide an overview of the Struts framework and discuss the main differences that exist when using the Struts portlet framework.
415
13.1 Overview
The MVC pattern that is embodied by Struts provides an standard way to separate logic, presentation and data in modular applications, among the three application tiers, as shown in Figure 13-1: The model is the set of data and business rules for the application. This is commonly called the applications business logic. This business logic in most cases involves access to data stores, legacy systems and external applications. The view is the applications user interface. This tier is also known as the presentation layer. The controller defines the way that an application interacts with user input and the model, that is, the overall flow of events. This is called the application logic.
Mappings in struts-config.xml
User Actions
Backend Resources
invoke
return
JavaBeans EJBs Corba Legacy Web Appls Domino Web Services Databases
Control
forward JSP
Model
View
Figure 13-1 Struts and the Model-View-Controller pattern
The following considerations apply to Struts applications: All browser requests are submitted to the Struts ActionServlet. When the HTML form is submitted, the ActionForm subclass is automatically populated with the form data.
416
The Struts ActionServlet determines which Action subclass route to using the mapping that is pre-configured in struts-config.xml. The ActionServlet passes the control to the Action subclass. The Action subclass can access the form data that is stored in the ActionForm subclass and pass it to the back-end business logic for further actions. The Action subclass invokes the back-end business logic, that can be implemented with any technology. The Action subclass may update the any necessary data in the ActionForm subclass. The Action subclass passes the control to the JSP that will render the view to be returned to the browser. The JSP that will render the view can access the form data that is stored in the ActionForm. This data is updated with all changes made by the Action. The view is returned to the browser. Note: The core of the Struts framework is to provide a flexible controller layer, interacting with both the view and the model layers.
Struts components
The main Struts components are illustrated in Figure 13-2 on page 418 and they are: Actions and ActionServlet The struts ActionServlet (org.apache.struts.action.ActionServlet) handles all browser requests. According to the rules configured in a Struts configuration file (struts-config.xml), the ActionServlet invokes an Action subclass (subclass of org.apache.struts.action.Action). The ActionServlet is said to perform actions, which means that the servlet invokes the perform method of each of the instantiated action classes. Each browser request is mapped to an Action subclass in the struts-config.xml file. The ActionServlet loads the mapping during initialization. To configure the Web project to pass all browser requests to the ActionServlet, map all URIs that end with .do (for example, *.do) to the ActionServlet in the Web deployment descriptor. You can then provide the actual Action subclass mappings in the Struts configuration file for individual request URI, such as /order.do. ActionForm Struts provides the class org.apache.struts.action.ActionForm, which a Java developer subclasses to create a form bean. At runtime, the bean is used in two ways: when a JSP prepares the related HTML form for display, the JSP accesses the bean, which holds values to be placed into the form.
417
Those values are provided from business logic or from previous user input. When user input is returned from a Web browser, the bean validates and holds that input either for use by business logic or (if validation failed) for subsequent redisplay. Tag libraries Struts provides a variety of custom JSP tags which are simple to use but powerful in the sense that they hide information. The page designer does not need to know much about form beans beyond, for example, the bean names and the names of each field in a given bean. The custom tags support: Pre-population of a HTML from with values taken from an ActionForm subclass. Internationalization, such as providing text that is determined by the users locale. Logic, such as showing a different title for a page based on how it is used.
View
Tag Libraries
Controller
Bean
Model
Action 1
JSP JSP
ActionServlet
Action 2 Action 3
Backend Resources
Action 4
418
3. The ActionServlet calls the DepositAction sub-class and passes the DepositForm 4. DepositAction class invokes Deposit class to perform the business logic 5. The Deposit class performs the database retrieval and update 6. The result is the Account bean returned to the DepositAction and placed into session (or request) data 7. The DepositAction class returns either a "success" or "fail" ActionForward result 8. The ActionServlet calls either the result.jsp (for "success") or the error.jsp (for "fail") 9. The result.jsp displays the Account data.
View
taglib resources
Controller
DepositForm
Model
2
Action Servlet
index.jsp
1 8
3 7
ActionForward "success" "fail"
Deposit Action
Deposit
config
Account JDBC
Session
Bank Database
419
Developers that have worked with Struts in the servlet environment should adapt easily to the Struts portlet framework. The packaging of a Struts portlet application is very similar to a Struts application in the servlet environment. However, WebSphere Portal also introduces additional concepts, such as portlet modes, multiple device support, and portlet communication, that might need to be addressed by the Struts application. The Struts portlet framework also supports Struts portlets developed using either the IBM Portlet API or the JSR the JSR 168 API. For example, the processing of a Struts portlet using the IBM Portlet API consists of three steps: 1. Initialization. This process is called the first time that the portlet is invoked. In this step the Portal initializes the WpsStrutsPortlet. One of the tasks accomplished by the WpsStrutsPortlet.init method is initializing the ActionServlet. At the end of this step, the ActionServlet has already loaded all the configurations stored in the struts-config.xml file. 2. Action phase. This process is called during the portlet action phase (for example, when the user clicks a submit button). In this step, the WpsStrutsPortlet.actionPerformed method is invoked. This method will call the WpsRequestProcessor.process method, that owns the responsibility to invoke the Struts Action that was requested. The WpsRequestProcessor also creates an IViewCommand (in this case, a subclass named WpsViewCommand). The IViewCommand encapsulates the information so that the command object can be rendered at a later time, during the rendering phase. 3. Render phase. This process is called during the portlet render phase (that is, after action processing or as the result of refreshing the page). In this step, the WpsStrutsPortlet.service method is invoked. This method essentially invokes the WpsViewCommand.execute method. The execute method receives the request and response objects as parameters. As part of the processing, the previously saved attributes are populated into the request object that is passed in on the execute method. Also, during the render phase, an execution context object (ViewCommandExecutionContext) is passed in on the execute call, which provides additional objects to help with the actual execution. At the end of this phase, control is passed to some jsp page.
420
URI construction
URIs are constructed differently for portlets than for servlets. The portlet creates the URI programmatically using the PortletResponse.createURI method. The Struts portlet framework has modified the tags in Struts so that they create portal links. The Struts link tags behave the same as they do in the servlet environment, but the URL is a portlet URL and the Struts URL is passed as a parameter on the URL.
421
Response object
During portlet action processing the response object is not available. The methods called during Struts processing expect both a request and response. To address the need for a response object, the Struts Portlet framework creates a temporary one. Other than for calling sendError, you should not write to the response object. Instead your action should return an ActionForward object and let the Struts RequestProcessor complete the doForward set.
sendError() processing
The typical Struts application has no need to access the response object during the action processing. However, when an application checks and finds some state information invalid during action processing, it is not unusual for a Struts application to report the error using the response.sendError method. The Struts portlet framework intercepts the calls to sendError and saves the error information in the session. During the later view render phase, this error information is found, and the error information is displayed in lieu of displaying other content for the portlet.
The PortletApiUtils.getInstance method is called to see if a PortletApiUtils instance is available. If a non null value is returned, then the execution is inside of WebSphere Portal. In this case, the portletApiUtils.foward method is used instead of the PageContext.forward. If a forward is required in a JSP, then the logic forward tag is the suggested solution. The forward tag has been modified to use the PortletApiUtils forward
422
implementation, and is the preferred method for a forward from a JSP. The PortletApiUtils can be obtained in a JSP through java code, but obtaining the Struts ModuleConfig to prefix the path is problematic. The logic forward tag handles these issues.
StrutsAction
The StrutsAction class provides an execute method that is passed portlet objects instead of servlet objects so a cast is not required to access portal-specific objects.
Stylesheets
Many existing Struts applications use the rewrite tag to create a link element for a cascading style sheet. This is not the exact intention of the rewrite tag, which is supposed to create the same path as the link tag without the <a> element. Since the Struts portlet framework had to modify how links are created, the rewrite tag required some customizations to be used to create link elements for style sheets.
423
The rewrite tag will create the same path as the link tag, except when the page or forward reference is to a CSS file. In the case where a CSS file is referenced, the rewrite tag will use the Jakarta Struts implementation, which results in a path to the CSS file. Here are examples of how to create link elements for stylesheets using the Struts portlet framework: Using a forward
<link rel="stylesheet" type="text/css" href="<html:rewrite forward='baseStyle'/>">
Using a page
<link rel="stylesheet" type="text/css" href="<html:rewrite page='/basestyle.css'/>">
Markup support
The Struts tag library has been modified to support the additional markup languages supported by WebSphere Portal. For HTML, the tags that create links have been modified to support portlet URIs. See Creating portlet URIs on page 423 for details. There might be cases where the JSPs for a Struts application need to run in both the servlet and portlet environment. For this reason, page level tags are implemented in tag libraries. The Struts application can use them in its JSPs, but the tags will not generate markup when executed within WebSphere Portal. You should also refrain from setting color, and fonts. The portal server supports skins and themes that give the page a consistent look and feel. The JSP should be authored so it adheres to the conventions of the theme by using the appropriate style sheet.
424
The use of the WML tags provided with this distribution is similar to the use of Struts HTML tags. The name of the WML tag library file is struts-wml.tld. The following is an example of the WML version of an index.jsp.
Example 13-2 Using the wml tag in struts portlets <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-wml.tld" prefix="wml" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <p> <wml:link page="/editRegistration.do?action=Create"> <bean:message key="index.registration"/> </wml:link> <br/> <wml:link page="/logon.jsp"> <bean:message key="index.logon"/> </wml:link> <br/> <wml:link page="/overview.do"> <bean:message key="index.overview"/> </wml:link> </p>
WML does not have a <form> element like HTML. Struts, however, uses the <form> tag for the scoping of parameters and supporting form beans. For that reason, the WML implementation also includes a <form> tag to support some of the Struts features in WML. The <form> tag in the WML taglib takes an action as an attribute. The following is an example of a form in WML.
Example 13-3 A form in WML <wml:form action="/logon"> <do type="options" label="send"> <wml:go method="post"> <postfield name="username" value="$username"/> <postfield name="password" value="$password"/> </wml:go> </do>
425
<bean:message key="prompt.username"/><wml:text property="username"/> <bean:message key="prompt.password"/><wml:password property="password" size="16" maxlength="16"/> </p> </wml:form>
The following are additional considerations: The else clause was the original statement for the servlet-only environment
426
An instance of PortletAPIUtils is initially obtained. In a non-portlet environment this call would return null. The initialization of the Struts portlet framework support would set the instance of the PortletAPIUtils implementation. A PortletResponse object is obtained as an Object The PortletURI is then created with a DefaultPortletAction and a parameter that contains the Struts path through the call to createPortletURIWithStrutsURL More about links can be found in the chapter on Struts portlet framework in WebSphere Portal Infocenter.
427
428
14
Chapter 14.
429
14.1 Overview
Struts portlet projects share common characteristics with standard portlets and Struts projects, although there are some differences of which you should be aware. The Struts portlet project structure and related resources are dictated by the Struts portlet framework support provided by WebSphere Portal and include in Rational Application Developer. Struts portlet projects are created using the New Portlet Project wizard. A default Struts-type portlet and, optionally, a Web diagram file will be added in the process of creating the project. The wizard automatically generates Struts portlet configuration files and the necessary updates to the web.xml file, and adds all of the Struts portlet framework tag libraries and JAR files to the project in the directory structure that is required. We will also add support to Edit mode to show how to update portlet objects (namely the PortletData) in order to store user preferences.
430
5. In the Portlet project page, type the name of the project: MyFirstStruts. 6. Check the Create a portlet check box, if it is not already checked. Typically, you do not need to create a portlet when you import a portlet WAR file into the project. 7. Click Show Advanced > and select WebSphere Portal V5.1 stub for the target server.
Chapter 14. Creating Struts portlets with the IBM Portlet API
431
Figure 14-2 The target server you need is WebSphere Portal V5.1 stub
8. Click Next >. 9. In the Portlet type page, select Struts portlet. Click Next >. 10.In the Features page, leave the Web Diagram option selected. Click Next >. 11.In the Portlet settings page, review the options provided. The settings provided in this page are the same when you select a basic portlet. 12.Leave all settings unchanged. Click Next >. 13.In the Struts portlet settings page, you can specify the name of the resource bundle that will be used for this Struts portlet application, as well as the Java package where it will be stored. Do not make any changes to these fields. 14.Click Next >.
432
15.In the Miscellaneous page, you can select additional markups and modes that will be supported in this portlet application. Do not check any of the options at this time; we will cover them later in the chapter. 16.Click Finish. 17.If the Confirm Perspective Switch window appears, click YES.
The web.xml deployment descriptor was created. A servlet was created and the WpsStrutsPortlet class was specified as the servlet class, which takes an init-param to specify the struts-config file to be used and another to specify the struts mapping. Other init-params were also included but omitted in the sample below to simplify understanding. For more information about all the elements added, see the WebSphere Portal Infocenter.
<servlet id="Servlet_1106865613683"> <servlet-name>myfirststruts.MyFirstStrutsPortlet</servlet-name> <display-name>myfirststruts.MyFirstStrutsPortlet</display-name> <servlet-class> com.ibm.wps.portlets.struts.WpsStrutsPortlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>struts-servlet-mapping</param-name> <param-value>*.do</param-value> </init-param> </servlet>
Chapter 14. Creating Struts portlets with the IBM Portlet API
433
Note: The definition of a config parameter above is the standard way to define a Struts configuration file and modules. In the Struts portlet framework, each markup and mode is mapped to a specific Struts configuration file and module. If you want to add support for Edit mode in the WML markup language, for example, you should create a config/wml/edit parameter pointing to the Struts configuration file for this mode/markup. For more information, refer to 14.2.6, Adding support to Edit mode on page 446. Struts portlet tag library definitions were included. Welcome-file list support for Struts modules was supplied.
<welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list>
434
4. Click Web Page on the palette view, then click anywhere in the Web diagram to place it. Name the Web page index.jsp. You should use this name because this will be the first page in our application. The welcome-file list in the web.xml deployment descriptor is already configured to find this page automatically. 5. Repeat these steps to place two other pages in the Web diagram. Name them configured.jsp. and notConfigured.jsp. 6. Click Action Mapping, click again anywhere in the Web page diagram and name the action mapping welcome. 7. Click Connection. Then connect index.jsp to the welcome action. 8. Click Connection. Then connect the welcome action to configured.jsp. d. In the Choose a connection window that appears, expand Local Forward, then select <new> and click OK. e. Name the connection configured. 9. Click Connection again. Then connect welcome to notConfigured.jsp. a. In the Choose a connection window that appears, expand Local Forward, then select <new> and click OK. b. Name the connection notConfigured. At this time, your Web diagram should look like Figure 14-4 on page 436.
Chapter 14. Creating Struts portlets with the IBM Portlet API
435
436
c. In the Struts configuration file editor, select the Global Forwards tab, at the bottom. d. Click Add. Name the global forward welcome. e. Under the forward attributes section, enter /welcome.do as the Path. Figure 14-6 on page 438 shows the global forward page filled.
Chapter 14. Creating Struts portlets with the IBM Portlet API
437
f. Save and close the editor. 2. The first component you will realize is the index.jsp file. a. In the Web diagram, double-click index.jsp. b. The New JSP file wizard appears. All fields should have been properly filled by the wizard. Refer to Figure 14-7 on page 439 to make sure of this.
438
c. Check Configure advanced options and click Next >. d. In the Tag libraries page, remove all tag libraries by selecting them and clicking Remove. e. Click Add. f. In the Select a tag library page, check the /WEB-INF/struts-logic.tld tag library. g. Click OK. h. Click Next. i. Click Next twice more. j. In the Form field selection page, uncheck Generate fields in a form. k. Click Finish. l. In the JSP editor, paste the following code at the end of the file:
<logic:forward name="welcome"/>
Chapter 14. Creating Struts portlets with the IBM Portlet API
439
This will automatically forward to the global forward welcome mapping, that is, the welcome.do action mapping you will build. m. Save the file and close the JSP editor. 3. The next component you will realize is the notConfigured.jsp file. a. In the Web diagram, double-click notConfigured.jsp. b. The New JSP file wizard appears. Uncheck the Advanced Options check box. c. Click Finish. d. In the JSP editor, erase all code that was automatically generated by the wizard. e. Paste the code from Example 14-1.
Example 14-1 notConfigured.jsp code <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <h3><bean:message key="myfirststruts.title"/></h3> <em><bean:message key="message.mustConfigureFirst"/></em>
We will create the messages in the resources file later on. f. Save the file and close the JSP editor. 4. Now, realize the configured.jsp file. a. In the Web diagram, double-click configured.jsp. b. The New JSP file wizard appears. c. Click Finish. d. Erase all code that was automatically generated by the wizard and paste the code from Example 14-2:
Example 14-2 configured.jsp code <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <h3><bean:message key="myfirststruts.title"/></h3> <em><bean:message key="message.configured"/></em>
5. Edit the resources file. a. In the Project Explorer view, expand Java Resources JavaSource myfirststruts.resources. b. Double-click ApplicationResources.properties.
440
d. Save the file and close the editor. 6. Now you will create a utility class to store the application constants. a. In the Project Explorer view, expand Java Resources. b. Right-click JavaSource and select New Class. c. Enter myfirststruts for the package and Constants for the class name. Click Finish. d. Paste the following code in the class:
public static final String USERNAME = "username"; public static final String PASSWORD = "password";
e. Save the file and close it. 7. Now you will realize the welcome action mapping. a. In the Web diagram, double-click welcome. The New Action Mapping wizard appears, as shown in Figure 14-8 on page 442. Note that the wizard has filled many data in this window. These data came from the Web diagram you designed. The name of the Action mapping became the Action mapping path. The connections you created as Local forwards were automatically inserted in the Forwards list. The Create an Action class radio button was selected, and the Model chosen was the Struts Portlet Framework Action Mapping. This is the mapping provided by Struts portlet framework.
b. Leave all data unchanged and click Next >. c. The Create an Action class for your mapping shows that our mapping will inherit from com.ibm.wps.struts.action.StrutsAction. d. Click Finish.
Chapter 14. Creating Struts portlets with the IBM Portlet API
441
e. Replace the execute method with the code from Example 14-4 on page 443:
442
Example 14-4 The execute method of the welcome Action Mapping ActionForward forward = mapping.findForward("notConfigured"); try { PortletApiUtils portletUtils = PortletApiUtils.getUtilsInstance(); if (portletUtils != null) { PortletRequest portletRequest = (PortletRequest) portletUtils.getPortletRequest( request ); PortletData portletData = portletRequest.getData(); if ( portletData.getAttribute( Constants.USERNAME ) != null ) { forward = mapping.findForward("configured"); } } } catch ( Exception ex ) { } return forward;
Note that the PortletApiUtils is used in order to retrieve the PortletData object. You can test to see if the data that the application expects (here, the username) is already stored at the PortletData and forward to different pages en each case. Note also that you have silenced the catch statement. This is not a good practice, but we will show how to enable logging in the application in 14.2.5, Adding logging support on page 444. f. Right-click anywhere in the code and select Source Organize imports. g. Select com.ibm.portal.struts.common.PortletApiUtils for the PortletApiUtils class and myfirstStruts.Constants for the Constants class. h. Click Finish. i. Save the file and close the editor. 8. Run the code with what you have done so far. a. Right-click the MyFirstStruts project. b. Select Run Run on Server. c. In the Define a new server window, select Manually define a server. d. Select the server type WebSphere Portal V5.1 Test Environment. e. Check the Set server as project default check box. f. Click Finish. g. After a few minutes, you will see a page similar to Figure 14-9 on page 444.
Chapter 14. Creating Struts portlets with the IBM Portlet API
443
Of course, you are not able to configure the portlet in Edit mode yet; our portlet is not configured to support that. This will be done in 14.2.6, Adding support to Edit mode on page 446.
Important: You can have only one traceString in the log.properties file, and the entire traceString should be specified in only one line.
444
c. Save the file and close it. 2. Additionally, the Struts Portlet Framework specifies the common logging Log Factory in the META-INF/services directory. The log factory class name is dependent on the WebSphere Portal container. To specify the correct commons LogFactory for our application, proceed as follows: a. Expand Dynamic Web Projects MyFirstStruts WebContent. b. Right-click the META-INF folder. Select New Folder. c. Enter services as the folder name. d. Click Finish. e. Right-click the services folder. Select New Other. f. In the Select a wizard window, expand Simple. Select File. g. Click Next >. h. Enter org.apache.commons.logging.LogFactory as the file name. i. Click Finish. j. Paste the following line in the file:
com.ibm.wps.portlets.struts.logging.StrutsLogFactory
k. Save the file and close it. 3. Now lets add some code in the welcome action mapping. a. Edit myfirststruts.actions.WelcomeAction. b. Add the following instance variable:
private Log log = LogFactory.getLog(this.getClass());
c. Right-click anywhere in the code and select Source Organize Imports. d. Select org.apache.comons.logging.Log for the Log class and click Next >. e. Select org.apache.commons.logging.LogFactory for the LogFactory class and click Finish. f. Add the following code at the beginning of the execute method:
if (log.isTraceEnabled()) { log.trace("###Welcome###"); }
Chapter 14. Creating Struts portlets with the IBM Portlet API
445
h. Save the file and close the editor. 4. Now you can run the application again, open the log file at wp_root/log folder and look for the Welcome message. You should see a message similar to the following:
2005.02.01 17:25:10.461 l myfirststruts.actions.WelcomeAction trace Servlet.Engine.Transports : 1 ###Welcome###
Note 1: You have to restart the server if it is still running, since you made changes to the configuration files.
446
e. Save the file and close the editor. 3. Add a new struts module to support the Edit mode. a. In the Project Explorer view, right-click the Struts icon. b. Select New Module. c. Fill in all the fields according to Figure 14-11 on page 448. Note that you pointed to the same resources file that you had already created.
Chapter 14. Creating Struts portlets with the IBM Portlet API
447
d. Click Finish. At this time, a new Struts module should appear under the Struts icon, as shown in Figure 14-12.
448
4. Create a new Web diagram for the Edit mode. a. Right-click Web Diagram. b. Select New Web Diagram. c. Enter editMode as the Web diagram file name. d. Place four Web pages in the diagram. Name them html/edit/welcome.jsp, html/edit/index.jsp, configured.jsp and notConfigured.jsp. e. Place two action mappings in the diagram. Name them saveConfiguration and editConfiguration. f. Place a form bean in the diagram. In the Form bean attributes window that appears, enter userBean as its name and request as the scope. g. Create the connections between the components; these are: i. welcome.jsp to editConfiguration. ii. editConfiguration to userBean. iii. saveConfiguration to userBean. iv. Local forward named success from editConfiguration to index.jsp. v. index.jsp to saveConfiguration. vi. Local forward named success from saveConfiguration to editConfiguration. vii. Local forward named user_not_set from saveConfiguration to notConfigured.jsp. viii.Local forward named user_set from saveConfiguration to configured.jsp. h. Right-click anywhere in the Web diagram. Select Change the Struts module association. i. Select /html/edit in the list. Click OK. j. Your diagram should look like Figure 14-13 on page 450. Save it.
Chapter 14. Creating Struts portlets with the IBM Portlet API
449
450
b. Click Finish. c. Replace all code in the new jsp with the following code:
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%> <logic:forward name="Welcome"/>
d. Save the file and close the editor. 3. Next, you will realize the userBean. a. Double-click userBean form bean. This will bring the New Form-Bean wizard. b. Leave the form bean name unchanged. Make sure that the Create an ActionForm class radio button is selected, and that Generic Form-bean Mapping is selected. c. Click Next >. d. Click Next > again. e. In the Create new fields for your ActionForm class page, you are going to add a couple of fields. ix. Click Add.... x. Enter the fields name username. Leave the field type as String. xi. Click Add... again. xii. Enter the fields name password and leave the field type as String. xiii.The window will look like in Figure 14-14 on page 452. f. Click Next >.
Chapter 14. Creating Struts portlets with the IBM Portlet API
451
g. In the Creating a mapping for your ActionForm class, check only reset (which takes an HttpServletRequest as parameter and inherited abstract methods. Leave all other method stubs unchecked. h. Click Finish. Rational Developer will generate the ActionForm bean, that extends from rg.apache.struts.action.ActionForm, along with its proper accessor methods. It will also include the form-bean tag in struts-config.xml file. i. Replace the reset method with the following code:
this.username = ""; this.password = "";
j. Save the file and close it. 4. Next, you will realize the editConfiguration action mapping. a. In the Web diagram, double-click /editConfiguration. b. The New action mapping wizard appears. As every fields are already properly set, click Finish. c. Replace the execute method with the following code:
452
Example 14-5 The execute method from editConfiguration action mapping. saveToken(request); form.reset(mapping, request); return (mapping.findForward("success"));
The code above initially calls the saveToken method, that Struts provides to avoid multiple submissions from one form. Next, the form bean is reset. This forces that the user always enter new values. Finally, the action forwards to success, that points to index.jsp. d. Save the file and close it. 5. The next component you will realize is index.jsp. a. In the Web diagram, double-click index.jsp. b. Click Finish. c. Replace the jsp contents with the code from Example 14-6:
Example 14-6 index.jsp for Edit mode <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <h3><bean:message key="editmode.heading"/></h3> <p><bean:message key="editmode.instructions"/></p> <ul> <li><bean:message key="editmode.instructions.1"/></li> <li><bean:message key="editmode.instructions.2"/></li> <li><bean:message key="editmode.instructions.3"/></li> <%--<li><bean:message key="editmode.instructions.4"/></li>--%> <li><bean:message key="editmode.instructions.5"/></li> </ul> <ul> <html:messages message="true" id="error"> <li><font color="red"><bean:write name="error"/></font></li> </html:messages> </ul> <html:form action="/saveConfiguration.do" urlType="return" validate="false"> <table> <tr>
Chapter 14. Creating Struts portlets with the IBM Portlet API
453
<th align="right"> <bean:message key="prompt.username"/>: </th> <td align="left"> <html:text property="username" size="25"/> </td> </tr> <tr> <th align="right"> <bean:message key="prompt.password"/>: </th> <td align="left"> <html:password property="password" size="25"/> </td> </tr> <tr><td><br></td></tr> <tr> <td align="right"> <html:submit value="Submit"/> </td> <td align="left"> <html:reset/> </td> </tr> </table> </html:form>
Note that you added the html:messages tag, to support that messages from the action can be processed and displayed correctly. Important: If you want execute an Action that will change the portlet mode to its previous mode, you must use the urlType attribute of the html:form tag with the value of return. This attribute was added by Struts portlet framework to the tags html:form, html:link and html:rewrite. 6. Finally, lets realize the saveConfiguration action. a. In the Web diagram, double-click saveConfiguration action. b. All values should be correct, as the Web diagram had all information the wizard needs to create the action. c. Click Finish.
454
d. This action is the one with the most logic. Replace its contents with the code in Example 14-7 and save the file:
Example 14-7 Listing of SaveConfigurationAction.java package myfirststruts.actions; import myfirststruts.Constants; import myfirststruts.forms.UserBean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jetspeed.portlet.PortletData; import org.apache.jetspeed.portlet.PortletRequest; import org.apache.jetspeed.portlet.Portlet.Mode; import org.apache.jetspeed.portlet.Portlet.ModeModifier; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionMessages; import com.ibm.portal.struts.common.PortletApiUtils; import com.ibm.wps.struts.action.StrutsAction; public class SaveConfigurationAction extends StrutsAction { private Log log = LogFactory.getLog (this.getClass ()); public ActionForward execute(ActionMapping mapping, ActionForm form, PortletRequest request) throws Exception { ActionForward forward = mapping.findForward("success"); ActionMessages messages = new ActionMessages(); if (form instanceof UserBean) { UserBean userBean = (UserBean) form; try { PortletApiUtils portletUtils = PortletApiUtils.getUtilsInstance(); if (portletUtils != null) { PortletRequest portletRequest = (PortletRequest) portletUtils .getPortletRequest(request); PortletData portletData = portletRequest.getData(); if (portletRequest.getMode() == Mode.EDIT) { processEditData(request, messages, userBean, portletData); } else { log.debug("this command is only valid in edit mode"); forward = null;
Chapter 14. Creating Struts portlets with the IBM Portlet API
455
} if (!messages.isEmpty()) { saveMessages(request, messages); portletRequest.setModeModifier(ModeModifier.CURRENT); forward = mapping.getInputForward(); } else { if (portletData.getAttribute(Constants.USERNAME) != null) { log.debug("Set view mode to user_set"); ActionForward actionForward = mapping.findForward("user_set"); if (actionForward != null) { portletUtils.createCommand(actionForward .getPath(), request, Mode.VIEW.toString()); } else { log.debug("Could not find action forward for view mode"); } } else { log.debug("Set view mode to user_not_set"); ActionForward actionForward = mapping.findForward("user_not_set"); if (actionForward != null) { portletUtils.createCommand(actionForward. getPath(), request, Mode.VIEW.toString()); } else { log.debug("Could not find action forward for view mode"); } } } } } catch (Exception ex) { log.debug("Could not save configuration " + ex.toString()); } } return forward; } private void processEditData(PortletRequest request, ActionMessages messages, UserBean userBean, PortletData portletData) throws Exception { if (!isTokenValid(request)) { messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.transaction.token")); log.debug("token is not valid");
456
} else { log.debug("token is valid"); // token was valid. String username = userBean.getUsername(); if (username != null && !username.equals("")) { if (username.equals("error")) { messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.invalid.username")); } else { portletData.setAttribute(Constants.USERNAME, userBean.getUsername()); } } else { portletData.removeAttribute(Constants.USERNAME); } if (userBean.getPassword() != null && !userBean.getPassword().equals("")) { portletData.setAttribute(Constants.PASSWORD, userBean.getPassword()); } else { portletData.removeAttribute(Constants.PASSWORD); } portletData.store(); } resetToken(request); } }
Lets take a look at the major points of this code: First of all, you had to retrieve the PortletApiUtils instance. It is the PortletApiUtils that gives us access to the PortletRequest object. Following, you check if the portlet is in Edit mode, with the following code:
if (portletRequest.getMode() == Mode.EDIT)
This way, you guarantee that the action will work only and if only the portlet mode is Edit. Next, the processEditData method is executed. First of all, it checks if the token is valid, with
if (!isTokenValid(request))
Chapter 14. Creating Struts portlets with the IBM Portlet API
457
If the username and password attributes from UserBean are null, the code removes these attributes from PortletData. If not, the code stores them in PortletData, using the setAttribute method. The only exception is when the enter error as the username. In this case, a message is generated, stating that this is an invalid user name. The processEditData also stores the PortletData object with its new attribute values, and reset the token. Back to the execute method, the code checks if there is any message to be shown. If there is any message, then it obligates the portlet to stay at the same mode, with
portletRequest.setModeModifier(ModeModifier.CURRENT);
Finally, the code tests if the username attribute was stored at PortletData, if yes, than it creates a command that makes the View mode page change to the one specified by the user_set local forward.
Example 14-8 The username attribute was found. Forwarding to user_set ActionForward actionForward = mapping.findForward("user_set"); if (actionForward != null) { portletUtils.createCommand(actionForward.getPath(), request, Mode.VIEW.toString()); } else { log.debug("Could not find action forward for view mode"); }
If the username attribute is not stored at PortletData, the View mode forward is to the user_not_set local forward:
Example 14-9 The username attribute was not found. Forwarding to user_not_set ActionForward actionForward = mapping.findForward("user_not_set"); if (actionForward != null) { portletUtils.createCommand(actionForward.getPath(), request, Mode.VIEW.toString()); } else { log.debug("Could not find action forward for view mode"); }
7. Add new keys to resources file: a. Edit myfirststruts.resources.ApplicationResources.properties. b. Add the following keys at the end of the file:
458
Example 14-10 The new messages to be included in the resources file editmode.heading=Edit Mode editmode.title=Struts Edit Mode Example editmode.instructions=Try the options below to demonstrate various Struts features. When a valid username and password are given, an action forwards to a JSP in view mode. Valid usernames and passwords are between 3 and 16 characters long. editmode.instructions.1=Click the <b>Submit</b> button without a valid username and/or password to see dynamic validation editmode.instructions.2=Enter a username of "error" and a valid password to see ActionErrors supported editmode.instructions.3=Enter a username of "delete" and a valid password to remove the configuration editmode.instructions.4=Refresh the page to demonstrate transaction support editmode.instructions.5=Enter a valid username and password to complete the configuration and change the mode to view mode prompt.username=Username prompt.password=Password button.back=back button.send=Send button.save=Save button.cancel=Cancel button.reset=Reset error.transaction.token=Transaction token is not valid error.invalid.username=The username is not valid
c. Save the file and close editor. 8. Run the code: a. Right-click the MyFirstStruts project. b. Select Run Run on Server. c. After the welcome page displays, click the edit icon, go to Edit mode. d. Try the code. You can choose any of the following: If you leave the username blank, or if you enter delete, the username and password attributes will be removed from PortletData, and you will be forwarded to notConfigured.jsp. If you enter error as the username, the application shows an error message and continues in Edit mode. If you enter any other value to username and password, the application stores these values at PortletData and forwards to configured.jsp.
Chapter 14. Creating Struts portlets with the IBM Portlet API
459
460
f. Select pt_BR in the Locale list. g. Click OK. h. Enter MyFirstStruts portlet as the Title. i. Save the file and close the editor. 2. Next, create a resources file for the new locale. a. Expand Dynamic Web Projects MyFirstStruts Java Resources JavaSource myfirststruts.resources. b. Right-click ApplicationResources.properties. c. Select Copy. d. Right-click the myfirststruts.resources package. e. Select Paste. f. Enter ApplicationResources_pt_BR.properties as the new name for the file. g. Click OK. h. Double-click ApplicationResources_pt_BR.properties to edit it. i. Replace all messages with brazilian portuguese messages. Use the messages from Example 14-11.
Example 14-11 Resources file for brazilian portuguese Locale myfirststruts.title=Portlet baseado em Struts utilizando a API IBM message.mustConfigureFirst=Voc deve primeiramente configurar este portlet em modo de Edio. message.configured=Este portlet foi configurado. editmode.heading=Modo de Edio editmode.title=Exemplo de Modo de Edio com Struts editmode.instructions=Tente as opes abaixo para demonstrar vrias capacidades do Struts. Quando um usurio e senha vlidos so inseridos, uma action encaminha para um JSP em modo view. editmode.instructions.1=Pressione o boto <b>Submit</b> com o usurio em branco para remover a configurao editmode.instructions.2=Digite "error" como nome de usurio para ver o suporte a ActionErrors editmode.instructions.3=Digite "delete" como nome de usurio para remover a configurao editmode.instructions.4=Atualize a pgina para demonstrar o suporte transacional editmode.instructions.5=Digite um nome de usurio e uma senha vlidos para completar a configurao e mudar para o modo View
Chapter 14. Creating Struts portlets with the IBM Portlet API
461
prompt.username=Nome prompt.password=Senha button.back=voltar button.send=Enviar button.save=Salvar button.cancel=Cancelar button.reset=Limpar error.transaction.token=Token de transao invlido error.invalid.username=O nome de usurio no vlido
j. Save the file and close the editor. 3. Test the new Locale. a. Run the application. b. In the top menu, click Edit My Profile. c. Under Preferred language, select Brazilian Portuguese. d. Click OK. e. The application now should look like Figure 14-17.
14.3 Messaging
The recommended way to exchanges messages involving Struts portlets is through the use of the Property Broker. See Chapter 24, IBM API declarative cooperative portlets on page 741 for details about how to implement it. The good thing is that the Struts portlet framework does the job of sending and retrieving the messages, once that there is a match between the properties that are through the wires and the form beans properties.
462
14.4 Migration
The file structure of the WAR file for a Struts Portlet Framework is the same as that used for a Struts servlet. The Struts application in the portal server is a WAR file just like it is in the servlet environment. The portlet WAR file has some additional JARS and other requirements, but the basis of the implementation is similar to the servlet application. Existing Struts applications can be migrated using the Struts Portlet Framework so the application can be deployed in WebSphere Portal. Since Struts is a framework there are many variations to how the application can be built with Struts. There are also numerous other frameworks that may also be incorporated into the Struts application. The steps in this section can be used as a starting point for the migration effort, but may not cover all of the issues that can be encountered. 1. Make sure the existing Struts application has been built as a Struts 1.1 application. 2. Check Struts actions to see if the action writes directly to the response object. If it does then, the action must be modified to either return an ActionForward instead, or check to see if the IStrutsPrepareRender interface should be used. 3. The Web deployment descriptor must be updated to use the WpsStrutsPortlet as the servlet class instead of the ActionServlet. (See 14.2.2, Inspecting the Struts portlet project on page 433) 4. The servlet mapping for Struts actions must be specified as the struts-servlet-mapping init parameter. (See 14.2.2, Inspecting the Struts portlet project on page 433.) 5. Create a portlet.xml. 6. Modify the struts-config.xml to specify the WpsRequestProcessor as the controller. (See 14.2.2, Inspecting the Struts portlet project on page 433) 7. Modify tags that use pageContext.forward to use the PortletApiUtils forward. 8. Modify JSPs that use a forward to use the logic forward tag. 9. Modify JSPs as necessary to use the Struts tags for creating URLs, like the Struts Link and Form tags. 10.The JAR files from the WEB_INF/lib directory of the SPFLegacyBlank.war must be used. These files are the Struts JARs and the required Struts Portlet Framework JARs. You can find the SPFLegacyBlank.war at wp_root/installableApps.
Chapter 14. Creating Struts portlets with the IBM Portlet API
463
11.The TLD files must be updated from the WEB_INF/lib directory of SPFLegacyBlank.war. Verify the taglib attributes and that the JSP correctly reference the TLD files. This has been a common source of problems when migrating existing applications. You can find the SPFLegacyBlank.war at wp_root/installableApps. 12.The JSPs should be modified so they do not use html, head and body elements. All HTML output to the portal is written in the context of an HTML table cell.
464
15
Chapter 15.
465
15.1 Overview
Struts portlet projects share common characteristics with standard portlets and Struts projects, although there are some differences that you should be aware of. The Struts portlet project structure and related resources are dictated by the Struts portlet framework support provided by WebSphere Portal and included in Rational Application Developer. Struts portlet projects are created using the New Portlet Project wizard. A default Struts-type portlet and, optionally, a Web diagram file, will be added in the process of creating the project. The wizard automatically generates Struts portlet configuration files and the necessary updates to the web.xml file, and adds all of the Struts portlet framework tag libraries and JAR files to the project in the directory structure that is required. Edit mode support will also be added to show how you can update portlet objects (portlet preferences) in order to store user preferences. The sections in this chapter show how a Struts portlet application is created in Rational Application Developer, and configured to work with the JSR 168 Portlet API. Note: The sample scenario described in this chapter requires WebSphere Portal Test Environment version 5.1.0.1. The sample scenario described in this chapter is illustrated in Figure 15-1.
View mode
index.jsp configured.jsp notConfigured.jsp
viewBean
request
Actions
notConfigured
Portlet Preferences
setValue
Edit mode
Figure 15-1 JSR 168 Struts portlet
466
3. The configuration file (struts-config.xml) has a global forward indicating that the action welcome should be invoked:
<global-forwards> <forward name="welcome" path="/welcome.do"> </forward> </global-forwards>
4. The configuration file (struts-config.xml) states that, for action welcome, the execute method in WelcomeAction will be called and the Action Form viewBean will be passed in the request object:
<action-mappings> <action name="viewBean" path="/welcome" scope="request" type="myfirstjsr168struts.actions.WelcomeAction"> ................. </action> </action-mappings>
5. The configuration file (struts-config.xml) states that the viewBean is the ViewBean class:
<form-beans> <form-bean name="viewBean" type="myfirstjsr168struts.forms.ViewBean"> </form-bean> </form-beans>
6. The execute method in the welcome action reads the portlet preferences and if a username field is not found, the forward notConfigured is returned:
forward = mapping.findForward("notConfigured"); .................... return forward;
Chapter 15. Struts portlet development using the JSR 168 API
467
7. The configuration file (struts-config.xml) shows that forward notConfigured indicates that notConfigured.jsp should be called.
<action-mappings> <action name="viewBean" path="/welcome" scope="request" type="myfirstjsr168struts.actions.WelcomeAction"> ................. <forward name="notConfigured" path="/notConfigured.jsp"> </forward> </action> </action-mappings>
8. The notConfigured.jsp displays a message indicating to the user that he or she needs to switch to Edit mode and enter a user name and password. 9. When entering Edit mode, Portal invokes the initial Edit mode page configured in the portlet descriptor (portlet.xml):
<portlet-preferences> <preference> ................. <name>com.ibm.struts.portal.page.edit.html</name> <value>html/edit/index.jsp</value> </preference> </portlet-preferences>
10.The Edit mode index.jsp page executes and displays a form for the user to enter a user name and a password. 11.The user enters a user name and a password. The form Submit button sends an action with name saveConfiguration.do:
<html:form action="/saveConfiguration.do" .............
Note: The do option in the action parameter indicates that the forward is an action and not a JSP. 12.The Struts Action Servlet populates (setter methods) the associated form bean (editBean) with the user name and password. The Edit mode configuration file (struts-html-edit.xml) shows that the execute method in SaveConfigurationAction should be called and the form bean EditBean should be passed in the request object.
<form-beans> <form-bean name="editBean" type="myfirstjsr168struts.html.edit.forms.EditBean"> </form-bean> </form-beans> <!-- Action Mappings --> <action-mappings> <action name="editBean" path="/saveConfiguration" scope="request" type="myfirstjsr168struts.html.edit.actions.SaveConfigurationAction"> ...................
468
13.The execute method in SaveConfigurationAction executes and gets the user name and password from the bean (getter methods). These values are stored as portlet preferences. 14.The execute method (SaveConfigurationAction) returns the forward user_set:
forward = mapping.findForward("user_set"); return forward;
15.The Edit mode configuration file (struts-html-edit.xml) showsthat the View mode index.jsp should now be called.
<action-mappings> <action name="editBean" path="/saveConfiguration" scope="request" type="myfirstjsr168struts.html.edit.actions.SaveConfigurationAction"> <forward contextRelative="true" name="user_set" path="/index.jsp"> </forward> </action> </action-mappings>
Note: contextRelative="true" indicates that the page is found in the context root and not in /html/edit/. 16.This closes the loop. View mode index.jsp sends the forward to execute its associated action. The action in View mode reads the portlet preferences, stores the values in the viewBean (setters) and this time returns the forward configured. 17.View mode configuration file (struts-config.xml) states that configured.jsp should now be invoked:
<action-mappings> <action name="viewBean" path="/welcome" scope="request" type="myfirstjsr168struts.actions.WelcomeAction"> <forward name="configured" path="/configured.jsp"> </forward> ........... </action> </action-mappings>
18.Page configured.jsp executes, reads the user name from the viewBean and displays it.
Chapter 15. Struts portlet development using the JSR 168 API
469
1. Start Rational Application Developer V6 if not already running. 2. If prompted, select the default workspace or any other workspace of your preference for this session. 3. In the menu, select File New Project. 4. In the New Project window, select the wizard for Portlet Project (JSR 168).
5. Click Next. 6. If the Confirm Enablement window appears, asking you if you want to enable the portal development role, click OK.
470
7. In the Portlet Project (JSR 168) page, enter the project name as MyFirstJSR168Struts. 8. Check the Create a portlet check box, if it is not already checked. Note: Typically, you will not need to create a portlet when importing a portlet WAR file into your project. 9. Click Show Advanced and select WebSphere Portal V5.1 stub for the target server. Click Next. 10.In the WebSphere Portal version box, select 5.1.
Chapter 15. Struts portlet development using the JSR 168 API
471
11.Click Next. 12.In the Portlet type page, select Struts portlet (JSR 168).
13.Click Next. 14.In the Features page, leave the Web Diagram option selected.
472
15.Click Next. 16.In the Portlet settings page, review the options provided. Note: The settings provided in this page are the same when you select a basic portlet. 17.Leave all settings unchanged. Click Next. 18.In the Struts portlet settings page, you can specify the name of the resource bundle that will be used for this Struts portlet application, as well as the Java package where it will be stored. Take default values and do not make any changes to these fields.
Chapter 15. Struts portlet development using the JSR 168 API
473
19.Click Next. 20.In the Miscellaneous page, you can select additional markups and modes that will be supported in this portlet application. Check Add Edit mode as illustrated in Figure 15-8.
21.Click Finish to generate the project. 22.If the Confirm Perspective Switch window appears, click Yes.
474
The web.xml deployment descriptor was created. Struts portlet tag library definitions were included. Welcome-file list was supplied.
<welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list>
Note: This list is not really necessary; as we will see, the Struts Portlet framework provides a mechanism to identify which page must be displayed first for each different mode and markup language. The portlet.xml deployment descriptor was created. A portlet was created and the StrutsPortlet class was specified as the servlet class. The config init-param was created to specify the struts-config file to be used and the struts-servlet-mapping init-param was created to specify the struts mapping. Other init-params were also included but omitted in the sample below to simplify understanding. A preference pointing to the initial page to be used in View mode as well as a preference pointing to the initial page for Edit mode were also created by the wizard as shown in Figure 15-1 on page 476. Note: For more information about other added elements, see the WebSphere Portal Infocenter.
Chapter 15. Struts portlet development using the JSR 168 API
475
Example 15-1 Generated portlet preferences in portlet descriptor (portlet.xml) <portlet-preferences> <preference> <name>com.ibm.struts.portal.page.view.html</name> <value>index.jsp</value> </preference> <preference> <name>com.ibm.struts.portal.page.edit.html</name> <value>html/edit/index.jsp</value> </preference> </portlet-preferences>
The definition of a config parameter and a com.ibm.struts.portal.page.<mode>.<markup> preference above is the standard way to define a Struts configuration file and modules and the first page to be displayed for each specific mode and markup language. In the Struts portlet framework, each markup and mode is mapped to a specific Struts configuration file and module. If you want to add support for Edit mode in the wml markup language, for example, you should create a config/wml/edit parameter pointing to the Struts configuration file and a com.ibm.struts.portal.page.edit.wml preference pointing to the initial page for this mode/markup. Figure 15-9 illustrates the generated modules for View mode (default module) and Edit mode (/html/edit).
476
4. You will add nodes to the Web diagram using the Palette view. Notice that you have some Struts parts that you can pick up from the palette and then insert into the Web diagram.
Chapter 15. Struts portlet development using the JSR 168 API
477
5. Click Web Page on the palette view, then click anywhere in the Web diagram to place it. Name the Web page index.jsp. See Figure 15-14 on page 480 for a suggested location of index.jsp. Note: Unless you change the default name in the portlet descriptor, you should use this name because this will be the first page in your application, and the portlet.xml deployment descriptor is already configured to find this page automatically:
<portlet-preferences> <preference> <name>com.ibm.struts.portal.page.view.html</name> <value>index.jsp</value> </preference> ................ </portlet-preferences>
6. Repeat these steps and place two other pages in the Web diagram. Name them configured.jsp and notConfigured.jsp. 7. Click Action Mapping on the palette view. Click again anywhere in the Web page diagram and name the action mapping welcome. 8. Click Form Bean on the palette view and place it in the Web diagram. Name it viewBean and scope should be request.
478
Figure 15-12 Action Form bean for View mode welcome action
9. Create four connections as follows: a. Click Connection on the palette view. Then connect index.jsp to welcome action. b. Click Connection on the palette view. Then connect welcome action to configured.jsp. i. In the Choose a connection window that appears, expand Local Forward, then select <new> and click OK.
ii. Name the connection configured as shown in Figure 15-14 on page 480. c. Click Connection again. Then connect welcome to the notConfigured.jsp. i. In the Choose a connection window that appears, expand Local Forward, then select <new> and click OK. ii. Name the connection notConfigured. d. Click Connection. Then connect welcome action to viewBean. At this time, your portlet application Web diagram should look as illustrated in Figure 15-14 on page 480.
Chapter 15. Struts portlet development using the JSR 168 API
479
480
3. In the Struts configuration file editor, select the Global Forwards tab, at the bottom. Click Add and name the global forward welcome. 4. Under the forward attributes section, enter /welcome.do as the Path. Figure 15-16 on page 482 shows the Global Forwards page.
Chapter 15. Struts portlet development using the JSR 168 API
481
Note: The global forward will indicate a broken link since the action has not been defined yet. 6. Save and close the file.
482
3. Check Configure advanced options and click Next >. 4. In the Tag libraries page, remove all tag libraries by selecting them and clicking Remove. 5. Click Add. 6. In the Select a tag library page, check the /WEB-INF/struts-logic.tld tag library. Click OK.
Chapter 15. Struts portlet development using the JSR 168 API
483
7. Click Next.
484
8. JSP file options. Take default values for encoding, content type and other values. Click Next. 9. JSP file method stubs. Take default values for method stub creation. Click Next. 10.In the Form field selection page, uncheck Generate fields in a form. Click Finish to generate the JSP.
Chapter 15. Struts portlet development using the JSR 168 API
485
11.In the JSP editor, select the source tab and replace the page with the following code shown in Example 15-2.
Example 15-2 index.jsp page <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <logic:forward name="welcome"/>
Note: This tag indicates that it will forward to the global forward welcome and the action mappings should resolve where to go. In this scenario welcome.do should point to the action code. This will be done later in 15.4.7, Realizing the welcome action mapping on page 490. 12.Save the file and close the JSP editor.
Note: The messages will be created later in the resources file. 5. Save the file and close the JSP editor.
486
3. Click Next. 4. In the Choose new fields for your ActionForm class, do not select any items. New fields will be added manually to other items (for example configured.jsp). Click Next. 5. In the Create new fields for your ActionForm class window: a. Click Add to add a field b. Enter userid for the name and select type String. Note: A setter and a getter methods will be generated for this property (userid).
Chapter 15. Struts portlet development using the JSR 168 API
487
6. In the Create a mapping window, uncheck all method stubs and click Finish to generate the bean.
488
Chapter 15. Struts portlet development using the JSR 168 API
489
490
2. Notice that the wizard has filled in most of the data in this window. This data came from the Web diagram you previously designed. For example: a. Action mapping name was entered as the Action Mapping Path.
Chapter 15. Struts portlet development using the JSR 168 API
491
b. The connections you created as Local forwards were automatically inserted into the Forwards list. c. The Create an Action class radio button was selected, and the Model chosen was the Struts Portlet Framework Action Mapping. This is the mapping provided by Struts portlet framework. 3. No changes are required at this time so leave all data unchanged and click Next. 4. The Create an Action class for your mapping window shows that the mapping will inherit from com.ibm.wps.struts.action.StrutsAction. Click Finish to generate the Action class for your mapping.
5. Replace the generated execute method with the code shown in Example 15-6.
Example 15-6 Execute() method in the Welcome Action class public ActionForward execute(ActionMapping mapping, ActionForm form, PortletRequest request, PortletResponse response) throws Exception { System.out.println("===>View: Entering welcome action..."); ActionForward forward = mapping.findForward("configured"); ViewBean viewBean = (ViewBean) form; try {
492
PortletPreferences portletPreferences = request.getPreferences(); String username = portletPreferences.getValue("username", null); System.out.println("===>View: username = *" + username + "*"); viewBean.setUserid(username); if (username == null || username.equals("")) { System.out.println("===>View: portlet not configured..."); forward = mapping.findForward("notConfigured"); } } catch (Exception ex) { } return forward; }
6. If you get errors, you might need to organize your imports. 7. Save the file and close the editor.
Chapter 15. Struts portlet development using the JSR 168 API
493
Note: The portlet has not been updated to fully support Edit mode yet. Adding the portlet Edit mode code will be done next in 15.5, Designing the application (Edit mode) on page 494 in this chapter.
494
Chapter 15. Struts portlet development using the JSR 168 API
495
1. In the Web diagram, double-click EditBean form bean. This will bring up the New Form-Bean wizard.
2. Leave the form bean name unchanged. Make sure that the Create an ActionForm class radio button is selected, and that Generic Form-bean Mapping is selected. 3. Click Next. 4. In the Choose new fields for your ActionForm class window, do not select any fields and click Next. 5. In the Create new fields for your ActionForm class window, you will add two fields: a. Click Add. Enter the fields name username. Leave the field type as String. b. Click Add again. Enter the fields name password and leave the field type as String. 6. The window will look like in Figure 15-30 on page 497. Click Next.
496
7. In the Creating a mapping for your ActionForm class window, uncheck all method stubs as shown Figure 15-31. These methods are not needed in this scenario.
Chapter 15. Struts portlet development using the JSR 168 API
497
8. Click Finish to create the bean. Rational Application Developer will generate the ActionForm bean, that extends from rg.apache.struts.action.ActionForm, along with its proper methods. In addition, it will also include the required form-bean tag in the struts-config.xml file for Edit mode. 9. Save the file and close it.
498
<tr><td><br></td></tr> <tr> <td align="right"> <html:submit value="Submit"/> </td> <td align="left"> <html:reset/> </td> </tr> </table> </html:form>
4. Review the code: a. The saveConfiguration action has not been defined yet, so you get a broken link message. b. The portletMode="view" attribute in the form tag instructs that the portlet will return to View mode after submitting the form. 5. Save the file and close it.
Chapter 15. Struts portlet development using the JSR 168 API
499
import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import com.ibm.portal.struts.action.StrutsAction; public class SaveConfigurationAction extends StrutsAction {
public ActionForward execute(ActionMapping mapping, ActionForm form, PortletRequest request, PortletResponse response) throws Exception { System.out.println("===>Edit: Entering saveConfiguration action..."); // get the ActionForward for the mode that is being set. ActionForward forward = mapping.findForward("user_not_set"); EditBean userBean = (EditBean) form; String username = userBean.getUsername(); String password = userBean.getPassword(); System.out.println("===>Edit: username = *" + username + "*"); PortletPreferences portletPreferences = request.getPreferences(); portletPreferences.setValue("username", username); portletPreferences.setValue("password", password); portletPreferences.store(); forward = mapping.findForward("user_set"); return forward; } }
5. Review the code. See 15.2, Message flow on page 467 for details. 6. Save the file. 7. Close the file.
500
editmode.title=Struts Edit Mode Example editmode.instructions=Try the options below to demonstrate various Struts features. When a valid username is given, an action forwards to a JSP in view mode. editmode.instructions.1=Enter a valid username to complete the configuration prompt.username=Username prompt.password=Password button.back=back button.send=Send button.save=Save button.cancel=Cancel button.reset=Reset error.transaction.token=Transaction token is not valid error.invalid.username=The username is not valid
Chapter 15. Struts portlet development using the JSR 168 API
501
6. Enter your name and a password. 7. Click Submit to invoke the action (Edit Mode). 8. Click Reset to clear the fields.
502
Chapter 15. Struts portlet development using the JSR 168 API
503
editmode.heading=Modo de Edio editmode.title=Exemplo de Modo de Edio com Struts editmode.instructions=Tente as opes abaixo para demonstrar vrias capacidades do Struts. Quando um usurio vlido inserido, uma action encaminha para um JSP em modo view. editmode.instructions.1=Digite "error" como nome de usurio ou deixe o nome de usurio em branco para ver o suporte a ActionErrors. editmode.instructions.2=Digite "delete" como nome de usurio para remover a configurao. editmode.instructions.3=Atualize a pgina para demonstrar o suporte transacional. editmode.instructions.4=Digite um nome de usurio vlido para completar a configurao e retornar para view mode. prompt.username=Nome prompt.password=Senha button.back=voltar button.send=Enviar button.save=Salvar button.cancel=Cancelar button.reset=Limpar error.transaction.token=Token de transao invlido error.invalid.username=O nome de usurio no vlido
504
8. You do not have to add or change anything in this file, unless you want to change the portlet title for example. 9. Save the file and close the editor.
5. Select pt_BR in the Locale list. 6. Click OK. 7. Save the file and close the editor.
Chapter 15. Struts portlet development using the JSR 168 API
505
7. Select Editar Meu Perfil and locale Ingles if you want to go back to locale English.
506
Important: Make sure you have only one traceString in the log.properties file. Also, the entire traceString must be specified in only one line. In addition, in this sample scenario, myfirst168struts is the name of the package. c. Save the file and close it. 2. Additionally, the Struts Portlet Framework specifies the commons logging Log Factory in the META-INF/services directory. The log factory class name is dependent on the WebSphere Portal container. To specify the correct commons LogFactory for our application, proceed as follows: a. Expand Dynamic Web Projects MyFirstJSR168Struts WebContent. b. Right-click the META-INF folder. Select New Folder. c. Enter services as the folder name. Click Finish. d. Right-click the services folder. Select New Other. e. In the Select a wizard window do the following: i. Expand Simple. ii. Select File iii. Click Next >.
Chapter 15. Struts portlet development using the JSR 168 API
507
f. Enter org.apache.commons.logging.LogFactory as the file name. g. Click Finish. h. Paste the following line in the file:
com.ibm.portal.struts.logging.StrutsLogFactory
i. Save the file and close it. 3. Add code in the welcome action mapping: a. Go to MyFirstJSR168Struts Java Resources JavaSource myfirstjsr168struts.actions b. Edit WelcomeAction. c. Add the following instance variable just before the execute method:
private Log log = LogFactory.getLog(this.getClass());
d. You will need to organize your imports. Right-click anywhere in the code and select Source Organize Imports. e. Select org.apache.comons.logging.Log for the Log class and click Next.
508
f. Select org.apache.commons.logging.LogFactory for the LogFactory class and click Finish. g. Add the following code at the beginning of the execute method:
if (log.isTraceEnabled()) { log.trace("Welcome ###"); }
Note: Use ### or any other symbols to facilitate the search in the log file. h. Finally, add the following code in the catch statement:
if (log.isDebugEnabled()) { log.debug("WelcomeAction: Error determining if user is configured"); }
i. Save the file and close editor. 4. Now you can run the application again. For example: a. Stop the server to pick up the new property changes. b. Select MyFirstJSR168Struts and click Run Run on Server. c. Open the log file at <wp_root>/log folder and look for the Welcome message by searching for the ### or any other symbols you used. d. In this sample scenario the log files are in the following directory: C:\Program Files\Portal51UTE\PortalServer\log\ e. Figure 15-37 illustrates a sample Struts log entry.
Chapter 15. Struts portlet development using the JSR 168 API
509
510
16
Chapter 16.
511
16.1 Overview
JavaServer Faces is based on the Model-View-Controller (MVC) design pattern and used to develop and build Web and Portal applications. For JSF, this means that the controller component is a servlet, the model component is represented by Java Beans, and the view comprises JSF components with little or no application code. As illustrated in Figure 16-1, the main components of JSF technology are: An API for representing and managing UI components and their state; handling events, input validation at the server-side, and data conversion; defining page navigation; supporting internationalization and accessibility; and providing extensibility for all these features. An extensive set of reusable user interface (UI) components. JavaServer Pages (JSP) custom tag libraries for expressing a JavaServer Faces interface within a JSP.
JSF Application
Browser
Faces Servlet
JSF technology provides a clean separation between behavior and presentation, like the one that is traditionally offered by client-side UI architectures. Figure 16-2 on page 513 illustrates the JavaServer Faces components within the MVC design pattern.
512
Controller
Request
FacesServlet
Model
JavaBeans
Response
Backend Resources
View
JSP, UI components
Figure 16-2 JavaServer Faces technology and the MVC design pattern
The actual work of a JSF application is executed by processing events triggered by the JSF components on the pages. These events are fired by user actions. For example, when the user clicks a button, the button triggers an event. The JSF programmer has to decide what the JSF application will do when a particular event happens. This is accomplished by implementing event listeners. Note: JavaServer Faces technology is event-driven. When an event is created, an HTTP request is sent to the server. Specifically, the request is sent to the JSF provided servlet called the FacesServlet. A JSF application in the Web container has its own FacesServlet. The FacesServlet servlet creates an object called FacesContext, which holds the information required for request processing. That is, for Web applications, the ServletContext, ServletRequest and ServletResponse objects are passed to the service method of FacesServlet. For portlet applications, Portal passes the PortletContext, PortletRequest and PortletResponse objects to the service method of FacesServlet.
FacesContext
(object)
513
Once the FacesContext object has been created and populated, the FacesServlet servlet passes control to the lifecycle object. The lifecycle object processes the FacesContext object in six different phases, as follows: 1. 2. 3. 4. 5. 6. Restore component tree Apply request values Process validators Update model values Invoke application Render response
Also, the JSF application configuration is done through an application configuration file, where you can register Java Beans used in the application, define the program-control flow with page-navigation rules, and perform many other configuration tasks. The application configuration file is an XML file that is normally put in the WEB-INF directory and called faces-config.xml.
514
Response Complete
Response Complete
Request
Process Events
Process Validators
Process Events
Render Response
Response Complete
Response Complete
Response
Render Response
Process Events
Invoke Application
Process Events
The life cycle handles two kinds of requests: initial requests and postbacks. When a user makes an initial request for a page, he or she is requesting the page for the first time. When a user executes a postback, he or she submits the form contained on a page that was previously loaded into the browser as a result of executing an initial request. When the life cycle handles an initial request, it only executes the restore view and render response phases because there is no user input or actions to process. Conversely, when the life cycle handles a postback, it executes all of the phases.
515
the JSF implementation restores the view by using the state information that is saved.
Process validations
As part of this creation of the view for this request, zero or more validator instances can be registered for each component. In addition, component classes themselves can implement validation logic in their validate() methods. At the end of this phase, all configured validations are completed. Validations that fail cause messages to be enqueued via calls to FacesContext.addMessage, and the valid property on the corresponding components are set to false. If any of the validate() methods that were invoked called FacesContext.responseComplete, the life cycle processing of the current request must be immediately terminated. If any of the validate() methods that were invoked called FacesContext.renderResponse, control must be transferred to the render response phase of the request processing life cycle. The same conditions are true for an event listener that processed a queued event. If none of these conditions occurs, control proceeds to the next phase to update model values.
516
component tree has been updated, and it is now appropriate to update the applications model data in preparation for performing any application events that have been queued. The JSF implementation will update only the bean properties pointed at by an input components value attribute. If the local data cannot be converted to the types specified by the bean properties, the life cycle advances directly to the render response phase so that the page is re rendered with errors displayed. This is similar to what happens with validation errors. If the application needs to redirect to a different Web application resource or generate a response that does not contain any JSF components, it can call FacesContext.responseComplete. If events have been queued during this phase, the JSF implementation broadcasts them to interested listeners.
Invoke application
During this phase, the JSF implementation handles any application-level events, such as submitting a form or linking to another page. If the application needs to redirect to a different Web application resource or generate a response that does not contain any JSF components, it can call FacesContext.responseComplete. If the view being processed was reconstructed from state information saved by a previous request and if a component has fired an event, these events are broadcast to interested listeners.
Render response
This phase accomplishes two things at the same time: causes the response to be rendered to the client, and causes the state of the response to be saved for processing on subsequent requests. The reason for handling both of these responsibilities in one phase is because the act of rendering the response in JSP applications can cause the view to be built as the page renders. Therefore, the state of the view cannot be saved until after it is rendered to the client. If the request is a postback and errors were encountered during the apply request values phase, process validations phase, or update model phase, the original page is rendered during this phase. If the page contain message or messages tags, any queued error messages are displayed on the page.
517
Event processing
During several phases of the request processing life cycle, events can be queued, for example, via a call to the queueEvent method on the source UIComponent instance, or a call to the queue() method on the FacesEvent instance. These queued events must now be broadcast to interested event listeners. The broadcast is performed as a side effect of calling the appropriate life cycle management method (processDecodes(), processValidators(), processUpdates(), or processApplication()) on the UIViewRoot instance at the root of the current component tree. For each queued event, the broadcast() method of the source UIComponent will be called to broadcast the event to all event listeners who have registered an interest on this source component for events of the specified type. A boolean flag is returned indicating whether this event has been handled completely and whether the JSF implementation can remove it from the event queue. It is also possible for event listeners to cause additional events to be queued for processing during the current phase of the request processing life cycle. Such events must be broadcast in the order they were queued after all originally queued events are broadcast, but before the life cycle management method returns.
518
519
This page demonstrates some important features used in most of the JSF applications you will write.
The first tag directive above points to the component tag library. The second one points to the core tag library.
520
The required attribute tells to JSF implementation that a validation error should be fired if the components value was not entered. We talk about validation in 16.3.5, Validation model on page 536. The operation component also has an validator attribute. This attributes tells to JSF implementation to call the calculator.validateOperator method, to verify whether the value entered in the component is valid or not.
521
Example 16-2 Navigation rules for the calculator application <navigation-rule> <from-view-id>/welcome.jsp</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/result.jsp</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>/result.jsp</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/welcome.jsp</to-view-id> </navigation-case> <navigation-case> <from-outcome>end</from-outcome> <to-view-id>/end.jsp</to-view-id> </navigation-case> </navigation-rule>
Each navigation-rule element defines how to get from one page (specified in the from-view-id) to other pages in the application. The navigation-rule elements can contain any number of navigation-case elements, each of which defines the page to open next (defined by to-view-id) base on a logical outcome (defined by from-outcome. The outcome can be defined either by the action attribute of the UICommand component that submits the form, or by the return value of an action method in a backing bean. The welcome.jsp page illustrates the first way of defining the outcome:
<h:commandButton id="submit" value="Submit" action="success" />
Here, the commandButton component specifies the action success. If we look at the navigation rules, we will see that this outcome makes the JSF implementation call the result.jsp page. If we wanted to perform some processing to determine the outcome, for example, to check if the password entered by the user is valid or not, and return an outcome of success or failure, the commandButton action attribute would point to a method, instead of specifying directly the outcome. If we have a login component that implements this logic in its verify method, this would result in a tag similar to the following:
<h:commandButton id="submit" value="Submit" action="#{login.verify}" />
522
Note that this bean property is just like any other bean property, that is, it is defined by a set of accessor methods and a private field. A property can be any of the basic primitive and numeric types or any Java object type for which an appropriate converter is available. JSF technology automatically converts the data to the type specified by the bean property. Table 16-1 shows which types are accepted by which component tags.
Table 16-1 The acceptable types for each component value. Component UIInput, UIOutput, UISelectItem, UISelectOne Acceptable types Any basic primitive and numeric types, as well as any Java object type for which an appropriate Converter implementation is available. array of beans, List of beans, single bean, java.sql.ResultSet, javax.servlet. jsp.jstl.sql.Result, javax.sql.RowSet. java.lang.String, Collection, Array, Map.
UIData
UISelectItems
523
Acceptable types boolean or Boolean. array or List. Elements of the array or List can be any of the standard types.
In addition to binding components and their values to backing bean properties, we can refer to a backing bean method from a component tag. See 16.5, Backing bean management on page 540 for more information about this topic.
The application configuration file can specify initial values to the beans properties. This is done by using the managed-property tag in the managed-bean definition. If we want to specify an initial value of 30 for number1, the managed-bean tag would look like the one in Example 16-5. Properties that are not set an initial value in the managed-bean declaration will be initialized to whatever the constructor of the bean class has the instance variable set to.
Example 16-5 Declaration of CalculatorBean with an initial value set to number1 property <managed-bean> <managed-bean-name>calculator</managed-bean-name> <managed-bean-class>pagecode.beans.CalculatorBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> <managed-property> <property-name>number1</property-name> <property-class>int</property-class> <value>30</value> </managed-property> </managed-bean>
The JSF implementation processes the configuration file on application startup time. When the CalculatorBean is first referenced from a page, the JSF
524
implementation initializes it and stores it in session scope if no instance exists. The bean is then available for all pages in the application. The scope can also be set as request or application.
525
UIForm, which represents an input form. UIGraphic, which displays an image. UIInput, which represents a component that gets and displays user input. UIMessage, which displays a localized message. UIMessages, which displays a set of localized messages. UIOutput, which displays data output. UIPanel, which is a container to child components. UIParameter, which represents substitution parameters used to configure parent components. UISelectBoolean, which represents a component that have a boolean value. UISelectItem, which represents a single select item for select components. UISelectItems, which represents an entire set of items. UISelectMany, which represents a component that allows the user to select zero or more values from a set of values. UISelectOne, which represents a component that allows the user to select zero or one value from a set of values. UIViewRoot, which represents the root of the component tree. The component classes also implement one or more behavioral interfaces, each of one defines a different behavior for a set of components whose classes implement it. These interfaces are as follows: ActionSource: By implementing this interface, the component is able to fire an action event. EditableValueHolder: This interface extends ValueHolder and specifies additional features for editable components, such as validation and emitting value-change events. NamingContainer: Each component rooted at the component that implements this interface must have a unique ID. StateHolder: Indicates that the component has state that must be saved between requests. ValueHolder: Denotes that the component maintains a local value as well as the option of accessing data in the model tier. You only need to use the component classes and behavioral interfaces if you are a component writer. Otherwise, you only need to know the appropriate tags that will instruct the JSF implementation on how to render a specific component. These tags are covered in the next section.
526
<h:commandLink> <h:dataTable>
A hyperlink A table that can be updated dynamically Invisible An image Invisible A text field, which displays a row of characters instead of the actual string
Represents an input form. Displays an image A hidden variable in a page Allows a user to input a string without the actual string appearing in the field
An HTML <form> tag An HTML <img> tag An HTML <input type=hidden> tag An HTML <input type=password> tag
527
Description Allows the user to input a string Allows the user to input a multiline string Displays a localized message Displays localized messages Displays a nested component as a label for a specified input field Links to another page or location on a page without generating an action event Displays a localized message Displays a line of text Displays a table Groups a set of components under one parent Allows a user to change the value of a Boolean choice Represents one item in a list of items in a UISelectOne component Represents a list of items in a UISelectOne component Displays a set of checkboxes from which the user can select multiple values Allows a user to select multiple items from a set of items, all displayed at once
Rendered As An HTML <input type=text> tag An HTML <input type=textarea> tag An HTML <span> tag if styles are used An HTML <span> tag if styles are used An HTML <label> tag
Appearance A text field A multirow text field A text string A text string Plain text
<h:outputLink>
A hyperlink
An HTML <p> tag An HTML <p> tag An HTML <table> tag with <tr> and <td> tags An HTML <tr> tag with <td> tags An HTML <input type=checkbox> tag An HTML <option> tag
<h:selectBooleanCheckbox> <h:selectItem>
A checkbox Invisible
<h:selectItems> <h:selectManyCheckbox>
<h:selectManyListbox>
A list box
528
Tag <h:selectManyMenu>
Description Allows a user to select multiple items from a set of items Allows a user to select one item from a set of items, all displayed at once Allows a user to select one item from a set of items Allows a user to select one item from a set of items
Rendered As An HTML <select> element An HTML <select> element An HTML <select> element An HTML <input type=radio> element
<h:selectOneListbox>
<h:selectOneMenu> <h:selectOneRadio>
529
Faces component Panel Group Box - Snap to border Panel Group Box - List Panel Menu Bar
Description Creates a container to group other components, organizing them along the sides of the panel. Creates a container to group other components, organizing them as a vertical or horizontal list. Inserts a panel that places commands into a menu bar. You can drag and drop buttons, hyperlinks and horizontal rules to the panel or add them from within the Properties views of the components. You can also add a sub-menu bar within a menu bar. Creates a set of overlapping tabbed pages. Each page can contain a set of components. The user clicks a tab to show the contents of that page. Inserts a rich text editor component. Unlike the <h:inputTextarea>, this component can contain different fonts and sizes, tables, links, and numbered and bulleted lists. Creates a media player on the Web page and plays a file. Creates an instance of the Macromedia Flash Player in order to play Flash files on the Web page. Plays Macromedia Shockwave Player files on the Web page. Plays Real Network RealOne Player files on the Web page. Creates an instance of the Windows Media Player in order to play media files on the Web page.
Tag <hx:panelLayout>
Type UIPanel
<hx:panelBox>
UIPanel
<hx:panelActionbar>
UIPanel
Panels Tabbed
<odc:tabbedPanel>
<r:inputRichtext>
Generic A/V Player Macromedia Flash Player Macromedia Shockwave Player RealOne Player Windows Media Player
<hx:playerGenericPlayer> <hx:playerFlash>
<hx:playerShockwave>
<hx:playerRealPlayer> <hx:playerMediaPlayer>
530
The first method converts a string into an object of the desired type, throwing a ConverterException if the conversion cannot be carried out. This method is called when a string is submitted from the client, typically in a text field. The
531
second method converts an object into a string representation to be displayed in the client interface. Imagine that we had to develop a custom converter for credit card numbers. This converter would allow users to enter a credit card number with or without spaces. That is, the user could type the card number in any of the following forms:
1234567890123456 1234 5678 9012 3456
Example 16-6 shows the code for the custom converter. Notice that the getAsString method tries to format the credit card number in a way that is pleasing to the user. The digits are separated with white spaces, depending on the credit card type. Table 16-4 shows the most common credit card formats.
Table 16-4 Most common credit card formats Card Type MasterCard Visa Visa Discover American Express American Express Diners Club, Carte Blanche Digits 16 16 13 16 15 22 14 Format 5xxx xxxx xxxx xxxx 4xxx xxxx xxxx xxxx 4xxx xxx xxx xxx 6xxx xxxx xxxx xxxx 37xx xxxxxx xxxxx 3xxxxx xxxxxxxx xxxxxxxx 3xxxx xxxx xxxxx
Example 16-6 CreditCardConverter.java package com.yourco.converters; import import import import import javax.faces.application.FacesMessage; javax.faces.component.UIComponent; javax.faces.context.FacesContext; javax.faces.convert.Converter; javax.faces.convert.ConverterException;
import com.yourco.beans.CreditCard; public class CreditCardConverter implements Converter { public Object getAsObject(FacesContext context, UIComponent component, String newValue) throws ConverterException { boolean foundIllegalChar = false; char illegalChar = '\0';
532
StringBuffer sb = new StringBuffer(newValue); int i = 0; while (i < sb.length() && !foundIllegalChar) { char c = sb.charAt(i); if (Character.isDigit(c)) { i++; } else if (Character.isWhitespace(c)) { sb.deleteCharAt(i); } else { foundIllegalChar = true; illegalChar = c; } } if (foundIllegalChar) { FacesMessage errmsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "An invalid character was found.", "The character " + illegalChar + "is not valid."); throw new ConverterException(errmsg); } return new CreditCard(sb.toString()); } public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException { int[] boundaries = null; String v = value.toString(); int length = v.length(); switch (length) { case 13: boundaries = new int[]{4, 7, 10}; break; case 14: boundaries = new int[]{5, 9}; break; case 15: boundaries = new int[]{4, 10}; break; case 16: boundaries = new int[]{4, 8, 12}; break; case 22: boundaries = new int[]{6, 14}; break; default: return v; } StringBuffer sb = new StringBuffer(); int start = 0; for (int i = 0; i < boundaries.length; i++) {
533
int end = boundaries[i]; sb.append(v.substring(start, end)); sb.append(" "); start = end; } sb.append(v.substring(start)); return sb.toString(); } }
The CreditCard class referenced in the code is very simple. It contains just the credit card number. See Example 16-6 on page 532:
Example 16-7 CreditCard.java package com.yourco.beans; public class CreditCard { private String creditCardNumber; public CreditCard(String creditCardNumber) { this.creditCardNumber = creditCardNumber; } public String toString(){ return creditCardNumber; } }
In Example 16-8, we chose theID CreditCard for our credit card converter.
534
We could also be more succint and use the converter attribute of the UIInput component:
<h:inputText value="#{someBean.card}" converter="CreditCard"/>
535
In Example 16-9, you can see how to attach a value change listener to a menu. The example also uses the onchange attribute to force a form submit after the menus value is changed. This example was extracted from the calculator application that is built in Chapter 17, JavaServer Faces portlet development on page 551.
Example 16-9 Attaching a valueChangeListener to a component <h:selectOneMenu styleClass="selectOneMenu" id="operation" valueChangeListener="#{pc_Calculate.handleOperationValueChange}" onchange="sumit()"> .......... </h:selectOneMenu>
Example 16-10 handles the ValueChangeEvent. This example was also extracted from the calculator application that is built in Chapter 17, JavaServer Faces portlet development on page 551.
Example 16-10 Handling a ValueChangeEvent public void handleOperationValueChange(ValueChangeEvent valueChangedEvent) { log("OperationValueChangeEvent start"); HtmlSelectOneMenu operation = (HtmlSelectOneMenu)valueChangedEvent.getSource(); String opnew = (String)valueChangedEvent.getNewValue(); String opold = (String)valueChangedEvent.getOldValue(); log("operation old="+opold+" new="+opnew); getCalculator().setOperation(opnew); // is also done later by binding getOpchanged().setValue("changed"); // change the output text field getCalc().setValue(opnew); // change the Calculate button log("OperationValueChangeEvent end"); }
536
page from which the form was submitted, without copying the local value to the Java Bean instance bound to the input component. The JSF core tag library defines a set of tags that correspond to a set of standard classes for performing common data validation checks. Table 16-5 shows all the standard validation classes and corresponding tags.
Table 16-5 The validator classes Validator Class DoubleRangeValidator Tag validateDoubleRange Function Checks whether the local value of a component is within a certain range. The value must be a floating-point or convertible to floating-point Checks whether the length of a components local value is within a certain range. The value must be a java.lang.String Checks whether the local value of a component is within a certain range. The value must be any numeric type or String that can be converted to a long
LengthValidator
validateLength
LongRangeValidator
validateLongRange
To specify the range that the validators shown in Table 16-5 must check, you use the maximum and minimum attributes inside each of the validators tag. These values can be hardcoded in the tag or refer to a backing-bean property. You can also specify that an input component has a required attribute. By doing this, the JSF implementation checks whether the value of the component is null or is an empty String. The validation model also allows you to create your own custom validator and corresponding tag to perform custom validation. There are two ways to implement custom validators: Implement a Validator interface that performs the validation (see Example 16-11 on page 538). By doing this, you must also: Register the Validator implementation with the application (See Example 16-12 on page 538). Create a custom tag or use a validator tag to register the validator on the component (See Example 16-13 on page 539).
537
Implement a backing-bean method that performs the validation. By doing this, you must also reference the validator from the component tags validator attribute.
Example 16-11 Implementing a validator package itso.jsf.calculator; import import import import import import javax.faces.application.FacesMessage; javax.faces.component.UIComponent; javax.faces.component.UIInput; javax.faces.context.FacesContext; javax.faces.validator.Validator; javax.faces.validator.ValidatorException;
public class OddValidator implements Validator { public void validate(FacesContext arg0, UIComponent arg1, Object arg2) throws ValidatorException { System.out.println("OddValidator start"); UIInput field = (UIInput)arg1; int value = ((Long)arg2).intValue(); System.out.println("Field="+field.getId()+" Value="+value); if (value%2 == 1) { field.setValid(true); System.out.println("OddValidator end: valid"); } else { System.out.println("OddValidator end: invalid"); FacesMessage errmsg = new FacesMessage (FacesMessage.SEVERITY_ERROR, "2nd number not odd.", "Second number must be odd."); throw new ValidatorException(errmsg); } } } Example 16-12 Registering the validator <validator> <description>Registers the OddValidator</description> <validator-id>oddValidator</validator-id> <validator-class>itso.jsf.calculator.OddValidator</validator-class> </validator>
538
Example 16-13 Invoking the validator with the validator tag h:inputText styleClass="inputText" id="number2" required="true" maxlength="2" size="12" value="#{pc_Calculate.calculator.number2}"> <f:validator validatorId="oddValidator"></f:validator> </h:inputText>
In more complicated applications, you also must provide one or more action methods, which perform some processing to determine which page should be displayed next. For example, a login action can be triggered when the user submits a form by clicking a button. This action can determine whether the login data entered is valid or not, and return a logical outcome String (in this case either success or failure). The NavigationHandler receives this outcome and determines which page to display next by matching the outcome or the action method reference against the navigation rules in the application configuration resource file. The Example 16-14 shows how to reference a method that implements this kind of dynamic navigation. Example 16-15 on page 540 shows how this method can be implemented. This method has no parameters and a return type String.
Example 16-14 Invoking an action method to determine the correct outcome <h:commandButton label="login" action="#{loginController.veryfyUser}"/>
539
Example 16-15 Deciding the correct outcome String veryfyUser() { if (...) return "success"; else return "failure"; }
Each navigation rule defines how to navigate from one particular page to any number of other pages in the application. Each navigation case within a navigation rule defines a target page and either a logical outcome, a reference to an action method, or both. Example 16-16 shows an example navigation rule from the calculator application described in 16.2.2, Defining navigation rules on page 521.
Example 16-16 An example navigation rule <navigation-rule> <from-view-id>/welcome.jsp</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/result.jsp</to-view-id> </navigation-case> </navigation-rule>
This rule states that when a button or hyperlink component on welcome.jsp is activated, the application will navigate from the welcome.jsp page to the result.jsp page if the outcome referenced by the button or hyperlink components tag is success.
540
To illustrate value-binding expressions and method-binding expressions, take a look at Example 16-17, which defines the operation component of the calculator application.
Example 16-17 An UIInput component using both value-binding and method-binding expressions <h:inputText id="operation" value="#{calculator.operation}" validator="#{calculator.validateOperator}" required="true"> </h:inputText>
This tag binds the operation components value to the calculator.operation backing bean property. it also refers to the calculator.validateOperator method, which performs validation of the components local value, which is whatever the user enters into the field corresponding to this tag. A tag representing a component that implements ActionSource can refer to backing bean methods using actionListener and action attributes. The actionListener attribute refers to a method that handles an action event. The action attribute refers to a method that performs some processing associated with navigation and returns a logical outcome String, which the navigation system uses to determine which page to display next. A tag can also bind a component instance to a backing bean property, instead of a components value. This is done by referencing the property from the binding attribute:
<inputText binding=#{calculator.operationComponent}
The property referenced from the binding attribute must accept and return the same component type as the component instance to which it is bound. Example 16-18 shows an example property the can be bound to the component represented by the preceding inputText tag.
Example 16-18 Binding a component instance to a backing bean property UIInput operationComponent = null; ... public void setOperationComponent(UIInput operationComponent) { this.operationComponent = operationComponent; } public UIInput getOperationComponent() { return operationComponent;
}
541
The backing bean can programmatically modify component attributes, like the rendered attribute, that specify whether the component will be rendered or not. The backing bean can instantiate components rather than let the page author do so. These are the advantages of binding a components value to a bean property: The page author has more control over the component attributes. The backing bean has no dependencies on the JSF API, allowing a greater separation between the presentation layer and the model layer. The JSF implementation can perform conversions on the data based on the type of the bean property without the developer needing to apply a converter. In most situations, you will bind a components value rather than its instance to a bean property. Backing beans are created and stored with the application using the managed bean creation facility, which is configured in the application configuration resource file, as shown in 16.2.3, Developing the beans on page 523. When the application starts up, it processes this file, making the beans available to the application and instantiating them when they are first referenced by the component tags.
542
Has
Faces Context
Has
External Context
Create
Faces Portlet
Call
Lifecycle
Call
Faces Servlet
c2a Person
UI Components/ Renderers
Figure 16-5 How the JSF portlet runtime interacts with the common JSF runtime
The common JSF runtime defines the elements that were covered in the previous sections. The only new element here is the variable resolver. The variable resolver determines the value of the first variable in an expression. For example, the standard variable resolver looks up managed beans and handles the predefined variables such as cookie, view and session. The JSF servlet runtime is based on the FacesServlet, and is in charge of calling the JSF life cycle and creating an ExternalContext. Through the ExternalContext, the JSF application can access the servlet objects, such as ServletContext, ServletRequest and ServletResponse. The JSF portlet runtime is based on the FacesPortlet, that accomplishes essentially the same tasks as the FacesServlet does. That is, it is in charge of calling the JSF life cycle and creating a PortletExternalContext. Through the PortletExternalContext, the JSF portlet can access the portlet objects, such as PortletContext, PortletRequest and PortletResponse. The JSF portlet runtime also defines some portlet specific UI components, such as c2a for cooperation and person for people awareness. Finally, the JSF portlet runtime defines a variable resolver that permits value-binding of portlet objects, such as PortletPreferences and PortletData.
543
Render Request
FacesPortlet processAction()
Call
Process Validations
Process Validations
Invoke Application
Invoke Application
FacesPortlet render()
FacesPortlet render()
Call
As we know, portlets have two processing phases: the action phase and the render phase. If the portlet fired an action, it will send an action request to the server. An example of firing an action is submitting a form or clicking a link. In this case, the WebSphere Portal runtime will call both the action phase and the render phase of the portlet. In the action phase, the FacesPortlet.processAction method is invoked and it calls the five first phases of the JSF life cycle. In the render phase, the FacesPortlet.render method is invoked and it calls the remaining phase of the JSF life cycle. There are times when the server receives only a render request, instead of an action request. An example is when you have portlets A and B in the same page and the user interacts with portlet A. While portlet A will process the action and
544
render again, portlet B will only have to be rendered, because there are no actions associated with it. In this case, the WebSphere Portal runtime will call only the FacesPortlet.render method of portlet B. The FacesPortlet.render method will call all six JSF life cycle phases, but since there are no parameters associated with the request, most of the life cycle phases will be skipped.
In JSR 168 API, this parameter is a init-param in portlet.xml. Example 16-20 specifies the initial pages to both View and Edit modes of a portlet that uses the JSR 168 API.
Example 16-20 Configuration of JSF initial pages in JSR 168 API <init-param> <name>com.ibm.faces.portlet.page.view</name> <value>/index.jsp</value> </init-param> <init-param> <name>com.ibm.faces.portlet.page.edit</name> <value>/html/edit/index.jsp</value> </init-param>
Once the initial page is displayed, the navigation between pages follows the page navigation rules defined in faces-config.xml. See 16.2.2, Defining navigation rules on page 521 for more details.
545
Tip: Navigation is defined within a portlet mode. If you want to switch portlet mode, refer to Changing portlet modes on page 547. Take care when switching modes during navigation, to avoid the portlet being in a inconsistent state.
Table 16-6 shows the methods available at ExternalContext and the objects returned for each API.
Table 16-6 Objects returned by each ExternalContext method ExternalContext method getRequest() getResponse() getContext() JSR 168 API PortletRequest (ActionRequest or RenderRequest) PortletResponse (ActionResponse or RenderResponse PortletContext IBM portlet API PortletRequest PortletResponse PortletContext
546
</config-param>
You can bind this value to a UIInput components value, with the following code:
<h:inputText id="name" value="#{portletSettings.myName}"/>
The value binding of portlet API attributes can be done because when you create a JSF portlet, a portlet variable resolver is defined, according to what we have seen in 16.6.1, JSF portlet runtime on page 542. Table 16-7 shows the configuration objects that Portlet variable resolvers enable the use of value binding to, and the corresponding expressions that bind the values of these objects.
Table 16-7 Configuration objects that can be obtained through value binding Portlet API JSR 168 API IBM portlet API Expression #{portletPreferences.name} #{portletApplicationSettings.name} #{portletSettings.name} #{portletData.name} Configuration Object PortletPreferences PortletApplicationSettings PortletSettings PortletData
Example 16-23 on page 548 shows how to change mode using the IBM portlet API.
547
Example 16-23 Changing mode in a JSF portlet using IBM portlet API PortletRequest request = (PortletRequest) facesContext.getExternalContext().getRequest(); try { request.setModeModifier(Portlet.ModeModifier.PREVIOUS); } catch (AccessDeniedException e) { // Your exception handling code here }
Media Player (hx:playerGenericPlayer, hx:playerFlash, hx:playerMediaPlayer, hx:playerRealPlayer, hx:playerShockwave), when bound to data, as in:
<hx:playerGenericPlayer value="#{myBean.movie}"/>
When adding an Image component to a Faces portlet page, you must specify the URL relative to the document WebContent root folder, rather than relative to the project root folder: Wrong:
<hx:graphicImageEx value="/.YourPortlet/theme/yourimage.gif"/>
Correct:
<hx:graphicImageEx value="theme/yourimage.gif"/>
16.7 Migration
Existing JSF applications can be migrated in order to run in WebSphere Portal. Since JSF is a framework there are many variations to how the application can be built with JSF. The steps in this section can be used as a starting point for the migration effort, but may not cover all of the issues that can be encountered.
548
1. Check if there are any file upload component in the application, or if the application uses components that support the download of binary data. These features are not supported in JSF portlets and cannot be migrated (see 16.6.5, Limitations in JSF portlets on page 548). 2. Decide to which Portlet API you want to migrate your JSF application. The migration can be done either to the JSR 168 API or to the IBM Portlet API. 3. Create a portlet.xml according to the API you decided to migrate to. For the JSR 168 API, refer to 8.9, Deployment descriptors on page 287. For the IBM Portlet API, refer to 4.12.2, portlet.xml on page 160. 4. Specify the FacesPortlet class. This class will perform the tasks that the FacesServlet does in a conventional JSF application. In JSR 168 API, the FacesPortlet is declared in portlet.xml and points to the class com.ibm.faces.webapp.FacesGenericPortlet. The existing FacesServlet in web.xml should be deleted. In IBM Portlet API, FacesPortlet is declared in web.xml and points to the class com.ibm.faces.webapp.WPFacesGenericPortlet. The existing FacesServlet in web.xml should be replaced by the FacesPortlet. 5. Specify the initial page to be loaded in each portlet mode, according to 16.6.3, Welcome page and navigation in JSF portlets on page 545. 6. Import the correct jar file that contains the JSF Portlet Runtime into the WEB-INF/lib directory: In JSR 168 API, this is the jsf-portlet.jar. In IBM Portlet API, this is the jsf-wp.jar. 7. Include a faces-context-factory in faces-config.xml: In JSR 168 API, include this factory tag:
<factory> <faces-context-factory> com.ibm.faces.context.PortletFacesContextFactoryImpl </faces-context-factory> </factory>
8. However, in faces-config.xml, you need to specify variable and property resolvers to correctly use value binds:
549
In JSR 168 API, include this application tag. If you already have an application tag in your file, include only the inner tags shown below:
<application> <variable-resolver> com.ibm.faces.databind.SelectItemsVarResolver </variable-resolver> <variable-resolver> com.ibm.faces.application.PortletVariableResolver </variable-resolver> <property-resolver> com.ibm.faces.databind.SelectItemsPropResolver </property-resolver> </application>
In IBM Portlet API, include this application tag. If you already have an application tag in your file, include only the inner tags shown below:
<application> <variable-resolver> com.ibm.faces.databind.SelectItemsVarResolver </variable-resolver> <variable-resolver> com.ibm.faces.application.WPPortletVariableResolver </variable-resolver> <property-resolver> com.ibm.faces.databind.SelectItemsPropResolver </property-resolver> </application>
9. Be careful with pages that use JavaScript code. You will have to namespace all JavaScript calls and references to JSF components. Refer to 17.9, Implementing a value change event on page 583 for an example of how to do this. 10.If your existing JSF application already supports multiple locales, remember to update portlet.xml to include the supported languages. Refer to 17.10, Implementing internationalization on page 587 to see how to do this. 11.The JSPs should be modified so they do not use HTML head and body elements. All HTML output to the portal is written in the context of an HTML table cell. 12.Refer to Chapter 8, JSR 168 API on page 251 and Chapter 4, IBM Portlet API on page 115 for details about each API, so that you can make your application take advantage of the features provided by each one.
550
17
Chapter 17.
551
552
8. In the Portlet type page, select Faces portlet (JSR 168). Click Next >.
553
554
10.In the Portlet settings page, review the options provided and leave all settings unchanged. Click Next >. 11.In the Miscellaneous page, select only the View mode for the portlet. Enter /calculate.jsp as the name of the initial page.
12.Click Finish to generate the portlet. 13.If the Confirm Perspective Switch window appears, click Yes.
555
a. Open Calculate.java. The class is empty for now. It is a subclass of PageCodeBase. b. Open PageCodeBase. This class contains shared code for all JSF pages: Access to FacesContext, requestScope and so forth. gotoPage - switch to another JSF page findComponent - find any JSF component findComponentInRoot - find a JSF component under the root component resolveExpression - resolves a JSF reference: #{xxxx} log - simplified test output to System.out.println
2. There is a new style sheet, stylesheet.css, in the WebContent/theme folder (Figure 17-6 on page 557). Open the file; it has many predefined styles. 3. In the WEB-INF/lib folder you find the JSF runtime JAR files (Figure 17-6 on page 557): jsf-api and jsf-impl are the basic JSF JAR files from Sun. jsf-ibm has the IBM extensions. jsf-portlet has the JSF portlet runtime for JSR 168 API. jstl and jstl_el are JSP standard tag libraries. commons-xxx are common utilities. jaxen-full, saxpath and standard are JAR files needed by the JSP standard tag libraries.
556
4. In the WEB-INF you find the JSF configuration file, faces-config.xml. Open this file: It contains the definition of a FacesContextFactory specific to JSR 168: com.ibm.faces.context.PortletFacesContextFactoryImpl. In an IBM portlet API, this factory would be:
com.ibm.faces.context.WPPortletFacesContextFactoryImpl
It contains a life cycle listener and already one managed bean, pc_Calculate, which is the Java code for the JSF page. It contains a PropertyResolver and two VariableResolver. One of the variable resolvers is specific to JSR 168:
com.ibm.faces.application.PortletVariableResolver
5. Open the deployment descriptor, web.xml. It contains a servlet named JS Resource Servlet. This servlet is an IBM extension for generated JavaScript functions.
557
6. Open the portlet deployment descriptor, portlet.xml. It contains the FacesPortlet definition. The portlet name is Calculator and it points to the class com.ibm.faces.webapp.FacesGenericPortlet. The portlet has an init parameter pointing to the initial page of View mode:
<init-param> <name>com.ibm.faces.portlet.page.view</name> <value>/calculate.jsp</value> </init-param>
3. Select the Output component in the Faces Components palette. Drag the cursor under the heading and drop the component.
558
4. Select the outputText component in the Design view. a. In the Properties view, change the Id to comment. b. Change the Value to Created using Application Developer. c. Enter style properties: font-size: 18; font-weight: bold.
5. Position the cursor behind the output text and press Enter twice to add <BR>s. 6. From the palette, select the Display Errors component and drop it under the breaks. This is for validation error messages. 7. Select the new component and notice in the Properties view the messages style class (see Figure 17-10 on page 560).
559
8. Open the file stylesheet.css. a. In the Styles view (Figure 17-11), find the .messages class and open it (double-click it).
b. In the Add Style --- .messages window, select Font and set the color to Red. Click OK and the code is inserted into the stylesheet.css file:
.messages { color: red }
c. Change the .message entry in the same way. This class will be used for error messages attached to one field. Save and close the stylesheet.css. The {Error Messages} component is now red. 9. Insert a table with five rows and two columns under the error messages field. Select Insert Table, change Rows to 5, Border width to 0, and Padding inside cells to 3.
560
10.Click OK. Your page now should look similar to Figure 17-13.
11.Select File Save All. 12.Open the Calculate.java file by selecting Edit Page Code from the context menu inside the JSP in Page Designer (right-click). You can also open the Java file directly, if you prefer. Notice the code that has been generated, for example:
protected HtmlOutputText comment; protected HtmlOutputText getComment() { if (comment == null) { comment = (HtmlOutputText) findComponentInRoot("comment");
561
} return comment; }
Notice also that each component is defined with a lazy getter method; therefore you will never use the variable in your own methods, but always in the associated getter method. 13.Fill in the recently created table (Figure 17-14): a. b. c. d. e. Drop Output components into column 1, rows 1, 2, 3, and 5. Drop Input components into column 2, rows 1 and 3. Drop a Combo Box component into column 2 row 2. Drop a Command - Button component into column 2 row 4. Drop an Output component into column 2 row 5.
14.Change the Output components in column 1. Select each component and in the Properties view change the Value field (Figure 17-15 on page 563): a. b. c. d. Column 1 row 1: Number 1 (11-888): Column 1 row 2: Operation: Column 1 row 3: Number 2 (0-19, odd): Column 1 row 5: Result:
The table should now look as illustrated in Figure 17-15 on page 563.
562
563
2. Change to the Validation tab: a. Check the Value is required checkbox. b. Set Minimum and Maximum to 11 and 888. c. Check the Display validation error messages in a error message control checkbox. d. An error message field is added to the right of the input field. Select it and on the Properties view, if not already there, enter message in the Styles: Classes property.
3. Select the Input component again and change to the Behavior tab: a. Check Auto-advance to next field. b. Enter 3 in the field After user types ... characters.
564
c. Click the All Attributes icon to see all the values you have entered. You can also enter the values directly there.
4. Similarly, select the second Input component. In the Properties view, do the following: a. h:inputText tab: id=number2, width=12, Format=Number, Type=Decimal and Integer only checkbox. b. Validation tab: required, minimum= 0, maximum=19. c. Behavior tab: Auto-advance to next field and enter 2 in the field After user types ... characters. 5. Select the Combo Box component (operation column 2). In the Properties view, h:selectOneMenu tab: a. Change id to operation. b. In the Add a choice for each item in the combo box dropdown table (Figure 17-19 on page 566): i. Click the Add Choice button four times. ii. Change the generated names to add, subtract, multiply, and divide. iii. Change the generated values to Add, Subtract, Multiply and Divide.
565
6. Select the Submit button. In the Properties view: a. hx:commandExButton tab: enter calc as the id. b. In the Display options tab: Replace Submit for Calculate as the Button label. 7. Select the Result: Output component (column 1). In the Properties view, set the Style: Props to:
color: blue; font-size: 18; font-weight: bold
8. Select the last Output component (column 2). In the Properties view, set the id to result and the Style: Props to:
color: blue; font-size: 18; font-weight: bold
566
10.Now, save the JSF page and take a look at some of the tags in the source tab. For example: Tag libraries:
<%@taglib <%@taglib <%@taglib <%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%> uri="http://java.sun.com/portlet" prefix="portlet"%> uri="http://java.sun.com/jsf/html" prefix="h"%> uri="http://www.ibm.com/jsf/html_extended" prefix="hx"%>
The whole page is contained in a <f:view> tag. Around the table is a <f:form> tag. Input field with error message field:
<h:inputText styleClass="inputText" id="number1" size="12" required="true" maxlength="3"> <f:convertNumber integerOnly="true"/> <f:validateLongRange minimum="11" maximum="888"></f:validateLongRange> </h:inputText> <h:message for="number1"></h:message>
Combo box:
<h:selectOneMenu styleClass="selectOneMenu" id="operation"> <f:selectItem itemValue="Add" itemLabel="add" /> <f:selectItem itemValue="Subtract" itemLabel="subtract" /> <f:selectItem itemValue="Multiply" itemLabel="multiply" /> <f:selectItem itemValue="Divide" itemLabel="divide" /> </h:selectOneMenu>
567
5. Check the Set server as project default check box. 6. Click Finish. 7. The calculator application appears in the browser. Leave the fields empty and click Calculate. 8. You get two error messages in the common error message field, and one message in the error message field of number 1 (Figure 17-21).
9. Enter 9 and 2 for the two numbers. You should get a validation error for the first number. 10.Enter an invalid value for the second number and you get an error message as well. 11.Enter valid values and no error messages should be displayed. Note: The second number is only validated for the range. Standard validation does not check for odd numbers. You will implement this in 17.8, Implementing a validator on page 580.
568
b. Select the package and in the context menu, select Import.... c. Select File System, locate and select the file CalculatorBean.java. d. Click Finish. 2. Open the calculate.jsp page in Page Designer and go to the Page Data view. In the context menu, select New JavaBean (Figure 17-23).
569
a. Enter calculator as the name. b. For the class click the Browse icon, then enter Calcu to locate the CalculatorBean. Click OK. c. Select Make this JavaBean reusable (this will create a new Managed Bean in faces-config.xml) and select session for the scope. d. Click Initialize Properties.... i. In the dialog, click Add. ii. In the name column, select the operation property. iii. In the value column, enter Subtract and press Enter. iv. Click OK. When you run the code, the subtract operation will be preselected. e. Click Finish so the bean can be added to the Page Data view. f. Save the JSP.
4. Open the faces-config.xml file and you will see the new managed bean. 5. The calculator bean is also added to the Calculate.java code with a getCalculator method. 6. Select the first input field (number1).
570
a. In the Properties view, for the Value field, click the number1 property of the calculator bean.
7. Repeat this for the number2 input field and bind it to the number2 property. 8. Expand the calculator bean in the Page Data view. Select the operation property and drag/drop it onto the combo box. This has the same effect. Select the result property and drag/drop it onto the output field in column 2. 9. Save the file. 10.The property names appear in the Design view for the input and output components, as shown in Figure 17-26.
571
2. Replace the generated action code with the code shown in Example 17-1.
Example 17-1 Action code to invoke the calculate method log("CalculateAction start"); try { getCalculator().calculate(); } catch (Exception e) { log("Calculator-Exception: "+e.getMessage()); if (getCalculator().getErrorMessage() == null) getCalculator().setErrorMessage("Exception: "+e.getMessage()); } finally { log("CalculateAction end"); } return null;
572
3. Save the calculate.jsp. Open the Calculate.java file and notice that the action code has been added in the doCalcAction method. You can also make further changes either in the Quick Edit view or in the Java class. 4. Select the Calculate button. In the Properties view, click the Show All Attributes icon ( ). You can see that the action is set to #{pc_Calculate.doCalcAction}. This is the link to the Java code. 5. Rerun the project (Figure 17-27). Perform some calculations. For example, try to multiply 123 by 14.
6. Try the divide operation with values that result in a non-integer result. Notice the exception in the Console. The result is still calculated. Note: You will implement an error page to display the error message in 17.6.1, Implementing an error page on page 573.
573
2. Replace the Place content here content with a heading 1, JSF Calculator Error. See 17.3, Creating the page layout on page 558. 3. In the Page Data view, right-click and select New JavaBean. 4. In the dialog, select Add existing reusable JavaBean (Figure 17-29 on page 575). 5. Select the calculator bean and click Finish.
574
6. Expand the calculator bean. 7. Select the errorMessage property and drag/drop it under the heading. 8. In the Insert JavaBean dialog (Figure 17-30 on page 576), select Displaying data (read-only). 9. If needed, change the label to Error message: and click Finish. Note: This creates a table with a label (Error message:) and an output text bound to the calculatorBean.errorMessage property, plus a generic Error Messages component.
575
Figure 17-30 Binding the error message from the bean to the error JSP
10.From the palette, add a Command - Button at the bottom. 11.Change the label in the Properties view (Display options tab) to Back. 12.Save the error.jsp (Figure 17-31). 13.Notice the matching Error.java code in the pagecode package.
576
3. Select the calculate.jsp page, enter back as the outcome and leave other fields unchanged (Figure 17-33). 4. Click OK to add the rule.
577
5. Select the Back button and in the Properties view, click the All Attributes icon ( ). 6. Enter back in the action attribute.
Note: The symbolic alias name of the action can be entered directly, or in the action logic. You will enter the action alias in the logic for the calculate.jsp. 7. Save the file. 8. In the calculate.jsp, select the Calculate button. 9. In the Properties view, hx:commandExButton tab, add two new rules: a. calculate, which stays on the page. b. error, which goes to the error page. Note: If needed, click All Attributes again to add new rules. 10.Define the error rule as a global rule, by selecting the All pages option. 11.For this case, a global rule is not really necessary, but it illustrates the concept of using global rules. Figure 17-35 illustrates the entered rules.
12.Open the faces-config.xml file and locate the code that was added for the navigation rules.
Example 17-2 Navigation rules in faces-config.xml <navigation-rule> <from-view-id>/error.jsp</from-view-id> <navigation-case> <from-outcome>back</from-outcome>
578
<to-view-id>/calculate.jsp</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>/calculate.jsp</from-view-id> <navigation-case> <from-outcome>calculate</from-outcome> <to-view-id>/calculate.jsp</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <navigation-case> <from-outcome>error</from-outcome> <to-view-id>/error.jsp</to-view-id> </navigation-case> </navigation-rule>
13.Next, change the action code for the Calculate button as follows: a. In case of an exception, go to the error page. b. Otherwise, stay on the calculate page. This process is accomplished by returning one of the two symbolic names, calculate or error as highlighted in Example 17-3. Note: You can make the changes in the Quick Edit view or directly in the Calculate.java file.
Example 17-3 Action code for the Calculate button changed to reflect new rules log("CalculateAction start"); try { getCalculator().calculate(); return "calculate"; // stay on the calculate page } catch (Exception e) { log("Calculator-Exception: "+e.getMessage()); if (getCalculator().getErrorMessage() == null) getCalculator().setErrorMessage("Exception: "+e.getMessage()); return "error"; // go to the error page } finally { log("CalculateAction end"); } return null; // remove the existing return
Note: Instead of returning the calculate String, you can return null or an empty string and the calculate.jsp is redisplayed. 14.Save all files.
579
15.Run the code again, then try a division that produces the exception. The error page is displayed (Figure 17-36). Click Back to get back to the calculator.
580
Where: FacesContext arg0: provides access to all components UIComponent arg1: component to be validated Object arg2: value (of the component) to be validated 3. Replace the method body with the code below:
Example 17-4 The validator code System.out.println("OddValidator start"); UIInput field = (UIInput)arg1; int value = ((Long)arg2).intValue();
581
System.out.println("Field="+field.getId()+" Value="+value); if (value%2 == 1) { field.setValid(true); System.out.println("OddValidator end: valid"); } else { System.out.println("OddValidator end: invalid"); FacesMessage errmsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "2nd number not odd.", "Second number must be odd."); throw new ValidatorException(errmsg); }
4. To resolve the classes, right-click in the code and select Source Organize imports. 5. Review the validator code: a. Access the arg1 field to display its name, you can use UIInput or HtmlInputText classes. b. Obtain the value from arg2, you know its data type. c. Create an error message, a FacesMessage, to handle errors: i. The first message is the summary message (displayed in the global error message field). ii. The second message is the detailed message (displayed in an error message field attached to the input field). A detailed message has not been defined and therefore it will not be used in this case. 6. Save and close the OddValidator. 7. Register the validator. Open the faces-config.xml file and at the bottom ad the lines shown in Example 17-5.
Example 17-5 The validation registration in faces-config.xml <validator> <description>Registers the OddValidator</description> <validator-id>oddValidator</validator-id> <validator-class>itso.jsf.calculator.OddValidator</validator-class> </validator>
8. In the calculate.jsp, select the number2 field and go to the Source page in the Design view. a. Place the cursor before the end of the number2 field and just after the </f:validateLongRange> tag:
....</f:validateLongRange .......> PUT CURSOR HERE .......
582
c. In the Insert Custom Tag dialog select the f tag library (at left) and validator (at right) and click Insert. d. Click Close. The custom tag is inserted. e. Enter oddValidator for the validatorId attribute, either directly in the Source, or in the Properties view. This generate the following tag:
<f:validator validatorId="oddValidator"></f:validator>
f. Save the calculator.jsp. g. Restart the enterprise application. Enter an even value and you should see the error message. Notice that the summary message is displayed. h. Notice also the test output in the Console.
Figure 17-38 Output text to monitor changes in the combo box values
2. In the Properties view, change the Id to Opchanged. Make sure the output component is still selected.
583
3. Select the operation combo box. In the Quick Edit view, select Value Changed (left). Click in the code pane (right), and enter this code:
Example 17-6 Code to handle the value change event log("OperationValueChangeEvent start"); HtmlSelectOneMenu operation = (HtmlSelectOneMenu)valueChangedEvent.getSource(); String opnew = (String)valueChangedEvent.getNewValue(); String opold = (String)valueChangedEvent.getOldValue(); log("operation old="+opold+" new="+opnew); getCalculator().setOperation(opnew); // is also done later by binding getOpchanged().setValue("changed"); // change the output text field getCalc().setValue(opnew); // change the Calculate button log("OperationValueChangeEvent end");
4. Save the calculate.jsp. 5. Open the Calculate.java file and you will find the handleOperationValueChange method with your code. 6. Review the generated code: a. The method that is generated (Calculate.java) has one parameter (valueChangedEvent). b. From this parameter you can access the source component and the old and new values. c. In this scenario, you set the Opchanged output field to the value changed. d. You use the new value to change the label in the Calculate button. e. Notice in the Properties view, with All attributes button selected, the valueChangeListener is set to #{pc_Calculate.handleOperationValueChange1}. Note: The last character in the method name can be nothing or a consecutive digit, depending on how many operations you have. 7. Run the code. You can see in the Console that the event is invoked. However, no change is visible in the GUI. This is because the event is not immediate and the refresh of the GUI after the action looses the changes if a navigation rule is involved. If your action logic returned null, the changes would be visible. 8. To make the event immediate, you have to handle the onchange event. But before you do that, you will create a method to facilitate dealing with the portlet namespace: a. Open the PageCodeBase.java file. Create the following attribute:
protected String namespace;
584
Example 17-7 getNamespace method public String getNamespace() { if (namespace == null) { RenderResponse response = (RenderResponse)facesContext.getExternalContext().getResponse(); namespace = response.getNamespace(); } return namespace; }
c. You may need to organize your imports. 9. Now you can handle the onchange event. Select the operation combo box. In the Quick Edit view select onchange (left). Click in the code pane (right) and enter this code:
document.forms['view<portlet:namespace/>:form1'].submit(); return false;
10.This creates a JavaScript function that is invoked from the combo box (Example 17-8).
Example 17-8 JavaScript to handle the onchange event <SCRIPT type="text/javascript"> function func_1(thisObj, thisEvent) { //use 'thisObj' to refer directly to this component instead of keyword 'this' //use 'thisEvent' to refer to the event generated instead of keyword 'event' document.forms['view<portlet:namespace/>:form1'].submit(); return false; }</SCRIPT>
11.Next, you have to change the name of the JavaScript function, to include the namespace, so that it does not collide with the name of other portlets scripts on the page: a. Change the name of func_1 function to <portlet:namespace/>func_1. The function signature should now be like this:
function <portlet:namespace/>func_1(thisObj, thisEvent) {
You need to change the call to the JavaScript function. Change the onchange attribute value to the following:
onchange="return #{pc_Calculate.namespace}func_1(this, event);"
585
Note 1: "view<namespace>" is a client ID of UIView (<f:view>). In the normal portlet programming pattern, it should be "<namespace>view", so that the portlet container removes "<namespace>" part in the request parameters. However, JSF requires the request parameter names match to the client IDs and if the porlet container removes "<namespace>" part, JSF would fail to apply the request parameters to UI components. That's why JSF portlet uses "view<namespace>" instead, in order to avoid ID collision with other JSF portlets in the same page.
Note 2: The reason to use value bind to retrieve the namespace in the onchange attribute is because JSF components does not accept runtime expressions as values. So you cannot use portlet:namespace here. For this reason we created a namespace property in PageCodeBase class, with its proper get method. Inside the JavaScript function, you do not have this limitation, so you could use the portlet:namespace tag. 12.Save the file and retest. When you change the operation, you see the immediate result (Figure 17-40). 13.Notice that the button changes back to Calculate at the next operation because a navigation rule is involved. If you change the action logic to return null, the changes to the user interface will be permanent.
586
Note: CalculatorPortletResource.properties file is the name of the default file (no locale suffix). If a locale is not supported by the portlet or the specific properties file is not found, the user gets values from this properties file. For example, if you want to create specific values for English, you will need to create a file with name CalculatorPortletResource_en.properties. 2. Create a properties file for Brazilian Portuguese. a. Right-click CalculatorPortletResource.properties. b. Select Copy. c. Right-click the calculator.nl package. d. Select Paste. e. Enter CalculatorPortletResource_pt_BR.properties as the new name for the file. This is the suffix for brazilian Portuguese. f. Click OK. g. Change the three last keys, to use Brazilian Portuguese text:
page_comment=* Criado com Application Developer * text_operation=Operao: text_calculate=Efetuar Clculo
h. Save the file and close the editor. 3. Edit the Portlet deployment descriptor. a. Double-click Portlet Deployment Descriptor. b. In the Portlet deployment descriptor window, click Portlets Calculator.
587
c. Under the supported locales section, click Add. See Figure 17-41.
d. Enter pt_BR in the Locale list. e. Click OK. f. Save the file and close the editor. 4. You also need to specify the supported languages in the faces-config.xml file. a. Open the faces-config.xml file. b. Within the <application> tag, include the following tags:
<locale-config> <supported-locale>en</supported-locale> <supported-locale>pt_BR</supported-locale> </locale-config>
c. Save the file and close it. 5. In the calculate.jsp Source code, after the jsf/core tag library, enter:
<f:loadBundle var="constants" basename="calculator.nl.CalculatorPortletResource"/>
Note: This tag provides access to the text constants for the selected language. 6. In the Design view, select the comment field (Created using Application Developer). In the Properties view, change the value attribute to:
#{constants.page_comment}
8. Change the Calculate button label in the Display options tab to:
#{constants.text_calculate}
588
10.Run the project again and change your preferred language in the profile to Brazilian Portuguese. Use Edit my profile option and enter the required information (password is wpsadmin). 11.You will have to run the project again so that the JSF framework accepts the change. 12.Notice that the text has changed to the values contained in the resource file. 13.Now try to specify a value for number1 that is out of the permitted range (11-888). You will notice that the error message is also translated to Brazilian Portuguese, although you did not have to be concerned about (Figure 17-43 on page 590).
589
Note: The message-bundle element represents a set of localized messages. This element contains the fully-qualified path to the resource bundle containing the localized messages, in this case, calculator.nl.CalculatorPortletResource. 2. Include the following key in CalculatorPortletResource.properties (default locale bundle):
javax.faces.validator.NOT_IN_RANGE=Valid values must be in the range of {0} to {1}
590
4. Run the code again and try to specify a value for number1 that is out of the permitted range (11-888). You will notice that the error message has changed to the one specified in the properties file. For your reference, the Table 17-1 shows the keys for the standard messages associated with standard JSF components. The messages for the IBM extended components can be found at the jsf-ibm.jar file.
Table 17-1 Keys for the standard messages Key javax.faces.component.UIInput.CONVERSION javax.faces.component.UIInput.REQUIRED javax.faces.component.UISelectOne.INVALID javax.faces.component.UISelectMany.INVALID javax.faces.validator.NOT_IN_RANGE javax.faces.validator.DoubleRangeValidator.MAXIMUM javax.faces.validator.DoubleRangeValidator.MINIMUM javax.faces.validator.DoubleRangeValidator.TYPE javax.faces.validator.LengthValidator.MAXIMUM javax.faces.validator.LengthValidator.MINIMUM javax.faces.validator.LongRangeValidator.MAXIMUM javax.faces.validator.LongRangeValidator.MINIMUM javax.faces.validator.LongRangeValidator.TYPE Standard Message Conversion error occurred Value is required Value is not a valid option Value is not a valid option Specified attribute is not between the expected values of {0}and {1} Value is greater than allowable maximum of {0} Value is less than allowable minimum of {0} Value is not of the correct type Value is greater than allowable maximum of {0} Value is less than allowable minimum of {0} Value is greater than allowable maximum of {0} Value is less than allowable minimum of {0} Value is not of the correct type
591
592
18
Chapter 18.
593
594
15.Select Zip file and click Next. 16.Click Browse and select the com.ibm.faces.portlet.example.zip file. 17.Click Finish. The prebuilt Java Bean package (com.ibm.faces.portlet.examples) is shown under JavaSource.
Figure 18-1 The Page Data view showing the ticketList Java Bean
5. Expand ticketList and drag and drop ticketList/tickets from Page Data view onto Page Designer. 6. Type com.ibm.faces.portlet.examples.Ticket in Type and click OK in the Object Type dialog (Figure 18-2 on page 596).
595
7. In the Insert JavaBean dialog (Figure 18-3 on page 597), click None and select the fields ticketId, title and status. Sort the fields in this order clicking Up/Down (icons). Enter Ticket ID as the label of the ticketId field.
596
8. Click Finish. A data table with outputs is inserted. 9. Select one of the cells and change the focused node by clicking h:dataTable on the Change focused node button at the top of Design view (Figure 18-4 on page 598).
597
10.In the Display options tab, Enter 5 in Rows per page and click the Add a deluxe pager button (Figure 18-5)
598
Figure 18-6 Layout of the dataTable after setting the display options
3. Type customerId as the Attribute name and click OK. 4. Select ticketList and select Configure from the context menu. 5. Click Initialize Properties. 6. Click Add. 7. Select customerId from Name.
599
8. Select PortletData/customerId from Value (click the dialog shown in Figure 18-8).
9. Click OK. 10.Click OK again. 11.Click Finish. 12.Select File Save from the menu bar. 13.Open WebContent/TicketEdit.jsp with Page Designer. 14.Replace the text Place content here. with Customer ID:. 15.Drag and drop Combo Box from Faces Components of Palette view, next to the text Customer ID:. 16.Drag and drop Command - Button from the Faces Components of the Palette view, next to the combo box. Your page should look like Figure 18-9.
17.Select New JavaBean from the context menu of Page Data view.
600
18.Enter customerList for the Name and com.ibm.faces.portlet.examples.CustomerList for the class. 19.Click Finish. customerList is shown in Page Data view. 20.Expand customerList/customers/Contained Type. 21.Enter com.ibm.faces.portlet.examples.Customer in Type and click OK in the Object Type dialog. 22.Drag and drop customerList/customers/Contained Type/customerId on the combo box in Page Designer. The combo box gets wider to show the choice specification, but it is normal behavior. 23.Expand Portlet in Page Data view. 24.Select PortletData and select Add Attribute from the context menu. 25.Enter customerId in Attribute name and click OK. 26.Select the combo box and in the Properties view, h:selectOneMenu tab, click the icon next to Value. 27.Select PortletData/customerId and click OK in Page Data Object dialog. 28.Select File Save from the menu bar.
601
10.Drag and drop Command - Button from Faces Components of Palette view, next to the input. 11.Drag and drop ticketDetail/ticketId from Page Data view on the input. 12.Drag and drop ticketDetail/ticket from Page Data view onto Page Designer, below the Ticket ID text. 13.Select the following fields: title status owner contact note
and sort in the above order by clicking Up/Down (icons). 14.Click Finish. A table with outputs is inserted (Figure 18-10).
602
the right place, be sure that during the drop the enclosing black rectangle is shown around the output component. The Insert Click-to-Action Output Property dialog is shown (Figure 18-11). 3. Type ticketId in Data type. 4. Select Ticket portlet from Source portlet and click OK. The wizard creates the Ticketportlet.wsdl file and import the pbportlet JAR file in the project.
5. Select File Save from the menu bar. 6. Select Portlet Deployment Descriptor/Ticket2 portlet in Project Explorer view and select Cooperative > Enable Target from the context menu. 7. Click the first Browse button to select the Data type for the Input property. 8. Select ticketId (Figure 18-12) and click OK.
603
9. Click the second Browse to select the target action. 10.Select /Ticket2View.jsp/form1/button1 (Figure 18-13) and click OK.
11.Type Show Detail in Label. Your dialog should look like Figure 18-14 on page 605. 12.Click OK. The wizard creates the Ticket2portlet.wsdl file.
604
605
606
4. If the Confirm Enablement window appears, asking if you want to enable the portal development role, click OK. 5. In the Portlet project page, type the name of the project TrafficCondition. 6. Check the Create a portlet check box, if it is not already checked. Typically, you do not need to create a portlet when you import a portlet WAR file into the project. 7. In the WebSphere Portal version box, select 5.1. 8. Click Next >. 9. In the Portlet type page, select Faces portlet (JSR 168). 10.Click Finish. 11.If the Confirm Perspective Switch window appears, click YES.
2. In the Web Service Discovery Dialog (Figure 18-17 on page 608), select Web Services from a known URL.
607
3. On the next page (Figure 18-18 on page 609), enter http://www.xmethods.net/sd/2001/CATrafficService.wsdl for the URL field. Then click Go.
608
Figure 18-19 The Web Services dialog displaying the Web Services Information window
4. The dialog will display information about the Web service in the Web Services Information window (Figure 18-19). To explore the Web Service, select it and click Details. This will launch the Web Services Explorer.
609
5. Invoke the WSDL operation by clicking getTraffic in the WSDL Binding Details window. This will bring the Invoke a WSDL Operation window. Enter a highway number as the hwynums parameter (Figure 18-20). The return of the operation will be displayed in the Status window.
6. Click the Source link at the right of the Status window (Figure 18-21).
The details of the SOAP request (Figure 18-22) and response (Figure 18-23 on page 611) are displayed in the Status window.
610
7. Close the Web Services Explorer. In the Services and Ports section of the Web Services Discovery dialog, expand the Service element and select the Port: CATrafficPort. Click Add to Project. This will generate a client proxy that will hide the details of the Web service JAX-RPC API. 8. In the Add Web Service dialog (Figure 18-24 on page 612), select Finish.
611
The published methods available for the Web service will now be visible in the Page Data view (Figure 18-25)
Figure 18-25 The page data window with the new Web service defined
612
1. In the TrafficConditionView.jsp file, replace the default text with Enter a highway number to get traffic conditions: 2. From the Faces Components palette, drag and drop a Panel - Group Box below the text. 3. In the Select pop-up (Figure 18-26), select List and click OK.
Now you will add the input and output components and the action button for the Web service to the Faces JSP. 4. In the Page Data view, expand catrafficporttypeproxyGetTrafficParamBean and select hwynums. Drag and drop it into the Panel - Group Box. 5. In the Insert Web Service dialog, make sure that the Control Type selected is Input Field (Figure 18-27 on page 614).
613
6. Click Finish. 7. Bind the doCatrafficporttypeproxyGetTrafficAction to the Submit button by dragging and dropping the action onto the button (Figure 18-28).
8. Insert a break after the Submit button. 9. The catrafficporttypeproxyGetTrafficResultBean(String) will return the result from the Web service. Drag and drop it below the break that was just inserted. 10.In the Insert Web Service dialog, make sure that the Control Type selected is Output Field. Change the label to Traffic Report:, then click Finish. The page layout should look like Figure 18-29 on page 615:
614
Figure 18-29 The page layout for the Web service client portlet
615
616
19
Chapter 19.
Portlet services
In this chapter, we will discuss portlet services. WebSphere portal provide the ContentAccessService, CredentialVaultService and the PropertyBrokerService. We will briefly discuss the ContentAccessService here, the CredentialVaultService and the PropertyBrokerService are discussed in their own sections. We will also discuss writing a custom portlet service.
617
19.1.1 ContentAccessService
ContentAccessService (CAS) provides portlets with access to remote systems and content from remote URLs. When developing portlets, if you are not certain if there will be a firewall when the portlet is deployed you should use the ContentAccessService. URLs on the other side of a proxyserver can be obtained using ContentAccessService. ContentAccessService provides three methods. getMarkup This method will return a String of the content returned by the URL. include The include method will use a RequestDispatcher to include the provided URL. getInputStream This method can be used if the URL to be requested does not specify the character encoding. You can specify the required encoding, otherwise it is returned as UTF-8. getURL Returns a URL object representing the provided URL.
618
import com.ibm.portal.portlet.service.PortletServiceHome; import com.ibm.portal.portlet.service.contentaccess.ContentAccessService; public class CASexamplePortlet extends GenericPortlet { private PortletServiceHome contentAccessServiceHome; public void init(PortletConfig config) throws PortletException { super.init(config); try { Context ctx = new InitialContext(); Object home = ctx.lookup("portletservice/com.ibm.portal.portlet.service.contentaccess.Content AccessService"); if(home != null){ contentAccessServiceHome = (PortletServiceHome)home; } } catch (NamingException e) { e.printStackTrace(); } }
619
public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { // Set the MIME type for the render response response.setContentType(request.getResponseContentType()); if(contentAccessServiceHome != null){ ContentAccessService service = (ContentAccessService)contentAccessServiceHome.getPortletService(ContentAccessS ervice.class); service.include(request.getParameter("URL"), request, response, getPortletConfig().getPortletContext()); } } }
620
Example 19-3 Defining the Service Interface for JSR 168 portlet service. package com.yourco.services.mailservice; import com.ibm.portal.portlet.service.PortletService; public interface MailService extends PortletService { public boolean sendMail(String to, String from, String subject, String message);
}
2. Implement the service The Service interface then needs to be implemented. The implementation class must implement the custom service interface you defined as well as the com.ibm.portal.portlet.service.spi.PortletServiceProvider interface. The PortletServiceProvider defines the init method that must be implemented. The init method may be called by the factory when the implementation class is first created. In practice, while your custom factories may choose not to utilize this method, the default factories do. The init method is an appropriate location to load initialization parameters, establish connection pools, etc. Initialization parameters are discussed in step 4. You can use one implementation for both APIs. You would need to implement both the JSR 168 portlet service interface and the IBM portlet service interface defined in step 1.
Example 19-4 Implementing the custom service package com.yourco.services.mailservice; import java.util.Date; import java.util.Properties; import java.util.prefs.Preferences; import import import import import javax.mail.Message; javax.mail.Session; javax.mail.Transport; javax.mail.internet.InternetAddress; javax.mail.internet.MimeMessage;
import com.ibm.portal.portlet.service.PortletServiceUnavailableException; import com.ibm.portal.portlet.service.spi.PortletServiceProvider; public class MailServiceImpl implements PortletServiceProvider, MailService { private String server_name; private String serverNotFound = "ServerNotFound";
621
public void init(Preferences servicePreferences) throws PortletServiceUnavailableException { // Set Mail Server name based on initialization parameters server_name = servicePreferences.get("server_name", serverNotFound); } public boolean sendMail(String to, String from,String subject, String message) { if(server_name.equalsIgnoreCase(serverNotFound)){ return false; } else { return send(to, from, subject, message); } } private boolean send(String from, String to, String subject, String message) { try { Properties props = System.getProperties(); // -- Attaching to default Session, or we could start a new one -props.put("mail.smtp.host", server_name); Session session = Session.getDefaultInstance(props, null); // -- Create a new message -Message msg = new MimeMessage(session); // -- Set the FROM and TO fields -msg.setFrom(new InternetAddress(from)); msg.setRecipients(MimeMessage.RecipientType.TO, InternetAddress.parse(to, false)); // -- Set the subject and body text -msg.setSubject(subject); msg.setText(message); // -- Set some other header information -msg.setHeader("X-Mailer", "LOTONtechEmail"); msg.setSentDate(new Date()); // -- Send the message -Transport.send(msg); return true; } catch (Exception ex) { ex.printStackTrace();
622
return false; } } }
3. Using Portlet Object - optional If you are creating a portlet service for both portlet APIs, you can use the same implementation class for both. If any method take a portlet object, they will have different signatures for the two. One will be a JSR 168 package and the other will be an IBM package. You could provide a single implementation method by converting the IBM portlet objects to JSR 168 portlet object. WebSphere Portal provides the com.ibm.portal.portlet.apiconvert.APIConverterFactory class. This class includes methods for converting the IBM portlet object to the appropriate JSR 168 portlet object. Example 19-5 demonstrates using the APIConverterFactory to convert the PortletRequest and PortletResponse to a RenderRequest and RenderResponse object.
Example 19-5 APIConverterFactory public void message(org.apache.jetspeed.portlet.PortletRequest request, org.apache.jetspeed.portlet.PortletResponse response) throws IOException { message(APIConverterFactory.getInstance().getRenderRequest(request), APIConverterFactory.getInstance().getRenderResponse(response)); }
4. Register the service Once the service interface has been defined and the implementation class created, the classes should be packaged into a jar file. This jar should be placed in the <WPS-ROOT>shared\app directory. Once the files have been deployed, the service must be registered. Open the PortletServiceRegistryService.properties file in the <WP-ROOT>\shared\app\config\services directory. It is recommended that you make a backup of this file prior to modifying it. The service and its implementation be registered. The first entry will be your custom service and the second entry is your service implementation. IBM Portlets will register portlet services as it seen in Example 19-6 on page 624.
623
JSR 168 portlet services are registered using JNDI. The syntax is jndi:service_interface = service_implementation. Example 19-7 show the addition of registering the JSR 168 portlet service. Note: The service implementation classes are the same for both portlet APIs in my example. In some case you may need to separate the service a implementations for each API.
Example 19-7 Registering JSR 168 Portlet Service # MailService jndi:com.yourco.services.mailservice.MailService = com.yourco.services.mailservice.MailServiceImpl com.yourco.services.mailservice.IBMMailService = com.yourco.services.mailservice.MailServiceImpl
Initialization parameters are also supplied in the PortletServiceRegistryService.properties file as illustrated in Example 19-8. Accessing these parameters is illustrated in Example 19-4 on page 621.
Example 19-8 Setting parameters in PortletServiceRegistryService.properties # MailService jndi:com.yourco.services.mailservice.MailService = com.yourco.services.mailservice.MailServiceImpl com.yourco.services.mailservice.IBMMailService = com.yourco.services.mailservice.MailServiceImpl com.yourco.services.mailservice.MailServiceImpl.server_name = your_server_name
5. Test the service In order for the service to become available in the Portal, the Portal Server must be restarted. Restart the WebSphere Portal Application Server. Example 19-9 on page 625 shows a simple JSR 168 portlet making use of the MailService service.
624
Example 19-9 Using the MailService service package jsrmailportlet; import java.io.IOException; import import import import import import import import import import import javax.naming.Context; javax.naming.InitialContext; javax.naming.NamingException; javax.portlet.ActionRequest; javax.portlet.ActionResponse; javax.portlet.GenericPortlet; javax.portlet.PortletConfig; javax.portlet.PortletException; javax.portlet.PortletRequestDispatcher; javax.portlet.RenderRequest; javax.portlet.RenderResponse;
public static final String VIEW_JSP = "JSRMailPortletPortletView"; public static final String FORM_SUBMIT = "JSRMailPortletPortletFormSubmit"; public static final String FORM_To = "JSRMailPortletPortletFormTo"; public static final String FORM_From = "JSRMailPortletPortletFormFrom"; public static final String FORM_Subject = "JSRMailPortletPortletFormSubject"; public static final String FORM_Message = "JSRMailPortletPortletFormMessage"; private PortletServiceHome mailServiceHome; public void init(PortletConfig config) throws PortletException { super.init(config); try { Context ctx = new InitialContext(); Object home = ctx.lookup("portletservice/com.yourco.services.mailservice.MailService"); if(home != null){ mailServiceHome = (PortletServiceHome)home; } } catch (NamingException e) { e.printStackTrace();
625
} } public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { // Set the MIME type for the render response response.setContentType(request.getResponseContentType()); //test for failure if(request.getParameter("error") == null){ // Invoke the JSP to render PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher("/jsrmailportlet/jsp/html/"+VIEW_JSP+" .jsp"); rd.include(request,response); } else if(request.getParameter("error").equalsIgnoreCase("true")){ response.getWriter().println("Error in sending mail!"); } } public void processAction(ActionRequest request, ActionResponse response) throws PortletException, java.io.IOException { if( request.getParameter(FORM_SUBMIT) != null ) { // Set form text in the session bean if(mailServiceHome != null){ MailService mailService = (MailService)mailServiceHome.getPortletService(MailService.class); String to = request.getParameter(FORM_To); String from = request.getParameter(FORM_From); String subject = request.getParameter(FORM_Subject); String message = request.getParameter(FORM_Message); if(!mailService.sendMail(to, from, subject, message)){ //set render parameter response.setRenderParameter("error", "true"); } } } } }
626
627
} catch (NamingException e) { e.printStackTrace(); } } ..... if(mailServiceHome != null){ MailService mailService = (MailService)mailServiceHome.getPortletService(MailService.class); .....
628
20
Chapter 20.
629
20.1 Overview
When integrating different back-end systems, portlets often need to provide some type of authentication to access these back-end systems. WebSphere Portal provides the use of a Credential Vault to store and retrieve user credentials. By using Credential Vault portlets, you can provide a single sign-on experience to the user, hiding the login challenge from the user. After reading this chapter, you will be able to: Understand the value of Credential Vault for portlet development Identify the different components of Credential Vault
20.1.1 Credentials
Portlets running on WebSphere Portal may need to access remote applications that require some form of authentication by using appropriate credentials. Examples of credentials are user IDs and passwords, SSL client certificates and private keys. In order to provide a single sign-on user experience, portlets should not ask the user for the credentials of individual applications each time the user starts a new portal session. Instead, they must be able to store and retrieve user credentials for their particular associated application and use those credentials to log in on behalf of the user. The Portal back-end secure access is illustrated in Figure 20-1.
630
The Credential Vault provides this functionality and portlets can use it through the Credential Vault Service.
Slot A
Slot B
Slot C
Slot X
Slot Y
Vault Implementations
Internal
Figure 20-2 Credential Vault organization
External
The portal server's credential vault is organized as follows: The portal administrator can partition the vault into several vault segments. Vault segments can be created and configured only by portal administrators. A vault segment contains one or more vault slots. Vault slots are the "drawers" where portlets store and retrieve a user's credentials. Each slot holds one credential. A vault slot is linked to a resource in a vault implementation. A vault implementation is the place where users credentials are actually stored. Examples of vault implementations include the WebSphere Portal's default database vault or the Tivoli Access Manager lock box.
631
The resource within the vault implementation corresponds to an application or backend system that requires its own authentication. Examples of resources include Lotus Notes, personnel records, or a bank account.
632
633
In IBM Portlet API, the service is accessed by using PortletContext.getService, as in Example 20-2.
Example 20-2 Acquiring a CredentialVaultService in IBM Portlet API com.ibm.wps.portletservice.credentialvault.CredentialVaultService vaultService = (CredentialVaultService)config.getContext().getService(CredentialVaultService.class);
634
This method returns a specific credential to be used in the application. It expects to receive the following parameters: String slotId: Identifies the slot where the credential will be retrieved from. String type: Identifies the type of credential to be retrieved. The valid types are the ones described in 20.4, Credential objects on page 635. Map config: List of name/value pairs. Typically, you will pass a new HashMap(). PortletRequest request: The PortletRequest object of the API being used. getUserSubject This method returns the user's JAAS subject. This is a special case of the getCredential call. It expects the PortletRequest as parameter. getCredentialTypes This method Returns an Iterator of Strings that represent all Credential Types that are registered in the Credential Type Registry. getAccessibleSlots This method returns a Iterator of all credential slots that a portlet is authorized to use. It expects the PortletRequest as parameter. setCredentialSecretBinary This method sets a credential's binary secret. It expects the slotId, the secret, and the PortletRequest as parameters setCredentialSecretUserPassword This method sets a credential's user/password secret. It expects the slotId, the user, the password, and the PortletRequest as parameters getAllVaultSegments This method returns a List of all Vault Segments. Note: For details about implementing a solution using the CredentialVaultService, refer to Chapter 21, The Credential Vault on page 641.
635
Note: The names of types presented in this section are the same both in IBM portlet API and JSR 168 API. The only difference is that the types will belong to different packages for each of API. For IBM portlet API, look at the com.ibm.wps.portletservice.credentialvault.credentials package. For JSR 168 API, look at the com.ibm.portal.portlet.service.credentialvault.credentials package.
UserPasswordPassive
This type of credential stores secrets in the form of user ID/password pairs. It is implemented by the class UserPasswordPassiveCredential and provides the following methods: getPassword This method returns the password of the credentials secret. getSecretType This method returns the credentials secret type in terms of the constants declared in the CredentialVaultService interface. getUserId This method returns the user ID of this credential's secret.
SimplePassive
This type of credential stores secrets in the form of serializable Java objects. It is implemented by the class SimplePassiveCredential and provides the following methods: getSecretType
636
This method returns the credentials secret type in terms of the constants declared in the CredentialVaultService interface. getSecret This method returns the user's secret as an object.
JaasSubjectPassiveCredential
This type of credential relies on the Java Authentication and Authorization Service, and stores secrets in the form of javax.security.auth.Subject objects. It is implemented by the class JaasSubjectPassiveCredential and provides the following methods: getSecretType This method returns the credentials secret type in terms of the constants declared in the CredentialVaultService interface. getSecret This method returns the user's secret as an jaas subject.
HttpBasicAuth
This type of credential stores userid/password secrets and supports HTTP Basic Authentication. It is implemented by the class HttpBasicAuthCredential and provides the following method: getAuthenticatedConnection This method returns a new HttpURLConnection with added authentication data. There are two different signatures available. One that receives a String object representing the url, and another that receives a URL object.
637
HttpFormBasedAuth
This credential object stores userid/password secrets and supports HTTP Form-Based Authentication. You must initialize this object prior to using it, using the provided init method. This method expects a Map of key/value pairs, and the value/name pairs that must be provided are shown in Table 20-1.
Table 20-1 Configuration data to initialize a HTTPFormBasedAuthCredential Key name KEY_CREDENTIAL_SECRET KEY_USERID_ATTRIBUTE_NAME Type UserPassword CredentialSecret String Description The credentials secret The name under which the user ID is posted The name under which the user password is posted The url to which the login data is posted The url to which an HTTP GET request is send in order to log out the user Specifies whether the authentication data are cookies [true] of URL rewriting [false] Any additional name=value pairs that need to be posted with the login POST Mandatory? Y Y
KEY_PASSWORD_ATTRIBUTE_NAME
String
KEY_LOGIN_URL KEY_LOGOUT_URL
String String
Y Y
KEY_USE_AUTH_COOKIES
Boolean
KEY_FORM_DATA
List
This type of credential is implemented by the class HttpFormBasedAuthCredential and provides the following methods: getAuthenticatedConnection This method returns a new HttpURLConnection with added authentication data. There are two different signatures available. One that receives a String object representing the url, and another that receives a URL object. This latter signature can be used only if cookies are used for authentication. login This method performs the HTTP form based login.
638
logout This method performs the logout through an HTTP GET request to the logoutUrl.
JavaMailCredential
This credential object stores userid/password pairs and leverages the authentication functionality of the javax.mail API. It is implemented by the class JavaMailCredential and provides the following methods: getSecretType This method returns the credentials secret type in terms of the constants declared in the CredentialVaultService interface. getAuthenticatedSession This method authenticates an javax.mail.Session. There is a signature that receives the Session to authenticated and the mail server host name, and another that receives these two parameters and also the mail server port number.
LtpaTokenCredential
This credential object is for authenticating with a backend system that is within the same WebSphere Application Server single sign-on domain as the portal. It is implemented by the class LtpaTokenCredential and provides the following method: getAuthenticatedConnection This method returns a new HttpURLConnection with added authentication data. There are two different signatures available.
SiteMinderTokenCredential
A credential object for authenticating with a backend system that is within the same SiteMinder single sign-on domain as the portal. This credential should be used when SiteMinder is used as an authentication proxy for the portal. It is implemented by the class SiteMinderTokenCredential and provides the following method: getAuthenticatedConnection This method returns a new HttpURLConnection with added authentication data. There are two different signatures available.
WebSealTokenCredential
A credential object for authenticating with a backend system that is within the same WebSEAL single sign-on domain as the portal. This credential should be
639
used when a WebSEAL authentication proxy is used by the portal. It is implemented by the class WebSealTokenCredential and provides the following method: getAuthenticatedConnection This method returns a new HttpURLConnection with added authentication data. There are two different signatures available.
640
21
Chapter 21.
641
HTTP
init()
create Vault Service
Secret Manager
8
processAction()
7 10
2 2
doView() 12
Request
Number of digits
6
Submit
userid password
Preferences
Server name Port number Secure server path
Browser
View 11 Mode 3
4
Portlet
Edit Mode
Configure Mode
642
The sequence flow for this scenario is as follows: 1. The init() method is used to initialize the Credential Vault Service. 2. Portal invokes the portlet doView() method. Since initially, no credentials have been stored, a message is written indicating that a user ID and password must be entered in Edit mode. 3. In the portlet View mode, a message is shown directing the user to use the Edit mode to enter credentials. 4. The user clicks Edit to go into Edit mode. 5. The Edit mode screen is displayed, that is, the doEdit() method is executed and a JSP displays a form to allow the user to enter credentials (user ID and password) and submit the action. 6. The user enters a user ID and password and select Save. 7. The processAction() method is executed to process the action. 8. The processAction() method invokes the wizard generated Secret Manager to create a slot and store the entered user ID and password information. 9. An administrator will switch to configure mode (through View mode) and enter or change preferences such as the backend server name, port number and secure server path. 10.The processAction() method is executed to process the action and save the preferences. 11.When the user returns to View mode, it displays a form to enter the value that you will send to the servlet as an argument. Once the user enters a value and clicks Send the processAction() method is executed to retrieve the value. 12.The doView() method is eventually executed. The following tasks are executed in this mode: a. An HttpBasicAuth active credential object is retrieved from the credential service. Because authentication is done in this object, we never get in touch with the real credentials. b. The authorization header is set in the request HTTP header. c. The connection to the back-end resource (protected servlet in this scenario) is invoked. 13.The user is authenticated and the servlet executes. The received content is rendered to the user in View mode.
643
644
a. Browse to the location of the SecureServlet.war file in c:\LabFiles\CredentialVault\SecureServlet.war. b. Web project: SecureServlet c. Target server: select WebSphere Application Server V5.1. d. EAR project: SecureServletEAR. Note: The SecureServletEAR project will also be created for you.
6. Click Finish to import the application. 7. Execute the secure servlet to check that it is running properly. In the Project Explorer view, expand the SecureServlet/WebContent/jsp folder. 8. Right-click input.jsp and select Run Run on server... from the context menu. 9. If a WebSphere V5.1 Test Environment server already exists, select it from the list and click Next. If it does not exist, select Manually define a server and select WebSphere V5.1 Test Environment as server type, see Figure 21-4 on page 646. Click Next, leave the default port number (9080) and click Next again.
645
10.In the last window ,you will see available projects and configured projects for this server. Make sure SecureServletEAR is a configured project. 11.Click Finish 12.The internal Web browser opens and you will see a form. You also can invoke the Web application from an external browser. Enter a number of digits you want the prime number to be. Click send.
646
13.Because this servlet is secure and requires HTTP Basic Authentication, you have to enter a user name and password. Enter user1 as the user name and password1 as the password. Click OK to invoke the secure servlet.
14.The browser displays the authenticated user name and the generated prime number with the number of digits you entered in the form.
647
15.In the Servers view, select WebSphere V5.1 Test Environment server and click the red Stop button to stop the server.
648
5. In the Portlet Type window, select Basic portlet (JSR 168). Click Next. 6. In the Features window, uncheck all the features and click Next. 7. In the Portlet Settings window, accept all the default values. Click Next. 8. In Actions and Preferences, uncheck Add form sample in Portlet action handling section so that only Add action request handler is checked. Click Next. 9. In the Single Sign-On window, check Add credential vault handling and enter the slot name SecureCredentialSlot as illustrated in Figure 21-9 on page 650. Click Next.
649
10.In the Miscellaneous window, check Add Edit mode and Add configure mode. In Edit mode, you will enter the credentials to be stored and in configure mode you will establish the settings that will be needed to gain access to the secure servlet running in a different application server. Click Finish to generate the JSR 168 portlet.
650
651
Note: In an early release the CredentialJSRPortletSecretManager class generated by the wizard had an error. If this is your case, apply the required fixes or just replace the following import statement:
import com.ibm.wps.portletservice.credentialvault.CredentialSlotConfig;
The following methods are provided in this class to handle Credential Vault issues: The init() method of this class initializes the vaultService data member. getCredential() returns the user name and password by using a string buffer. setCredential() sets the user name and password. getSlotId() returns the ID of the slot. Depending on the type of slot, this method uses Portlet Preferences or VaultService to get the ID. New slots are created in the createNewSlot() method. getPrincipalFromSubject() retrieves the specified Principal from the provided subject.
652
isWritable() checks whether the userid and password can be saved. The wizard has also created an input form for a user ID and password in the CredentialJSRPortletEdit.jsp. As previously described, when clicking the Save button, the processAction() method is called. This method retrieves the user ID and password from the form and uses the secret manager class to set the credentials. The current version of the doView method retrieves the user credentials from the secret manager and displays them in the associated JSP. Because, in this sample scenario, you want to include the content of the secured PrimesGeneration servlet, you will replace this method in the next section of this scenario.
5. Click OK and repeat the same to add the following preferences checking Read only in all of them:
653
a. Name: .CredentialPortletPortKey, value: 9080. b. Name: .CredentialPortletPathKey, value: /SecureServlet/PrimesGeneration c. Name: .CredentialPortletAttrKey, value: number See the updated portlet deployment descriptor with the new preferences as shown in Figure 21-13. 6. Save and close the file.
7. Open CredentialJSRPortletSecretManager.java from the credentialjsr package. 8. Using the Java editor, add the method shown in Example 21-1 to the class. Note: The getConnectionUsingActiveObject method returns an http connection.
Example 21-1 getConnectionUsingActiveObject method (active credentials) public static HttpURLConnection getConnectionUsingActiveObject( PortletRequest portletRequest, CredentialJSRPortletSessionBean sessionBean, String host,
654
String port, String path) { HttpURLConnection connection = null; try { URL urlSpec = new URL("http://" + host + ":" + port + path); String slotId = getSlotId(portletRequest, sessionBean, false); if (slotId != null) { HttpBasicAuthCredential credential = (HttpBasicAuthCredential) vaultService.getCredential( slotId, "HttpBasicAuth", new HashMap(), portletRequest); connection = credential.getAuthenticatedConnection(urlSpec); } } catch (Exception e) { e.printStackTrace(); } return connection; }
9. Some error codes appear because the required import statements are missing. To fix these errors, right-click the Java editor and select Source Organize Imports. 10.In the Organize Imports dialog, choose the following: a. java.net.HttpURLConnection. Click Next. b. java.net.URL. Click Next. c. com.ibm.portal.portlet.service.credentialvault.credentials.HttpBasicAuthCre dential. Click Finish to close the Organize Imports dialog. 11.Save and close the Java file. 12.Open the class CredentialJSRPortlet from the credentialjsr package. 13.Add the variables shown in Example 21-2.
Example 21-2 Define new variables in CredentialJSRPortlet class public static final String SERVER_KEY = ".CredentialPortletServerKey"; // Key for the portlet preferences public static final String SERVER_TEXT = "CredentialPortletServerText"; // Parameter name for the server text input public static final String PORT_KEY = ".CredentialPortletPortKey"; // Key for the portlet preferences public static final String PORT_TEXT = "CredentialPortletPortText"; // Parameter name for the port number text input
655
public static final String PATH_KEY = ".CredentialPortletPathKey"; // Key for the portlet preferences public static final String PATH_TEXT = "CredentialPortletPathText"; // Parameter name for the servlet path text input public static final String ATTR_KEY = ".CredentialPortletAttrKey"; // Key for the portlet preferences public static final String ATTR_TEXT = "CredentialPortletAttrText"; // Parameter name for the servlet attribute text input public static final String NUMBER = "CredentialPortletFormNumber"; // Parameter name for the number of digits text input public static final String NUMBER_SUBMIT = "CredentialPortletGeneratePrime"; // Action name for prime number generation submit form private String nDigits = null;
14.Next, you will modify the processAction method to store the preferences values that portal administrators could change when running in custom configuration mode. You will also add code to handle the new event when the user enters a number of digits and submits to generate a prime number by invoking the secure PrimesGeneration servlet. The new code to accomplish this is highlighted in Example 21-3.
Example 21-3 processAction method if( request.getParameter(CONFIG_SUBMIT) != null ) { PortletPreferences prefs = request.getPreferences(); try { //prefs.setValue(CONFIG_KEY,request.getParameter(CONFIG_TEXT)); prefs.setValue(SERVER_KEY, request.getParameter(SERVER_TEXT)); prefs.setValue(PORT_KEY, request.getParameter(PORT_TEXT)); prefs.setValue(PATH_KEY, request.getParameter(PATH_TEXT)); prefs.setValue(ATTR_KEY, request.getParameter(ATTR_TEXT)); prefs.store(); } catch( ReadOnlyException roe ) { } catch( ValidatorException ve ) { } } if ( request.getParameter(NUMBER_SUBMIT) != null ) { // Get the NUMBER field if ( request.getParameter(NUMBER) != null && !request.getParameter(NUMBER).equalsIgnoreCase("") ) { nDigits = request.getParameter(NUMBER); } }
656
...............
657
System.out.println("Using Active Credentials"); HttpURLConnection connection = CredentialJSRPortletSecretManager.getConnectionUsingActiveObject( request, sessionBean, host, port, path.toString()); if (connection != null) { connection.connect(); String responseMessage = connection.getResponseMessage(); int responseCode = connection.getResponseCode(); // Were we successful? if (HttpURLConnection.HTTP_OK == responseCode) { result.append("<H4>Successfully connected!</H4>"); } else { result.append( "<P>Unable to successfully connect to back-end server." + ", HTTP Response Code = " + responseCode + ", HTTP Response Message = \"" + responseMessage + "\"</P>"); } BufferedReader br = new BufferedReader( new InputStreamReader(connection.getInputStream())); String line; while ((line = br.readLine()) != null) result.append(line + "\n"); } else { result.append( "<h2>Credential not found. Please set it in edit mode! </h2>"); } } catch (IOException exc) { result.append( "<h2>Single-sign-on error, login at back-end failed! </h2>"); } // Set the result in the request attribute request.setAttribute("result",result.toString()); } // Invoke the JSP to render PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(getJspFilePath(request, VIEW_JSP)); rd.include(request,response); nDigits = null;
658
16.You will need to organize the import statements again as you did previously. 17.Save and close the Java file. 18.Open CredentialJSRPortletConfig.jsp file under credentialjsr/jsp/html folder. Modify the form to display the default values of the preference variables you created before as illustrated in Figure 21-13 on page 654. The entire new JSP code is shown in Example 21-5.
Example 21-5 CredentialJSRPortletConfig.jsp <%@ page session="false" contentType="text/html" import="javax.portlet.*,credentialjsr.*" %> <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <portlet:defineObjects/> <DIV style="margin: 6px"> <H3 style="margin-bottom: 3px">Write to the PortletPreferences</H3> <DIV style="margin: 12px; margin-bottom: 36px"> <% /******** Start of sample code ********/ %> <% PortletPreferences preferences = renderRequest.getPreferences(); if( preferences!=null ) { String server = (String)preferences.getValue(CredentialJSRPortlet.SERVER_KEY,""); String port = (String)preferences.getValue(CredentialJSRPortlet.PORT_KEY,""); String path = (String)preferences.getValue(CredentialJSRPortlet.PATH_KEY,""); String attr = (String)preferences.getValue(CredentialJSRPortlet.ATTR_KEY,""); %> <FORM ACTION="<portlet:actionURL/>" METHOD="POST"> <TABLE cellspacing="3" cellpadding="3" border="0"> <tr> <td><LABEL for="<%=CredentialJSRPortlet.SERVER_TEXT%>">Server name:</LABEL></td> <td><INPUT name="<%=CredentialJSRPortlet.SERVER_TEXT%>" value="<%=server%>" type="text"/></td> </tr> <tr> <td><LABEL for="<%=CredentialJSRPortlet.PORT_TEXT%>">Port number:</LABEL></td>
659
<td><INPUT name="<%=CredentialJSRPortlet.PORT_TEXT%>" value="<%=port%>" type="text"/></td> </tr> <tr> <td><LABEL for="<%=CredentialJSRPortlet.PATH_TEXT%>">Servlet path:</LABEL></td> <TD><INPUT name="<%=CredentialJSRPortlet.PATH_TEXT%>" value="<%=path%>" type="text" size="50"/></TD> </tr> <tr> <td><LABEL for="<%=CredentialJSRPortlet.ATTR_TEXT%>">Servlet attribute name:</LABEL></td> <td><INPUT name="<%=CredentialJSRPortlet.ATTR_TEXT%>" value="<%=attr%>" type="text"/></td> </tr> <tr> <td colspan="2"><INPUT name="<%=CredentialJSRPortlet.CONFIG_SUBMIT%>" value="Save" type="submit"/></td> </tr> </TABLE> </FORM> <% } else { %>Error: PortletPreferences is null.<% } %> <% /******** End of sample code *********/ %> </DIV> </DIV>
19.Save and close the jsp file. 20.Finally, open CredentialJSRPortletView.jsp and add a new form when there are user credentials in the credential vault. For clarity, you will also modify the message to be displayed when there are no credentials found in the vault. Example 21-6 shows the new code.
Example 21-6 New form in updated CredentialJSRPortletView.jsp <%@ page session="false" contentType="text/html" import="java.util.*,javax.portlet.*,credentialjsr.*" %> <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <portlet:defineObjects/> <% CredentialJSRPortletSessionBean sessionBean = (CredentialJSRPortletSessionBean)renderRequest.getPortletSession().getAttribute (CredentialJSRPortlet.SESSION_BEAN);
660
%> <DIV style="margin: 6px"> <% if ( renderRequest.getAttribute("result") != null ) { %> <%=(String)renderRequest.getAttribute("result") %> <% } %> <H3 style="margin-bottom: 3px">Single sign-on sample</H3> The following user credentials have been retrieved from the credential vault.<BR> Portlet should use these credentials to log in to the backend systems on behalf of the user. <DIV style="margin: 12px; margin-bottom: 36px"> <% /******** Start of sample code ********/ %> <% int secretType = sessionBean.getSecretType(); String secretTypeName = "Unknown"; switch (secretType) { case CredentialJSRPortletSecretManager.SECRET_PORTLET_PRIVATE_SLOT: secretTypeName = "Portlet Private Slot"; break; case CredentialJSRPortletSecretManager.SECRET_SHARED_SLOT: secretTypeName = "Shared Slot"; break; case CredentialJSRPortletSecretManager.SECRET_ADMINISTRATIVE_SLOT: secretTypeName = "Administrative Slot"; break; case CredentialJSRPortletSecretManager.SECRET_SYSTEM_SLOT: secretTypeName = "System Slot"; break; case CredentialJSRPortletSecretManager.SECRET_JAAS_SUBJECT: secretTypeName = "JAAS Subject"; break; } %>Secret type is <B><%=secretTypeName%></B>.<BR><% String userId = (String)renderRequest.getAttribute(CredentialJSRPortlet.USERID); if( userId.length()>0 ) { String password = (String)renderRequest.getAttribute(CredentialJSRPortlet.PASSWORD); %> User id is <b><%=userId%></b>. <H3>Generate a prime number</H3> <FORM ACTION="<portlet:actionURL/>" METHOD="POST"> <LABEL for="<%=CredentialJSRPortlet.NUMBER%>">Enter the number of digits:</LABEL> <INPUT name="<%=CredentialJSRPortlet.NUMBER%>" type="text">
661
<INPUT name="<%=CredentialJSRPortlet.NUMBER_SUBMIT%>" value="Send" type="submit"/> </FORM> <% } else { %><H5>There are no credentials in the vault for this portlet.<% if( CredentialJSRPortletSecretManager.isWritable(sessionBean) ) { %> Use <B>edit mode</B>.</H5><% } } %> <% /******** End of sample code *********/ %> </DIV> </DIV>
662
Note: The portlet has executed the init method to initialize the Credential Vault Service and the doView method. Since there are no credentials yet, a message is displayed to indicate that the user should switch to Edit mode and enter the required credentials. That is, the user ID and password in this sample scenario. 5. Switch the portlet to Edit mode.
663
6. Optionally, you may want to remove the top portion of portlet Edit mode display in the CredentialJSRPortletEdit.jsp page. Skip this step if you do not want to do this.
Example 21-7 Updated CredentialJSRPortletEdit.jsp <%@ page session="false" contentType="text/html" import="java.util.*,javax.portlet.*,credentialjsr.*"%> <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <portlet:defineObjects/> <% CredentialJSRPortletSessionBean sessionBean = (CredentialJSRPortletSessionBean)renderRequest.getPortletSession().getAttribute (CredentialJSRPortlet.SESSION_BEAN); %> <DIV style="margin: 6px"> <H3 style="margin-bottom: 3px">Single sign-on settings</H3> <% if( CredentialJSRPortletSecretManager.isWritable(sessionBean) ) { /******** Start of sample code ********/ String userId = (String)renderRequest.getAttribute(CredentialJSRPortlet.USERID); String password = (String)renderRequest.getAttribute(CredentialJSRPortlet.PASSWORD); %>Set user credentials to be stored in the credential vault. <DIV style="margin: 12px; margin-bottom: 36px"> <FORM ACTION="<portlet:actionURL/>" METHOD="POST"> <LABEL for="<%=CredentialJSRPortlet.USERID%>">Enter user id:</LABEL><BR> <INPUT name="<%=CredentialJSRPortlet.USERID%>" value="<%=userId%>" type="text"><BR> <LABEL for="<%=CredentialJSRPortlet.PASSWORD%>">Enter password:</LABEL><BR> <INPUT name="<%=CredentialJSRPortlet.PASSWORD%>" value="<%=password%>" type="password"><BR> <INPUT name="<%=CredentialJSRPortlet.USER_SUBMIT%>" value="Save" type="submit"/> </FORM> <% /******** End of sample code *********/ %> </DIV> <% } else { %>This credential vault is read-only.<% } %>
664
<FORM ACTION="<portlet:renderURL portletMode="view"/>" METHOD="POST"> <INPUT NAME="back" TYPE="submit" VALUE="Back to view mode"> </FORM> </DIV>
7. Enter the following information: User ID field: user1 Password field: password1
8. Press the Save button. This will generate an action that will be checked by the processAction() method in the CredentialJSRPortlet class. The portlet returns to Edit mode, showing the credentials you entered. Click the Back to view mode button to return the portlet to View mode. 9. Make sure the WebSphere Test Environment running the secure servlet is running. Start this server if it is not running. 10.Since you are also running as administrator, switch to configure mode. You will find the default values stored in the PortletPreferences to access the back-end resources, check them out and return to View mode. The correct values should be: Server name: localhost Port number: 9080 Servlet path: /SecureServlet/PrimesGeneration Servlet attribute name: number.
665
11.Go back to View mode. Now the portlet displays a form to enter the number of digits to generate a prime number with this length. In View mode, enter a number of digits and click Send button. At the top of the portlet you will see the result generated by the PrimesGeneration servlet.
666
3. Organize the import statements as you did in the previous scenario. Choose com.ibm.misc.BASE64Encoder. Click Finish.
667
Note: The Sun.misc.Bas64Encoder class can also be used. In some specific cases this can be the correct choice. 4. Save and close the Java file. 5. Open the class CredentialJSRPortlet from the credentialjsr package. In the doView method, change the line:
HttpURLConnection connection = CredentialJSRPortletSecretManager.getConnectionUsingActiveObject(request, sessionBean, host, port, path.toString());
to
HttpURLConnection connection = CredentialJSRPortletSecretManager.getConnectionUsingPassiveObject(request, sessionBean, host, port, path.toString());
6. Save and close the Java file. 7. Restart the project and access the secure servlet again as described in 21.3.4, Running the portlet on page 662. The portlet should run exactly as it did in the previous section when using active credentials. This time, the getConnectionUsingPassiveObject method has access to the credentials. Note: Having access to credentials could be a security risk, so when possible, use always active credential objects.
668
22
Chapter 22.
669
670
3. The Portlet Project window will appear; enter HRPortlet for the name of the project. Click the Show Advanced >> button and change Target Server to WebSphere Portal V5.1 stub and Context Root to /HRPortlet. Click Next.
671
672
5. In the Features window, leave the default values. Click Next. 6. In the Portlet Settings window, click the Change code generations options checkbox. Remove the portlet word from the Portlet name, Portlet Title and Class Prefix fields, so they will look as in Figure 22-5 on page 674. Click Next.
673
7. Accept default values for the Event Handling window by clicking Next. 8. Accept default values for the Single Sign-on window by clicking Next. The Credential Vault is not used in this scenario. 9. Click Finish in the Miscellaneous window. Note: If this is the first time that Rational Application Developer is used, you will be prompted to switch to the Web perspective. Click Yes.
674
As a result of this process you should see, in the Project Explorer view, all the project artifacts created by Rational Application Developer, under Dynamic Web Projects HRPortlet.
675
1. Create the c:\HRproject\database directory. 2. Copy the following files to this directory: CreateCloudTable.bat Tables.sql 3. Edit the provided CreateCloudTable.bat file and make sure the paths for variables JAVA_HOME and DB2J_LIB point to the correct location. For example, in this sample scenario, these directories (java and lib) can be found at <RAD_root>\runtimes as shown in Example 22-1.
Example 22-1 Sample CreateCloudTable.bat file set JAVA_HOME=C:\Progra~1\IBM\Rational\SDP\6.0\runtimes\base_v51\java set DB2J_LIB=C:\Progra~1\IBM\Rational\SDP\6.0\runtimes\base_v51\cloudscape\lib set CLASSPATH=%DB2J_LIB%\db2j.jar;%DB2J_LIB%\db2jtools.jar;%DB2J_LIB%\db2jcview.jar ;%DB2J_LIB%\jh.jar %JAVA_HOME%\bin\java -Dij.connection.myconn=jdbc:db2j:WSSAMPLE;create=true -Dcloudscape.system.home=%DB2J_LIB% -ms16m -mx32m com.ibm.db2j.tools.ij Tables.sql
4. Save and close the file. 5. Open a command window and change to the directory where these files are located. 6. Execute CreateCloudTable.bat to create and populate the sample database (WSSAMPLE) as illustrated in Figure 22-8.
7. After executing the batch file, the WSSAMPLE folder is created in c:\HRproject\database.
676
3. Start Cloudview by executing the cview.bat file. 4. The Cloudview application starts. Select File Open....
5. In the directory c:\HRproject\database, select WSSAMPLE. Click Open. 6. You should see the tables and their content (data) by selecting them.
677
678
679
3. Click Finish.
680
3. Click New Connection... from the context menu to create a new connection. The Establish a connection to a database window will appear. 4. Type ConnHR as connection name. Click Next. 5. In the Specify connection parameters window, select the following values: a. Select database manager: V5.1 b. JDBC driver: Cloudscape Embedded JDBC Driver c. Enter the path where the database for this scenario was created. For example:
681
c:\HRProject\database\WSSAMPLE
d. Uncheck Create the database since you have already created the database. e. Leave the default value for the Class location field. f. As a user ID, enter db2admin. As a password, use db2admin also. Note: The user ID and password are optional in this sample scenario since Cloudscape does not verify it.
6. Click the Test Connection button. You should see a dialog box with the message Connection to WSSAMPLE is successful. Click OK.
682
Note: If you do not receive the successful message, make sure the values you entered in the Specify connection parameters window are correct and try again until you get a successful message indication. 7. Click Finish. 8. Click Yes to the question Do you want to copy the database metadata to a project folder?. 9. In the Copy to Project window, click the Browse button and select the database folder you previously created: /HRPortlet/WebContent/WEB-INF/database
10.Click Finish.
683
1. In the Data Definition view, right-click the statement folder under the sample WSSAMPLE database. 2. Select New Select Statement.
3. Enter SQLUtility for the Statement Name and click OK. 4. The Editor will open; right-click the Tables area. 5. Select Add Table... on the context window. 6. Select the APP.EMPLOYEE from the Table name pop-up and click OK.
7. On the Data Definition view, expand the Statement folder. 8. Right-click the SQLUtility statement. 9. Select Execute from the context menu.
684
685
2. For the Source Folder, enter HRPortlet/JavaSource. For the Name, enter utilities. 3. Click Finish to create the package.
686
2. Enter the following values for the Java Class Specification window: Source Folder: HRPortlet/JavaSource Package: utilities Name: SQLUtilities
3. In the Specify Runtime Database Connection Information window, accept the default value for the Use Driver Manager Connection option. Click Next.
687
688
5. Click Finish. 6. Optionally, you can review the two Java classes created under Java Resources/utilities.
689
Development
Rational Appliocation Developer v6 Sample portlet Cloudscape v5.1.6
Runtime
Portal Test Environment v5.1 Cloudscape v5.1.6
Run on Server
22.3.1 Overview
In this section, you will review and understand the sample scenario. You will import the code to use JDBC to interact with databases to the current HRPortlet project. This example shows how the JDBC interface is used to read information from a Cloudscape sample table. In your portlet Edit mode, you will provide the information needed to establish a connection with the database in order to retrieve the content in View mode. The sample scenario is illustrated in Figure 22-29 on page 691.
690
8
actionPerformed()
1
JDBC Database Utilities Bean WSSAMPLE
Database wssample user ID db2admin password db2admin SQL statement select * from jobs
doView
doEdit
9
JSP
4 5
JSP
Browser
2 View
Mode
7 3 6 Edit
Mode
Note: It is assumed that the database has already been created and populated and the sample portlet WAR file is available. The sequence flow for this sample scenario is as follows: 1. Initially, the doView method is executed. 2. A JSP is invoked in View mode to render the initial screen containing a welcome message telling the user to enter the database inquiry values in Edit mode. 3. The user clicks Edit to go into Edit mode. 4. The doEdit method executes and invokes a JSP (include). 5. The JSP renders the form. to fill the connection information. 6. The user enters the database name, user ID, password and SQL statement. Note: A user ID and password have not been implemented for this version of Cloudscape, therefore any user ID and password can be used 7. The user submits the request (post) to the previous mode (View mode). An action event is generated.
691
8. The actionPerformed() method executes and the following processes take place: a. The database, user ID, password, and SQL statement are extracted and sent to the JDBCPortletResults Bean. b. A connection object is created. c. A DBResults object is created. This object encapsulates the database inquiry. d. Database Utilities (another bean) is invoked to execute the actual database inquiry. e. Results are stored as a String in the request object. 9. The doView method executes again: a. Results are obtained in View mode. b. Results are rendered directly in View mode.
692
2. In the Select window drag down and select WAR file, click Next. 3. In the Import Resources from a WAR file window, enter the following values: a. WAR file: C:\LabFiles\JDBC\HR\HRPortlet.war b. Web project: HRPortlet c. Select the Overwrite existent resources without warning checkbox. Note: Initially you will see a different Web project such as HRPortlet1. This is because HRPortlet already exists. Change this value to HRPortlet and therefore have the overwrite option available.
693
a. It gets the parameters (database, user ID, password and SQL statement) from the request (PortletRequest). b. It sets these values in the HRPortletSessionBean.java bean.
Example 22-2 HRPortlet.java - actionPerformed() method ..... // ActionEvent handler String actionString = event.getActionString(); // Add action string handler here PortletRequest request = event.getRequest(); HRPortletSessionBean sessionBean = getSessionBean(request); if (EDIT_ACTION.equals(actionString)) { String dbname = (String) request.getParameter("dbname"); String userid = (String) request.getParameter("userid"); String password = (String) request.getParameter("password"); String sqlstring = (String) request.getParameter("sqlstring"); sessionBean.setDbName(dbname); sessionBean.setUserId(userid); sessionBean.setPassword(password); sessionBean.setSqlString(sqlstring); } if (FORM_ACTION.equals(actionString)) { // Set form text in the session bean sessionBean.setFormText(request.getParameter(TEXT)); } } .....
2. The doEdit() method in the portlet does the following: a. It creates an instance of HRPorteltEditBean bean. b. It adds the action which will be executed when the form is submitted and sets this value in the Edit mode bean. c. When the database, user ID, password and SQL statement values exist for the session, it will also store these values in the Edit mode bean. d. The Edit mode bean is passed in the request to the HRPortletEdit.jsp.
Example 22-3 HRPortlet.java - doEdit() method ..... HRPortletEditBean editBean = new HRPortletEditBean(); editBean.setFormAction(EDIT_ACTION);
694
HRPortletSessionBean sessionBean = getSessionBean(request); String sqlstring = sessionBean.getSqlString(); if (sqlstring != null) { String dbname = sessionBean.getDbName(); String userid = sessionBean.getUserId(); String password = sessionBean.getPassword(); editBean.setDbname(dbname); editBean.setPassword(password); editBean.setUserid(userid); editBean.setSqlstring(sqlstring); } request.setAttribute(EDIT_BEAN, editBean); // Invoke the JSP to render getPortletConfig().getContext().include(EDIT_JSP + getJspExtension(request),request,response); .....
3. The doView() method does the following: a. It checks if there is a HRPortletSessionBean in the session. The first time this method is invoked, the session bean will be null. b. If there is an HRPortletSessionBean in session, it gets the database, user ID, password and SQL sentence values stored in it and creates an instance of HRPortletViewBean. c. The execute() method of SQLUtilities class is called to execute the SQL statement and the result is stored in the View mode bean by calling populateData() method in the SQLUtilities class. d. The View mode bean is passed in the request to HRPortletView.jsp.
Example 22-4 HRPortlet.java - doView() method ..... // Check if portlet session exists HRPortletSessionBean sessionBean = getSessionBean(request); if (sessionBean == null) { response.getWriter().println("<b>NO PORTLET SESSION YET</b>"); return; } String sqlstring = sessionBean.getSqlString(); if (sqlstring != null && !sqlstring.equals("")) { String dbname = sessionBean.getDbName(); String userid = sessionBean.getUserId(); String password = sessionBean.getPassword();
695
// Make a view mode bean HRPortletViewBean viewBean = new HRPortletViewBean(); SQLUtilities sqlUtility = new SQLUtilities(); try { sqlUtility.execute(userid, password, dbname, sqlstring); } catch (SQLException e) { e.printStackTrace(); } sqlUtility.populateData(viewBean); request.setAttribute(VIEW_BEAN, viewBean); } // Invoke the JSP to render getPortletConfig().getContext().include(VIEW_JSP + getJspExtension(request),request,response); .....
4. The HRPortletEdit.jsp is used to prompt for the database, user ID, password and SQL statement parameters. Double-click this file (located in /Web Content/hrportlet/jsp/html/ folder) to view its source code. a. Notice that the database name, user ID, password and SQL fields all have the HTML tag value=<%=editBean.getXXX()%>. This allows the JSP to display the persistent data stored in the bean.
Example 22-5 HRPortletEdit.jsp (Edit mode sample code) ..... <% HRPortletEditBean editBean = (HRPortletEditBean) portletRequest.getAttribute("hrportlet.HRPortletEditBean"); %> <HTML> <BODY> <h4><FONT size="3" face="Arial">Complete with your database information and click Submit</FONT></h4> <FORM method="post" action="<portletAPI:createReturnURI><portletAPI:URIAction name='<%=editBean.getFormAction()%>'/></portletAPI:createReturnURI>"> <TABLE border="0"> <TBODY> <TR> <TD width="805"> <TABLE border="0"> <TBODY> <TR>
696
<TD><FONT face="Arial" size="-1"><B>Database :</B></FONT></TD> <TD><INPUT type="text" value="<%=editBean.getDbname()%>" name='<portletAPI:encodeNamespace value="dbname"/>' size="20"></TD> .....
5. Once the database parameters are collected, the request is processed by the actionPerformed() and doView() methods. The results are displayed by HRPortletView.jsp. Double-click this file to view its source code. This JSP checks if there is a HRPortletViewBean.java bean in the request. If there is not then it displays a message to go to Edit mode and configure an SQL statement. If the bean exists then the result of the query is displayed.
Example 22-6 HRPortletView.jsp (View mode sample code) ..... <% HRPortletViewBean viewBean = (HRPortletViewBean)portletRequest.getAttribute(HRPortlet.VIEW_BEAN); HRPortletSessionBean sessionBean = (HRPortletSessionBean)portletRequest.getPortletSession().getAttribute(HRPortlet .SESSION_BEAN); %> <% if (viewBean == null) { %> <HTML> <BODY> <B>This is the JDBC Sample Portlet. Go to Edit mode and configure a SQL query</B> <% } else { try { com.ibm.db.beans.DBSelect results = viewBean.getResultFromDatabase(); %> .....
6. The classes SQLUtilities.java and SQLUtilitiesRow.java have been generated in the data perspective. In the data perspective, you can create a connection to database, import the tables, create SQL statements and generate Java beans for the statements as you did in previous sections. These classes contain the methods to execute and retrieve information from database. The execute() method is called by the doView() method of HRPortlet.java when there is a statement in the session and the information retrieved is stored in the View mode bean by calling the populateData() method of SQLUtilities.java.
697
Example 22-7 SQLUtilities.java public void execute(String userid,String password,String url,String command) throws SQLException { try { select.setUrl(url); select.setCommand(command); select.setUsername(userid); select.setPassword(password); select.execute(); } // Free resources of select object. finally { select.close(); } } public void populateData(HRPortletViewBean viewBean) { viewBean.setResultFromDatabase(select); }
698
4. In Edit mode, the JSP for this mode renders the form requesting the database parameters. For the first time, enter the following information and select Submit: Database: jdbc:db2j:C:\HRproject\database\WSSAMPLE User: db2admin Password: db2admin SQL statement: select * from jobs
5. Clicking Submit generates a creatEReturnURI and the portlet will return to View mode showing the results of your query against the WSSAMPLE database.
699
6. Enter Edit mode again. Notice that the database name, user ID, password and SQL statement, which have been stored in session object, are persistent.
700
7. Enter a new query, for example select * from survey, and click Submit. You will be presented with the results of your new query.
701
702
23
Chapter 23.
703
Portlet
Note: For more details about data source, see the WebSphere Portal Server V5 InfoCenter. When an application uses a version 5 data source, the data source will use the JCA connector architecture to get to the relational database. Although there is no difference between the existing WebSphere JDBC support and the new support
704
in terms of application development, there will be some connection behavior changes because of different architectures. For example: The connection factory delegates the request to a connection manager. The connection manager looks for an instance of a connection pool in the application server. If no connection pool is available, then the manager uses the ManagedConnectionFactory to create a physical (nonpooled) connection.
2. In the Select a wizard window, select Portlet Project (JSR 168). Click Next.
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
705
3. The Portlet Project window will appear; enter HRPortlet168 for the name of the project. Click Show Advanced >> and change Target Server to WebSphere Portal V5.1 stub and Context Root to /HRPortlet168. Click Next.
706
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
707
5. In the Features window, leave the default values. Click Next. 6. In the Portlet Settings window, click the Change code generations options checkbox. Remove the portlet word from the Portlet name, Portlet Title and Class Prefix fields, so they will look as in Figure 23-6 on page 709. Click Next.
708
7. Accept default values for the Event Handling window by clicking Next. 8. Accept default values for the Single Sign-on window by clicking Next. The Credential Vault is not used in this scenario. 9. Click Finish in the Miscellaneous window. Note: If this is the first time that Rational Application Developer is used, you will be prompted to switch to the Web perspective. Click Yes.
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
709
As a result of this process, you should see, in the Project Explorer view, all the project artifacts created by Rational Application Developer, under Dynamic Web Projects HRPortlet168.
710
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
711
3. Click Finish.
712
3. Click New Connection... from the context menu to create a new connection. The Establish a connection to a database window will appear. 4. Type ConnHR as the connection name. Click Next. 5. In the Specify connection parameters window, select the following values: a. Select database manager: V5.1 b. JDBC driver: Cloudscape Embedded JDBC Driver c. Enter the path where the database for this scenario was created. For example:
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
713
c:\HRProject168\database\WSSAMPLE
d. Uncheck Create the database since you already created the database. e. Leave the default value for Class location field. f. As a user ID , enter db2admin. As a password, use db2admin also. Note: The user ID and password are optional in this sample scenario since Cloudscape does not verify it.
6. Click the Test Connection button. You should see a dialog box with the message Connection to WSSAMPLE is successful. Click OK.
714
Note: If you do not receive the successful message, make sure the values you entered in the Specify connection parameters window are correct and try again until you get a successful message indication. 7. Click Finish. 8. Click Yes to the question Do you want to copy the database metadata to a project folder?. 9. In the Copy to Project window, click the Browse button, and select the database folder you previously created: /HRPortlet168/WebContent/WEB-INF/database
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
715
10.Click Finish.
1. In the Data Definition view, right-click the Statement folder under the sample WSSAMPLE database. 2. Select New Select Statement.
3. Enter SQLUtility for the Statement Name and click OK. 4. The Editor will open; right-click the Tables area.
716
5. Select Add Table... in the context window. 6. Select the APP.EMPLOYEE from the Table name pop-up and click OK.
7. On the Data Definition view, expand the Statement folder. 8. Right-click the SQLUtility statement. 9. Select Execute from the context menu.
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
717
718
2. For the Source folder, enter HRPortlet168/JavaSource. For the Name, enter utilities. 3. Click Finish to create the package.
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
719
720
2. Enter the following values for the Java Class Specification window: Source Folder: HRPortlet168/JavaSource Package: utilities Name: SQLUtilities
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
721
3. In Specify Runtime Database Connection Information window, accept the default value for the Use Driver Manager Connection option. Click Next.
722
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
723
5. Click Finish. 6. Optionally, you can review the two Java classes created under Java Resources/utilities.
724
Development
Rational Appliocation Developer v6 Sample portlet Cloudscape v5.1.6
Runtime
Portal Test Environment v5.1 Cloudscape v5.1.6
Run on Server
Figure 23-26 Development workstation
23.4.1 Overview
In this section, you will review and understand the sample scenario. You will import the java code to use JDBC to interact with databases to the current HRPortlet168 project. This example shows how the JDBC interface is used to read information from a Cloudscape sample table. In your portlet Edit mode, you will provide the statement that is needed to establish a connection with the database in order to retrieve the content in View mode. The sample scenario is illustrated in Figure 23-27 on page 726.
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
725
8
processAction()
1
JDBC Data Source WSSAMPLE
Database wssample user ID db2admin password db2admin SQL statement select * from jobs
doView
doEdit
9
JSP
4 5
JSP
Browser
2 View
Mode
6 Edit
Mode
Note: It is assumed that the database has already been created and populated and the sample portlet WAR file is available. The sequence flow for this sample scenario is as follows: 1. Initially, the doView method is executed. 2. A JSP is invoked in View mode to render the initial screen containing a welcome message telling the user to enter the database inquiry values in Edit mode. 3. The user clicks Edit icon to go into Edit mode. 4. The doEdit method executes and invokes a JSP (include). 5. The JSP renders the form to fill the connection information. 6. The user enters the user ID, password and SQL statement. Note: A user ID and password have not been implemented for this version of Cloudscape, therefore any user ID and password can be used. 7. The user submits the request (post). An actionURL is generated.
726
8. The processAction() method executes and the following processes take place: a. The user ID, password, and SQL statement are extracted and sent to the HRPortlet168SessionBean object. b. If the PortletMode is Edit mode, then change the PortletMode to VIEW mode, this execute the doView method. 9. In the doView: a. A DBSelect object is created and initialized with the DataSource. b. The user ID, password, and SQL statement are extracted and sent to the SQLUtilities through the execute method. c. Results are stored in the DBSelect object. d. A HRPortlet168ViewBean is sent to the SQLUtility through the populateData method. a. The BDSelect object is added to the HRPortlet168ViewBean sent in the previous activity. b. Finally, the HRPortlet168ViewBean is rendered in the request to the JSP.
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
727
A new class HRPortlet168ViewBean.java (Bean) to store the DBSelect object To import the WAR file, follow these steps: 1. Select File Import... 2. In the Select window, drag down and select WAR file, then click Next. 3. In the Import Resources from a WAR file window, enter the following values: a. WAR file: C:\HRPortlet168\5.1\HRPortlet168.war b. Web project: HRPortlet168 c. Select the Overwrite existent resources without warning checkbox. Note: Initially, you will see a different Web project such as HRPortlet1681. This is because HRPortlet already exists. Change this value to HRPortlet168 to have the overwrite option available.
728
2. The doEdit() method in the portlet does the following: a. It creates an instance of HRPortelt168EditBean bean.
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
729
b. When the user ID, password and SQL statement values exist for the session, it will also store these values in the Edit mode bean. c. The Edit mode bean is passed in the request to the HRPortlet168Edit.jsp.
Example 23-2 HRPortlet168.java - doEdit() method ..... HRPortlet168EditBean editBean = new HRPortlet168EditBean(); HRPortlet168SessionBean sessionBean = getSessionBean(request); String sqlstring = sessionBean.getSqlString(); if (sqlstring != null) { String dbname = sessionBean.getDbName(); String userid = sessionBean.getUserId(); String password = sessionBean.getPassword(); editBean.setDbname(dbname); editBean.setPassword(password); editBean.setUserid(userid); editBean.setSqlstring(sqlstring); } request.setAttribute(EDIT_BEAN, editBean); // Invoke the JSP to render PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(getJspFilePath(request, EDIT_JSP)); rd.include(request,response); .....
3. The doView() method does the following: a. It checks if there is a HRPortlet168SessionBean in the session. The first time this method is invoked, the session bean will be null. b. If there is an HRPortlet168SessionBean in session, it gets the user ID, password and SQL sentence values stored in it and creates an instance of HRPortlet168ViewBean. c. The execute() method of SQLUtilities class is called to execute the SQL statement and the result is stored in the View mode bean by calling populateData() method in the SQLUtilities class. d. The View mode bean is passed in the request to HRPortlet168View.jsp.
Example 23-3 HRPortlet168.java - doView() method ..... //Set the MIME type for the render response response.setContentType(request.getResponseContentType());
730
// Check if portlet session exists HRPortlet168SessionBean sessionBean = getSessionBean(request); if( sessionBean==null ) { response.getWriter().println("<b>NO PORTLET SESSION YET</b>"); return; } String sqlstring = sessionBean.getSqlString(); if (sqlstring != null && !sqlstring.equals("")) { String dbname = sessionBean.getDbName(); String userid = sessionBean.getUserId(); String password = sessionBean.getPassword(); // Make a view mode bean HRPortlet168ViewBean viewBean = new HRPortlet168ViewBean(); SQLUtilities sqlUtility = new SQLUtilities(); try { sqlUtility.execute(userid, password, sqlstring); } catch (SQLException e) { e.printStackTrace(); } sqlUtility.populateData(viewBean); request.setAttribute(VIEW_BEAN, viewBean); } // Invoke the JSP to render PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(getJspFilePath(request, VIEW_JSP)); rd.include(request,response); .....
4. The HRPortlet168Edit.jsp is used to prompt for the user ID, password and SQL statement parameters. Double-click this file (located in /Web Content/hrportlet168/jsp/html/ folder) to view its source code. a. Notice that the user ID, password and SQL fields all have the HTML tag value=<%=editBean.getXXX()%>. This allows the JSP to display the persistent data stored in the bean.
Example 23-4 HRPortlet168Edit.jsp (Edit mode sample code) ..... <portlet:defineObjects/> <% HRPortlet168EditBean editBean = (HRPortlet168EditBean)renderRequest.getAttribute(HRPortlet168.EDIT_BEAN); %> <HTML> <BODY> <h4><FONT size="3" face="Arial">Complete with an SQL Statement information and click Submit</FONT></h4>
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
731
<FORM method="post" action="<portlet:actionURL/>"> <TABLE border="0"> <TBODY> <TR> <TD width="805"> <TABLE border="0"> <TBODY> <TR> <TD></TD> <TD></TD> <TD rowspan="5"><IMG src='<%=response.encodeURL(request.getContextPath() + "/images/database.gif")%>'></TD> <TD width="476" valign="top"><FONT size="2" face="Arial"> This can be use as default values</FONT></TD> </TR> <TR> <TD><FONT face="Arial" size="-1"><B>User :</FONT></B></FONT></TD> <TD><INPUT type="text" value="<%=editBean.getUserid()%>" name="userid" size="20"></TD> .....
5. Once the database parameters are collected, the request is processed by the processAction() and doView() methods. The results are displayed by HRPortlet168View.jsp. Double-click this file to view its source code. a. This JSP check if there is a HRPortlet168ViewBean.java bean in the request. If there is not then displays a message to go to Edit mode and configure and SQL statement. If the bean exists then the result of the query is displayed.
Example 23-5 HRPortlet168View.jsp (View mode sample code) ..... <portlet:defineObjects/> <% HRPortlet168SessionBean sessionBean = (HRPortlet168SessionBean)renderRequest.getAttribute(HRPortlet168.SESSION_BEAN); HRPortlet168ViewBean viewBean= (HRPortlet168ViewBean)renderRequest.getAttribute(HRPortlet168.VIEW_BEAN); %> <% if (viewBean == null) { %> <HTML> <BODY> <B>This is the JDBC Sample Portlet. Go to Edit mode and configure a SQL
732
6. The classes SQLUtilities.java and SQLUtilitiesRow.java have been generated in the data perspective. In the data perspective, you can create a connection to database, import the tables, create SQL statements and generate Java beans for the statements as you did in previous sections. These classes contain the methods to execute and retrieve information from database. The execute() method is called by the doView() method of HRPortlet168.java when there is a statement in the session and the information retrieved is stored in the View mode bean by calling the populateData() method of SQLUtilities.java.
Example 23-6 SQLUtilities.java public void execute(String userid, String password, String command) throws SQLException { try { select.setUsername(userid); select.setPassword(password); select.setCommand(command); select.execute(); } // Free resources of select object. finally { select.close(); } } public void populateData(HRPortlet168ViewBean viewBean) { viewBean.setResultFromDatabase(select); }
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
733
2. Double-click the WebSphere Portal V5.1 Test Environment. This will open the Server config editor. 3. Select the Data source tab. 4. In the Server Settings section, select Cloudscape JDBC Driver from the JDBC provider list. 5. Click Add... from Data source defined in the JDBC provider selected above.
6. In the Create a Data source window, select Cloudscape JDBC provider and click Next. 7. Enter the following values for the Modify Data Source window. Name: jdbc/WSSample JDBC Name: WSSample
734
Figure 23-30
8. Click Next. 9. Select databaseName from Resource Properties, enter C:\HRproject\database\WSSAMPLE as the value (or the path where the database is stored).
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
735
10.Click Finish. 11.Close the Server configuration editor. 12.A window will prompt you if you want to save changes. Select Yes. Important: Close the connection created on the previous section, otherwise this can cause conflict error on the WebSphere Portal Server V5 Test Environment since this create the same connection to the database through the data source just created.
736
Note: You can also check the Set server as project default checkbox, so you will not be prompted again when you run the portlet. 3. The portlet executes and you will see it in the built-in browser. The View mode is shown with a message indicating that you have to provide an SQL query; you will also need to switch the portlet into Edit mode (as indicated in Figure 23-32) so you can enter these values.
4. In Edit mode, the JSP for this mode renders the form requesting the database parameters. For the first time, enter the following information and select Submit: User: db2admin Password: db2admin SQL statement: select * from jobs
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
737
5. Clicking Submit executes the processAction and the portlet will return to View mode showing the results of your query against the WSSAMPLE database.
6. Enter Edit mode again. Notice that the database name, user ID, password and SQL statement, which have been stored in session object, are persistent.
738
7. Enter a new query, for example select * from survey, and click Submit. You will be presented with the results of your new query.
Chapter 23. Accessing JDBC databases using Data Source in standard portlets
739
740
24
Chapter 24.
741
24.1 Overview
Cooperative portlets subscribe to a model for declaring, publishing and sharing information with each other using the WebSphere Portal property broker. At runtime, the property broker matches the data type of output properties from a source portlet with the data type of input properties from one or more target portlets. The transfer of the property can be initiated using the Portlet Wiring Tool. A user creates a persistent connection between two portlets, called a wire. For IBM portlets a user can launch a Click-to-Action event from an icon on the source portlet. The icon presents a pop-up menu containing the list of targets for the action. Selecting such a menu item results in the execution of the actionPerformed() method on a target cooperative portlet. Figure 24-1 illustrates an example of such a cooperative portlet menu.
In Figure 24-1, one piece of information, for example a department number, can be sent to a target portlet displaying all employees from the selected department number or to a portlet displaying detailed information of the selected department. In addition, the cooperative portlet technology also enables the broadcast of data to multiple portlets by sending multiple property values with only one click. The transfer of properties can be saved as wires using the Ctrl key. So the next time the user clicks the icon, the saved menu selection is used without prompting the user. Note: An IBM portlet cannot be wired to a JSR 168 compliant portlet and vice versa.
742
Portlet messaging versus cooperative portlets In general, both portlet messaging and cooperative portlets can be used to share data between two or more portlets. The most important difference is that cooperative portlets are more loosely coupled than portlets using messaging. Cooperative portlets do not have to know the name of the target portlet even if they do not broadcast data. The matching of source and target portlets is done at runtime based on registered properties and actions. Cooperative portlets can also include a menu with a list of executable portlet actions. For this menu, no additional programming is needed because it is part of the C2A tag library. It is recommended using cooperative portlets instead portlet messaging
Source Portlet
Output Properties
Target Portlet
Input Properties
Match
Figure 24-2 The property broker matches input and output properties
Target portlets optionally provide actions to process the properties that it receives. There is no difference between an action initiated by the portlet itself as mentioned in Chapter 6, IBM Portlet API portlet development on page 203 and an action initiated by a source cooperative portlet. Cooperative portlets can be source portlets, target portlets, or both. Source portlets identify to the property broker properties which they are able to share with other portlets. Target portlets identify to the property broker actions which are able to process properties contributed by other portlets.
743
Update portlet.xml
or
Create WSDL
4. Publish Properties
or
In general terms, there are two steps at runtime to establish cooperative portlet communication: 1. All properties must be registered with the cooperative broker. This can be done by using the declarative approach which includes the creation of a Web
744
Service Description Language (WSDL) file and the configuration of the portlet deployment descriptor. To register properties programmatically, you can use the property broker API. Please note that registration of properties can only be done during the event phase of the request-response cycle. 2. Notify the property broker about property changes. The easiest way to achieve this is to include the encodeProperty tag in your JavaServer Page. As an alternative, in the programmatic approach, you will use the changedProperties() method to publish properties. Also, in the programmatic approach, you have to configure a wire before you publish properties. Figure 24-4 shows how to develop target portlets.
Update portlet.xml
or
Create WSDL
or
To get information about changed properties, target portlets register properties and, optionally, actions with the property broker. When using the declarative approach both properties and actions are always registered. When using the programmatic approach, you can register properties without any actions. In addition, during runtime process such programmatic target portlets are notified about property changes by using the setProperties method from the PropertyListener interface instead of the actionPerformed method.
745
be specified as well as a specific value corresponding to an instance of this type. If you want to use wires source portlets, you must register properties by using a declarative or programmatic approach. Target portlets associate their actions with an input property which has been declared as an XML type. The actions are declared using WSDL, with a custom binding extension which specifies the mapping from the abstract action declaration to the actual action implementation. Associated with each action is a single input parameter described by an XML type and zero or more output parameters, each described by an XML type. Each input or output parameter encapsulates exactly one property. The input property's type is used for matching the action to sources, and its value is filled in when the end user triggers the action using Click-to-Action. The output parameters, if specified, are used to automatically trigger other compatible actions (ones which can consume the same type) on other wired portlets every time the action executes (this may be used to trigger chains of related actions). Note: The location of the WSDL file is configured as a portlet parameter.
Note: You can download a sample code available as additional materials using cooperative portlets with struts. See Appendix A, Additional material on page 1003.
24.2.3 Internationalization
To support translation for captions and descriptions associated with shared properties, portlets must provided resource bundles in the appropriate location in the WAR file. For IBM portlets, the resource file name to be used can be
746
specified using a configuration parameter in the portlet.xml file called c2a-nls-file. The value should be the file name, including the package but omitting the .properties suffix. See Example 24-4 on page 763. For portlets using the programmatic approach for registration, setTitleKey() and setDescriptionKey() methods can be used to set caption and descriptions respectively.
24.3.1 Description
This sample scenario provides step-by-step exercises to create a portlet project to work as a Click-to-Action source portlet. You will also create a portlet project to act as a source Click-to-Action portlet. You will create, deploy and run the portlet application. This exercise will allow you to understand the techniques used to develop portlets with Click-to-Action features using the C2A declarative approach. Cooperative portlets subscribe to a model for declaring, publishing, and sharing information with each other using the WebSphere Portal property broker. Portlets subscribe to the broker by publishing typed data items, or properties, that they can share, either as a provider or as a recipient. The portlet that provides a property is called the source portlet. The properties that the source portlet publishes are called output properties. The portlet that receives a property is called the target portlet. The properties that are received by the target are called input properties. The target portlets optionally provide actions to process the properties that they receive. Action processing in target portlets does not need to distinguish between an action initiated within its own portlet area and an action initiated by the transfer of a portlet property value. Each action is associated with a single
747
input parameter and zero or more output parameters, which provide information to the action about the objects in which the property value should be bound, such as the request or the session. Each parameter is associated with exactly one property. Parameters associated with input properties are called input parameters, while those associated with output properties are called output parameters. Instead of actions, the target portlet can receive property changes directly through the PropertyListener interface. At runtime, the property broker matches the data type of output properties from a source portlet with the data type of input properties from one or more target portlets. If a match is determined, the portlets are capable of sharing the property. The actual transfer of the property can be initiated by one of the following methods: A user launches a Click-to-Action event from an icon on the source portlet. The icon presents a pop-up menu containing the list of targets for the action. After the user selects a specific target, the property broker delivers the data to the target in the form of the corresponding portlet action. Using the Click-to-Action delivery method, users can transfer data with a simple click from a source portlet to one or more target portlets, causing the target to react to the action and display a new view with the results. The user can also broadcast the property to all portlets on the page that have declared an action associated with a matching input property. A user holds down the Ctrl key while clicking an action and chooses to have the selection saved persistently as a connection between two portlets, called a wire. If a wire is present the next time the user clicks the icon, no selection menu is shown. Instead, the wired action(s) is/are automatically fired. Subsequent updates to that property are transferred without further deliberate user choice. Wires can also be created using the Portlet Wiring Tool. The source portlet can perform a programmatic publish of properties to the broker when it determines that property values have changed. Such property values are transferred to the target(s) only if wires have been created. Cooperative portlets can be source portlets, target portlets, or both. Source portlets identify to the property broker properties which they are able to share with other portlets. Target portlets identify to the property broker actions which are able to process properties contributed by other portlets.
748
Source portlet
JSP 2.2 Process JSP tag 2.3 Perform match, generate menu code C2A Broker Wrapper
Target portlet
Action WSDL 2.1 Render view 1 Register actions 6 Process action WebSphere Portal Core 3 Return response Browser S 4 T User chooses action from menu
Wrapper
5 Send request
The sequence flow for this sample scenario is as follows: 1. At portlet initialization time, the C2A wrapper processes any action WSDL file associated with the application portlet and registers the actions with the C2A broker. 2. During the render phase of a request cycle, JSPs associated with C2A source portlets are processed. The custom C2A tags produce calls to the C2A broker, which examines the type information to determine matching actions. The broker generates additional code to create an icon to be used to display a pop-up menu of actions, and adds code to dispatch actions on portlets upon user selection from the menu. 3. After all render phase portlet callbacks are complete, the WebSphere Portal core assembles the response page and returns it to the client (for example, a browser). 4. When the user clicks the C2A icon for a source, he or she sees a menu of compatible actions (on the page) and selects one.
749
5. The client (for example, a browser) generates a new request containing the chosen source and action information and sends it to the WebSphere Portal Server. 6. The WebSphere Portal Core delivers the action to the target portlet. The action is intercepted by the wrapper, which may interact with the broker to further process the request before delivering the action to the target. All portlet actions are intercepted by the wrapper; however, actions which are invoked through direct interaction with the portlet (as opposed to interaction through the C2A menus) are passed through transparently to the portlet. In more advanced scenarios, such as the broadcast and scatter mentioned earlier, there will be more interactions between the wrappers and the broker to determine the appropriate target set and deliver the right data to the targets.
b. The original portlet application class should also be specified in the c2a-application-portlet-class initialization parameter. For example:
<init-param> <param-name>c2a-application-portlet-class</param-name> <param-value>hrportlet.HRPortlet</param-value> </init-param>
4. You will update the JSP for View mode to include Click-to-Action menus. Figure 24-6 on page 751 illustrates the source cooperative portlet for this sample scenario.
750
C2A Broker
751
3. In the next window, select Basic portlet as the Portlet Type. Click Next. 4. In the Features window, leave all Web project features unchecked. Click Next. 5. In the Portlet Settings window, select Change code generation options and enter HRPortlet as the Class prefix instead of HRPortletPortlet. Click Next. 6. In the Event Handling window, check Enable cooperative source and Add Click-to-Action taglib. Uncheck the rest of the options.
752
7. Click Next until the last window where you have to check Add Edit mode in Additional modes. Click Finish.
753
2. Select web.xml under the HRPortlet\WebContent\WEB-INF folder and open it. Select the Servlets tab and check the following values: Servlet class: com.ibm.wps.pb.wrapper.PortletWrapper The value of Initialization param c2a-application-portlet-class is the portlet name including the package: hrportlet.HRPortlet
754
4. In the Project Explorer, expand HRPortlet\WebContent\hrportlet\jsp\html and double-click HRPortletView.jsp. You will see the c2a.tld taglib included in the file: <%@ taglib uri="/WEB-INF/tld/c2a.tld" prefix="C2A" %>
755
2. In the JSP editor, switch to the Source tab. Note: Source portlets can publish their output properties by inserting tags from a custom JSP library in their JSPs. A JSP tag library is provided to allow source properties to be identified in JSPs. 3. In the third line of this JSP, include the following line:
<%@ taglib uri="/WEB-INF/tld/c2a.tld" prefix="C2A" %>
4. Two JSP tags can be used to declare output properties in the source portlet: <c2a:encodeProperty/> Uses source data and type information to insert markup that displays the icon, generating a pop-up menu. <c2a:encodeProperties/>
756
Used to enclose normal HTML markup and one or more encodeProperty tags with the markup. This tag is provided to support the scatter scenario, where a user can optionally send more than one unit of data to target portlets. You will now declare the output properties. Scroll down to the following lines:
<TD> <P><%=results.getCacheValueAt(row, col)%></P> </TD>
Note: As you can see, the table column name is used as an output property type. Therefore, a target portlet using the specified namespace should provide an inbound property with this name. Save all your files (you can use Ctlr-S). Note: At this time, you have your source cooperative portlet enabled for Click-to-Action but you will not be able to establish a wire between source and target portlets.
Implementing a wire
To prepare the source cooperative portlet to establish a persistent connection with the target cooperative portlet do the following steps: 1. Instead of inserting the C2A:encodeProperty tag manually, use the Palette panel provided by Rational Application Developer. This will create a wsdl file and update the portlet.xml to specify the path for that wsdl file. You need to open the HRPortletView.jsp to see the Palette contents. Figure 24-13 on page 758 shows this Palette.
757
In HRPortletView.jsp scroll down to the following lines and locate the cursor after the <P> tag:
<TD> <P><%=results.getCacheValueAt(row, col)%></P> </TD>
2. Right-click Click to Action Output Property and click Insert in the popped menu.
3. The window illustrated in Figure 24-15 on page 759 appears. Enter the following values: a. Data type: DEPT_NO b. Namespace: http://www.ibm.com/wps/c2a/examples/hrdetails c. Source portlet: HRPortlet portlet Click OK.
758
Change the HRPortletView.jsp code to display the C2A menu in the DEPT_NO column. Example 24-1 shows the snippet code for jsp, note that some attributes have been modified.
Example 24-1 Snippet code for jsp for(int col=1; col<=results.getColumnCount(); col++) { %> <TD><P> <% if (results.getColumnName(col).equalsIgnoreCase("DEPT_NO")) { %> <C2A:encodeProperty name="DEPT_NOParam" namespace="http://www.ibm.com/wps/c2a/examples/hrdetails" type="DEPT_NO" value="<%=results.getCacheValueAt(row, col).toString()%>" /> <% } %> <%=results.getCacheValueAt(row, col)%> </P></TD> <% }
5. Under the HRPortlet\WebContent\wsdl folder, you can see the HRPortletportlet.wsdl generated. Open this file and modify its content as indicated in Example 24-2.
Example 24-2 WSDL file associated to source cooperative portlet <?xml version="1.0" encoding="UTF-8"?> <definitions name="HRPortletportlet_Service" targetNamespace="http://www.ibm.com/wps/c2a/examples/hrdetails" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:portlet="http://www.ibm.com/wps/c2a"
759
xmlns:tns="http://www.ibm.com/wps/c2a/examples/hrdetails" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <types> <xsd:schema targetNamespace="http://www.ibm.com/wps/c2a/examples/hrdetails"> <xsd:simpleType name="DEPT_NO"> <xsd:restriction base="xsd:string"></xsd:restriction> </xsd:simpleType> </xsd:schema> </types> <message name="DEPT_NO_Response"> <part name="DEPT_NO_Output" type="tns:DEPT_NO" /> </message> <portType name="HRPortletportlet_Service"> <operation name="HRPortletportlet"> <output message="tns:DEPT_NO_Response" /> </operation> </portType> <binding name="HRPortletportlet_Binding" type="tns:HRPortletportlet_Service"> <portlet:binding /> <operation name="HRPortletportlet"> <portlet:action type="simple"/> <output> <portlet:param name="DEPT_NOParam" partname="DEPT_NO_Output" boundTo="request-parameter" caption="Department ID" description="Department ID"/> </output> </operation> </binding> </definitions>
The wsdl file registers the output property DEPT_NOParam. The value simple of the type attribute in the <portlet:action> element indicates a simple portlet action String is used. 6. If you open the portlet.xml file, you will also see that a reference to wsdl file has been included as a parameter under the concrete portlet application.
Example 24-3 Referencing wsdl file in portlet.xml <config-param> <param-name>c2a-action-descriptor</param-name> <param-value>/wsdl/HRPortletportlet.wsdl</param-value> </config-param>
760
view.jsp
C2A Broker
portlet xml C2A Wrapper web.xml
wsdl
Employee Details
view.jsp
To create the target portlet project, proceed as follows: 1. In Rational Application Developer, select File New Portlet Project.
761
2. In the Create a Portlet Project window, enter a project name of EmployeeDetailsPortlet and select WebSphere Portal V5.1 as the target server. Click Next. 3. In the Portlet Type window, select the Basic portlet. Click Next. 4. In the next window, uncheck the Web diagram feature and click Next. 5. In the Portlet Settings window, select Change code generation options and enter the following values: Package prefix: hrportlet Class prefix: HRPortlet Click Next.
6. In the Event Handling window, check Enable cooperative target in the Cooperative Portlets section. Click Next. 7. Click Next again and in the last window check Add Edit mode in Additional modes. 8. Click Finish.
762
Reviewing the code generated by the wizard for the target portlet
Look at the code generated by the wizard: In the Project Explorer panel, expand the EmployeeDetailsPortlet\Java Resources folder; you will see the property broker jar file (pbportlet.jar) has been imported. Open the web.xml file and select the Servlets tab. Select the hrportlet.HRPortlet servlet and check the following entries: servlet class: com.ibm.wps.pb.wrapper.PortletWrapper There is an initialization parameter configured with name c2a-application-portlet-class and value hrportlet.HRPortlet. Open portlet.xml. Under the concrete portlet application, you will see configuration parameters to locate the wsdl file and resource file for internationalization support.
Example 24-4 Configuration parameters in concrete portlets <config-param> <param-name>c2a-action-descriptor</param-name> <param-value>/hrportlet/wsdl/HRPortletC2A.wsdl</param-value> </config-param> <config-param> <param-name>c2a-nls-file</param-name> <param-value>hrportlet.nls.HRPortletC2A</param-value> </config-param>
For IBM portlets, the c2a-nls-file configuration parameter is used to specify the resource file name. The value should be the file name, including the package but omitting the .properties suffix. A WSDL file named HRPortletC2A.wsdl has been created.
763
Note: You can also download the sample code available as additional materials. See Appendix A, Additional material on page 1003.
Example 24-5 HRPortletC2A.wsdl file <?xml version="1.0" encoding="UTF-8" ?> <definitions name="HRPortlet_Service" targetNamespace="http://www.ibm.com/wps/c2a/examples/hrdetails" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:portlet="http://www.ibm.com/wps/c2a" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.ibm.com/wps/c2a/examples/hrdetails" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <types> <xsd:schema targetNamespace="http://www.ibm.com/wps/c2a/examples/hrdetails"> <xsd:simpleType name="DEPT_NO"> <xsd:restriction base="xsd:string" /> </xsd:simpleType> </xsd:schema> </types> <message name="HRPortlet_Request"> <part name="HRPortlet_RequestPart" type="tns:DEPT_NO" /> </message> <portType name="HRPortlet_Service"> <operation name="hrportlet.HRPortletFormAction"> <input message="tns:HRPortlet_Request" /> </operation> </portType> <binding name="HRPortlet_Binding" type="tns:HRPortlet_Service"> <portlet:binding /> <operation name="hrportlet.HRPortletFormAction"> <portlet:action name="hrportlet.HRPortletDetailsAction" type="simple" caption="show.all.employees" description="Get all employees for specified department id" /> <input> <portlet:param name="DEPT_NOParam" partname="HRPortlet_RequestPart" caption="department.ID" description="department.id.for.retrieving.details" /> </input> </operation> </binding> </definitions>
764
The namespace and type associated with the input parameter matches the source declaration usign the encodeProperty tag in the JSP file of the source portlet.If the source and target portlets were placed on the same page, the C2A broker would detect a match. Open HRPortletC2A.properties under the EmployeeDetailsPortlet\Java Resources\JavaSource\hrportlet\nls folder. Add the following key-value pairs to localize text of the HRPortlet C2A portlets captions.
Example 24-6 Including new captions in resource bundles # localized text of the HRPortlet c2a portlets captions Get.all.employees.for.specified.department.id=Get all employees for specified department id department.ID=Department ID (HRPortlet) department.id.for.retrieving.details=Department ID for retrieving details show.all.employees=Show all employees from this department.
The last step is to update the actionPerformed() method in the portlet class. In this scenario, you will use a special action string. a. Open the HRPortlet.java file from the EmployeeDetailsPortlet project. b. Insert the lines shown in Example 24-7 at the end of the actionPerformed() method.
Example 24-7 The hrportlet.HRPortletDetailsAction updates the SQL string if (actionString.equals("hrportlet.HRPortletDetailsAction")) { HRPortletSessionBean bean = this.getSessionBean(request); bean.setSqlString( "select * from employee where workdept='" + (String) request.getParameter("DEPT_NOParam") + "'"); }
765
c. In the next window, move HRPortletEAR and EmployeeDetailsPortletEAR to the right to configure them on the server. d. Click Finish. 2. If the Cloudscape sample database has not been populated, run the batch file to populate the test database to be used in this scenario. Click C:\HRproject\database\CreateCloudTable.bat to do this. Note: You can also download the sample code available as additional materials. See Appendix A, Additional material on page 1003. 3. Next, click the Project Explorer tab to see your project again. 4. Right-click HRPortlet. Then click Run Run on Server. Select WebSphere Portal V5.1 Test Environment server and click Finish. This will load your project into the test environment so that you can view it in the Rational Application Developer Web browser. It may take a minute or two for this process to complete. 5. You will now see your newly created portlets project running in the Web browser. You need to add EmployeeDetailsPortlet to the same page of HRPortlet to test the cooperation. 6. Click Edit Page.
7. In the Edit Layout window, you can see that the page contains HRPortlet. Click Add portlets button and search EmployeeDetailsPortlet from the list. 8. When you find it, check the EmployeeDetailsPortlet and click OK. 9. Now you will see the two portlets in the Edit Layout window. Click Done.
766
10.Switch to Edit mode in HRPortlet (c2a source portlet). 11.Enter the following information and click Submit. Database: jdbc:db2j:C:\HRproject\database\WSSAMPLE User: db2admin Password: db2admin SQL: select * from jobs
12.The HRPortlet now displays the jobs table, including a cooperative portlet menu in the DEPT_NO column. Before you can use this menu, you have to configure the data source in EmployeeDetailsPortlet.
767
13.Switch to Edit mode in EmployeeDetailsPortlet (c2a target portlet). 14.Enter the following information and click Submit. It is not necessary to enter an SQL command here, because it is built during the processing of the cooperative menu. Database: jdbc:db2j:C:\HRproject\database\WSSAMPLE User: db2admin Password: db2admin Note: There is no need to enter an SQL statement (optional). 15.In the source cooperative portlet View mode, click a DEPT_NO column, for example C01 or A00. 16.Click Show all employees from this department (see Figure 24-22 on page 769). Note: The EmployeeDetailsPortlet (target portlet) should now display all employees in the selected department.
768
Click1 Click2
17.Now select another department and hold Ctrl key while clicking a Click-to-Action icon. You will be prompted with the dialog illustrated in Figure 24-23
18.Select Yes. A persistent connection between the two portlets is created. This connection is called wire. 19.The next time you click the icon, no selection menu is shown, the wire action is automatically fired. 20.Select Edit Page and click the Wires tab. Figure 24-24 on page 770 shows the new wire.
769
Wire type is personal; that means its effect would be manifested only for the creator. If, in the menu shown in Figure 24-23 on page 769, you had selected Yes, all users, a global wire would have been created. Global wire means that wire effects are visible to all users who can view the page and portlets.
770
25
Chapter 25.
771
772
development time, such as when a portlet is generated by a builder application. Generate markup content directly in the portlet rather than using JSPs. The following packages are provided for portlets to publish properties to the property broker programmatically: com.ibm.wps.pb.property com.ibm.wps.pb.portlet com.ibm.wps.pb.service
773
Note: The process may continue recursively; however, the property broker detects loops and breaks them. Also, during the event phase of the subsequent request, the action is invoked on the corresponding target portlet or portlets. Figure 25-1 illustrates a simplified version of cooperative portlets implemented using the programmatic approach.
Source portlet
1. Update web.xml with wrapper 2. initConcrete() Iinitialize property attribute 3. beginEventPhase() If needed: register output properties 4. actionPerformed() createProperty() createPropertyValue() changedProperties()
Target portlet
1. Update web.xml with wrapper 2. initConcrete() Initialize property attribute 3. beginEventPhase() If needed: register input properties 4. setProperties()
In the general case using a programmatic approach, the source portlet needs to implement the following: 1. Specify the C2A wrapper in the web.xml descriptor as explained in Chapter 24, IBM API declarative cooperative portlets on page 741. 2. The property broker attribute needs to be initialized; this can be done in the initConcrete() method. 3. The portlet will need to register its output properties by using the registerProperties() method. This can be done by implementing a beginEventPhase() method so the portlet is notified when the event phase starts. Note: The EventPhaseListener interface requires that you also provide the endEventPhase() method. If needed, some cleanup can be done in this method. 4. The source portlet publishes its output properties, for example when processing an action in the actionPerformed() method. In a similar way, the target portlet needs to be updated as follows:
774
1. Specify the C2A wrapper in the web.xml descriptor as explained in Chapter 24, IBM API declarative cooperative portlets on page 741. 2. The property broker attribute needs to be initialized; this can be done in the initConcrete() method. 3. The portlet will need to register its input properties by using the registerProperties() method. This can be done by implementing a beginEventPhase() method so the portlet is notified when the event phase starts. 4. The target portlet implements the setProperties() method to be notified of property changes reported by other source portlets. Note: The target portlet must implement the PropertyListener interface.
setProperties
Invoked by the Property Broker to deliver new property values which were changed in the current event cycle of the current request. The Property Broker may be notified of such changes when a portlet invokes the changedProperties in the PropertyBrokerService interface (explicit notification), or when a portlet action which has declared output parameters is invoked (implicit notification). This method is only invoked during the event phase. Since multiple explicit or implicit property change notifications may be made during an event cycle, one or more setProperties calls may be invoked on a single portlet instance during a single event cycle. The runtime may batch property values from multiple changedProperties calls in a single setProperties call. All properties are guaranteed to be delivered before the first endEventPhase call is delivered, which marks the start of the render phase.
775
Source cooperative portlets report property changes may be made by using the changedProperties method.
changedProperties method
This method publishes changes in properties and may be used during the portlet's event phase only. This includes the beginEventPhase method of the EventPhaseListener interface, the actionPerformed method of the ActionListener interface, and the setProperties method of the PropertyListener interface. All properties must have been registered earlier, implicitly or explicitly. A simpler alternative to explicitly invoking this method is often applicable. For example, declare output parameters for registered actions (either programmatically or via an WSDL declaration). In this case, the action may bind the values of the output parameters on invocation, and at runtime the values will be transferred as if the changedProperties method had been explicitly invoked.
beginEventPhase method
At any point during the event phase, a portlet may explicitly publish the value of an output property to the property broker by invoking the changedProperties() method. This is an alternative to the declaration of output parameters for actions and binding the output parameter values to the request or session when the action is invoked. This may happen in the callback method associated with the start of the event phase (beginEventPhase()), in the invocation of the setProperties() method, or in the portlet action method invocation. Such publish calls are dealt with by the property broker in the same manner as output parameters of actions: wires associated with output properties are examined and the property values propagated using the information in the target end of the wire. To register properties, you will use the beginEventPhase method of EventPhaseListener, because only during the event phase is it possible to register and unregister properties.
endEventPhase method
The property broker guarantees the completion of all property value notifications to target portlets by the end of the event phase, whether through portlet actions or through the special setProperties() method. The end of the event phase is indicated by the invocation of the endEventPhase() method.
776
During the render phase of each request cycle, source portlets can write visual controls representing source data to their output stream. The end user interacts with the visual control in the response to trigger one or more actions on other portlets on the page. During the event phase of the subsequent request, the action is invoked on the corresponding target portlet or portlets.
3. Test the application as described in the last chapter. You need to include HRPortlet, EmployeeDetailsPortlet and DepartmentDetailsPortlet in the same page. Do not forget to enter the database attribute in the Edit mode of both the Employee and Department Details Portlets. 4. From the cooperative portlet menu, choose Invoke all actions. Now both details portlets display the details of the selected department number.
777
Figure 25-2 Choose Invoke all actions to broadcast the data to all portlets
778
WebSphere Portal V5.1 includes the Portlet Wiring Tool as part of the product and deployed to the Page Customizer.
HRPortlet
C2A Wrapper
view.jsp
779
3. In the WAR Import window, enter the following information: a. WAR file: browse to C:\LabFiles\HRPortlet.war. Note: The sample scenario included in this chapter requires that you download the sample code available as additional materials. See Appendix A, Additional material on page 1003. b. Web project: HRPortlet. c. Target server: select WebSphere Portal V5.1. d. EAR project: HRPortletEAR. e. Click Finish to import the WAR file.
4. Make sure the web.xml descriptor has been updated with the c2a wrapper. 5. Make sure the PortletView.jsp has been updated with the c2a tags and c2a tag library. See Example 25-2.
Example 25-2 C2a library and tags <%@ page contentType="text/html" import="java.util.*, hrportlet.*"%> <%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %> <%@ taglib uri="/WEB-INF/tld/c2a.tld" prefix="C2A" %>
780
................................................... <TD><P> <% if (results.getColumnName(col).equalsIgnoreCase("DEPT_NO")) { %> <C2A:encodeProperty name="DEPT_NOParam" namespace="http://www.ibm.com/wps/c2a/examples/hrdetails" type="DEPT_NO" value="<%=results.getCacheValueAt(row, col).toString()%>" /> <% } %> <%=results.getCacheValueAt(row, col)%> </P></TD>
781
HRPortlet
C2A Wrapper
view.jsp
782
3. Since the HRPortlet and DepartmentDetailsPortlet portlet applications use the same UID, a warning message will appear in the Problems pane.
783
4. To fix this problem, expand DepartmentDetailsPortlet/WebContent/WEB-INF in the Project Explorer view. Double-click portlet.xml.
784
5. In the portlet deployment descriptor editor, select Portlet Application and change the last digit of the UID for this portlet application. For example, in this sample scenario, the last digit was 0 and it was changed to 1.
785
6. In a similar way, select Concrete Portlet Application and change the last digit before the last dot of the UID to give it the same value as in the previous step.
7. Change the portlet name and portlet title to DepartmentDetailsPortlet portlet to identify the portlet when you run the application. 8. Save your changes. 9. Make sure the web.xml descriptor has been updated with the c2a wrapper. 10.If HRPortlet/WebContent/wsdl/HRPortlet.wsdl file exists delete it. When you delete this file an error appears in portlet.xml file. Open portlet deployment descriptor and delete the following config param to fix the error:
<config-param> <param-name>c2a-action-descriptor</param-name> <param-value>/wsdl/HRPortletportlet.wsdl</param-value> </config-param>
786
1. Open the file DepartmentDetailsPortlet/Java Resources/JavaSource/hrportlet/HRPortlet.java. 2. Update the class definition so it implements the PropertyListener and EventPhaseListener interfaces.
public class HRPortlet extends PortletAdapter implements PropertyListener, EventPhaseListener, ActionListener
Note: The setProperties method of the PropertyListener interface receives updates of property values. To register properties, you will use the beginEventPhase method of the EventPhaseListener interface, since only during the event phase is it possible to register and unregister properties. 3. Insert the following two new class attributes so the code looks like this:
public class HRPortlet extends PortletAdapter implements PropertyListener, EventPhaseListener, ActionListener { PropertyBrokerService pbService; PortletConfig portletConfig;
Note: pbService is an interface to the property broker and portletConfig stores the portlet config. 4. Update the init method so it looks as shown in Example 25-3.
Example 25-3 Update init method to obtain portlet configuration public void init(PortletConfig portletConfig) throws UnavailableException { super.init(portletConfig); this.portletConfig=portletConfig; }
5. Insert the initConcrete method shown in Example 25-4 to initialize the property broker attribute.
Example 25-4 Initialize property broker public void initConcrete(PortletSettings settings) throws UnavailableException { try { pbService = (PropertyBrokerService) getPortletConfig() .getContext() .getService( PropertyBrokerService.class); } catch (PortletServiceUnavailableException e) { throw new UnavailableException("Could not locate PropertyBrokerService."); } catch (PortletServiceNotFoundException e) {
787
6. For simplicity, create the registerPropertiesIfNecessary method to register the property we are interested in. Note: This method performs the same as the WSDL file when using the declarative approach. Notice also that you need to specify a direction of Property.IN. In addition, actions are not registered (which is possible using the registerActions method) in this scenario. In other words, this portlet will be invoked by the property broker using the setProperties method instead of actionPerformed.
Example 25-5 registerPropertiesIfNecessary method private void registerPropertiesIfNecessary(PortletRequest request) throws PropertyBrokerServiceException { PortletSettings settings = request.getPortletSettings(); Property[] properties = pbService.getAllProperties(request, settings); if (properties == null || properties.length == 0) { PortletContext context = getPortletConfig().getContext(); //not registered, register now properties = new Property[1]; properties[0] = PropertyFactory.createProperty(settings); properties[0].setName("DEPT_NOParam"); properties[0].setDirection(Property.IN); properties[0].setType("DEPT_NO"); properties[0].setNamespace("http://www.ibm.com/wps/c2a/examples/hrdetails"); properties[0].setTitleKey("HRDetails.Department"); properties[0].setDescriptionKey("Display department details"); pbService.registerProperties(request, settings, properties); } }
7. Add the beginEventPhase and endEventPhase methods, which are callback methods called during the event phase.
Example 25-6 The beginEventPhase methods registers the property if necessary public void beginEventPhase(PortletRequest request) { try { registerPropertiesIfNecessary(request); } catch (Throwable e) { e.printStackTrace(); }
788
8. Add the setProperties method. Using this method, the class is notified about property changes.
Example 25-7 setProperties updates the sql statement public void setProperties( PortletRequest request, PropertyValue[] properties) { PortletSession session = request.getPortletSession(); HRPortletSessionBean sessionBean = getSessionBean(request); for (int i = 0; i < properties.length; i++) { System.out.println(properties[i].toString()); if (properties[i].getProperty().getName().equals("DEPT_NOParam")) { String value = (String)properties[i].getValue(); if (value.equals("*")) sessionBean.setSqlString("select * from department"); else sessionBean.setSqlString( "select * from department where deptno='" + value + "'"); } } }
9. Right-click anywhere in the Java editor and select Source Organize Imports to include the missing import statements. In the Organize Imports dialog, choose to import the following classes: a. b. c. d. e. f. g. com.ibm.wps.pb.service.PropertyBrokerService org.apache.jetspeed.portlet.service.PortletServiceUnavailableException com.ibm.wps.pb.service.PropertyBrokerServiceException com.ibm.wps.pb.property.Property org.apache.jetspeed.portlet.PortletContext com.ibm.wps.pb.property.PropertyFactory com.ibm.wps.pb.property.PropertyValue
789
a. In Servers panel, right-click WebSphere Portal V5.1 Test Environment. b. Select Add and remove projects to add your project to the Test Environment (see Figure 25-12); add HRPortletEAR and DepartmentDetailsPortletEAR to the right panel, Configured projects, by selecting them in Available projects panel and clicking the Add button.
2. If the Cloudscape sample database has not been populated, run the batch file to populate the test database to be used in this scenario. Click C:\HRproject\database\CreateCloudTable.bat to do this. Note: The sample scenario included in this chapter requires that you download the sample code available as additional materials. See Appendix A, Additional material on page 1003. 3. In Project Explorer panel, right-click HRPortlet. Then click Run Run on Server. Select WebSphere Portal V5.1 Test Environment from the server list and click Finish. This will load your projects into the Test Environment so that you can view them in the Rational Application Developer internal Web browser. It may take a minute or two for this process to complete.
790
4. You will now see your newly created portlets project running in the Web browser in two different pages. You need to add DepartmentDetailsPortlet to the same page of HRPortlet to test the cooperation. a. Click Edit Page.
b. In the Edit Layout window you can see that the page contains HRPortlet. Click Add portlets button and search DepartmentDetailsPortlet from the list. c. When you find it check the DepartmentDetailsPortlet and click OK. d. Now you will see the two portlets in the Edit Layout window. Click Done. Note: You need to enable base portlets for portal administration and customization in server configuration options. 5. Switch to Edit mode in HRPortlet (c2a source portlet). 6. Enter the following information and click Submit. Database: jdbc:db2j:C:\HRProject\database\WSSAMPLE User: db2admin Password: db2admin SQL: select * from jobs
791
7. The HRPortlet now displays the jobs table, including a cooperative portlet menu in the DEPT_NO column. Before you can use this menu, you have to configure the data source in DepartmentDetailsPortlet.
8. Switch to Edit mode in DepartmentDetailsPortlet (c2a target portlet). 9. Enter the following information and click Submit. It is not necessary to enter an SQL command here, because it is built during the processing of the cooperative menu.
792
Database: jdbc:db2j:C:\HRProject\database\WSSAMPLE User: db2admin Password: db2admin Note: There is no need to enter an SQL statement (optional). 10.In the source portlet View mode, click the c2a icon in the DEPT_NO column, for example B01. 11.Click the c2a menu again to send the changed property to C2A.
2. Select one of the C2A menu items but this time press the Ctrl key during the selection. 3. A new dialog opens asking whether to Automate the action in future. Select Yes.
793
Figure 25-17 Pressing Ctrl key during menu selection to create a wire
4. Click Yes to activate the wire. 5. Try other departments again. 6. Press the Ctrl key again when using C2A to disable the wire. Note: Wiring can also be accomplished by using the Portlet Wiring Tool.
Source portlet
1. Update web.xml with wrapper 2. initConcrete() Initialize property attribute 3. beginEventPhase() If needed: register output properties 4. actionPerformed() createProperty() createPropertyValue() changedProperties()
Target portlet
1. Update web.xml with wrapper 2. initConcrete() Initialize property attribute 3. beginEventPhase() If needed: register input properties 4. setProperties()
You will update HRPortlet to change property data using the programmatic approach. You will also insert a button in the HRPortlet page to offer the display of department details in the DepartmentDetailsPortlet (C2A target portlet).
794
Notice that in this portlet, you will need to register the DEPT_NO property using the Property.OUT direction. In addition, the update of the property will be done in the actionPerformed method. Note: The sample scenario included in this chapter requires that you download the sample code available as additional materials. See Appendix A, Additional material on page 1003. Proceed as follows to update the HRPortlet: 1. Open the file HRPortlet/Java Resources/JavaSource/hrportlet/HRPortlet.java. 2. Update the class definition so it implements the EventPhaseListener interface:
public class HRPortlet extends PortletAdapter implements EventPhaseListener, ActionListener {
Note: The beginEventPhase method of the EventPhaseListener interface is used to register the output properties; this is because it is only possible to register and unregister properties during the event phase. 3. At the beginning of this class, insert two new class attributes so the code looks like this:
public class HRPortlet extends PortletAdapter implements EventPhaseListener, ActionListener { PropertyBrokerService pbService; PortletConfig portletConfig;
Note: pbService is an interface to the property broker and portletConfig stores the portlet configuration. 4. Change the init method so it looks as follows:
public void init(PortletConfig portletConfig) throws UnavailableException { super.init(portletConfig); this.portletConfig=portletConfig; }
5. Add the following initConcrete method to initialize the property broker attribute.
Example 25-8 The initConcrete method initializes the property broker attribute public void initConcrete(PortletSettings settings) throws UnavailableException { try { pbService = (PropertyBrokerService) getPortletConfig() .getContext() .getService( PropertyBrokerService.class);
795
} catch (PortletServiceUnavailableException e) { throw new UnavailableException("Could not locate PropertyBrokerService."); } catch (PortletServiceNotFoundException e) { throw new UnavailableException("Could not locate PropertyBrokerService."); } }
6. For simplicity, you will now add a new method called registerPropertiesIfNecessary to register the property we are interested in.
Example 25-9 Register a new output property private void registerPropertiesIfNecessary(PortletRequest request) throws PropertyBrokerServiceException { PortletSettings settings = request.getPortletSettings(); Property[] properties = pbService.getAllProperties(request, settings); if (properties == null || properties.length == 0) { PortletContext context = getPortletConfig().getContext(); //not registered, register now properties = new Property[1]; properties[0] = PropertyFactory.createProperty(settings); properties[0].setName("DEPT_NOParam"); properties[0].setDirection(Property.OUT); properties[0].setType("DEPT_NO"); properties[0].setNamespace("http://www.ibm.com/wps/c2a/examples/hrdetails"); properties[0].setTitleKey("HRDetails.Department"); properties[0].setDescriptionKey( "Display department details"); pbService.registerProperties(request, settings, properties); } }
7. Implement the eventPhaseListener interface by inserting the beginEventPhase and endEventPhase methods, which are callback methods invoked during the event phase. The beginEventPhase invokes the method to register the output property DEPT_NOParam. Note: The endEventPhase method does nothing in this scenario but needs to be included in the interface.
Example 25-10 beginEventPhase and endEventPhase methods public void beginEventPhase(PortletRequest request) { try { registerPropertiesIfNecessary(request); } catch (Throwable e) {
796
8. For simplicity, insert a new method with name changeProperty to notify the broker about a changed property value. This method uses the changedProperties method to notify property changes.
Example 25-11 Notify the broker of a property value change private void changeProperty(PortletRequest request, String value) { System.out.println("send data"); PortletSettings settings = request.getPortletSettings(); if (pbService != null) { try { Property p = PropertyFactory.createProperty(settings); p.setName("DEPT_NOParam"); p.setDirection(Property.OUT); p.setType("DEPT_NO"); p.setNamespace("http://www.ibm.com/wps/c2a/examples/hrdetails"); PropertyValue[] pva = new PropertyValue[1]; pva[0] = PropertyFactory.createPropertyValue(p, value); pbService.changedProperties(request, getPortletConfig(), pva); } catch (Exception e) { e.printStackTrace(); } } }
9. At the end of the actionPerformed method, include the following code to invoke the internal changeProperty method and notify the output property change.
Example 25-12 Invoke method to notify the property value change if (actionString.equals("DisplayAllDepartmentDetails")) { this.changeProperty(request, "*"); }
10.Right-click anywhere in the Java editor and select Source Organize Imports to include the missing import statements. In the Organize Imports dialog, choose to import the following class: a. com.ibm.wps.pb.service.PropertyBrokerService b. org.apache.jetspeed.portlet.service.PortletServiceUnavailableException c. com.ibm.wps.pb.service.PropertyBrokerServiceException
797
d. e. f. g.
11.Save and close the updated source cooperative portlet HRPortlet.java file. 12.Update the HRPortletView.jsp to insert a new action button offering to display department details from all departments. Open the HRPortletView.jsp file and insert the following code at the end of the file and before the </BODY> tag. Note: The action name is DisplayAllDepartmentDetails and it will be processed in the actionPerformed method. See Example 25-13.
Example 25-13 New button to offer display of all department details <p align="center"> <FORM method="post" action="<portletAPI:createReturnURI><portletAPI:URIAction name='DisplayAllDepartmentDetails'/></portletAPI:createReturnURI>""> <INPUT type="submit" name="submit" value="Display all department details"> </FORM> </p>
13.Optionally, preview the JSP and see the new button. 14.Save and close the file. 15.Since the source portlet is now using programmatic approach, if there is a HRPortlet/WebContent/wsdl/HRPortlet.wsdl file, delete it and its reference in portlet.xml file. Save and close the file.
798
Figure 25-19 Department Details Portlet displays details from all department
3. A portlet wire is required. Try the Display all department details button before and after adding the portlet wire. Important: The method changedProperties() will only trigger events on wired targets. If no wires exist for the published properties, it has no effect.
799
800
26
Chapter 26.
801
26.1 Overview
These are some considerations when implementing cooperative portlets using JSR 168 compliant portlets: The only way JSR 168 portlets communicate is using wires. Client-to-Action (C2A) is not supported. A JSR 168 portlet cannot be wired to an IBM API portlet. You only can create wires between portlets of the same standard. There is no c2a tag library for JSR 168 portlets a. Implementing a WSDL is the only way for JSR 168 portlets to provide information about properties and actions to the property broker. b. JSR 168 portlets must use the setProperties() method to publish properties to the broker. JSR 168 portlets only support the declarative approach to register properties and actions. The programmatic approach cannot be used as it is only available for IBM API portlets. The typical sequence flow for cooperative portlets using JSR 168 compliant portlets is illustrated in Figure 26-1.
Portlet A
Filter
4
Browser
WSDL
1 1
Property Broker
3 8
Portlet B (target)
WSDL
6
Portlet B Filter
2
Portlet actions Portlet properties Portlet wires
802
Make sure you understand the message flow by walking through the following steps illustrated in Figure 26-1 on page 802: 1. Cooperative portlets deployment: a. WSDL files are processed at deployment time b. Action and property metadata stored persistently in the portal datastore 2. Wiring: a. User (administrator) selects instances of cooperative portlets on a portal page and creates one or more wires between them b. Wiring information is also stored persistently in the portal datastore 3. User submits action in source portlet (Portlet A) 4. Portal engine receives the request: a. Determines that an action needs to be invoked b. Passes action to portlet container c. Portlet container delivers action to the source portlet 5. The portlet updates its state and produces new values for output parameters. a. Action filter determines the presence of output parameters b. For each active wire, it triggers an action invocation on the target portlet (Portlet B) through the portal container 6. Portal container delivers action(s) on the target portlet(s) 7. After action processing terminates: a. Render processing begins b. Portlet container delivers render method invocations 8. Portal collects markup from each portlet and generates the portal page.
803
In the processAction() method, send the output properties In the doView() method, check if the wire is active. Update the JSP for View mode to create dynamic links.
804
26.2.2 Internationalization
In this section you will do the following: Change the portlet titles. Since you are using the same portlet version as source and target, it is recommended that you change the portlet titles to avoid confusion (English and Default bundles). Add key value pairs for the following (English and Default bundles): department.ID show.all employees (English bundle): Execute the following steps to add this support to the cooperative source portlet: 1. In HRPortlet168 Java Resources JavaSource hrportlet168.nl HRPortlet168Resource.properties, add the entries highlighted in Example 26-11 on page 818.
Example 26-1 Target portlet HRPortlet168Resource.properties (Default) en Resource Bundle # # filename: HRPortlet168Resource_en.properties # Portlet Info resource bundle example javax.portlet.title=HRPortlet168 Source javax.portlet.short-title= javax.portlet.keywords= department.ID=department ID show.all.employees=Show all employees from this department
2. In HRPortlet168 Java Resources JavaSource hrportlet168.nl HRPortlet168Resource_en.properties, add the entries highlighted in Example 26-12 on page 819.
Example 26-2 Target portlet HRPortlet168Resource_en.properties (English) en Resource Bundle # # filename: HRPortlet168Resource_en.properties # Portlet Info resource bundle example javax.portlet.title=HRPortlet168 Source javax.portlet.short-title= javax.portlet.keywords= department.ID=department ID
805
4. Right-click the wsdl folder just created and choose Import... File system from the context menu. 5. Browse the directory where the HRPortlet168.wsdl file is located and select this file. The WSDL file is provided as additional material in the following directory: c:\LabFiles\Cooperative Portlets\JSR168\HRPortlet168.wsdl 6. Select the HRPortlet168.wsdl to be imported. 7. Make sure the Create selected folders only option is checked. 8. Click Finish.
806
807
<message name="GetResultsMessageNameResponse"> <part name="dept_Id" type="tns:DEPT_NO"/> </message> <portType name="DeptResults_Service"> <operation name="dept_Detail"> <output message="tns:GetResultsMessageNameResponse"/> </operation> </portType> <binding name="DeptResultsBinding" type="tns:DeptResults_Service"> <portlet:binding/> <operation name="dept_Detail"> <portlet:action name="departmentList" type="standard" caption="show.all.employees" description="Get.Results.for.specified.sql.string" actionNameParameter="ACTION_NAME"/> <output> <portlet:param name="DEPT_NO" partname="dept_Id" boundTo="request-parameter" caption="department.ID"/> </output> </operation> </binding> </definitions>
10.Review the wsdl file: The syntax used in this WSDL file is the standard WSDL with some custom extensions for cooperative portlets. The <binding> tag is extended to associate portlet actions with operations. The elements prefixed with portlet: are part of the custom WSDL extension schema. For each operation, the portlet action name must be provided using the name attribute of the action tag. For each operation parameter, the action parameter name must be provided using the name attribute of the param tag. The boundTo attribute may be used to specify where the parameter will be bound, the options are request-parameter, request-attribute, session-attribute or action-attribute. In this wsdl file you see that this portlet declares a single action called departmentList, using the portlet:action element. The type attribute standard indicates that it will be implemented as a standard portlet action (JSR 168).
808
Other type attribute options are: i. default: indicates a DefaultPortletAction object is used ii. simple: indicates a simple portlet action String is used iii. struts: indicates a Struts action is used iv. standard-struts: indicates a struts action is used with a JSR portlet. The portlet:param element identifies that the action has an output parameter. The name and boundTo attributes need to take into account in processAction method.
809
4. In the Source tab, visually verify the new added preference as highlighted in Example 26-4.
Example 26-4 Updating portlet deployment descriptor <?xml version="1.0" encoding="UTF-8"?> <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" id="hrportlet168.HRPortlet168.8d364d6120"> <portlet> <portlet-name>HRPortlet168</portlet-name> <display-name>HRPortlet168</display-name> <display-name xml:lang="en">HRPortlet168</display-name> <portlet-class>hrportlet168.HRPortlet168</portlet-class> <init-param> <name>wps.markup</name> <value>html</value> </init-param> <expiration-cache>0</expiration-cache> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> </supports> <supported-locale>en</supported-locale> <resource-bundle>hrportlet168.nl.HRPortlet168Resource</resource-bundle> <portlet-info> <title>HRPortlet168</title> </portlet-info>
810
Note: The portlet descriptor (portlet.xml) should always point to the location of the WSDL file so that Portal will register the portlet properties at initialization time and when the portlet is deployed. 5. If you select the Problems tab, you will find that the WSDL file gives you two warning messages; this is not a problem. 6. Save and close the portlet.xml file.
4. Update the init method to obtain a reference to the property broker services as highlighted in Example 26-5:
Example 26-5 Obtaining a reference to the property broker service public void init(PortletConfig portletConfig) throws PortletException, UnavailableException { super.init(portletConfig); try { Context ctx = new InitialContext(); PortletServiceHome serviceHome = (PortletServiceHome) ctx.lookup("portletservice/com.ibm.portal.propertybroker.service.PropertyBrokerService");
811
pbService = (PropertyBrokerService)serviceHome.getPortletService(com.ibm.portal.propertybroker.service.Prop ertyBrokerService.class); pbServiceAvailable = true; }catch(Throwable t) { getPortletContext().log("OrderDetailPortlet could not find property broker service!"); } }
Note: The boolean variable pbServiceAvailable will set to true only if the service is available. This variable can be used to guard access to IBM-only services (WebSphere Portal) and it can be used to ensure that the portlet can still function in other environments where the service is not available. 5. You need to declare three new variables: ACTION_NAME, DEPARTMENT_LIST and DEPT_NO as follows:
public static final String ACTION_NAME = "ACTION_NAME"; public static final String DEPARTMENT_LIST = "departmentList"; public static final String DEPT_NO = "DEPT_NO";
6. In the processAction method, publish an output property, as highlighted in Example 26-6. The property must be set as an attribute (setAttribute) in the request as configured in the WSDL.
Example 26-6 Publishing the output property public void processAction(ActionRequest request, ActionResponse response) throws PortletException, java.io.IOException { //Add action string handler here HRPortlet168SessionBean sessionBean = getSessionBean(request); if(request.getPortletMode().equals(PortletMode.EDIT)) { String dbname = (String) request.getParameter("dbname"); String userid = (String) request.getParameter("userid"); String password = (String) request.getParameter("password"); String sqlstring = (String) request.getParameter("sqlstring"); sessionBean.setDbName(dbname); sessionBean.setUserId(userid); sessionBean.setPassword(password); sessionBean.setSqlString(sqlstring); if(request.getPortletMode().equals(PortletMode.EDIT)) response.setPortletMode(PortletMode.VIEW); } if( request.getParameter(FORM_SUBMIT) != null ) { // Set form text in the session bean sessionBean = getSessionBean(request); if( sessionBean != null ) sessionBean.setFormText(request.getParameter(TEXT)); }
812
// add this code String actionName = request.getParameter(ACTION_NAME); String dept_number = request.getParameter(DEPT_NO); if (actionName != null && actionName.equalsIgnoreCase(DEPARTMENT_LIST)) { request.getPortletSession().setAttribute(ACTION_NAME, DEPARTMENT_LIST); request.setAttribute(DEPT_NO, dept_number); } // end of added code }
7. Save your files. 8. Review the code: Here is where you need to take into account the attributes name and boundTo of portlet:param element mentioned previously in Example 26-3 on page 807. The name attribute of portlet:action element must match with the request parameter to determine the action name (departmentList in our example). When the action matches it sets the value of output parameter (DEPT_NO). Depending on the value of boundTo attribute, the variable will be set as: request attribute, if the value of boundTo attribute is request-attribute session attribute, if the value of boundTo attribute is session request parameter, if the value of boundTo attribute is request-parameter. This is the default value for this attribute.
9. In the Project Explorer view: a. Open the HRPortlet168ViewBean bean located in HRPortlet168/Java Resources/JavaSource/hrportlet168/. b. Add a variable to indicate if the department code property is wired to an active action and add its corresponding getter and setter methods. c. Necessary changes on HRPortlet168ViewBean bean are highlighted in Example 26-7.
Example 26-7 Setting an indicator on HRPortlet168ViewBean package hrportlet168; import com.ibm.db.beans.*; public class HRPortlet168ViewBean { private DBSelect resultFromDatabase; // add this code
813
private boolean deptIDActive; public boolean isDeptIDActive() { return deptIDActive; } public void setDeptIDActive(boolean deptIDActive) { this.deptIDActive = deptIDActive; } // end of added code public DBSelect getResultFromDatabase() { return resultFromDatabase; } public void setResultFromDatabase(DBSelect resultFromDatabase) { this.resultFromDatabase = resultFromDatabase; } }
10.Save the HRPortlet168ViewBean.java file. 11.Return to HRPortlet168 portlet code and add the following code to the doView method to inform the JSP whether or not the service is available and there are wires active so that dynamic links should be shown.
Example 26-8 Set the indicator value in doView method public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { response.setContentType(request.getResponseContentType()); HRPortlet168SessionBean sessionBean = getSessionBean(request); if( sessionBean==null ) { response.getWriter().println("<b>NO PORTLET SESSION YET</b>"); return; } String sqlstring = sessionBean.getSqlString(); if (sqlstring != null && !sqlstring.equals("")) { String dbname = sessionBean.getDbName(); String userid = sessionBean.getUserId(); String password = sessionBean.getPassword(); HRPortlet168ViewBean viewBean = new HRPortlet168ViewBean(); SQLUtilities sqlUtility = new SQLUtilities(); try { sqlUtility.execute(userid, password, sqlstring); } catch (SQLException e) { e.printStackTrace(); } sqlUtility.populateData(viewBean);
814
// add this code if (pbServiceAvailable == true) { try { viewBean.setDeptIDActive(pbService.areWiresActive(request, DEPT_NO)); } catch (Exception e) { } } // end of added code request.setAttribute(VIEW_BEAN, viewBean); } PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(getJspFilePath(request, VIEW_JSP)); rd.include(request,response); }
12.Review the added code: The areWiresActive method of the PropertyBrokerService interface tests if a property is currently wired to active actions. It receives the following: PortletRequest object for the current portlet request The name of the property to check.
with the code shown in Example 26-9. The code illustrates how to generate a link when the boolean variable, indicating that the property is wired to an action, is true.
Example 26-9 Generating a link in the HRPortlet168View.jsp <TD> <%-- If the output property "deptNo" is wired with active actions, then set a link to trigger action--%>
815
<% if ( results.getColumnName(col).equalsIgnoreCase(HRPortlet168.DEPT_NO) && viewBean.isDeptIDActive() ) { PortletURL actionURL = renderResponse.createActionURL(); actionURL.setParameter(HRPortlet168.DEPT_NO, (String)results.getCacheValueAt(row, col)); actionURL.setParameter(HRPortlet168.ACTION_NAME, HRPortlet168.DEPARTMENT_LIST); %> <A href="<%= actionURL%>"> <p><%=results.getCacheValueAt(row, col)%></p> </A> <% } else { %> <p><%=results.getCacheValueAt(row, col)%></p> <% } %> </TD>
2. Save your files. Note: Ignore the warnings indicating that there are unhandled exception types (SQLException). This is because there are no try and catch sections in the code.
816
2. HRPortlet168 and the new HRDetails168 portlet application use the same UID so it will cause an error executing the portlets. To fix this problem follow these steps: a. Expand the HRDetails168/WebContent/WEB-INF folder and double-click in portlet.xml. b. Change the portlet UID to avoid duplicates. In the portlet deployment descriptor editor, select the Source panel and change the last digit of the UID of this portlet. For example, in this scenario, the last digit is 0 and you will change it to 1, as highlighted in Example 26-10.
Example 26-10 Changing a digit in portlet application ID <?xml version="1.0" encoding="UTF-8"?> <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" id="hrportlet168.HRPortlet168.8d364d6121"> <portlet> <portlet-name>HRPortlet168</portlet-name> <display-name>HRPortlet168</display-name> <display-name xml:lang="en">HRPortlet168</display-name> <portlet-class>hrportlet168.HRPortlet168</portlet-class>
817
<init-param> <name>wps.markup</name> <value>html</value> </init-param> <expiration-cache>0</expiration-cache> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> </supports> <supported-locale>en</supported-locale> <resource-bundle>hrportlet168.nl.HRPortlet168Resource</resource-bundle> <portlet-info> <title>HRPortlet168</title> </portlet-info> </portlet> </portlet-app>
26.3.1 Internationalization
In this section, you will do the following: Change the portlet titles. Since you are using the same portlet version as source and target, it is recommended that you change the portlet titles to avoid confusion (English and Default bundles). Add key value pairs for the following (English and Default bundles): department.ID show.all employees (English bundle): Execute the following steps to add this support to the cooperative target portlet: 1. Target portlet. In HRDetails168 Java Resources JavaSource hrportlet168.nl HRPortlet168Resource.properties, add the entries highlighted in Example 26-11.
Example 26-11 Target portlet HRPortlet168Resource.properties (Default) en Resource Bundle # # filename: HRPortlet168Resource_en.properties # Portlet Info resource bundle example javax.portlet.title=HRPortlet168 Target javax.portlet.short-title= javax.portlet.keywords= department.ID=department ID
818
2. Target portlet. In HRDetails168 Java Resources JavaSource hrportlet168.nl HRPortlet168Resource_en.properties, add the entries highlighted in Example 26-12.
Example 26-12 Target portlet HRPortlet168Resource_en.properties (English) en Resource Bundle # # filename: HRPortlet168Resource_en.properties # Portlet Info resource bundle example javax.portlet.title=HRPortlet168 Target javax.portlet.short-title= javax.portlet.keywords= department.ID=department ID show.all.employees=Show all employees from this department
819
820
</xsd:schema> </types> <message name="GetResultsMessageNameRequest"> <part name="dept_Id" type="tns:DEPT_NO"/> </message> <portType name="DeptResults_Service"> <operation name="dept_Detail"> <input message="tns:GetResultsMessageNameRequest"/> </operation> </portType> <binding name="DeptResultsBinding" type="tns:DeptResults_Service"> <portlet:binding/> <operation name="dept_Detail"> <portlet:action name="departmentList" type="standard" caption="show.all.employees" description="Get.Results.for.specified.sql.string" actionNameParameter="ACTION_NAME"/> <input> <portlet:param name="DEPT_NO" partname="dept_Id" caption="department.ID"/> </input> </operation> </binding> </definitions>
Note: Here the departmentList action has an input parameter element named DEPT_NO. Note: While the number of output parameters associated with an action is unlimited, input parameters can only be received one at a time in the processAction() method.
821
2. In the Portlets tab: a. Select the HRPortlet168 b. Scroll down to the Persistent Preference Store section 3. Add the following portlet preference: a. name: com.ibm.portal.propertybroker.wsdllocation b. value: /wsdl/HRDetails168.wsdl 4. The portlet.xml with the new reference to the WSDL file is illustrated in Example 26-14.
Example 26-14 Update HRDetails168 portlet.xml file <?xml version="1.0" encoding="UTF-8"?> <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" id="hrportlet168.HRPortlet168.8d364d6121"> <portlet> <portlet-name>HRPortlet168</portlet-name> <display-name>HRPortlet168</display-name> <display-name xml:lang="en">HRPortlet168</display-name> <portlet-class>hrportlet168.HRPortlet168</portlet-class> <init-param> <name>wps.markup</name> <value>html</value> </init-param> <expiration-cache>0</expiration-cache> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> </supports> <supported-locale>en</supported-locale> <resource-bundle>hrportlet168.nl.HRPortlet168Resource</resource-bundle> <portlet-info> <title>HRPortlet168</title> </portlet-info> <portlet-preferences> <preference> <name>com.ibm.portal.propertybroker.wsdllocation</name> <value>/wsdl/HRDetails168.wsdl</value> </preference> </portlet-preferences> </portlet> </portlet-app>
822
5. From the Project Explorer view, open the HRPortlet168 portlet located in: /HRDetails168/Java Resources/JavaSource/hrportlet168/HRPortlet168.java 6. Include the following import statements in the HRPortlet168 portlet:
import import import import javax.naming.Context; javax.naming.InitialContext; com.ibm.portal.portlet.service.PortletServiceHome; com.ibm.portal.propertybroker.service.PropertyBrokerService;
8. In the same way as with the source portlet, update the init method to obtain a reference to the property broker services as highlighted in Example 26-15:
Example 26-15 Obtaining a reference to the property broker service public void init(PortletConfig portletConfig) throws PortletException, UnavailableException { super.init(portletConfig); try { Context ctx = new InitialContext(); PortletServiceHome serviceHome = (PortletServiceHome) ctx.lookup("portletservice/com.ibm.portal.propertybroker.service.PropertyBrokerService"); pbService = (PropertyBrokerService)serviceHome.getPortletService(com.ibm.portal.propertybroker.service.Prop ertyBrokerService.class); pbServiceAvailable = true; }catch(Throwable t) { getPortletContext().log("OrderDetailPortlet could not find property broker service!"); } }
9. You need to declare three new variables: ACTION_NAME, DEPARTMENT_LIST and DEPT_NO as follows:
public static final String ACTION_NAME = "ACTION_NAME"; public static final String DEPARTMENT_LIST = "departmentList"; public static final String DEPT_NO = "DEPT_NO";
10.Update the processAction method to receive the property value. as highlighted in Example 26-16.
Example 26-16 Receiving a property value in processAction method public void processAction(ActionRequest request, ActionResponse response) throws PortletException, java.io.IOException { HRPortlet168SessionBean sessionBean = getSessionBean(request); if(request.getPortletMode().equals(PortletMode.EDIT)) { String dbname = (String) request.getParameter("dbname");
823
String userid = (String) request.getParameter("userid"); String password = (String) request.getParameter("password"); String sqlstring = (String) request.getParameter("sqlstring"); sessionBean.setDbName(dbname); sessionBean.setUserId(userid); sessionBean.setPassword(password); sessionBean.setSqlString(sqlstring); } if( request.getParameter(FORM_SUBMIT) != null ) { sessionBean = getSessionBean(request); if( sessionBean != null ) sessionBean.setFormText(request.getParameter(TEXT)); } // add this code String actionName = request.getParameter(ACTION_NAME); if (actionName != null && actionName.equalsIgnoreCase(DEPARTMENT_LIST)) { String dept_number = (String)request.getParameter(DEPT_NO); request.getPortletSession().setAttribute(ACTION_NAME, DEPARTMENT_LIST); HRPortlet168SessionBean bean = getSessionBean(request); bean.setSqlString("select * from employee where workdept='" + dept_number + "'"); } // end of added code }
Note: The input parameter DEPT_NO is retrieved as request parameter because boundTo attribute has been omitted in the WSDL file.
824
Tables.sql 3. Edit the provided CreateCloudTable.bat file and make sure the paths for variables JAVA_HOME and DB2J_LIB point to the correct location. For example, in this sample scenario, these directories (java and lib) can be found at <RAD_root>\runtimes as shown in Example 26-17.
Example 26-17 Sample CreateCloudTable.bat file set JAVA_HOME=C:\Progra~1\IBM\Rational\SDP\6.0\runtimes\base_v51\java set DB2J_LIB=C:\Progra~1\IBM\Rational\SDP\6.0\runtimes\base_v51\cloudscape\lib set CLASSPATH=%DB2J_LIB%\db2j.jar;%DB2J_LIB%\db2jtools.jar;%DB2J_LIB%\db2jcview.jar ;%DB2J_LIB%\jh.jar %JAVA_HOME%\bin\java -Dij.connection.myconn=jdbc:db2j:WSSAMPLE;create=true -Dcloudscape.system.home=%DB2J_LIB% -ms16m -mx32m com.ibm.db2j.tools.ij Tables.sql
4. Save and close the file. 5. Open a command window and change to the directory where these files are. 6. Execute the CreateCloudTable.bat to create and populate the sample database (WSSAMPLE). 7. After executing the batch file, the WSSAMPLE folder is created in the c:\HRproject\database. 8. Display the directory to make sure WSSAMPLE was created.
825
6. In the Create a Data source window, select Cloudscape JDBC provider and data source V5. Click Next. 7. Enter the following values for the Modify Data Source window. Name: WSSample JNDI Name: jdbc/WSSample
826
8. Click Next. 9. Select databaseName from Resource Properties, enter C:\HRproject\database\WSSAMPLE as the value (or the path where the database is stored).
827
10.Click Finish. 11.Close the Server configuration editor. 12.A window will prompt you if you want to save changes. Select Yes. Important: As a general rule, make sure there are no other connections active to the database you plan to use as this could create conflict errors.
828
ii. Uncheck the option Enable multiple pages when deploying portlet projects.
2. Save the configuration and close the editor. 3. Add the portlet projects to the WebSphere Portal V5.1 Test Environment server: a. Right-click the server and select Add and remove projects. b. From the list of available projects, select HRPortlet168 and click Add > to add this project to the list of configured projects on the right side. c. Repeat the same steps to add the HRDetails168 project. d. Click Finish. 4. Right-click HRPortlet168. Then click Run Run on server, select WebSphere Portal V5.1 Test Environment server and click Finish. Note: This will load your project into the test environment so that you can view it in Rational Application Developer internal browser. It may take few minutes for this process to complete. 5. Wiring. Click Edit Page and you will see the two portlets in the Edit Layout page.
829
6. Click the Wires tab. You will see different combos to select a source portlet, a property to send, a target portlet and a property or action to receive. If you select Global wire, that means that their effects are manifested to all users that can view the page and portlets.
7. Select the following values: a. HRPortlet168 as the Source Portlet. b. Sending combo: select department ID. c. HRPortlet168 as the Target Portlet. d. Receiving combo: select Show all employees, department ID. e. Check Global wire. f. Click Add wire.
830
8. You will now see the new wire as shown in Figure 26-14.
9. Click Done. 10.Switch to Edit mode in HRPortlet168 Source portlet by clicking the pencil icon in the top right-hand corner of the portlet. 11.Enter the following information and click Submit: a. User: db2admin
831
12.The HRPortlet168 Source portlet now displays the jobs table, including links in the DEPT_NO column. Before you can use the links, you have to configure the user and password in HRDetails168 portlet. 13.Switch to Edit mode in HRDetails168 (HRPortlet168 Target) portlet by clicking the pencil icon in the top right-hand corner of the portlet. 14.Enter the following information and click Submit: a. User: db2admin b. Password: db2admin c. It is not necessary to enter an SQL statement. 15.In the source cooperative portlet View mode, click a DEPT_NO column link, for example C01.
832
16.You will see all the employees for this department listing in HRDetails168 portlet.
Figure 26-17 HRDetails168 portlet displays all employees for same department
833
834
27
Chapter 27.
835
27.1 Overview
In this scenario, two different versions of the same portlet, called Echo portlet, are used. You will change the portlet UID of one of the portlets to be able to run the two portlets in the Portal Test Environment. As and option, you will also change the portlet name and the portlet title to visually identify the portlets. The Echo portlet has a simple form that allows the user to enter a message. When the message is submitted, it is displayed back on the same portlet. In this scenario, the portlets will be enhanced to work in a cooperative portlet environment. The following portlets are used: The source cooperative portlet, named EchoSource, shows a form to submit a message and will be enhanced to send the message to the Cooperative Portlets property broker. The target cooperative portlet, named EchoTarget, will be enhanced to receive the message from the Cooperative Portlets property broker and display it. Note: The Cooperative Portlets property broker uses Struts Actions to notify input properties. The sample scenario is illustrated in Figure 27-1. The Struts source portlet publishes (broadcasts) an output property and the Struts target portlet receives the message.
Message
Filter
2
Message
Property Broker
Message
3
WSDL WebSphere Portal Core
age Mess
Echo Target Filter
836
This scenario shows various cooperative portlet concepts in Struts portlets using the JSR 168 API.For example: You will enable portlet cooperation using a declarative approach. For JSR 168 compliant portlets, WSDL is the only way to provide information about portlet actions as programmatic cooperative portlets are not available for JSR 168 portlets. You will create wires between cooperative portlets using the Portlet Wiring Tool. This is the only way to connect JSR 168 compliant portlets as Click-to-Action is not available for JSR 168 portlets.
837
It is recommended that you change the portlet title to be able to identify the portlets since they will be deployed on the same page. Follow these steps for each resource bundle: 1. Open the resource bundles (EchoPortletResource properties file), located in the EchoSource/Java Resources/JavaSource/echo.nl directory. 2. Add a title in the line javax.portlet.title. For example:
javax.portlet.title=Echo Source portlet
4. Right-click the wsdl folder just created and choose Import... File system from the context menu. 5. Browse the directory where the EchoSource.wsdl file is located and select this file. The WSDL file is provided as additional material of this redbook.
838
6. Make sure you check the Create selected folders only option and click Finish.
839
<xsd:restriction base="xsd:string"> </xsd:restriction> </xsd:simpleType> </xsd:schema> </types> <message name="GetResultsMessageNameResponse"> <part name="input_msg" type="tns:inmsg"/> </message> <portType name="Message_Service"> <operation name="msg_Detail"> <output message="tns:GetResultsMessageNameResponse"/> </operation> </portType> <binding name="MessageBinding" type="tns:Message_Service"> <portlet:binding/> <operation name="msg_Detail"> <portlet:action name="/welcome.do" type="standard-struts" actionNameParameter="spf_strutsAction" caption="show.message" description="Get.Message.from.user"/> <output> <portlet:param name="inmsg" partname="input_msg" boundTo="request-parameter" caption="in.message"/> </output> </operation> </binding> </definitions>
The syntax used in this file is the standard WSDL with some custom extensions for cooperative portlets. The <binding> tag is extended to associate portlet actions with operations. The elements prefixed with portlet: are part of the custom WSDL extension schema: For each operation: The portlet action name must be provided using the name attribute of the action tag. The type attribute must be set as standard-struts to indicate that it is a struts action used with a JSR portlet. Other type attribute options are standard (indicates a standard portlet action, invoked using processAction method), default (indicates a DefaultPortletAction object is used), simple (indicates a simple portlet action String is used) and struts (indicates a Struts action is used).
840
The actionNameParameter attribute always must be set as spf_strutsAction to indicate the property broker to use a special binding to support Struts actions. In the above file you see that this portlet declares a single action called /welcome.do, using the portlet:action element.
<portlet:action name="/welcome.do" type="standard-struts" actionNameParameter="spf_strutsAction" caption="show.message" description="Get.Message.from.user"/>
For each operation parameter: The portlet:param element identifies that the action has an output parameter. The action parameter name must be provided using the name attribute of the param tag. The boundTo attribute may be used to specify where the parameter will be bound, the options are request-parameter, request-attribute, session-attribute or action-attribute. Each operation parameter appears as a child element of a <input> or <output> tag, to specify if it is a parameter consumed or produced by the portlet action, respectively. In the above file, you see that this portlet has produced a parameter named inmsg.
<output> <portlet:param name="inmsg" partname="input_msg" boundTo="request-parameter" caption="in.message"/> </output>
841
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" id="echo.EchoPortlet.0190a47430"> <portlet> ..... <portlet-preferences> <preference> <name>com.ibm.struts.portal.page.view.html</name> <value>index.jsp</value> </preference> <preference> <name>com.ibm.portal.propertybroker.wsdllocation</name> <value>/wsdl/EchoSource.wsdl</value> </preference> </portlet-preferences> </portlet> </portlet-app>
4. Modify the execute method to obtain a reference to the property broker only the first time the method is invoked. For purposes of this sample, you will use the portlet session to set an attribute the first time the method is invoked and avoid referencing the property broker for successive method invocations.
842
JSR 168 portlets using cooperation should be programmed so that they perform correctly in other containers other than IBM Portal container. A good practice is to use a boolean variable that indicates if the PropertyBrokerService interface is available. The variable pbServiceAvailable will set to true only if the service is available. This variable can be used to guard accesses to IBM-only services and it is used to ensure that the portlet can still function in environments where the service is unavailable.
Example 27-4 Obtaining a reference to the property broker service public ActionForward execute(ActionMapping mapping, ActionForm form, PortletRequest request, PortletResponse response) throws Exception { PortletSession session = request.getPortletSession(true); if (session.getAttribute("INIT")==null) { System.out.println("obtaining a reference to the property broker services ======"); session.setAttribute("INIT","INIT"); try { Context ctx = new InitialContext(); PortletServiceHome serviceHome = (PortletServiceHome)ctx.lookup("portletservice/com.ibm.portal.propertybroker.se rvice.PropertyBrokerService"); pbService = (PropertyBrokerService)serviceHome.getPortletService(com.ibm.portal.propertybro ker.service.PropertyBrokerService.class); pbServiceAvailable = true; } catch(Throwable t){ System.out.println("Echo portlet could not find property broker service!"); } } System.out.println("executing action======"); UserBean userBean = (UserBean) form; if (userBean.getInmsg()== "") userBean.setInmsg("No message"); else userBean.setInmsg("Message received: " + userBean.getInmsg()); ActionForward forward = mapping.findForward("result"); return forward; }
843
27.2.5 Internationalization
To support translation of captions and descriptions associated with shared properties or actions you need to provide resource bundles in the appropriate location in the WAR file. These captions and descriptions are shown when you create wires using the Portlet Wiring Tool. For JSR 168 compliant portlets, the resource file is specified using the resource-bundle parameter located in the portlet deployment descriptor (portlet.xml file), for example:
<resource-bundle>echo.nl.EchoPortletResource</resource-bundle>
Caption and description attributes of the portlet:action and portlet:param elements in WSDL file specify the name of the key in the resource bundles where the value of these attributes is to be retrieved. For example:
<portlet:action name="/welcome.do" type="standard-struts" actionNameParameter="spf_strutsAction" caption="show.message" description="Get.Message.from.user"/> <output> <portlet:param name="inmsg" partname="input_msg" boundTo="request-parameter" caption="in.message"/> </output>
The values of these attributes are obtained from the appropriate resource bundle (EchoPortletResource properties file), located in the EchoSource/Java Resources/JavaSource/echo.nl directory. For example:
Example 27-5 EchoPortletResource.properties. # # PortletInfo Resource Bundle # javax.portlet.title=Echo portlet Source javax.portlet.short-title= javax.portlet.keywords= show.message=Show message in.message=Input message Get.Message.from.user=Get message from user
844
It is recommended that you change the portlet title to identify each portlet when they will be on the same page. Follow these steps for each resource bundle: 1. Open the resource bundles (EchoPortletResource properties file), located in the EchoTarget/Java Resources/JavaSource/echo.nl directory. 2. Add a title in the line javax.portlet.title. For example:
845
4. Right-click the wsdl folder just created and choose Import... File system from the context menu. 5. Browse the directory where the EchoTarget.wsdl file is located and select this file. The WSDL file is provided as additional material of this redbook. Be sure to check the Create selected folders only option and click Finish.
846
847
</xsd:simpleType> </xsd:schema> </types> <message name="GetResultsMessageNameRequest"> <part name="input_msg" type="tns:inmsg"/> </message> <portType name="Message_Service"> <operation name="msg_Detail"> <input message="tns:GetResultsMessageNameRequest"/> </operation> </portType> <binding name="MessageBinding" type="tns:Message_Service"> <portlet:binding/> <operation name="msg_Detail"> <portlet:action name="/welcome.do" type="standard-struts" actionNameParameter="spf_strutsAction" caption="show.message" description="Get.Message.from.user"/> <input> <portlet:param name="inmsg" partname="input_msg" caption="in.message"/> </input> </operation> </binding> </definitions>
7. Update the portlet.xml to include a reference of WSDL file, as highlighted in Example 27-8.
Example 27-8 Updating portlet deployment descriptor <?xml version="1.0" encoding="UTF-8"?> <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" id="echo.EchoPortlet.0190a47431"> <portlet> ..... <portlet-preferences> <preference> <name>com.ibm.struts.portal.page.view.html</name> <value>index.jsp</value> </preference> <preference>
848
4. Modify the execute method to obtain a reference to the property broker only the first time the method is invoked. For purposes of this sample, we use the portlet session to set an attribute the first time the method is invoked and avoid to reference the property broker for successive method invocations. JSR 168 portlets using cooperation should be programmed so that they perform correctly in other containers than IBM container. A good practice is to use a boolean variable that indicates if the PropertyBrokerService interface is available. The variable pbServiceAvailable will set to true only if the service is available. This variable can be used to guard accesses to IBM-only services and it is used to ensure that the portlet can still function in environments where the service is unavailable.
Example 27-10 Obtaining a reference to the property broker service public ActionForward execute(ActionMapping mapping, ActionForm form, PortletRequest request, PortletResponse response) throws Exception { PortletSession session = request.getPortletSession(true); if (session.getAttribute("INIT")==null) {
849
System.out.println("obtaining a reference to the property broker services ======"); session.setAttribute("INIT","INIT"); try { Context ctx = new InitialContext(); PortletServiceHome serviceHome = (PortletServiceHome)ctx.lookup("portletservice/com.ibm.portal.propertybroker.se rvice.PropertyBrokerService"); pbService = (PropertyBrokerService)serviceHome.getPortletService(com.ibm.portal.propertybro ker.service.PropertyBrokerService.class); pbServiceAvailable = true; } catch(Throwable t){ System.out.println("Echo portlet could not find property broker service!"); } } System.out.println("executing action======"); UserBean userBean = (UserBean) form; if (userBean.getInmsg()== "") userBean.setInmsg("No message"); else userBean.setInmsg("Message received: " + userBean.getInmsg()); ActionForward forward = mapping.findForward("result"); return forward; }
27.3.4 Internationalization
To support translation of the captions and descriptions associated with shared properties or actions you need to provide resource bundles in the appropriate location in the WAR file. These captions and descriptions are shown when you create wires using the Portlet Wiring Tool. For JSR 168 compliant portlets, the resource file is specified using the resource-bundle parameter located in the portlet deployment descriptor (portlet.xml file), for example:
<resource-bundle>echo.nl.EchoPortletResource</resource-bundle>
Caption and description attributes of the portlet:action and portlet:param elements in WSDL file specify the name of the key in the resource bundles where the value of these attributes is to be retrieved. For example:
<portlet:action name="/welcome.do" type="standard-struts" actionNameParameter="spf_strutsAction" caption="show.message" description="Get.Message.from.user"/> <input>
850
The values of these attributes are obtained from the appropriate resource bundle (EchoPortletResource properties file), located in EchoTarget/Java Resources/JavaSource/echo.nl directory. For example:
Example 27-11 EchoPortletResource.properties # # PortletInfo Resource Bundle # javax.portlet.title=Echo portlet Source javax.portlet.short-title= javax.portlet.keywords= show.message=Show message in.message=Input message Get.Message.from.user=Get message from user
851
Select the option Enable base portlets for portal administration and customization. This option is necessary to create wires. Uncheck the option Enable multiple pages when deploying portlet projects. This allows you to deploy both the EchoSource and the EchoTarget portlets on the same page.
4. Save the changes. 5. From the Project Explorer view, right-click EchoSource project. Then click Run Run on server, select WebSphere Portal V5.1 Test Environment server and click Finish. This will load your project into the test environment so that you can view it in Rational Application Developer Web browser. It may take few minutes for this process to complete. 6. Now you will see the two portlets in the browser. Click Edit Page and select the Wires tab to make the cooperation happen at runtime. You will see different combos to select a source portlet, a property to send, a target portlet and a property or action to receive. If you select Global wire that means that their effects are manifested to all users that can view the page and portlets. 7. Select the following values:
852
Source Portlet: Echo Source portlet Sending: Input message Target Portlet: Echo Target portlet Receiving: Show message, Input message. Check Global wire Click Add wire
8. You will see the new wire as shown in Figure 27-9 on page 854.
853
9. Click Done. 10.Enter a message in the Echo Source portlet, for example: Testing cooperative portlets. 11.Click Submit. 12.The message will be echo back in the source portlet and sent to the Echo Target portlet.
854
855
856
28
Chapter 28.
857
28.1 Overview
There are two things that need to be done in order to access a Web Service through a portlet. The first is have access to a Web Service either through UDDI registry, WSDL URL or running in the Rational Application Developer workspace. And the second is create a portlet that provides access to this Web Service. In Rational Application Developer V6 Web Service Client portlet applications are developed within Faces portlet projects and a wizard is provided to adding Web Services to Faces portlet pages. Figure 28-1 shows how portlet applications can be easily integrated with existent Web Services without the need to write extra code.
Operational System
Figure 28-1 Accessing Web Services from portlet applications
858
b. Use the available wizards in Rational Application Developer V6, to transform this JavaBean into a Web Service so it can be accessed from portlet applications. 2. Using the wizards provided by Rational Application Developer V6, create a Web Services client portlet project to access the sample Web Service. Note: This sample scenario requires of a Web Services be available. A java bean sample code can be downloaded from additional materials. Also, the generated Web Service and a test client are available. See Appendix A, Additional material on page 1003.
3. In the Dynamic Web Project window, click the Show Advanced>> button. 4. Enter the following values: Name: PrimesWebService Servlet version: 2.3 Target Server: Websphere Application Server V5.1 Leave the other values as default.
859
860
7. Click Next. 8. In the Select a Page Template for the Web Site window, check the Use a default Page Template for the Web site box. 9. Select A_blue.htpl.
861
Figure 28-5 Select a Page Template for the Web Site window
10.Click Finish.
862
To review this class, once imported, double-click Primes.java under PrimesWebService/JavaResources/JavaSource/com/itso. 1. Right-click the com.itso new package under the PrimesWebService/JavaResources/JavaSource/ in the ProjectExplorer view. 2. From the context menu, select Import...
863
3. In the Select window, select File System from the Select an import source field. 4. Click Next. 5. In the File System window, click the Browse... button right next to the From directory field and select the directory where download the Primes.java file. 6. Select the directory on the left column; this will show the files contained in that directory. 7. Select the Primes.java file on the right column, by checking the box next to it. 8. Make sure the PrimesWebService/JavaSource/com/itso value is already entered in the Into folder field. If not, click the Browse... button right next to this field and select this value.
864
9. Click Finish.
865
3. Expand the Web Services folder. 4. Select the Web Services wizard.
5. Click Next. 6. In the Web Services window, enter the following values: Web Service type: Java Bean Web Service. Check the Start Web service in Web Project box. Check the Generate a Proxy box. Client proxy type: Java proxy. Check the Test the Web service box.
866
7. Click Next. 8. In the Object Selection Page, click the Browse files... button.
867
10.Click OK. 11.Verify the value in the Bean field in the Object Selection Page window.
12.Click Next. 13.In the Service Deployment Configuration window, accept the default values: a. Server-Side Deployment Selection: Web service runtime: IBM WebSphere Server: WebSphere V5.1 Test Environment @ localhost J2EE version: 1.3 Service project: PrimesWebService EAR project: PrimesWebServiceEAR Web service runtime: IBM WebSphere Server: WebSphere V5.1 Test Environment @ localhost J2EE version: 1.3 Client type: Web Client project: PrimesWebServiceClient EAR project: PrimesWebServiceClientEAR
868
14.Click Next. 15.Click Next in the Service Endpoint Interface Selection window. 16.In the Web Service Java Bean Identity window, deselect the all the methods except the getPrime(int) method. Leave the rest as default.
869
17.Click Next. 18.Leave defaults in Web Service Test Page window, click Next. 19.Leave defaults in Web Service Proxy Page window.
870
20.Click Next. 21.In the Web Service Client Test window, deselect all the methods except the getPrime(int) method. Leave the rest values as default.
871
22.Click Next. 23.In the Web Service Publication window, leave the default values.
872
24.Click Finish. 25.A Web browser will open within the Rational Application Server workspace as shown in Figure 28-18.
873
27.Enter a value in the numDigits field and click the Invoke button. This will show the result as illustrated in Figure 28-20.
Every time you invoke this Web Service to generate a prime number, you will probably get a different result.
874
applications are developed within Faces portlet projects. Rational Application Developer provides tool support for adding Web Services to Faces portlet pages.
2. In the Portlet Project window, click the Show Advanced >> button. 3. Enter the following values: Name: WSClientPortlet WebSphere Portal version: 5.1 Servlet version: 2.3 Target server: WebSphere Portal V5.1 stub EAR project: WSClientPortletEAR Context Root: WSClientPortlet
875
876
877
8. Click Next. 9. In the Portlet Settings window, leave defaults. 10.Click Next. 11.In the Miscellaneous window, leave defaults 12.Click Finish. 13.The WSClientPortletView.jsp will open in the JSP Editor, from the palette select the Web Service item under the Data drawer.
878
14.Drag and Drop the Web Service item form the palette to the WSClientPortletView.jsp.
879
15.The Web Service Discovery Home window will show. Click the Web Services from your workspace link.
Important: The WebSphere Application Server V5.1 Test Environment must be running in order to access the Web Service during the wizard process. 16.The Web Service information is shown, click the Primes.wsdl link.
17.In Web Service from a known URL, select the Port:Primes from the Web Services Information field.
880
18.Click the Add to Project button. 19.The Add Web Service window will open. Leave defaults. The class com.itso.PrimesProxy and the method getPrime(int) of the class for the Web Service should already be enter in the Select a Web Service and Select a method field respectively.
881
20.Click Next. 21.In the Import From window, leave the default values. This information is used to construct the form where the needed values for the getPrimes method will be entered.
22.Click Next. 23.In Result From, leave the default values. These values are going to be used to construct the form where the result will be shown.
882
24.Click Finish. 25.The JSP Editor will open showing the WSClientPortletView.jsp. From the main menu, select File Save.
883
3. In the Define a New Server window, select WebSphere Portal V5.1 Test Environment. 4. Click Finish. 5. Once the server starts, a Web browser will open within the workspace, showing the WSClientPortlet in the WPS V5 Test Environment server.
6. Enter a value in the NumDigits field and click the Submit button.
884
29
Chapter 29.
885
29.1 Overview
The Web Services for Remote Portlets (WSRP) standard allows the integration of remote content and Web applications into an end-user portal, simplifying the effort of portal administrators in selecting and including a rich variety of remote content and applications from external sources into their portals without no programming effort. Some terms used in WSRP are: Producer: is a portal that provides one or more portlets as WSRP services and makes them available to Consumers. Consumer: is a portal that invokes WSRP services from Producer portals and integrates them to be used by its users. Users: use remote portlets integrated in their Consumer portals. Remote portlets: standard portlets that have been published to be WSRP services by a Producer. As illustrated in Figure 29-1, desktop browsers connected to the Consumer portal server can have access to the following portlets: Local portlets in Consumer portal Remote portlets in Producer portal
Consumer Portal
Local Portlets "JSR 168" Portlets
Producer Portal
Remote Portlets Local Portlets
HTTP (SOAP)
HTTP (HTML)
Consumer Users
886
WSRP services are presentation-oriented, providing the application data and the presentation layer of the portlet. On the other hand, Web Services are data-oriented, providing just the application data, as shown in Figure 29-2.
Web Services
Producer Portal
Data
00101101 10001111 11001110 00101101 10001111 11001110 11001110 00101101 10001111 11001110 00101101 10001111 10001111 11001110 00101101 10001111 11001110 00101101
WSRP
Producer Portal
Data
00101101 10001111 11001110 00101101 10001111 11001110 11001110 00101101 10001111 11001110 00101101 10001111 10001111 11001110 00101101 10001111 11001110 00101101
Consumer Portal
Consumer Portal
WSRP standard defines a set of interfaces provided by Producer portals to be exposed to the Consumers. These WSRP interfaces are described in a Web Services Description Language (WSDL) document accessed by the Consumer portal. It provides information to Consumers about how to bind to the Producer. The interfaces are: Service Description: is a self-description of the Producer, its capabilities and its available portlets. It provides further information to Consumers about the Producer and its properties. This interface is mandatory.
887
Markup: is an interface to get and process interactions with markup fragments. It allows to get portlet markups from the Producer and submit portlet requests from Consumer users. This interface is mandatory. Portlet Management: defines operations for cloning, customizing and deleting portlets. It allows to customize and manage remote portlets preferences in Consumer portals. This interface is optional. Registration: an optional interface for Consumers registration in a Producer portal. It allows Producer to identify each Consumer. The following is an example of a WSDL document (Example 29-1).
Example 29-1 WSDL document sample <?xml version="1.0" encoding="UTF-8" ?> <wsdl:definitions targetNamespace="urn:oasis:names:tc:wsrp:v1:wsdl" xmlns:bind="urn:oasis:names:tc:wsrp:v1:bind" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"> <import namespace="urn:oasis:names:tc:wsrp:v1:bind" location="wsrp_v1_bindings.wsdl" /> <wsdl:service name="WSRPService"> <wsdl:port binding="bind:WSRP_v1_Markup_Binding_SOAP" name="WSRPBaseService"> <soap:address location="http://wps51rem.itso.ral.ibm.com:9081/wsrp/WSRPBaseService" /> </wsdl:port> <wsdl:port binding="bind:WSRP_v1_ServiceDescription_Binding_SOAP" name="WSRPServiceDescriptionService"> <soap:address location="http://wps51rem.itso.ral.ibm.com:9081/wsrp/WSRPServiceDescriptionServ ice" /> </wsdl:port> <wsdl:port binding="bind:WSRP_v1_PortletManagement_Binding_SOAP" name="WSRPPortletManagementService"> <soap:address location="http://wps51rem.itso.ral.ibm.com:9081/wsrp/WSRPPortletManagementServi ce" /> </wsdl:port> </wsdl:service> </wsdl:definitions>
888
services as remote portlets. These remote portlets act in the same manner as local JSR 168 portlets in the Consumer portal, regardless of how they are implemented on the Producer portal. To implement WSRP with WebSphere Portal is necessary to do the following tasks: For Producer portals: Provide or withdraw a portlet. For Consumer portals: Register an existing Producer in the Consumer portal. Consume a WSRP service provided by the Producer. For purposes of this scenario we have two WebSphere Portal V5.1 servers, one of them acting as a Provider server and the other one acting as a Consumer server. We will show the necessary steps to implement remote portlets on these servers. Note: WebSphere Application Server Version 5.1.1 Cumulative Fix 1 is required to use WSRP with WebSphere Portal.
889
5. A message will appear indicating that the portlet was successfully provided. A check will appear in the Provided column of the portlet.
890
3. Click Yes to withdraw the portlet. A message will appear indicating that the portlet was successfully withdrawn.
891
892
At the moment of this publication, WebSphere Portal does not support WSRP registration interfaces for Producers, however, Consumers can handle Producers that support this interface. In this section we explain how to create a Producer that does not require registration, using the Web Service Configuration portlet. Note: For information about registering Producer portals using the XML configuration interface, see the WebSphere Portal InfoCenter at:
http://publib.boulder.ibm.com/infocenter/wp51help
1. Log in as an administrator user of the Consumer portal. 2. Select Administration Portlet Management Web Services and click New Producer.
3. Enter a name and the URL for the WSDL service definition of the Producer. A description and user attributes are optional. In WebSphere, the WSDL of the Producer is located at the following URL:
http://<Producer portalhost>:<port>/<wp_contextRoot>/wsdl/wsrp_service.wsdl
For this example, we use the following values: Title: WPS51-Remote URL:
http://wps51rem.itso.ral.ibm.com:9081/wps/wsdl/wsrp_service.wsdl
Click OK.
893
894
895
3. Select a Producer (the Producer created in Registering an existing Producer portal on page 892).
896
4. Select the Web service you want to integrate from the Producer selected previously. Click OK.
5. The remote portlet has been integrated into the Consumer portal and it is available like any other local portlet.
897
Remote portlets appear in WebSphere Portal Consumers under the name RP:[<remote portlet name>], for example RP:[ActionEvent portlet]. You can see remote portlets selecting the option Administration Portlet Management Portlets and searching for all the portlets whose title starts with the prefix RP, as shown in Figure 29-14.
898
29.3 Security
Web Services for Remote Portlets (WSRP) standard does not specify additional security mechanism, therefore the same security considerations than for other kinds of Web services may be applied for WSRP. In WebSphere Portal implementation of WSRP the following security options can be configured:
899
3. Consumer authentication for portal users, using SSL. 4. Consumer and portal user authentication (if supported), using SSL with Client Certificate Authentication in Consumer portals and portal users.
Consumer Portal
Producer Portal
Note: For more information about implementing SSL between a producer portal and a consumer portal see the WebSphere Portal InfoCenter at:
http://publib.boulder.ibm.com/infocenter/wp51help
900
30
Chapter 30.
Portlet debugging
Rational Application Developer provides a powerful debugger tool for suspending launches, stepping through the program code, and examining the contents of variables. This chapter gives you a brief introduction to the techniques used to debug portlets and discusses how to detect error during compile and runtime.
901
30.1 Overview
For software development, we can distinguish two different kinds of errors: Compile errors appear during compile and are thrown by the Java compiler. A typical example for this type of error is an improperly typed method or class name. This type of error can be found very easily because the compiler checks the code and presents a meaningful message. There are also runtime errors, which cannot be found by the compiler; thus they appear only during runtime. An example might be a loop stepping through an array with a size smaller than the loop variable. These kinds of errors are typically fixed using a debugger.
902
To create a compile error, proceed as follows: 1. In the Project Explorer view, select ActionEvent Java Resources JavaSource actionevent ActionEventPortlet.java as shown in Figure 30-2 on page 904. Double-click the Java file; it will open in the editor in the upper right-hand portion of the screen. If the file is already opened, scroll to the top.
903
3. Place the letter x at the beginning of this statement to create an error. See Figure 30-3.
4. Press Ctrl-S to save this file. 5. The compilation process fails due to the error you introduced. In the Problems view in the lower right-hand portion of the screen, an error message appears, as shown in Figure 30-4 on page 905.
904
6. Double-click the red error icon in the Problems view. The problem area in the code will be highlighted. Tip: If you cannot see the whole error message in the Problems view because of its length, move over the red error symbol to the left of the Java editor. A small help window appears with the whole error message. 7. Remove the letter x before public statement to return the code to its original condition. Press Crtl-S. The code will be validated again, and the error message will disappear from the Problems view. 8. Close the editor by clicking the X on the ActionPortlet.java tab.
905
2. The editor will open in the upper right-hand corner of the screen. 3. In this portlet class, there are five methods: init doView doEdit actionPerformed getJspExtension
4. In the actionPerfomed method, you will set a breakpoint. Place the cursor on the setAttribute statement as highlighted on Example 30-1.
Example 30-1 Setting a breakpoint public void actionPerformed(ActionEvent event) throws PortletException { if( getPortletLog().isDebugEnabled() ) getPortletLog().debug("ActionListener - actionPerformed called"); String actionString = event.getActionString(); PortletRequest request = event.getRequest(); if(actionString.equalsIgnoreCase(ACTION_RED)){ String value = "Action <FONT color=\"#ff0000\">RED</FONT>"; PortletData portData = request.getData(); try{ portData.setAttribute("value", value); portData.store(); } catch (AccessDeniedException ade){ }catch (IOException ioe){} }
906
if(actionString.equalsIgnoreCase(ACTION_BLUE)){ String value = "Action <FONT color=\"#0000ff\">BLUE</FONT>"; PortletData portData = request.getData(); try{ portData.setAttribute("value", value); portData.store(); } catch (AccessDeniedException ade){ }catch (IOException ioe){} } }
Note: This is the statement where the actionPerformed method has identified the action and set an attribute (setAttribute) in the request object; the attribute is to be rendered later in View mode. 5. Right-click the context bar to the left of the setAttribute statement, then select Toggle Breakpoint from the context menu as shown in Figure 30-6.
6. After setting the breakpoint, you should see a blue dot on the marker bar, that means the breakpoint is enabled. Enabled breakpoints are shown with a checkmark overlay after their class has been loaded by the Java VM. If the breakpoint is disabled, it will not cause threads to suspend and is indicated with a white circle.
907
7. Click the Servers tab. If the WebSphere Portal V5.1 Test Environment has been started, right-click and select Stop and wait until you see the message indicating that WebSphere Portal has stopped. 8. Once the Test Environment is stopped, right-click ActionEvent from the Project Explorer view, then select Debug on Server...
908
Note: Starting WebSphere Portal Test Environment in debug mode will take a few minutes.
10.Click the Debug button at the upper-right corner of the workbench to change to the Debug perspective. It will look as shown in Figure 30-10 on page 910.
909
11.The portlet will run on the built-in browser shown in the middle left panel (in the Debug perspective) or the upper right panel (in the Portal perspective). 12.Now click Edit icon to select an action.
910
13.Select the Red Action button; remember that the breakpoint has been set in this action path (actionPerformed method). 14.The action (Red Action) will now execute up to the breakpoint you have previously set. When the breakpoint is reached, the Java editor displays the code and the statement with the breakpoint is highlighted.
911
15.Place the cursor in the context bar where the breakpoint is located and right-click to select Toggle Breakpoint from the context menu to remove the break. Take a moment to examine the code before proceeding. 16.From the Debug perspective, select the Variables tab. 17.Locate the variable with name value. 18.Select the value variable (value=Action <FONT color=\"#ff0000\">RED</FONT>), then right-click it and select Change Value... from the context menu.
912
20.Click the Resume icon located in the Debug tab (green triangle on the left side of the toolbar icons) to let the portlet continues its execution.
Figure 30-15 Click the Resume icon to resume the execution of the portlet
21.Select the Web browser tab from the display toolbar in the middle left panel of the screen. The result of the action will be displayed. Note that the action displayed in View mode is now Action GREEN ...was selected ! (and not Action RED ...was selected ! as originally shown).
913
914
31
Chapter 31.
915
31.1 Overview
Testing and debugging portlets are tasks that involve running portlets on a server, either on the test environment server within the workbench (local), or on a separate portal server (remote). A WebSphere Portal Server Attach is a server type that allows Rational Application Developer tool to attach to a remote WebSphere Portal Server already started, so that you can run and debug a portlet application running on a remote WebSphere Portal system accessible through a network connection. The sample development workstation and the remote Portal server are illustrated in Figure 31-1.
Development
Rational Application Developer v6 Sample portlets
Runtime
IBM WebSphere Application Server v5.1 IBM WebSphere Portal v5.1
In order to debug a portlet on a remote server, the following main task must be performed: 1. Prepare the WebSphere Portal server to work in debug mode. 2. Create a user on the WebSphere Portal server for the developer that will be performing debug tasks (optional). 3. Create a WebSphere Portal Server Attach on the Rational Application Developer workbench. 4. Run and debug a portlet on the remote WebSphere Portal server. Figure 31-2 on page 917 shows how portlets are debugged on a remote server.
916
Debug Perspective
Web Browser
Portlet Toolkit label
user/portlet page
Sample Portlet
31.2.1
917
6. In the Additional Properties section, scroll down and select Debugging Service.
918
Select the Startup check box. Leave the default value for the JVM debug port (7777). Enter the following JVM debug arguments:
-Djava.compiler=NONE -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=7777
8. Click Ok 9. Click the Save link. 10.Click the Save button to apply changes to the master configuration. 11.Configuring the debugging service should also configure the JVM. Review this by doing the following: a. In the Administrative console of the WebSphere Application Server, expand Servers Application Servers and select WebSphere_Portal b. Click Process Definition link in Additional Properties and scroll down to select Java Virtual Machine. c. In the General Properties section, verify the following: The Debug Mode check box must be selected. The Debug arguments must be:
-Djava.compiler=NONE -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=7777
919
Note: The address value must match with the value enter in the previous procedure. For this scenario the default value 7777 is used. 12.Logout from WebSphere Application Server Administrative console. 13.Stop the WebSphere Application Server by clicking Start Programs IBM WebSphere Application Server Stop the Server. 14.Start the WebSphere Application Server 15.Restart the WebSphere Portal server. Stop the Portal server by clicking Start Programs IBM WebSphere Portal Server Stop the Server. Start the Portal server by clicking Start Programs IBM WebSphere Portal Server Start the Server. Wait a few minutes for Portal to be started (open for e-business message).
920
3. In the Define a New Server window, enter the following values: Host Name: the hostname of the remote portal server, in our scenario it is wps51.itso.ral.ibm.com Select the server type: WebSphere Portal V5.1 Server Attach
921
4. Click Next. 5. In the Server Ports window, leave the default values. Note that the JVM port value must be equal to the JVM debug port entered in the WebSphere Application Server of the remote portal. Click Next.
6. In the next window, set the values according to the remote WebSphere Portal server configuration. You must enter the WebSphere Portal administrator user and the user you want to log on to the server for running the portlets (wpsadmin for this scenario).
922
Note: The WebSphere Portal Install location field is only used for portal projects and is not covered in this scenario.
7. Click Next. 8. Leave the default values for the Publishing Settings window. This is information needed only for portal projects. Click Next.
923
924
2. Go to the actionPerformed method and select the line highlighted in Example 31-1.
Example 31-1 Setting a breakpoint public void actionPerformed(ActionEvent event) throws PortletException { if( getPortletLog().isDebugEnabled() ) getPortletLog().debug("ActionListener - actionPerformed called"); String actionString = event.getActionString(); PortletRequest request = event.getRequest(); if(actionString.equalsIgnoreCase(ACTION_RED)){ String value = "Action <FONT color=\"#ff0000\">RED</FONT>"; PortletData portData = request.getData(); try{ portData.setAttribute("value", value); portData.store(); } catch (AccessDeniedException ade){ }catch (IOException ioe){} } if(actionString.equalsIgnoreCase(ACTION_BLUE)){ String value = "Action <FONT color=\"#0000ff\">BLUE</FONT>"; PortletData portData = request.getData(); try{ portData.setAttribute("value", value); portData.store(); } catch (AccessDeniedException ade){ }catch (IOException ioe){} } }
3. Set a breakpoint in the line portData.setAttribute("value", value); by selecting the code line, right-clicking the marker bar (first left column of the JSP Editor) and selecting Toggle Breakpoint, as shown in Figure 31-14 on page 926.
925
4. A new breakpoint marker will appear on the marker bar. Enabled breakpoints are identificated with a blue circle.
5. From the Project Explorer view, right-click the ActionEvent project and select Debug Debug on Server...
926
6. In the Define a New Server window, select WebSphere Portal V5.1 Server Attach.
927
7. Click Finish. It may take few minutes for this process to complete. 8. An internal Web browser will open within the workspace and the portlet will be shown.
928
10.Click the Red Action button. 11.If the following message box appears, click Yes.
929
12.The Debug perspective will be open and since the breakpoint is reached, the Java editor displays the code and the statement with the breakpoint highlighted.
13.In the Variables tab, right-click value=Action <FONT color=#ff0000>RED</FONT> and select Change value...
930
15.Click OK. 16.From the Debug tag, click the Resume button to let the portlet continues its execution.
931
17.View the internal Web browser where the portlet is shown. Note that the action displayed in View mode is now Action GREEN ...was selected !
932
2. You can add a new Web browser or device emulator by providing a name, the location of the executable program and any parameter you want to pass to the Web browser or device emulator. 3. You can edit the values for an existing Web browser by selecting it and clicking the Edit button 4. You can delete a Web browser or device emulator entry by clicking the Remove button. 5. Select the check in the left side of the Web browser entry to indicate Rational Application Developer to use this browser.
933
934
32
Chapter 32.
935
32.1 Overview
Importing a Portal configuration from an existing Portal site is one of several ways available to create a new Portal project. It is also a recommended practice since the new and updated configuration will overwrite the original Portal configuration of the Portal site where the deployment takes place; in this way, part of the default original configuration can be lost. In this section, you will be guided to execute the following tasks: Create a connection to the Portal server Import a WebSphere Portal Server V5.1 configuration Modify the Layout navigation by creating additional labels, pages, etc. Modify the Layout content areas by adding new columns and a portlet to a page Change the page look and feel to use a different theme Test the new configuration on the WebSphere Portal V5.1 Test Environment The development workstation components for this scenario can be seen in Figure 32-1 on page 937.
936
Development
IBM Rational Application Developer V6.0 Portal Tools V6.0 for Rational Application Developer Portal Project
Runtime
IBM WebSphere Portal V5.1 Test Environment
Runtime
WebSphere Portal Server V5.1
937
1. For this scenario, a Map Network Drive connecting to the WebSphere Portal Server V5.1 machine is created. This connection will use W as the drive letter. Note: Use the proper user ID and password to access the Portal server machine.
2. If not already running, start the Rational Application Developer; select Start Programs IBM Rational IBM Rational Application Developer V6.0 Rational Application Developer. 3. Select New Other from the File menu. 4. In the Select a wizard window, drag down to the Server folder and expand it. 5. Select Server.
938
6. Click Next to define the server. 7. In the Define a New Server window, enter the following values: a. Host name: wps51. This is the host name of the WebSphere Portal server you want to import into Rational Application Development. b. Select the server Type: WebSphere Portal V5.1 for Import, Export & Deploy.
939
8. Click Next. 9. In the WebSphere Portal Settings windows: a. Except for Install location, accept default values for the fields. b. Install location: enter the location where WebSphere Portal is installed. In this sample scenario, the correct location is: w:\WebSphere\PortalServer Note: If your portal server uses values other than the default values, you must enter the proper paths for the fields shown in Figure 32-5 on page 941.
940
10.Click Next. 11.In the Publishing Settings windows, enter the following values: a. Transfer method can be local or FTP. For this scenario, select the following transfer method:
Local Copy
b. Web application:
w:\WebSphere\AppServer\installedApps\wps51\wps.ear
c. Library:
w:\WebSphere\PortalServer\shared\app
941
12.Click Finish to create the server. The Add and Remove Projects configuration window is not needed to configure for the Import, Export & Deploy server connection. 13.The newly created server can now be seen in the Servers view, as illustrated in Figure 32-7.
942
943
4. Click Finish. 5. You will be prompted with a message indicating that this process may take a long time and it should not be interrupted. 6. Click Yes to continue. 7. A status bar at the bottom of the current window shows the process status as shown in Figure 32-9.
8. When the process finishes you can verify that TestPortalServer has been created in the Dynamic Web Projects folder. Also, the following EAR files have been created in the Enterprise Applications folder: TestPortalServerEAR TestPortalServerPortletsEAR
944
Adding navigation
Execute the following instructions: 1. Expand the TestPortalServer under Dynamic Web Projects by clicking the plus symbol next to it and double-click Portal Configuration.
945
2. The Portal Designer editor will open in the center of the workbench. This view serves as the design editor for your portal project. The Portal configuration shows the WebSphere Portal Server V5.1 layout as illustrated in Figure 32-12.
3. Add a new Label next to the Welcome page a. Click Label from the palette to create a new label. b. Drag it to the portal configuration on the Portal Designer and next to the Welcome label as shown in Figure 32-13 on page 947.
946
Tip: Once you position the cursor arrow, a plus symbol appears meaning that you can create the label at that location.
c. In the Properties view, select the Title tab. d. Change the Title from New Label to WebSphere as shown in Figure 32-14.
e. Select File Save to save the new values. 4. Create a new page a. In the Outline view, right-click the WebSphere label you just created. b. Select Insert Page As Child.
947
5. When a New Page is created the Outline takes you to the Layout root node, change to the Navigation root node so you can see the result hierarchy for the WebSphere label. For WebSphere Portal V5.1 portal projects, Content Root is the root navigation node. Select New Page in the outline view.
6. In the properties view, select Title tab and change the Title from New Page to Sample.
948
7. Select File Save to save the new values. The resulting hierarchy will now look as shown in Figure 32-18.
949
next to the highlighted column indicating where the new column will be placed.
4. After adding the second column, the result should look as illustrated in Figure 32-20.
5. Select File Save. Note: The column widths can be changed using the mouse pointer, or by setting new values in the Properties view.
Importing a portlet
Execute the following steps to import the HelloWorld sample portlet: 1. Select File Import 2. From the Select window, select WAR file.
950
3. Click Next. 4. In the WAR Import window enter the following values: a. WAR file. The location of the portlet WAR file. For example:
c:\LabFiles\Portal Project\HelloWorld.war
b. Web Project. This field will be automatically set when the WAR file is selected. HelloWorld in this sample scenario. c. Target server: WebSphere Portal V5.1 stub. d. Take defaults for other values. 5. Click Finish. 6. Select File Save
3. The Insert Portlet dialog box appears, select the HelloWorld portlet. Note: All portlets associated with the workspace will appear in the Insert Portlet dialog box.
951
4. Click OK and select File Save. Tip: Adding portlets to a page can also be done by right-clicking the column or row where the portlet is to placed and selecting the Insert Portlet option.
952
4. The Insert Portlet dialog box appear, select the portlet. 5. Click OK. 6. Select File Save
953
Figure 32-25 Adding portlets from the layout node in the Outline view
3. The Insert Portlet dialog box appears, select the portlet. 4. Click OK. 5. Select File Save.
954
3. In the Define a New Server window select WebSphere Portal V5.1 Test Environment. Click Finish.
955
5. The internal Web browser will open within the Rational Application Developer workbench, showing the Portal Server Welcome page.
Note: When you run a portal project in the local Portal Test Environment, some portlets may display error messages. 6. Click the WebSphere page you created.
956
8. Stop the Portal server. In the Servers view right-click the WebSphere Portal V5.1 Test Environment and select Stop.
957
Note: Child pages for this label will also be displayed with the Finance theme.
5. Select File Save to save the new property values. 6. From Project Explorer right-click the TestPortalServer and select Run Run on Server... 7. In the Define a New Server window select WebSphere Portal V5.1 Test Environment 8. Click Finish. Wait until the browser is started showing the WebSphere Portal Server again. 9. Select the WebSphere label. The Finance theme that you previously selected will now be shown.
958
10.Stop the server. In the Servers view right-click the WebSphere Portal V5.1 Test Environment and select Stop.
3. Test again the Portal Project, the result should look as illustrated in Figure 32-36.
959
960
33
Chapter 33.
961
33.1 Overview
In this section, you will be guided to execute the following tasks: Create a new theme Edit a new theme. Edit styles Apply new themes and skins. Test the new configuration on WebSphere Portal V5.1 Test Environment. Export and publish the Portal project from Rational Application Developer to WebSphere Portal.
962
4. Click Next. 5. In the Select Skins window, do the following: a. Select Shadow, Outline, Clear and Fade as skins allowed for this theme. b. Select the Outline skin and click Set as Default Skin. c. Click Finish.
963
6. As a result you should find a new theme entry in the folder Themes under TestPortalServer, as shown in Figure 33-3 on page 965. The associated files will be under PortalContent\themes\html\MyCorp.
964
965
3. By default, the Page Designer shows the JSP tags with symbols. You can hide these tags as follows: a. From the menu, select Window Preferences Web Tools Page Design Editing Symbols. b. Select None and click OK as illustrated in Figure 33-5 on page 967.
966
4. Remove the following design elements from the theme using the Page Design: a. Select My Favorites combo box and press the Delete key. b. Repeat this procedure to delete the little blue icon (to the right of My Favorites combo box). c. Repeat the same procedure to delete the label Go (to the right of the blue icon).
967
Figure 33-6 Delete My Favorites combo box, blue icon and Go label
5. Select File Save. 6. Use the File system option to import two images that will be used in this scenario: a. Select File Import File system. b. Select the files d-w_logo.gif and line.gif as shown in Figure 33-7 on page 969. c. Click Finish to import the files into the following subdirectory:
TestPortalServer\PortalContent\themes\html\MyCorp
968
7. Change the background image: a. Get to the Switch Active Document, right-clicking the Design area of the Default.jsp file and select Switch Active Document. b. Right-click the current background image.
Background Image
969
f. You can view the active properties of the selected class by clicking >>Preview Properties button. g. Leave the default values in the Edit Style window and click OK.
970
h. Leave the style that appears selected by default in the Select one style window and click OK.
i. In the Set Style Properties window, select Background and modify the following values : Color: #FFFFFF Image: browse to the TestPortalServer\themes\html\MyCorp\d-w_logo.gif Repeat: No repeat Top: Center
971
j. Click OK. k. Also notice that the Styles.css file has been opened in the Page Designer. l. Select File Save all 8. The resulting new style should looks as shown in Figure 33-14.
972
9. Change the PlaceBarInclude.jsp: a. Select the Switch Active Document to switch to PlaceBarInclude.jsp
Tip: Selecting the Source tab of the Page Designer, you will see the JSP selected in the Switch Active Document. In this Editor you can also select any of the embedded JSP to edit it, except the Control.jsp of Skins. b. Right-click the purple row of the theme, select Style Show Applied Styles from context menu.
973
974
d. Leave the default values in the Edit Style window and click OK. e. Leave the style that appears selected by default in the Select one style window and click OK. f. In the Set Style Properties window, select Background and modify the following values : Image: browse to the TestPortalServer\themes\html\MyTheme\line.gif Repeat: Repeat Horizontally
975
g. Click OK. h. Select File Save all 10.The resulting new style should looks as shown in Figure 33-19.
976
from TestPortalServer\PortalContent\Project Explorer\themes\html\MyCorp\ie\en\Style.css. Note: The themes\html\MyCorp\ie\en\Style.css file is the default style sheet. In case you need to modify any other style sheet, you can find it in the themes root. For example, in our scenario the themes root is: TestPortalServer\PortalContent\Project Explorer\themes\html\MyCorp
b. Right-click the Unselected Label style rule and select Edit Style Rule [.wpsUnSelectedPlace, .wpsUnSelectedPlace:visited, .wpsUnSelectedPlace:hover, .wpsUnSelectedPlace:active]... option, as shown in Figure 33-21 on page 978.
977
c. In the Set Style Properties window, do the following: Select Background and change the value Color to #d6cfe7.
978
Select Border and change the value Color (right and left) to #d6cfe7.
d. Click OK.
979
2. Edit the Unselected Label Link. a. Right-click the Unselected Label Link style rule and select Edit Style Rule [.wpsUnSelectedPlaceLink, .wpsUnSelectedPlaceLink:visited, .wpsUnSelectedPlaceLink:hover, .wpsUnSelectedPlaceLink:active]... option. b. In the Set Style Properties window, do the following: Select Background and change the value Color to #d6cfe7.
980
c. Click OK. d. Select File Save to save the Styles.css file. 3. Edit a Skin. a. Select the Switch Active Document to select Control.jsp (/TestPortalServer/PortalContent/skins/html/clear/Control.jsp). Note: Notice how the corresponding embedded element is highlighted in the editor, when you mouse over the jsp files in the list.
981
b. Click the Title row of the skin by moving the mouse next to the left margin of the skin until a black arrow appears, as shown in Figure 33-28 on page 983.
982
c. The properties for this element are already active in the properties view. In the Background option change the value of Color to #e5eff7.
d. Select File Save to save the changes. 4. The final theme should look like Figure 33-30 on page 984.
983
984
Note: Note how the child pages for this labels are also displayed with the new theme. 5. Select File Save.
985
986
3. In the Define a New Server window, select WebSphere Portal V5.1 Test Environment.
987
6. A Web browser will open within the Rational Application Developer workbench showing the Portal Server Welcome page.
988
8. The new themes and skins are shown in the WebSphere Portal V5.1 (Figure 33-40).
989
9. In the Servers view, right-click the WebSphere Portal V5.1 Test Environment. 10.Select Stop.
990
3. Select Portal Project Deploy Set from Select window and click Next.
991
4. In the Portal Project Export window, enter the following values: Portal Project EAR: TestPortalServer Destination: c:\PortalProject Select the portal server that you want to use: WebSphere Portal V5.1 Import, Export & Deploy. This is the portal connection created in the scenario described in Chapter 32, Updating a portal layout on page 935.
Important: Do not attempt to manually deploy the exported files to a portal server other than the one you selected in step 4. The generated files contain information from this portal server and the procedure will not work in other servers.
992
5. Click Finish. 6. Click Yes in the message box that appears alerting the following: This process may take a long time and should not be interrupted. Do you wish to continue? Note: Even the Export wizard does not automatically update your project with server information, it needs to communicate with the portal server during export. The following files are generated by the Export wizard: WPS.ear Deployproject.xml for deployment portal configuration. DeployInstructions.txt file with instructions for deploying to a server. These files could also be included: WAR files for each portlet project used in the portal project. DeployPortlets.xml script for deploying portlets.
993
Important: You can deploy the project only into the Portal server specified in the RAD Export wizard. 1. Copy the generated files in a directory of the server where WebSphere Portal is running. 2. Open the DeployInstruccions.txt from the destination directory where the exported files where created.
994
Each step must be successfully completed before starting subsequent steps. Step 1 Open a command prompt and change directories to the Deployment Manager bin directory. For example, c:\WebSphere\DeploymentManager\bin, or if the Deployment Manager is not installed: c:\WebSphere\appserver\bin. Copy the wps.ear that was exported by IBM Rational Application Developer to this directory. Use the wsadmin command to update the WebSphere Portal EAR. This action will automatically cause the application to be synchronized across each node in the cluster. Windows: wsadmin.bat -user <admin_user_id> -password <admin_password> -c "$AdminApp install wps.ear {-update -appname wps}" Unix: wsadmin.sh -user <admin_user_id> -password <admin_password> -c "$AdminApp install wps.ear {-update -appname wps}" where: <admin_user_id> is the administrator's user ID <admin_password> is the administrator's password Note: Updates to the configuration of a WebSphere Portal cluster must occur on the Deployment Manager and resynchronized with the other nodes in the cluster. If updates are made to individual nodes in the cluster, the updates will be lost when the master configuration on the Deployment Manager resynchronizes with the nodes again. Step 2 Copy each of the portlet application WAR files to <wps_home>\installableApps on your server. The following is a list of portlet application WAR files that you must copy: HelloWorld.war Step 3 Copy DeployPortlets.xml to <wps_home>\bin and execute the following XMLAccess command from the same location Windows: xmlaccess -in DeployPortlets.xml -user <WpsAdminUser> -pwd <WpsAdminPassword> Unix: xmlaccess.sh -in DeployPortlets.xml -user <WpsAdminUser> -pwd <WpsAdminPassword>
995
Step 4 Copy DeployProject.xml to <wps_home>\bin and execute the following XMLAccess command from the same location Windows: xmlaccess -in DeployProject.xml -user <WpsAdminUser> -pwd <WpsAdminPassword> Unix: xmlaccess -in DeployProject.xml -user <WpsAdminUser> -pwd <WpsAdminPassword> Step 5 To ensure that string changes in the project are correctly used by the portal server, copy the following list of properties files from the 'nls' directory to '<wps_home>/shared/app/nls', where <wps_home> is the installed location of WebSphere Portal server. CSRes.properties CSRes_ar.properties CSRes_cs.properties CSRes_da.properties CSRes_de.properties CSRes_el.properties CSRes_en.properties CSRes_es.properties CSRes_fi.properties CSRes_fr.properties CSRes_hu.properties CSRes_it.properties CSRes_iw.properties CSRes_ja.properties CSRes_ko.properties CSRes_nl.properties CSRes_no.properties CSRes_pl.properties CSRes_pt.properties CSRes_pt_BR.properties CSRes_ro.properties CSRes_ru.properties CSRes_sv.properties CSRes_th.properties CSRes_tr.properties CSRes_uk.properties CSRes_zh.properties CSRes_zh_TW.properties Step 6 Browse to the portal server administration pages. Using the administrative portlets, set access control as needed on the deployed configuration.
996
Note: If the portal server is configured to use a HTTP port other than port 80, append the parameter '-url <portal config url>' to the XMLAccess commands where <portal config url> is of the form 'http://host:port/wps/config'. For more information refer to the following topics in WebSphere Portal InfoCenter: Administering your portal\XML configuration interface Designing your portal\Deploying customized themes and skins Designing your portal\Customizing the portal
4. To Verify that WebSphere_Portal and Server1 are running, open a command window, and change to directory c:\WebSphere\AppServer\bin 5. Type the command serverStatus -all
6. If any of the servers is stopped, enter startServer and the name of the server.
Example 33-2 Starting server1 c:\WebSphere\AppServer\bin>startServer server1
or
Example 33-3 Starting PortalServer c:\WebSphere\AppServer\bin>startServer WebSphere_Portal
In case portal security is enabled also add to the command the suffix -user user -password password.
997
7. Once both servers are running, continue with the steps specified in the DeployInstruction.txt file. Copy the wps.ear file created by the Export wizard to c:\WebSphere\AppServer\bin Important: It is recommendable that back up your Portal in case you want to return the original configuration. 8. Update the WebSphere Portal EAR using the wsadmin command as shown: wsadmin.bat -user wpsadmin -password wpsadmin -c "$AdminApp install wps.ear {-update -appname wps}"
9. Copy the HelloWorld.war portlet application WAR file to c:\WebSphere\PortalServer\installableApps on your server. 10.Copy DeployPortlets.xml and DeployProject.xml to c:\WebSphere\ProtalServer\bin 11.Execute the following XMLAccess command from c:\WebSphere\ProtalServer\bin xmlaccess -in DeployPortlets.xml -user wpsadmin -pwd wpsadmin -url http://wps51.itso.ral.ibm.com:9081/wps/config
998
12.Execute the following XMLAccess command from c:\WebSphere\ProtalServer\bin xmlaccess -in DeployProject.xml -user wpsadmin -pwd wpsadmin -url http://wps51.itso.ral.ibm.com:9081/wps/config
13.Copy the properties files from the 'nls' directory to c:\WebSphere\PortalServer\shared\app\nls 14.Stop the server.
999
c:\WebSphere\AppServer\bin\stopServer WebSphere_Server
16.In a Web browser enter the Portal Server url to open the site. For example, http://wps51.itso.ral.ibm.com:9081/wps/portal. The Welcome portal page will be displayed. 17.Login to the portal by enter the user wpsadmin and password wpsadmin. The portal should look as Figure 33-49. Notice that the WebSphere page was created as part of the sample scenario described in Chapter 32, Updating a portal layout on page 935.
18.Click WebSphere Page. The page will be shown with the MyCorp theme.
1000
1001
1002
Appendix A.
Additional material
This redbook refers to additional material that can be downloaded from the Internet as described below.
Select the Additional materials and open the directory that corresponds with the redbook form number, SG246681.
1003
1004
Related publications
The publications listed in this section are considered particularly suitable for a more detailed discussion of the topics covered in this redbook.
IBM Redbooks
For information about ordering these publications, see How to get IBM Redbooks on page 1007. Note that some of the documents referenced here may be available in softcopy only. Rational Application Developer V6 Programming Guide, SG24-6449 IBM WebSphere Portal V5 A Guide for Portlet Application Development, SG24-6076 IBM WebSphere Portal for Multiplatforms V5.1 Handbook, SG24-6689 IBM WebSphere Everyplace Access Version 4.3 Handbook for Developers, SG24-7015-01 IBM WebSphere Everyplace Access V5 Handbook for Developers and Administrators Volume I: Installation and Administration, SG24-6462 IBM WebSphere Everyplace Access V5 Handbook for Developers and Administrators Volume II: Application Development, SG24-6463 IBM WebSphere Everyplace Access V5 Handbook for Developers and Administrators Volume III: E-Mail and Database Synchronization, SG24-6676 IBM WebSphere Everyplace Access V5 Handbook for Developers and Administrators Volume IV: Advanced Topics, SG24-6677 WebSphere Portal V5.0 Production Deployment and Operations Guide, SG24-6391 IBM WebSphere Portal for Multiplatforms V5 Handbook, SG24-6098 WebSphere Portal on z/OS, SG24-6992 Patterns: Portal Search Custom Design, SG24-6881 Develop and Deploy a Secure Portal Solution Using WebSphere Portal V5 and Tivoli Access Manager V5.1, SG24-6325 Deploying a Secure Portal Solution on Linux Using WebSphere Portal V5.0.2 and Tivoli Access Manager V5.1, REDP-9121 WebSphere Portal Collaboration Security Handbook, SG24-6438
1005
WebSphere Portal Server and DB2 Information Integrator: A Synergistic Solution, SG24-6433 Document Management Using WebSphere Portal V5.0.2 and DB2 Content Manager V8.2, SG24-6349 Portal Application Design and Development Guidelines, REDP-3829 IBM WebSphere Application Server V5.1 System Management and Configuration, WebSphere Handbook Series, SG24-6195 IBM WebSphere V5.1 Performance, Scalability, and High Availability, WebSphere Handbook Series, SG24-6198
Other publications
These publications are also relevant as further information sources: IBM WebSphere Portal security solutions white paper, Integrating WebSphere Portal software with your security infrastructure, G325-2090, available at:
ftp://ftp.software.ibm.com/software/websphere/pdf/WS_Portal_Security_G325-2 090-01.pdf
Troubleshooting Pickers in Collaborative Portlets, Technote 1157249 Troubleshooting Automatic Detection of your Mail File with the Different Collaborative Portlets, Technote 1157029
Online resources
These Web sites and URLs are also relevant as further information sources: IBM WebSphere Portal for Multiplatforms Version 5.1 Information Center
http://publib.boulder.ibm.com/pvc/wp/510/ent/en/InfoCenter/index.html
1006
Related publications
1007
1008
Index
A
Abstract and concrete portlet applications 155 accessibility 7 Accessing resource bundles in JSPs 382 Accessing resource bundles in portlets 381 Action Event Handling 145 Action events 50 ActionEvent 142, 208, 889 ActionEvent portlet 208 ActionForm 417 ActionListener 141142 ActionServlet 417 actionURL 362 active credential 635 Add action request handler 649 Add configure mode 650 Add edit mode 650 Add form sample 649 Add portlets 952 addCookie 360 addDataHeader 360 addHeader 360 addIntHeader 360 Adjusting Portal resource bundles 391 administrator 253, 889, 891 Administrators 19 allows 339 Apache Jetspeed 9 applets 355 application components 450 architecture 9 Attribute storage summary 145 Authentication 5 Authorization 5 Business to Consumer 4, 8 Business to Employee 4, 8
C
c2a.tld 146 cache 337 calculator project 552 call center application 594 Canonical Portal URLs 35 Cascading Style Sheets 353 Cascading style sheets 366 cHTML 355 classes 18 Clear 985 Click-to-Action 837 Click-to-Action tag 594 Client Certificate Authentication 899 client portlet 606 Collaboration 5, 17 collections 18 Commons-Logging interface 444 Compile errors 902 components 436 concrete-portlet-app 337 config-param 339 configuration 936 Connection 435 Consume 892 Consumer portal 885886 Consumer portals 889 containsHeader 360 Content Management 5, 16, 36 Content management 5 content.tld 147 ContentAccessService 618619 Controller 174 cooperative broker 777 cooperative portlet 772 Cooperative Portlets 741 beginEventPhase() 774 broadcast 777 broadcasting 771 broadcasts 836
B
B2E 4 Basic Portlet 54 Basic portlet (JSR 168) 649 benefits 935 bidi 152, 364 Building a war file 171 Business to Business 4, 8
1009
C2A wrapper 775 callback method 773 changedProperties() 772 Click-to-Action architecture 749 Click-to-Action event 772 Click-to-Action menus 778 combined scenario 779 Ctrl key 772 declarative approach 837 deployment descriptor 841 encodeProperty 745 encodeProperty tag 777 event phase 773 exchange capabilities 838 Import Resources from a WAR File 780 input parameters 772 Internationalization 844 internationalization 837 JSR 168 portlets 837 messaging communication 835 method invocations 849 output parameters 772 Overview 742 portlet code 842 Portlet Messaging 743 portlet.xml 844 programmatic approach 771, 774 Programming model 744 property broker 772, 836 PropertyBrokerService 843 PropertyListener 745 Register and publish properties 745 registerProperties() 774 resource bundle 844 Run the cooperative portlets 765 sample scenario 779 setProperties() 773 source 836 Source cooperative portlet 750 Steps to program a source cooperative portlet 744 Struts Actions 836 Struts portlets 835 target 836 Target cooperative portlet 761 Web Service Description Language 744 WebSphere Portal Property Broker 743 wires 837 WSDL 838
WSDL file 844 Cooperative portlets 335, 741 core 365 Create the Service Factory 623 createReturnURI 147 createURI 148 credential slots 632 Credential Vault 335, 630, 641, 648 administrative slot 633 Credential slots 633 Credentials objects 635 Import a protected servlet application 644 portlet private slot 633 private keys 630 shared slot 633 SSL client certificates 630 system slot 633 user credentials 630 Using passive credentials 667 Vault segments 632 Credential Vault Portlet Service 631 credentials 630 CredentialVaultService 617 cryptography 900 CSS 353, 366 CSS file 424 current 937 custcmd 423 Custom developed portlets 41 Custom Services 620 Customizable portlets from a vendor 41
D
dataAttribute 148 Database service 12 dataLoop 149 Debug a portlet application 905 debugger 902 declarative approach 744 Default.jsp 969 define a parameter 599 defineObjects 361 Delegators 19 Demilitarized Zone 10 deployment concerns 115 deployment descriptor 336, 435 design 990 Design area 969
1010
dir 364 Directory service 11 Directory services 5 doctype 336 Document Categories 36 documents 3 doView 643 drag and drop 595 Dynamic Web Projects 944
E
e-commerce 6 Edit mode 594 edit mode 446 Editing a theme 965 Editing Styles 976 Editors 19 e-learning 6 encodeNameSpace 150 encodeRedirectURL 359 encodeRedirectUrl 359 encodeURI 150 encodeURL 360 Enterprise portal 48 Event Handling 118 event handling 773 execute method 849 Export Wizard 990 extend.tld 147 extranet 10
listeners 518 managed bean 524 message tag 521 navigation rules 521 Render Response 517 rendering model 525 tag library 518 validation model 525 validators 518 Web service client 607 WSDL Binding Details 610 feature 935 Features page 554 First generation portals 3 first generation portals 3 flushBuffer 360 Flyweight pattern 21 fmt 365 Forms 369 Fourth generation portals 3 framework 51
G
generations of portal technology 3 getAttribute 359 getAttributeNames 359 getAuthType 359 getBufferSize 360 getCharacterEncoding 358, 360 getContentLength 358 getContentType 358 getcontextPath 359 getCookies 359 getDateHeader 359 getEncodeURL 355 getHeader 359 getHeaderNames 359 getHeaders 359 getInputStream 358, 618 getIntHeader 359 getLocale 359 getLocales 359 getMarkup 618 getMethod 359 getOutputStream 360 getParameter 359 getParameterMap 359 getParameterNames 359
F
Faces 594 Faces Portlet 54 Faces portlet (JSR 168) 553 Faces portlets broadcast 518 Click-to-Action 602 commandButton tag 521 component classes 525 components 518 core tag library 518 Create 594 database access 518 event 525 event handlers 518 Event Processing 518 listener model 525
Index
1011
getParameterValues 359 getPathInfo 358 getProtocol 358 getQueryString 358 getreader 358 getRealPath 358 getRemoteAddr 358 getRemoteHost 358 getRequestDispatcher 359 getRequestedSessionId 359 getRequestURI 358 getRequestURL 358 getScheme 359 getServerName 359 getServerPort 359 getServletPath 358 getSession 359 getURL 618 getWriter 360 Global Forwards 437
J
J2EE technology 48 Java Community Process 12 Java Standard Tag Library 365 Java-based pluggable modules 49 JavaBean 594 JavaScript 355 JavaServer Faces portlets accessibility 512 applications 511 Apply request values 514 behavior 512 configuration file 514 converters 515 event handlers 515 event-driven 513 faces-config.xml 514 internationalization 512 Invoke Application 517 Invoke application 514 Java Beans 514 life cycle 514 main components 512 model 511 Model-View-Controller 512 MVC 512 presentation 512 Process validators 514 Render response 514 Restore component tree 514 reusable user interface 512 server-side events 511 technology 512 Update model values 514 validation errors 517 validators 515 Web container 513 JDBC 669, 703 create a database connection 675 DB Servers view 681, 713 Importing the WAR file 692, 727 Overview 690, 725, 962 Jetspeed implementation 13
H
high availability 10 Highlights 31 Highlights in WebSphere Portal V5 31 Host integration 6 HTML form 208 HTTP Servers 11 HttpBasicAuth 643
I
IBM Portlet API 50 IBM Rational Application Developer 51 IBM Styles 370 IBM tags 364 IFRAME 354 images 18 include 618 include() method 356 InfoCenter 53, 893 infrastructure 15 init 147, 643 Insert 952 Internationalization 67, 551 Internet 10 Inter-Portlet Communication 118 isCommited 360 isRequestedSessionIdFromCookie 359
1012
JSF 335 JSF portlet project 555 JSP editor 439 JSP tags 353, 360, 966 JSPs 353 JSR 168 355, 557 JSR 168 API administrator 253 Deployment descriptors 251 life cycle 251 Listeners 251 local variables 254 multiple threads 254 Objects 251 parameters 252 portlet deployment descriptor 252 Portlet Modes 251 Portlet windows 252 portletPreferences object 252 PortletRequest 254 PortletResponse 254 web.xml 252 Window States 251 JSR 168 compliant portlets 844 JSR 168 portlets 618, 843, 849 JSTL 365
message 51 Message events 51 Messaging 335 Mode 23 Model 174 Model View Control (MVC) architecture 23 Model-View-Controller 21, 174, 208 ModeModifier 144 Modes 50 multiple devices 9 multiple sources 48 MVC 23 Model, View and Control 23 MyFirstStruts 431
N
name 364 namespace 363 National Language Support Accessing resource bundles in JSPs 382 Accessing resource bundles in portlets 381 Adjusting Portal resource bundles 391 Creating Resource Bundles in WebSphere Studio 376 NLS administration 386 NLS best practices 392 Portal NLS administration 390 Portlet NLS administration 386 Sample scenario 393 Setting NLS titles 390 Translating Resource Bundles 379 Translating whole resources 383 Working with characters 392 navigation 551, 945 next-generation desktop 5 NLS administration 373, 386 NLS best practices 373, 392
L
Label 984 labels 945 Layout 18 layout 18 Lightweight Third Party Authentication 900 Listeners 145 Load balancing 11 Local Test Environment 986 locale 365 log 152 logging 444 logging facility 444 LTPA 900 LTPA token 900
O
OCS 13 online access 892 Open Content Syndication 12 Open standards 12 Organization for the Advancement of Structured Information Standards 12 original 936 Overview 1 Aggregation Module 15
M
Managed Bean 551 Managers 19 menu option 952 menu.tld 147
Index
1013
Authentication Server 15 Authorization 33 Click-To-Action 39 Client to remote application 42 comparison of V4.x permission with V5.x roles 19 credentials 9 Customer Relationship Management 41 database structures 16 Document Categories and Summaries 36 e-Business needs 4 Enabling for Communities 32 Enterprise Resource Planning 41 Event Broker 32 events 9 evolution process 3 First generation portals 3 Fourth generation portals 3 General Infrastructure 32 generations of portal technology 3 high availability 10 J2EE platform 4 J2EE Security 33 Jetspeed implementation 9 LDAP 16 Member Subsystem 33 model-view-controller 23 Open Source Portal 9 Overview 4 Page content 14 page structure 14 page structure. 14 Permissions 19 Portal concepts 18 Portal Document Manager 36 Portal engine 1415 Portal Install 32 Portal Servlet 15 portal technology 4 Portlet container 15 portlet container 26 Portlet events and messaging 26 portlets 8, 14 Presentation services 14 profile information 9 Property Broker 32 remote content 9 Reverse Proxy Security Server 11 Roles 19
Search 35 Second generation portals 3 Security services 16 services 16 Site Analysis 17 Skins 20 SSO Functionality 33 Struts Portlet Framework 37 Themes 20 Third generation portals 3 Transcoding 37 Transcoding Technology 16 User and Group Management 16 WebSphere Portal 1516
P
Page 18 Page Aggregation 118 Page Designer 966 page designer 418 page layout 558, 595 Page transformation 16 pages 945 palette 434435 Palette view 434 param 364 Parameter summary 168 passive credentials 636 patterns 1 PCs to PDAs 48 person.tld 147 Personalization 6, 16 Pervasive computing 6 PlaceBarInclude.jsp 973 Portal access control 34 portal administrator 20 Portal Configuration 984, 986 Portal configuration 945 Portal Designer 952 Portal Document Manager 36 Portal Framework 78 portal layout 935 Portal NLS administration 390 Portal page 48 portal page 48 Portal project Adding portlets 950 Apply new themes and skins 962
1014
Change 936 configuration 936 Create 936 Deploy 939 Edit a new theme 962 Edit styles 962 Editing a theme 965 Export 939 Host name 939 Import 936, 939 Import a portlet 950 Label 946 Layout 936, 945 look and feel 962 Modify 936 Navigation 945 new label 946 new themes 961 page layout 949 portal server 940 portlets 952 publish 962 scenario 936 server connection 937 Test 936 wizard 937 portal project 962 Portal services 16 Portal technology 12 portal themes 961 Portal Toolkit 40 Portal Tools 52 Portals First generation portals 3 Second generation portals 3 Third generation portals 3 portals 1, 3 Portlet 18, 49, 121 portlet 337 Portlet API 115, 120 Abstract and concrete portlet applications 155 Action Event Handling 145 ActionEvent 142 ActionListener 142 Attribute storage summary 145 Building a war file 171 Client 125 Configure 119 Control 2425
Create the Service Factory 623 Custom Services 620 Define the Service 620 destroy(PortletConfig config) 137 destroyConcrete(PortletSettings settings) 136 Edit 119 Event Handling 118 Help 119 Hierarchy 120 Implement the Service 621 init(PortletConfig config) 135 initConcrete(PortletSettings settings) 135 Inter-Portlet Communication 118 Listeners 145 login(PortletRequest request) 136 logout(PortletSession session) 136 Mode 23 Model 2425 Model View Control architecture 23 ModeModifier 144 Page Aggregation 118 Parameter summary 168 Portlet 121 Portlet deployment 155 Portlet JSPs 146 Portlet life cycle 115 Portlet lifecycle 134 Portlet messaging 145 Portlet MVC architecture 24 Portlet MVC Sample 25 Portlet Services 146 Portlet Tag Library 147 portlet terms 22 Portlet window 22 portlet.xml 160 PortletAdapter 121 PortletApplicationSettings object 129 PortletApplicationSettingsAttributesListener 141 PortletConfig object 126 PortletContext object 127 PortletData object 130 PortletException 132 PortletLog object 131 PortletPageListener 138 PortletRequest 121 PortletResponse 123 PortletSession object 124 PortletSessionListener 139
Index
1015
PortletSettings object 128 PortletSettingsAttributesListener 141 PortletTitleListener 137 PortletURI 133, 142 PortletWindow object 132 PropertyListener 145 Real Estate 117 Register the Service 623 Security 119 service(PortletRequest request, PortletResponse response) 136 Servlets vs. Portlets 117 State 22 Test the service 624 UID Guidelines 171 UnavailableException 132 User object 133 View 2425, 119 web.xml 157 Web.xml and Portlet.xml relationship 170 What is a portlet 116 What is a Portlet Application 116 WindowListener 140 Portlet application 18, 116 Portlet applications 49 Portlet debugging 901 Fix compile errors 902 Portlet deployment 155 portlet development 593 Portlet events 50 Portlet life cycle 115, 134 Portlet lifecycle 134 Portlet messaging 145, 226, 743 Portlet messaging versus cooperative portlets 743 Portlet modes Configure 50 Edit 50 Help 50 View 50 portlet modes 254 Portlet MVC architecture 24 Portlet MVC sample 25 Portlet NLS Administration 386 Portlet NLS administration 386 Portlet Project 594 Portlet Project (JSR 168) 648 Portlet Project (JSR168) 552 portlet services 617 Portlet solution patterns 41
Portlet states 50 Maximized 50 Minimized 50 Normal 50 portlet terms 22 Portlet to remote application 42 Portlet to Web application 43 Portlet window 22 Portlet Wiring Tool 844 portlet wiring tool 778 portlet.tld 146 portlet.xml 160 PortletAction 208 portlet-app 336 PortletApplicationSettings object 129 PortletApplicationSettingsAttributesListener 141 portletConfig 361 PortletConfig object 126 PortletContext object 127 PortletData object 130 PortletException 132 PortletLog object 131 portletMode 362363 portlet-name 337 PortletPageListener 138 PortletRequest 121 PortletResponse 123 Portlets 21 Portlet 121 Portlet API 120 Portlet deployment 155 Portlet JSPs 146 Portlet life cycle 134 Portlet MVC architecture 24 Portlet MVC Sample 25 Portlet Services 146 portlet terms 22 Portlet window 22 portlet.xml 160 PortletAdapter 121 PortletApplicationSettings object 129 PortletApplicationSettingsAttributesListener 141 PortletConfig object 126 PortletContext object 127 PortletData object 130 PortletException 132 PortletLog object 131 PortletPageListener 138
1016
PortletRequest 121 PortletResponse 123 PortletSession object 124 PortletSessionListener 139 PortletSettings object 128 PortletSettingsAttributesListener 141 PortletTitleListener 137 PortletURI 133, 142 PortletWindow object 132 Servlets versus Portlets 117 Web.xml and Portlet.xml relationship 170 What is a Portlet Application? 116 WindowListener 140 portlets 421 PortletSession object 124 PortletSessionListener 139 PortletSettings 128 PortletSettingsAttributeListener 141 PortletSettingsAttributesListener 141 PortletTitleListener 137 PortletURI 133, 142 Privileged Users 19 processAction 643 Producer 889 Producer portal 886 Producer portals 889 product installation 53 programmatic approach 745 properties 18 properties view 983 PropertyBrokerService 617
Markup 888 portals 889 Portlet Management 888 Producer 886 Registration 888 sample scenario 885 Service Description 887 standard portlets 886 Users 886 withdraw a portlet 889 WSDL 887 removeAttribute 359 render action 356 render method 356 renderRequest 361 renderResponse 361 RenderResponse object 355 renderURL 361 reset 360 resetBuffer 360 Resource bundles 373 Resources 171 Resume 913 retrieve credentials 641 Reverse proxy 11 Rich Site Summary 12 RSS 12 Run on Server 443 runtime errors 902
S
Samples Gallery 54 Search 6, 16 search and taxonomy 7 search functionality 35 Second generation portals 3 secure 362363 secure servlet 644 SecureCredentialSlot 649 SecureServletEAR 645 Security 899 security options 899 security server 11 sendError 360 sendRedirect 360 server configuration 943 Servers 11 Service Provider Interface 34
R
RAD 962 rates 18 Rational Application Developer 51, 430, 988 recommendations 935 Redbooks Web site 1007 Contact us xxii Register 892 Register the Service 623 registration 892 relational database 689, 724 remote content 9 Remote Portlets 899 Remote portlets Consumer 886 Local portlets 886
Index
1017
ServletResponse 123 Servlets 353 servlets 421 Servlets versus portlets 117 Servlets vs. Portlets 117 Set Style Properties 971 setAttribute 359 setBufferSize 360 setCharacterEncoding 358 setContentLength 360 setContentType 360 setDataHeader 360 setHeader 360 setIntHeader 360 setLocale 360 setProperties 775 setStatus 360 Setting NLS titles 390 settingsAttribute 149 settingsLoop 149 simple action Strings 761 Single Sign-On 630 site analytics 7 Site usage 6 Skin 985986 Skin Properties 986 Skins 963 skins 963 sql 366 SSL 11, 899 SSL support 11 SSO 900 Standard MVC architecture 23 State 22 store credentials 641 Struts 335, 429, 836 Struts Action 420 struts action 837 Struts applications 430 Struts Portlet Framework 38 Struts portlet configuration 430 Struts Portlet Framework 54 Struts portlets Action phase 420 ActionForm 416 actions 421 ActionServlet 417 application development 419 business logic 416
components 417 configuration file 417 controller 416 CSS file 424 execute method 423 external applications 416 forwards 422 framework 415 IBM Portlet API 420 Initialization 420 Internationalization 418 JSP tags 418 JSR 168 API 420 legacy systems 416 life cycle 418 Logic 418 look and feel 424 mappings 423 Markup support 424 model 416 modular applications 416 portlet communication 420 portlet framework 415 portlet life cycle 418 processing 422 Render phase 420 render phase 422 rendering 421 Tag libraries 418 tags 424 view 416 struts_source 420 struts-config.xml 436 style sheet 976 stylesheet.css 560 Stylesheets 423 supports 338 Switch Active Document 969 symbols 966 Syndicated content 12
T
TAM 11 target portlet application 782 Target server 645 techniques 901 technology 3 Test Environment 52, 648
1018
Test the service 624 TestPortalServer 962, 984, 986 text 152 The Portlet Wiring Tool 40 theme 963, 965, 983984 themes and skins 989 Third generation portals 3 Title 947 Tooling 17 top 420 Transcoding technology 37 Translating Resource Bundles 379 Translating whole resources 373, 383 translation 850 Tutorials Gallery 55
U
UID Guidelines 171 URIAction 148 URIParameter 148 URL 35 URL Generation, Processing and Mappings 35 URL Mappings 35 user 891 User object 133 User-friendly Portal URLs 35 Users 19 Using resource bundles 373
V
validators 551 value 364 value change events 551 var 362363 vault segment 632 View 174 View mode 643
Web Services 17, 857, 887 JavaBean 859 Sample scenario 858 wizard 858 WSDL 858 Web Services Client Create a Web Services client portlet 874 Web Services Description Language 887 Web Services for Remote Portals 12 Web Services for Remote Portlets 885 web.xml 157, 337, 435 Web-based content 8 WEB-INF 433 WebSEAL 11 WebSphere 1 WebSphere Portal 18, 54, 899, 990 WebSphere Portal Property Broker 743 WebSphere Portal V5.1 885 What is a portlet 116 What is a Portlet Application 116 Window events 51 WindowListener 140 windowState 361, 363 wizard 430 wizards 434 WML 355 workbench 52, 988 Working with characters 373, 392 WpsStrutsPortlet 433 WSRP 366, 885, 899 WSRP services 887 WSRP styles 366
X
xml 365 XML feed 49
W
WAR file 844 Web browser 988 Web deployment descriptors 426 Web Diagram 434 Web perspective 644 Web service 606 Web Service client 857 Web Service Description Language 744
Index
1019
1020
IBM Rational Application Developer V6 Portlet Application Development and Portal Tools
Back cover IBM Rational Application Developer V6 Portlet Application Development and Portal Tools
Learn how to develop MVC, Struts and JavaServer Faces portlet applications Learn about IBM Portlet API and the JSR 168 standard API Access Web Services and secure Web applications
This IBM Redbook provides an overview and hands-on scenarios to help you design, develop and implement portlet applications using Rational Application Developer V6.0 and the provided Portal Tools. The sample scenarios included in this redbook target Business-to-Employee (B2E) enterprise applications, but most of the scenarios presented will also apply to Business-to-Consumer (B2C) applications. You will find step-by-step examples and scenarios showing ways to integrate your enterprise applications into an IBM WebSphere Portal environment using the WebSphere Portal APIs provided by the Portal Tools to develop portlets. You will also learn how to extend your portlet capabilities to use advanced functions such as cooperative portlets, internationalization, action events, using the Credential Vault to enable Single Sign-On, Web Services, remote portlets, portal design and portlet debugging capabilities. Elements of the Portlet API and the standard JSR168 API are described and sample code is provided. The scenarios included in this redbook can be used to learn about portlet programming and as a basis for your own portlet applications. You will also find scenarios describing recommended ways to develop portlets and portlet applications that follow the MVC design pattern, the Struts framework and JavaServer Faces technology. Basic knowledge of Java technologies such as servlets, JavaBeans, EJBs, JavaServer Pages (JSPs), as well as of XML applications and the terminology used in Web publishing, is assumed.
BUILDING TECHNICAL INFORMATION BASED ON PRACTICAL EXPERIENCE IBM Redbooks are developed by the IBM International Technical Support Organization. Experts from IBM, Customers and Partners from around the world create timely technical information based on realistic scenarios. Specific recommendations are provided to help you implement IT solutions more effectively in your environment.
SG24-6681-00
ISBN 073849352X