This document was uploaded by user and they confirmed that they have the permission to share
it. If you are author or own the copyright of this book, please report to us by using this DMCA
report form. Report DMCA
Overview
Download & View Ibm.press.rapid.portlet.developme.with.websphere.portlet.factory.sep.2008 as PDF for free.
IBM PRESS NEWSLETTER Sign up for the monthly IBM PRESS NEWSLETTER at ibmpressbooks.com/newsletters
LEARN • NEW PODCASTS from your favorite authors • ARTICLES & INTERVIEWS with authors • SPECIAL OFFERS from IBM Press and partners • NOTICES & REMINDERS about author appearances and conferences
WIN Sign up for the IBM PRESS NEWSLETTER and you will be automatically entered into a QUARTERLY GIVE-AWAY for 3 months access to Safari Books Online – online access to more than 5000 books A $150 VALUE!
Sign up at ibmpressbooks.com/newsletter
REGISTER YOUR BOOK ibmpressbooks.com/ibmregister REGISTRATION ENTITLES YOU TO: • Supplemental materials that may be available • Advance notice of forthcoming editions • A coupon that can be used on your next purchase from ibmpressbooks.com
Visit ibmpressbooks.com for all product information
Related Books of Interest
Service-Oriented Architecture (SOA) Compass
WebSphere Business Integration Primer
Business Value, Planning, and Enterprise Roadmap
Process Server, BPEL, SCA, and SOA
by Norbert Bieberstein, Sanjay Bose, Marc Fiammante, Keith Jones, and Rawn Shah ISBN: 0-13-187002-5 ®
In this book, IBM Enterprise Integration Team experts present a start-to-finish guide to planning, implementing, and managing ServiceOriented Architecture. Drawing on their extensive experience helping enterprise customers migrate to SOA, the authors share hard-earned lessons and best practices for architects, project managers, and software development leaders alike. Well-written and practical, Service-Oriented Architecture Compass offers the perfect blend of principles and “how-to” guidance for transitioning your infrastructure to SOA. The authors clearly explain what SOA is, the opportunities it offers, and how it differs from earlier approaches. Using detailed examples from IBM consulting engagements, they show how to deploy SOA solutions that tightly integrate with your processes and operations, delivering maximum flexibility and value. With detailed coverage of topics ranging from policy-based management to workflow implementation, no other SOA book offers comparable value to workingIT professionals.
by Ashok Iyengar, Vinod Jessani, and Michele Chilanti ISBN: 0-13-224831-X
Using WebSphere® Business Integration (WBI) technology, you can build an enterprise-wide Business Integration (BI) infrastructure that makes it easier to connect any business resources and functions, so you can adapt more quickly to the demands of customers and partners. Now there’s an introductory guide to creating standards-based process and data integration solutions with WBI. WebSphere Business Integration Primer thoroughly explains Service Component Architecture (SCA), basic business processes, and complex long-running business flows, and guides you to choose the right process integration architecture for your requirements. Next, it introduces the key components of a WBI solution and shows how to make them work together rapidly and efficiently. This book will help developers, technical professionals, or managers understand today’s key BI issues and technologies, and streamline business processes by combining BI with Service Oriented Architecture (SOA).
Sign up for the monthly IBM Press newsletter at ibmpressbooks/newsletters
Related Books of Interest
The New Language of Business SOA & Web 2.0 by Sandy Carter ISBN: 0-13-195654-X
Executing SOA A Practical Guide for the ServiceOriented Architect by Norbert Bieberstein, Robert G. Laird, Dr. Keith Jones, and Tilak Mitra ISBN: 0-13-235374-1
In The New Language of Business, senior IBM executive Sandy Carter demonstrates how to leverage SOA, Web 2.0, and related technologies to drive new levels of operational excellence and business innovation.
In Executing SOA, four experienced SOA implementers share realistic, proven, “from-thetrenches” guidance for successfully delivering on even the largest and most complex SOA initiative.
You’ll learn how to organize your business into reusable process components — and support them with cost-effective IT services that adapt quickly and easily to change. Then, using extensive examples — including a detailed case study describing IBM’s own experience — Carter identifies best practices, pitfalls, and practical starting points for success.
This book follows up where the authors’ bestselling Service-Oriented Architecture Compass left off, showing how to overcome key obstacles to successful SOA implementation and identifying best practices for all facets of execution— technical, organizational, and human. Among the issues it addresses: introducing a services discipline that supports collaboration and information process sharing; integrating services with preexisting technology assets and strategies; choosing the right roles for new tools; shifting culture, governance, and architecture; and bringing greater agility to the entire organizational lifecycle, not just isolated projects.
Listen to the author’s podcast at: ibmpressbooks.com/podcasts
Listen to the author’s podcast at: ibmpressbooks.com/podcasts
Writing for executives and business leaders inside and outside IT, Carter explains why flexibility and responsiveness are now even more crucial to success — and why services-based strategies offer the greatest promise for achieving them.
Visit ibmpressbooks.com for all product information
Related Books of Interest IBM WebSphere and Lotus Lamb, Laskey, Indurkhya ISBN: 0-13-144330-5
Enterprise Messaging Using JMS and IBM WebSphere
Enterprise Java Programming with IBM WebSphere Second Edition
Yusuf ISBN: 0-13-146863-4
IBM WebSphere System Administration
by Kyle Brown, Dr. Gary Craig, Greg Hester, David Pitt, Russell Stinehour, Mark Weitzel, Jim Amsden, Peter M. Jakab, and Daniel Berg
Williamson, Chan, Cundiff, Lauzon, Mitchell
ISBN: 0-321-18579-X
Outside-in Software Development
Enterprise Java™ Programming with IBM WebSphere, Second Edition is the definitive guide to building mission-critical enterprise systems with J2EE™, WebSphere, and WebSphere Studio Application Developer. Fully updated for Versions 5.x of WebSphere Application Server and WebSphere Studio Application Developer, it combines expert architectural best practices with a case study that walks you through constructing an entire system. The authors are an extraordinary team of WebSphere insiders: developers, consultants, instructors, and IBM WebSphere development team members. Together, they offer unprecedented insight into the use and behavior of WebSphere’s APIs in real-world environments — and systematic guidance for delivering systems of exceptional performance, robustness, and business value.
Sign up for the monthly IBM Press newsletter at ibmpressbooks/newsletters
ISBN: 0-13-144604-5
Kessler, Sweitzer ISBN: 0-13-157551-1
Enterprise Master Data Management Dreibelbis, Hechler, Milman, Oberhofer, van Run, Wolfson ISBN: 0-13-236625-8
Rapid Portlet Development with WebSphere Portlet Factory
®
Requirements Requirements
This page intentionally left blank
IBM WebSphere Rapid Portlet Development with ® Deployment and Advanced WebSphere Configuration Portlet Factory [SUBTITLE ]
Step-by-Step Guide for Building Roland Barcia, Bill Hines, TomPortlets Alcott, and Keys Botzum Your Own
David Bowley
IBM Press Pearson plc Upper Saddle River, NJ • Boston • Indianapolis • San Francisco New York • Toronto • Montreal • London • Munich • Paris • Madrid Cape Town • Sydney • Tokyo • Singapore • Mexico City Ibmpressbooks.com
The following terms are trademarks or registered trademarks of International Business Machines Corporation in the United States, other countries, or both: IBM, the IBM logo, IBM Press, DB2, Domino, Domino Designer, Lotus, Lotus Notes, Rational, and WebSphere. 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 the Microsoft Corporation in the United States, other countries, or both. Linux is a registered trademark of Linus Torvalds. Intel, Intel Inside (logo), MMX, and Pentium are trademarks of Intel Corporation in the United States, other countries, or both. Other company, product, or service names may be trademarks or service marks of others. Library of Congress Cataloging-in-Publication Data Bowley, David. Rapid portlet development with WebSphere portlet factory : step-by-step guide for building your own portlets / David Bowley. p. cm. Includes index. ISBN 0-13-713446-0 (hardback : alk. paper) 1. Web portals—Computer programs. 2. User interfaces (Computer systems) Computer programs. 3. Web site development. 4. WebSphere. I. Title. TK5105.8885.W43B69 2008 006.7’6—dc22 2008029014 All rights reserved. This publication is protected by copyright, and permission must be obtained from the publisher prior to any prohibited reproduction, storage in a retrieval system, or transmission in any form or by any means, electronic, mechanical, photocopying, recording, or likewise. For information regarding permissions, write to: Pearson Education, Inc Rights and Contracts Department 501 Boylston Street, Suite 900 Boston, MA 02116 Fax (617) 671 3447 ISBN-13: 978-0-13-713446-5 ISBN-10: 0-13-713446-0 Text printed in the United States on recycled paper at R.R. Donnelley in Crawfordsville, Indiana. First printing September 2008
This page intentionally left blank
Contents at a Glance Foreword
xxvii
Preface
xxix
Acknowledgments
xxxiii
About the Author
xxxv
Chapter 1
Introduction to WebSphere Portlet Factory
Chapter 2
Providing and Consuming Services
37
Chapter 3
Using Data from a Relational Data Source
53
Chapter 4
Using Domino Data
77
Chapter 5
Customizing Portlet Appearance
107
Chapter 6
Adding Basic User Interface Controls to Your Portlets
149
Chapter 7
Communicating Between Portlets
165
Chapter 8
Using Java in Portlets
195
Chapter 9
Using Web Services and Manipulating XML
239
Chapter 10
Using Charts in Portlets
267
Chapter 11
Field Validation, Formatting, and Translation
293
Chapter 12
Profiling Portlets
323
Chapter 13
Using Ajax and Dojo
345
Chapter 14
Error Handling, Logging, and Debugging Portlets
371
Chapter 15
Performance and Process Optimization
393
Chapter 16
More Techniques for Domino
413
Appendix A
Setting Up your Environment
439
Appendix B
Portlet Factory Properties
461
Glossary
475
Index
493
xi
1
This page intentionally left blank
Contents
Foreword
xxvii
Preface
Chapter 1
xxix
Acknowledgments
xxxiii
About the Author
xxxv
Introduction to WebSphere Portlet Factory
What Is a Portal and What Are Portlets? Portal Key Benefits What Is WPF? WPF Key Benefits WPF Architecture Builders Models Profiles Generating and Executing the WebApp Portlet Factory WAR Files Deployment Configurations Introduction to the WebSphere Portlet Factory Designer The User Interface Folder Structure Building Your First Portlet Creating a Project Manual Deployment Creating a Model Testing the Application Summary Important Points xiii
The Service Provider/Consumer Pattern Creating a Service Provider Creating a Model Defining the Service Adding Roster Data Adding an Action to Retrieve a Roster Adding a Service Operation Testing the Service Provider Creating a Service Consumer Creating a Model Configuring the Portlet Adapter Testing the Service Consumer Creating a Stub Service Applying the Service Provider/Consumer Pattern Summary Important Points
Chapter 3
Using Data from a Relational Data Source
Using Data Services Creating a Service Provider Creating a Model Defining the Service Specifying the First Operation Specifying the Second Operation Specifying the Third Operation Specifying the Fourth Operation Specifying the Fifth Operation Testing the Service Provider Testing the retrieveContactsView Operation Testing the getContactDetail Operation Testing the createContact Operation Testing the setContact Operation Testing the deleteContact Operation Creating a Contacts Portlet Creating a Model Configuring the Portlet Adapter Adding Update Functionality Adding Delete Functionality Adding Create Functionality Testing the Contacts Portlet Summary Important Points
Portalizing Notes Functionality Configuring Your Environment Configuring the Domino Properties File Testing the Connection to Domino Creating a Service Provider Creating a Model Defining the Service Specifying Operations Adding a Delete Operation Testing the Service Provider Testing the readSupplierView Operation Testing the readSupplierDocument Operation Testing the updateSupplierDocument Operation Testing the createSupplierDocument Operation Testing the deleteSupplierDocument Operation Creating a Suppliers Portlet Creating a Model Configuring the Portlet Adapter Adding Update Functionality Adding Delete Functionality Adding Create Functionality Removing Unwanted Fields Testing the Service Consumer Using a Stub Service Creating a Stub Model Using the Stub Model Summary Important Points
Chapter 5
Customizing Portlet Appearance
Customizing Portlet Appearance Creating a Service Provider Creating a Model Defining the Service Adding an Asset Schema Adding Asset Data Adding an Action to Retrieve a List of Assets Specifying the getAssetsList Operation Testing the Service Provider Creating an Assets Portlet Creating a Model Adding a Portlet Adapter
Contents Consuming the Service Displaying Assets Data Testing the Assets Portlet Pagination Turning on Pagination Modifying the Paging Buttons Data Modifiers The Data Column Modifier Builder The Data Hierarchy Modifier Builder The Data Field Modifier Builder The Form Layout Builder Working with Web Pages HTML Builders JSP Builders JavaScript HTML Templates Cascading Style Sheets Adding a Style Sheet Summary Important Points
Chapter 6
Adding Basic User Interface Controls to Your Portlets
User Interface Controls in WPF Creating a Survey Portlet Creating a Model Modifying the Page Adding a Portlet Adapter Testing the Survey Portlet Summary Important Points
Chapter 7
Communicating Between Portlets
The Benefits of Inter-Portlet Communication The WebSphere Property Broker Creating a Service Provider Creating a Model Defining the Service Adding Loan Data Adding an Action to Retrieve a List of Loans Specifying the getLoansList Operation Adding a Method to Retrieve a Specific Loan Specifying the getLoanDetail Operation
Testing the Service Provider Creating a List Portlet Creating a Model Specifying the Service Specifying List and Detail Operations Configuring the Portlet Adapter Defining the Portlet as a Cooperative Source Testing the List Portlet Creating a Detail Portlet Creating a Model Adding a Default Message Page Adding a main Action Adding an Interface for Loan Details Defining the Portlet as a Cooperative Target Handling an Inter-Portlet Communication Event Testing the Detail Portlet Configuring the WebSphere Property Broker Testing Inter-Portlet Communication Alternative Communication Methods Property Broker Action WPF Event Model Shared Variables Configuring Actions for loansList Running selectLoan Using the Shared Variable in the loanDetail Model Click-to-Action (C2A) When to Use Inter-Portlet Communication Summary Important Points
Chapter 8
Using Java in Portlets
Java Development Considerations Java Methods Inline Java Action Lists The Method Builder The Linked Java Object Builder Java Application Programming Interfaces com.bowstreet.WebApp.WebAppAccess com.bowstreet.webapp.Variables com.bowstreet.webapp.RequestInputs com.bowstreet.util.IXml and com.bowstreet.util.XmlUtil javax.servlet.http.HttpServletRequest and javax.servlet.http.HttpServletResponse
Contents Java Beans Creating a Java Bean and Bean Manager Creating a Shopping Cart Item Bean Creating a Shopping Cart Item Manager Creating a Service Provider Creating a Model Defining the Service Adding a Linked Java Object Adding a Java to XML Converter Clearing a Shopping Cart Viewing a Shopping Cart Viewing a Shopping Cart Item Adding a Shopping Cart Item Updating a Shopping Cart Item Deleting a Shopping Cart Item Testing the Service Provider Testing the addShoppingCartItem Operation Testing the viewShoppingCart Operation Testing the viewShoppingCartItem Operation Testing the updateShoppingCartItem Operation Testing the deleteShoppingCartItem Operation Testing the clearShoppingCart Operation Creating a Shopping Cart Portlet Creating a Model Configuring the Portlet Adapter Implementing the updateShoppingCartItem Operation Implementing the addShoppingCartItem Operation Implementing the clearShoppingCart Operation Implementing the deleteShoppingCartItem Operation Testing the Shopping Cart Portlet Testing the Add Item Button Testing the Edit Item Button Testing the Delete Item Button Testing the Clear Cart Button Java Archives Importing JARs Excluding JARs from WAR Files Summary Important Points
Web Services in WPF Creating an Order Stock Web Service Creating a Model Defining the Service Defining the Request Object Defining the Response Object Adding Order Data Adding a Method to Place a Stock Order Adding a Service Operation Testing the orderStockWebService Web Service Creating a Service Provider Creating a Model and Defining the Service Calling the Web Service Adding a Service Operation Testing the Service Provider Creating an Order Stock Portlet Creating a Model Modifying the Page Adding a Portlet Adapter Consuming the Service Creating a Variable to Store the Request Displaying the Interface Adding Submit Functionality Testing the Order Stock Portlet XML Transform Builders Transform Transform - Filter Transform - Rename Transform - Sort Summary Important Points
Chapter 10 Using Charts in Portlets Charting Data in WPF Creating a Service Provider Creating a Model Defining the Service Adding Sales Data Adding a Sales Schema Adding an Action to Retrieve Sales Data Specifying the getSales Operation Testing the Service Provider
Contents Creating a Sales Chart Portlet Creating a Model Modifying the Page Modifying the Main Action List Adding a Portlet Adapter Consuming the Service Adding a Chart Configuring the Data Transformation Specifying Chart Properties Specifying Minimum Scale Values Testing the Test Data Portlet Adding Drill-Down Capabilities Adding Temporary Variables Defining the Transform Specifying a Method to Get the Current Sales Item Specifying an Action for the getSalesArea Operation Specifying the getSalesArea Operation Testing the getSalesArea Operation Adding a Page for the Sales Area Chart Adding an Action to Display the salesAreaPage Configuring the Click Action Populating the salesAreaPage Page Testing the Drill-Down from the salesChart Model Custom Chart Styles Displaying TargetSales Using the Custom Style Upgrading to a Deployment License Summary Important Points
Chapter 11 Field Validation, Formatting, and Translation Validating, Translating, and Formatting Fields Schema Typed and Non-Schema Typed Fields Formatter Classes Client-Side and Server-Side Validation Creating a Projects Portlet Creating a Model Modifying the Page Adding a Portlet Adapter Organizing Builders for page1 Adding a Schema Adding a Variable to Hold User Input Modifying the Main Action List Creating a Form
Contents Adding a Submit Button Adding a Confirmation Page Modifying the Submit Button Displaying the User’s Inputs Adding a Link to Return to the Project Form Running a Preliminary Test Adding Formatting, Validation, and Translation Testing the Projects Portlet Writing a Formatter Class Adding a Formatter Class The CustomFormatter Class Adding a Linked Java Object Adding a Data Field Modifier Further Validation Customizations Adding a Resource Bundle Changing Messages for Regular Expressions Creating the Post-Save Action Modifying the Post-Save Action Code Running the Post-Save Action Summary Important Points
Chapter 12 Profiling Portlets Profiling Builder Inputs Creating an Announcements Portlet Creating a Model Modifying the Page Adding a Portlet Adapter Creating a Default Resource Bundle Creating a US English Resource Bundle Creating an ES Spanish Resource Bundle Localizing the Announcement Headings Profiling the Language Input Profiling the Country Input Setting a Selection Handler Placing Headings on the Page Adding English Announcements Adding Spanish Announcements Importing the Announcements Displaying the Announcements Running a Preliminary Test Testing the Selection Handler Profiling the hrHeader Based on Department Profiling the itHeader Based on Department
Contents Profiling the hrAnnouncements Based on Department Profiling the itAnnouncements Based on Department Configuring the userDepartment Profile Set Testing the companyAnnouncements Portlet Summary Important Points
Chapter 13 Using Ajax and Dojo Using Ajax Creating a Service Provider Creating a Model Defining the Service Adding Performance Data Adding Functionality for Retrieving Performance Data Specifying the getPerformanceData Operation Adding Functionality for Updating Performance Data Specifying the updatePerformanceData Operation Testing the Service Provider Testing the getPerformanceData Operation Creating a Performance Portlet Creating a Model Modifying the Page Adding a Portlet Adapter Consuming the Service Displaying the Interface Hiding and Showing a Column Using Ajax Responding to a Change in the Checkbox Value Adding an Area Select Field Adding Submit Functionality Adding Ajax Type Ahead Testing the Performance Portlet Using Dojo Enabling Dojo Capabilities Adding an Action to Save Comments Adding Inline Edit Capabilities Adding Tooltips Adding a Feedback Bar Summary Important Points
Chapter 14 Error Handling, Logging, and Debugging Portlets Error Handling Creating a Model Modifying the Results Page
Contents Adding Division Variables Defining the Division Process Displaying the Result Running a Preliminary Test Creating a Custom Exception Throwing the Custom Exception Adding an Error Page Adding an Error Flag Adding an Error Action Adding an Error Handler Testing the Division Model Debugging Debugging Statements Using Eclipse Debugging Logging Debug Tracing Using a Custom Logging Category Server Stats Summary Important Points
Chapter 15 Performance and Process Optimization Performance Caching Data Set Size Builder Calls Session Size Profiling Using Ajax and Dojo Custom Builders Creating a Model Modifying the Page Adding a Portlet Adapter Creating a Terms & Conditions Builder Defining Builder Inputs for the Custom Builder Generating the Custom Builder Artifacts Modifying the Custom Builder’s Functionality Modifying the Coordinator Using the Terms & Conditions Builder Testing the Information Model Creating a New Model Wizard Testing the New Model Wizard Builder Summary Important Points
Chapter 16 More Techniques for Domino Adding Common Notes Functionality Notes Formulas, Validation, and Translation Notes Agents Keyword Lookups Categorized Views Hide-When Fields Rich Text Attachments Domino Data Access Methods getDominoSession() and getDominoDatabase() setComputeWithFormEnabled(boolean) getUserName() sendDocument(IXml, boolean, boolean) and getDocumentData(String) Summary Important Points
Appendix A Setting Up Your Environment Installing WebSphere Portlet Factory Configuring WebSphere Portal Setting Up a New Target Page Setting Up Access Configuring Lotus Domino Start the DIIOP and HTTP Tasks Enable HTTP Clients to Browse Databases Creating a Test Database in DB2 Creating a Test Database in SQL Server Configuring a JDBC Resource Setting Up a DB2 Database Driver Provider Setting Up a SQL Server Driver Provider Configuring the Driver Configuring a DB2 Data Source Configuring a SQL Server Data Source Configuring an Authentication Mechanism Testing the Data Source
Appendix B Portlet Factory Properties Properties Files bowstreet.properties, cluster.properties, and server.properties jdbcDrivers.properties, pageprocessors.properties, persistentstore.properties log4j.properties logging.properties migrate-profilesets.properties
Using Properties Domino Server Settings File Upload Settings Specifying an Alternate Compiler Dynamic Class Loading SOAP and Proxy Access for Web Services Avoiding Errors Caused by File Length Limitations The WPF Cache log4j Logging Debug Tracing Event Logging Server Statistics Logging Page Automation
Building good software applications is hard. Improvements in languages, frameworks, and tools do make things easier, and there are more of these improvements each year. But at the same time, the technology landscape that developers live in keeps changing and getting more complex. Just when you get productive with one set of tools and technology, there’s something new that you have to adapt to or integrate with. And there’s a perpetual demand for “more software quicker”—organizations can never get all the software they want as soon as they want it. WebSphere® Portlet Factory was created to apply concepts of software automation to help address this ongoing problem of software development complexity. This software automation moves the developer up a level, above the level of individual code artifacts. Instead of directly manipulating elements such as JSP, Java™, JavaScript, and XML files, the developer interacts with builders in a model, and the builders then generate all the necessary code artifacts in response to the developer’s high-level instructions. You can think of builders as encapsulations of software features or design patterns. Each builder implements one feature of an application, controlled by instructions provided by the developer in a wizard-like user interface. An application is built by successively adding and modifying features (builders) until the application works as intended. The net effect for developers is that they can rapidly develop complex applications without having to learn (and remember) all the underlying technology. In the past several years working with this technology, we’ve found that developers can consistently get big productivity gains from this software automation. We’ve seen the technology adopted by an ever-increasing customer base, first at Bowstreet (where the software was initially developed), and now at IBM, which acquired Bowstreet in late 2005. At IBM, the technology has
xxvii
xxviii
Foreword
also been adopted by a number of other product groups that build products on top of Portlet Factory technology and take advantage of its software automation. For example, the Lotus® ActiveInsight Dashboard Framework is built on Portlet Factory and provides a set of builders that implement high-level design patterns tailored for dashboard-style applications. We’ve also found that automation makes it possible to quickly add support for new technology, such as integrating new back-end services or generating new user interface technologies. One example is support for Ajax (Asynchronous Java and XML) user interfaces. Implementing an Ajax user interface through hand-coding is quite complex and involves coordinated client-side code (JavaScript) and server-side code. Using builder technology, a small team with Ajax expertise was able to capture their expertise in a set of builders that automate common Ajax patterns and generate the necessary client and server code. Once the builders were created, those Ajax patterns became easily accessible to any developer using Portlet Factory. In this book, David Bowley gives a clear “soup-to-nuts” guide to building applications with Portlet Factory, from creating your first project, to back-end integration, to user interface and Ajax techniques. Each chapter tackles one aspect of application development, and for each task David shows you which builders you need and how to use them. In his examples, I think you’ll see that David has found just the right level of complexity—the examples are simple enough to easily understand, but not unrealistically simple or trivial. Portlet Factory development—using builders and models instead of working directly with code—represents a different development paradigm than with other tools. I hope you find as much value in this automation paradigm as we have. You can use this book as your guide as you learn your way around Portlet Factory and get comfortable with this way of working. Jonathan Booth Senior Architect WebSphere Portlet Factory IBM
Preface
Portlet development can often be arduous and complicated; indeed, the word “rapid” is not normally associated with building portlets. IBM’s award-winning1 WebSphere Portlet Factory (WPF), however, provides developers with a wizard-based development environment that greatly expedites the process of building, testing, and deploying portlets. WPF shields developers from much of the complexity of traditional portlet development, and portlets built using WPF often require little or no coding—enlarging the potential pool of people who are able to build portlet applications. Having said this, WPF developers also have the full power of Java 2 Enterprise Edition (J2EE) available to them should they choose to use it, making WPF a flexible (and powerful) development tool. This book is about how to use WPF to rapidly build portlets. No previous development experience is required to understand this book, and anyone with a remote interest in portlet development should find something of interest here. The book is structured to facilitate rapid portlet development: It is a collection of independent chapters, each walking through the process of creating a portlet while focusing on a particular aspect of WPF. Due to the independent nature of the chapters (and the nature of portlet development using WPF), you can skip to the chapters that interest you without needing to read chapter after chapter of abstract theory and/or background information beforehand. For example, if you want to learn how to build a portlet that displays a graphical chart, skip to Chapter 10, “Using Charts in Portlets;” if you want to find out how to work Ajax into your portlets, skip to Chapter 13, “Using Ajax and Dojo.” If you are completely new to WPF (or portals and portlets) and are looking for some basic information to get you started in WPF, Chapter 1, “Introduction to WebSphere Portlet Factory,” provides an overview of 1
WebSphere Portlet Factory won the 2006 JavaPro readers’ choice award for “Best Java Enterprise Portal Technology” (www.fawcette.com/javapro/).
xxix
xxx
Preface
portal terminology, WPF architecture, and the WPF Designer interface. Chapter 1 also walks you through the process of creating, testing, and deploying a simple Hello World! portlet. Other introductory information is available in Appendix A, which contains some useful information for setting up your WPF development environment, and there is a glossary at the back of the book that defines common WPF terms. This book also contains useful tidbits that I have picked up during my time with WPF—the sort of things developers need to know but normally wouldn’t without a great deal of experimentation and frustration. These snippets of information are highlighted as follows:
TIP Tips contain useful information that can be employed in WPF, usually with the purpose of expediting portlet development. Tips are useful, but they are not critically important and can be skipped if desired.
WARNING Warnings are important points that usually obviate a common sticking point, and heeding them may spare you future frustration.
Due to the width of the book’s printed page, some lines of code might be too long to fit on only one line. If a line of code wraps to another line(s), and it’s not obvious it’s part of the preceding line, we have inserted a code continuation character ([ccc]) at the beginning of the runover line to indicate that the code is part of the line preceding it. (You should type lines of code that have this character as one long line without breaking it.) For example, WebAppAccess remoteWebAppAccess = ¯webAppAccess.getModelInstance(“modelPath/ModelName”, “”, ¯false);
All the examples discussed in this book are available for download from ibmpressbooks. com/title/9780137134465. More advanced readers can import what they want from these examples directly into their projects, without necessarily consulting the book itself—although you are encouraged to follow along with the examples to increase your understanding of WPF. By walking through each example, you will learn how to build portlets in WPF by actually building them, and not just reading about it; so, by the end of each chapter, you should have a practical understanding of how to work the discussed features into your own portlets. Although this book does discuss the theory of WPF portlet development, this information is discussed in the context of the practical examples in the book, which gives you a more concrete
Preface
xxxi
understanding of how the abstract side of portlet development is applied. Readers unconcerned with what is going on under the covers can skip the theory sections without adversely affecting their portlets. Indeed, one of the advantages of using WPF is that you don’t need to learn vast amounts of theory to begin development—you can start building portlets right away. The focus of this book, then, is on the practical side of portlet development, with an emphasis on rapidly building portlets with certain functionality—this is not intended as a book of theory. Similarly, given its short length, this book is not intended to cover every aspect of portlet development—only those areas that are deemed most useful to portlet developers (and WPF developers, in particular). I hope you find this book useful and enjoyable to read; I certainly enjoyed writing it. At the least, I would like this book to go some way toward expediting your portlet development process and increasing your understanding of WPF. If you have any comments about the content or structure of the book, feel free to drop me a line at [email protected]
This page intentionally left blank
Acknowledgments
I’d like to extend my sincerest appreciation to everyone who supported me during the process of writing this book. In particular, I’d like to thank John Bergland and Dee Zepf for their early support of the concept, and Jonathan Booth and Louise Simonds for their insightful comments and advice. I’m eternally grateful to Katherine Bull, my acquisitions editor, for her continual support and direction throughout the project; and to Kevin Howard, my development editor, who carefully surveyed each chapter and made many helpful suggestions. Thanks also to Carlos Llopart for the Spanish translations in Chapter 12, and to my sister Kirsten for providing several of the diagrams that appear in this book. Of course, a thank you is also due to you, the reader, for purchasing this book and investing the time to read it. I hope that this book increases your understanding of WebSphere Portlet Factory and provides you with a valuable portlet development resource.
xxxiii
This page intentionally left blank
About the Author
David Bowley is a consultant for e-Centric Innovations, an e-business solution provider in Melbourne, Australia. Over the last nine years, David has worked extensively with IBM technologies, particularly WebSphere Portal and WebSphere Portlet Factory. David has a bachelor’s degree in computing and holds numerous I.T. certifications in areas such as WebSphere Portal, WebSphere Portlet Factory, Java, Lotus Notes, .Net, DB2, Rational, SOA, and RUP. David is a frequent contributor to various technical publications, including The View and Lotus Advisor.
xxxv
This page intentionally left blank
C
H A P T E R
1
Introduction to WebSphere Portlet Factory
This chapter explains basic portal concepts (such as what portals and portlets are), and introduces the fundamentals of portlet development using WebSphere Portlet Factory (WPF). By the end of the chapter, you will have a working Hello World! portlet and you will understand the basic concepts and techniques required to build, deploy, and test portlets using WPF. This chapter is intended for WPF newcomers, but those who are interested in brushing up on the basics of WPF should also find something of interest in it. If you already feel confident using WPF, feel free to skip ahead to any of the later chapters—you can always come back to this chapter if you need to. The files used in this chapter are available for download from ibmpressbooks.com/title/ 9780137134465 under the Chapter 1 folder (instructions for copying these files into your project are included in a readme.txt file in the same folder); however, to increase your understanding of the topics discussed, it is recommended that you create these files yourself by following the example in this chapter. Doing so will give you a good understanding of the basics of WPF and position you to tackle some of the other chapters in this book. The following topics are covered in this chapter: • What is a portal and what are portlets? • Portal key benefits • What is WebSphere Portlet Factory? • WebSphere Portlet Factory key benefits • WebSphere Portlet Factory architecture • Introduction to the WebSphere Portlet Factory Designer • Building your first portlet
1
2
Chapter 1 Introduction to WebSphere Portlet Factory
What Is a Portal and What Are Portlets? A portal is a user interface (UI) for people to interact with information and processes, often via Web pages accessed using a Web browser (such as the portal shown in Figure 1.1). Portals are provided by a portal server, such as WebSphere Portal Server, in a similar way to how Web pages are provided by an ordinary Web server. Portals usually aim to streamline business operations by bringing together information and processes from disparate parts of an organization. For example, an online bookstore may provide a portal for customers to preview new releases, purchase new books, check the status of any current orders, and even chat with other customers to share their thoughts on a particular book. Portal functionality is normally provided through one or more portlets, which are UI components of the portal that serve a particular function (similar to how a window offers a particular function—or suite of functions—within a Microsoft® Windows® application). In the case of an online bookstore, then, a portal might include a New Releases portlet and a Forum portlet.
Figure 1.1
An example portal at www.ibm.com/developerworks/.
Portlets are laid out onto various pages within the portal, each page effectively containing a set of portlets that serves a common purpose. So, a Purchase page might contain portlets for
Portal Key Benefits
3
browsing a catalogue and for buying new books, and a Community page might contain portlets that facilitate communication with other customers. Portals don’t necessarily have to be customer facing; for example, an intranet portal might be available only to employees, and an extranet portal might be available only to suppliers. A portal can be anything from a simple front-end to a backend data source, to a complex collaborative tool integrating several different departments across an enterprise. Due to the current bandwidth limitations of the Web, portals are usually kept relatively simple in comparison with more traditional rich clients (like Microsoft Word, for example). Similarly, portlets are not normally as packed with features as their rich client cousins, and they offer a more focused service. As a result, portlets normally satisfy some key business requirement or process (either individually or in conjunction with other portlets). Having said this, portals available through a Web browser can be accessed on a wide variety of platforms, using a number of different devices, and from anywhere in the world; so, potential exposure to a portal available through a Web browser is vastly superior to that gained through a rich client. Careful consideration, then, needs to be applied to the portal design process, and a portal should aim to supplement rich client applications rather than replace them.
Portal Key Benefits Depending on its exact nature, a portal usually offers one or more of the following key benefits: • Integration of Business Functions—Probably the most common use for a portal is as an integrator of business functions, usually across departmental or technological boundaries within an enterprise. For example, a portal might provide portlets for accessing accounting applications in SAP, reports on a SQL server, and email capabilities from Lotus Domino®, and it can enable you to use these portlets (and even send information between them) as if they were all part of the same system. This sort of integration promotes standardization of business processes and discourages the practice of viewing an organization as a collection of isolated business units. • Aggregation of Information—Another popular use for a portal is as an aggregator of information—that is, as a means to bring together information from disparate locations or data sources into a single interface (again, giving the impression that all of the information is stored in the same place). A portal can save users from needing to use several different programs to hunt through multiple systems in search of information. • Collaboration between Users—Portals often provide functionality that facilitates communication and information sharing across an enterprise (such as document sharing, email, blogs, and project team rooms). • Accessibility of Information and Services—One of the greatest features of the Web is how it quickly and easily facilitates communication between people from vastly different locations and backgrounds. As a result, portals often provide an experience tailored
4
Chapter 1 Introduction to WebSphere Portlet Factory
to the context in which they are accessed. For example, a portal may be offered in several different languages, and different markups (such as HTML and WML) may be used to allow access to the portal using different devices (such as mobile phones, laptops, PCs, and PDAs). Portal servers also provide mechanisms to restrict accessibility through customizable authentication and authorization mechanisms that can leverage an organization’s existing security methods (such as Lightweight Directory Access Protocol [LDAP] and Lotus Notes® Access Control Lists [ACL]). Putting this into practice, an employee from a sales department might be able to see only the sales portion of a portal, and an unauthorized person might not be able to log on at all. • User Interface Personalization—Portals often provide a certain degree of personalization, such as allowing customers to specify the information they see and how it should be displayed. For example, a portal can provide facilities for end users to choose the position of portlets on the page or to add and remove portlets as required.
What Is WPF? WPF is a tool for Eclipse-based Integrated Development Environments (IDE) that facilitates rapid application development. This is achieved primarily through the use of wizard-based, reusable components called builders, which can be parameterized based on different contexts and automatically built into executable code. Developers can create WPF applications by creating a WPF project in an Eclipse-based IDE (such as Rational® Software Architect [RSA], Rational Application Developer [RAD], or Eclipse itself), which has WPF installed into it. After it is installed, WPF adds new design artifacts to the IDE that enable the development of WPF applications. WPF projects can then be automatically assembled into Web ARrchive (WAR) files (a WAR file is an archive of Java design artifacts for a Web application) from the IDE and deployed to local or remote application servers or portal servers. Note that although you can use any version of WPF to complete the examples in this book, it is assumed that you are using WPF 6.0.2.
WPF Key Benefits Some of the key benefits of WPF are: • Reduced Need for Hand Coding—Developers can build standards-based applications (such as portlets, Web services, or Java 2 Enterprise Edition [J2EE] applications) with little or no coding. Given the reduced need to write code, the skill set required to build complex applications (in J2EE, for example) is drastically reduced. • Faster Development Times—As WPF development is mostly wizard-based, it is less error prone than traditional development and can greatly speed up the development
WPF Architecture
5
process. WPF applications are also more flexible than traditional Java applications in that they are easier to maintain and extend. • Best Practices—WPF encourages component- and service-based design, and can be used to enforce architectural patterns and best practices. • Powerful Capabilities—WPF has a suite of around 160 wizard-like builders that can be used to automate many common development tasks. These builders have strong integration capabilities with a broad range of backend data sources.
WPF Architecture At design time, WPF applications consist of three main building blocks: builders, models, and profiles. Developers work with these elements (along with other design elements like HTML pages, XML documents, and Java classes) in a special type of project called a WebSphere Portlet Factory project (see the “Introduction to the Eclipse IDE” sidebar later in this chapter if you are unfamiliar with projects in Eclipse). Using all of these elements, a WPF mechanism known as the Factory generation engine can automatically generate executable applications called WebApps. WPF can also automatically package these WebApps into different types of deployable WAR files, which you can configure via deployment configurations. Each of these elements is discussed in the following sections.
Builders Builders are the basis of any WPF application; they are essentially wizard-based interfaces that automate common development tasks (see, for example, the SQL Call builder in Figure 1.2). A builder can do anything from add a button to a HTML page, to send data to a backend data source, to publish parts of an application as a Web service. Builders contain sets of parameters (often called builder inputs or fields) that developers use to customize what they want a builder to do. You can modify builder inputs at any time and regenerate your application as many times as you like. As of release 6.0.2, WPF contains about 160 builders, and developers are free to create their own custom builders as well. Using builders encourages component-based design, and a custom builder library can enforce architectural and coding standards across a development team. For example, after a Domino Data Access builder call has been added to your project, you just have to enter some parameters (such as a Domino database to connect to) and save your changes, after which WPF automatically generates all the appropriate code to access and manipulate the appropriate backend Domino database. You then use another builder call (or several, depending on the degree of customization required) to display and format data from the database. Developers usually interact with builders through a graphical interface (shown in Figure 1.2), but a combination of Java classes and XML files are used to create builders on the backend. Developers can manipulate these files to extend the pre-packaged builders or to create their own builders. For an example of how to create your own builder, see Chapter 15, “Performance and Process Optimization.”
6
Chapter 1
Figure 1.2
Introduction to WebSphere Portlet Factory
The SQL Call builder.
Models
Developers assemble collections of builder calls inside models, which list what builders WPF should use to generate your application and to store the specific values used for each builder input. Through builder calls, models can link to other models as well; you might, for example, use one model for UI-related builder calls, which, in turn makes calls to another model for data retrieval. Models are stored as XML files so they can be easily distributed, but are manipulated in a GUI using the WPF Designer. Structuring your projects with models enables you to build flexible, highly modular applications.
TIP
If a model contains an Action List builder called ‘main’, you can run the model directly from your IDE, which executes the sequence of actions defined in the main builder from the code generated by WPF. If a model contains a Portlet Adapter builder, when your application is generated, a portlet is created based on the builder calls in the model.
A single WPF project can contain one or more models, and subsequently can contain several portlets (when you deploy your project to a portal server, you will have access to all of the portlets contained in the project). For example, the Hello World! project built later in this chapter
WPF Architecture
7
contains a single model consisting of several builders. One of these builders is a Portlet Adapter builder, which means that when you deploy the project to a portal server, a Hello World! portlet will become available.
Profiles WPF uses profiles to facilitate context sensitive functionality in your applications and can actually generate different applications for different contexts, roles, or settings. A profile is essentially a set of values for one or more builder inputs, and WPF decides which of an application’s profiles to use based on certain conditions. These conditions can be anything from a user’s J2EE role, to an LDAP group, to a custom condition specified by the developer. For example, you might set up two profiles for an application: one for administrators and one for ordinary users. The context in this case is the type of user (which might be specified in an LDAP group). Each profile might contain different values for certain builder inputs in your application (which might lead to different pages displaying or different execution paths being followed). Depending on how you configure your profiles, two separate applications might be generated (one for each type of user); or perhaps only one application, which responds differently depending on the user who is accessing it. Profiles are stored in files called profile sets, which have a .pset extension and can be found under the WebContent/WEB-INF/profiles directory in your project. For an example of how to use profiles in your application, skip to Chapter 12, “Profiling Portlets.”
Generating and Executing the WebApp Whenever you open, save, or apply changes to a model or builder call, a WPF mechanism known as the Factory generation engine creates one or more executable versions of your model called WebApps. WebApps consist of things like Java code, HTML pages, and XML files, and are built according to the inputs in your builder calls. For example, a Page builder called page1 causes an HTML page called page1 to be created in a WebApp by the Factory generation engine. When generating your WebApps, the actions taken by the Factory generation engine are determined by backend Java classes for each builder used (called builder regeneration classes). You can view the artifacts generated as part of the WebApp by selecting the WebApp Tree tab, in the editor area of your IDE when you have a model currently selected (for more information on the WPF Designer interface, see “The User Interface” section later in this chapter). The WebApp is also accessible via API calls made from Java or from inside other builders. The design time generation process is shown diagrammatically in Figure 1.3. In step 1, you create your models, populate them with builder calls, and then assign values to the builder inputs that these calls contain. In step 2, you generate a particular model (usually by saving one of its builder calls), which causes the Factory generation engine to build a WebApp based on your builder input values, using the corresponding builder regeneration classes.
8
Chapter 1
Introduction to WebSphere Portlet Factory
1 WebSphere Portlet Factory Designer Model A
Model B
Model D Builder Call 1 * Builder Inputs Builder Call 2 * Builder Inputs
In step 3, you have the finished product: a WebApp, which contains all of the artifacts needed by your application at runtime. If you have only one set of builder input values for a particular model, you will have only one WebApp for that model. The Factory generation engine will create the WebApp using the appropriate builder regeneration classes, passing in values from the builder input values in the builder calls contained in that model. However, if you profile your builder inputs, then after WPF regenerates your application, it is possible to have more than one WebApp for a single WPF model. For example, you might have two profiles in your application—one for administrators and one for ordinary users—each one specifying different values for a particular builder. Subsequently, two WebApps will be created; one will use the values from the administrator’s profile, and the other will use the values from the ordinary user’s profile. Figure 1.4 shows a diagram of this process.
When WPF applications are executed on a server, they pass through several stages, and generation can occur during this process. The execution process is shown diagrammatically in Figure 1.5. In step 1, a user requests access to a WPF application, and the request is received by the Factory controller servlet (WebEngineServlet) at step 2. The Factory controller servlet then routes the request to a WPF process called WebAppRunner in step 3. At step 4, the WebAppRunner process determines which profile (if any) to associate with the user request. If no profile is required, then control skips to step 6. If a profile is required, then the profile is selected at step 5 and control proceeds to step 6. At step 6, a check is performed to see if an appropriate WebApp for the current request already exists in the cache (that is, one that matches the requested profile and model combination). If an appropriate WebApp does exist, then it is retrieved from the cache at step 7, and control passes to the final step, where an executable WebApp is made available. If an appropriate WebApp does not exist, then control passes to the generation engine at step 8, and an appropriate WebApp is generated at step 9. Finally, an executable WebApp is available in step 10.
10
Chapter 1
Introduction to WebSphere Portlet Factory
1
User request
2
Factory controller servlet
3
WebAppRunner
4
Is Profile needed?
Yes
No 5
Profile selected
6
Is WebApp in cache? Yes
No
7
WebApp retrieved 8
Generation engine 9
WebApp generated 10
WebApp available
Figure 1.5
The WebApp execution process.
Although the process of generating (and executing) your WebApps occurs mostly behind the scenes in WPF, understanding it will enable you to make better sense of the WPF development process and ultimately enable you to directly manipulate the WebApp in your applications (via the WebApp API). Use of the WebApp API is discussed in Chapter 8, “Using Java in Portlets.”
WPF Architecture
11
Portlet Factory WAR Files WPF applications can be automatically assembled into WAR files, which can then be deployed to an application server or portal server. Target servers for WAR deployment can be either local or remote to your development machine. There are three different types of WAR files you can create in WPF (one application can use all three WAR files simultaneously), and each one has slightly different characteristics. The three different WAR files are discussed in each of the following sections.
Development WAR The development WAR contains functionality to assist development, which is not contained in the other types of WAR files (such as the capability to run models directly from a URL). Whenever you run models directly from your IDE, you use the version of the WebApp contained in the development WAR. The development WAR must be deployed to an application server. You can create development WARs by right-clicking your project and selecting Application Server WAR, Build WAR for dev testing; however, you need an application server deployment configuration before this option becomes available (deployment configurations are discussed in the “Deployment Configurations” section).
Portlet WAR The portlet WAR contains functionality that enables you to run models as portlets within a portal, as well as take advantage of functionality offered by the portal (such as inter-communication between portlets). This is not contained in the other WAR file types. Portlet WARs do not contain any development-specific design elements, so they are suitable for any sort of environment (test, production, and so on). The portlet WAR must be deployed to a portal server. You can create portlet WARs by rightclicking your project and selecting Portal Server WAR, Build Portlet WAR; however, you need a portal server deployment configuration before this option becomes available.
TIP Even though you need a portlet WAR to view your models as portlets, you can still preview these models as standalone Web applications using the development WAR. This is useful for quickly testing features of your portlets that don’t require functionality that is available only in the portal (such as inter-portlet communication). After testing, you can then create a portlet WAR to test any portal-specific functionality.
12
Chapter 1 Introduction to WebSphere Portlet Factory
Deployment WAR The deployment WAR doesn’t contain any of the development artifacts included in the development WAR, and can’t take advantage of portal server specific functionality. You would normally use a deployment WAR when you want to deploy your application to a production environment, but don’t want to use any of the capabilities of a portal (for example, your application doesn’t contain any portlets). You might create a Web service and deploy a deployment WAR to an application server in your production environment, so that the Web service is constantly available to external consumers. You can also use deployment WARs in test environments when you don’t want to include any unnecessary design artifacts in the application. The deployment WAR must be deployed to an application server. You can create deployment WARs by right-clicking your project and selecting Application Server WAR, Build WAR for a production deployment; however, you need an application server deployment configuration before this option becomes available.
Deployment Configurations To run your applications, you need to deploy them to an application server or portal server. In WPF, you use deployment configurations to manage settings for these deployments. You can use two deployment configurations: • Application server deployment configuration • Deployment server deployment configuration The application server deployment configuration is used mainly to specify settings for testing your application directly from the IDE. The development WAR and deployment WAR, if you are using them, are both deployed to the server specified in the application server deployment configuration. The portal server deployment configuration specifies settings for deploying the portlet WAR to a portal server. Running WPF portlets on a portal server lets you take advantage of the functionality offered by the portal (such as inter-portlet communication and user-configurable, shared-settings menus), and running your WPF portlets on an ordinary application server lets you view them as standalone applications. There are advantages to both approaches (an application server is often quicker for testing, but doesn’t have all the functionality of a portal server), and common practice is to use an application server for testing quick changes that don’t require functionality from the portal, and then use a portal server for testing portlets as they would appear in a production environment. WPF 6.0.2 comes with a lightweight application server called WAS CE, which is perfect for local testing. It is possible to link to local servers (those installed on the same machine as WPF) and remote servers (those installed on a different machine to WPF) from your deployment configurations, and each scenario has different advantages. Local servers are the easiest to set up as they don’t require you to manually install WAR files; however, deploying to a remote server can free
Introduction to the WebSphere Portlet Factory Designer
13
up valuable resources on your development machine. If you want to have the capability to automatically refresh WAR files from your IDE (highly useful in test environments), you need to link to servers that are installed either on your local machine or are accessible through the file system via a mapped drive. If you’re deploying to a remote machine that you can’t access through the file system, you have to manually update the installed application from the WAR file created by WPF.
TIP When mapping to a remote server, you don’t need access to the entire file system. If security is a concern on the remote server, you should map only the application server or portal server directories on the remote machine. Note that WAS 6 and WPS 6 both have multiple parent directories by default (usually c:\Program Files\IBM\WebSphere and c:\IBM).
Introduction to the WebSphere Portlet Factory Designer The WebSphere Portlet Factory Designer (or WPF Designer) is an Eclipse-based IDE with the WPF software installed (as a result, the terms IDE and WPF Designer are often used interchangeably throughout this book). WPF Designer enables developers to create WPF applications and to utilize WPF-specific design elements such as builders, models, and profiles in their projects.
The User Interface After WPF Designer is installed (see Appendix A, “Setting Up Your Environment,” if you want more information on how to do this), developers should switch to the WebSphere Portlet Factory perspective if it is not already visible (if you are unfamiliar with the term perspective, please see the “Introduction to the Eclipse IDE” sidebar). You can switch to the WebSphere Portlet Factory perspective by selecting Window, Open Perspective, Other, selecting WebSphere Portlet Factory from the list, and then pressing OK. After the WebSphere Portlet Factory perspective loads, your IDE should display similar to the screenshot shown in Figure 1.6 (note that there may be slight differences depending on the version of WPF you use).
TIP You don’t necessarily have to use the WebSphere Portlet Factory perspective if you don’t want to, but it does provide you with an easy-to-use interface with several commonly used views. You can switch between perspectives at any time by selecting Window, Open Perspective, Other, and then selecting the appropriate perspective from the list; you can even create your own perspective by adding and removing views and editors (which you can do from the Window menu in the IDE).You can then save your perspective by selecting Window, Save Perspective As, giving your perspective a name, and pressing OK.
14
Chapter 1
Introduction to WebSphere Portlet Factory
Project Explorer view
Outline view
Figure 1.6
Model Editor
Problems view
The WebSphere Portlet Factory perspective.
As with any other Web applications, your development artifacts display in the Project Explorer view on the left side of the screen (or in the Navigator if you’re using a version of WPF prior to 6.0.1), which is in section 1 of Figure 1.6. If you are using WPF 6.0.2 or later, underneath the models in this section, you will also see a list of artifacts referenced by the model (such as HTML files and images). The builder calls in the currently active model are displayed below this view in the Outline view, contained in section 2. You can edit a builder call by double-clicking it, and you can add new builder calls to the model by clicking the icon in the top right of the Outline view. The top-right quadrant of the IDE (contained in section 3 of Figure 1.6) displays editors for a number of different aspects of your application, depending on what you currently have open in the IDE. If you have a model currently selected (you can select a model by double-clicking on it in the Project Explorer view), then this section of the screen displays the Model Editor, which contains a series of tabs for the model. These tabs are WebApp Tree, Model XML, Builder Call Editor, and WebApp Diagram.
Introduction to the WebSphere Portlet Factory Designer
15
The WebApp Tree tab (shown open in Figure 1.6) gives you a hierarchical display of the items used in your WebApps and a read-only view of the code generated by the generation engine. The Model XML tab (shown in Figure 1.7) gives you a read-only view of the backend XML representation of your model.
Figure 1.7
The Model XML tab.
The WebApp Diagram tab (shown in Figure 1.8) gives you a diagrammatic representation of the execution flow in your WebApps. Switching between these tabs gives you a different view of the currently active model. The Model Editor tab (shown in Figure 1.9) enables you to edit the currently active builder call in your model. To open a builder call in the Model Editor, simply double-click on the appropriate builder call in Outline view.
TIP
Perusing the WebApp Tree tab is a great way to see what the generation engine is doing on the backend and can really improve your understanding of WPF. Also, code snippets from the WebApp Tree tab can be copied and pasted into your own code. Using the WebApp code can help you learn the WPF Application Programming Interface (API), and it will also help Java novices improve their Java skills.
16
Figure 1.8
Figure 1.9
Chapter 1
The WebApp Diagram tab.
The Builder Call Editor tab.
Introduction to WebSphere Portlet Factory
Introduction to the WebSphere Portlet Factory Designer
17
If you are not currently editing a model or builder, the top-right quadrant of the screen can also be used to display editors opened from other views in the IDE. For example, double-clicking an XML file in the Project Explorer view opens that XML file in a text editor, and double-clicking on a Java source file opens the code in a code editor. The final portion of the screen shows the Problems view (contained in section 4 of Figure 1.6), which displays any errors with your project or with the generation of your application. Another useful view in this section of the screen is the Applied Profiles view, which lets you simulate what your application would do when a particular profile is used. For example, you can test what your application would do when an administrator accesses your portlet, without actually having to log in to the portal as an administrator. You can switch to this view by clicking the Applied Profiles tab next to the Problems tab in the WPF Designer.
INTRODUCTION TO THE ECLIPSE IDE There are several fundamental concepts that newcomers to Eclipse-based IDEs (such as RAD, RSA, or Eclipse) should be familiar with before using WPF: Project—A project is a container for all of the design artifacts in your application. There are different types of projects for different types of applications; for example, a Web project is used to create standard Web applications, and a WebSphere Portlet Factory project is used to create WPF applications.You can create projects by selecting File, New, Project in the IDE. View—A view is a window in the IDE used to display, organize, or navigate through information. The Problems view, which displays errors in your application and with the generation process, is an example of a view. You can add new views to the IDE by selecting Window, Show view, Other. Editor—An editor (such as the Model Editor) is a window in the IDE used to enter and save information for your project. You can open an editor for a file by double-clicking that file from a view in your IDE. You can assign particular editors to file types by selecting Window, Preferences, and then navigating to the General, Editors, File associations category. Perspective—A perspective is a particular arrangement of views and editors in the IDE. The WebSphere Portlet Factory perspective, for example, contains the views and editors that are most conducive to WPF development. You can switch between perspectives by selecting Window, Open Perspective, Other in the IDE. Workspace—A workspace contains IDE settings and a set of projects that you’re working on. For example, you might have a workspace that contains all of your WPF projects and another workspace for all of your standard Java projects. You can switch between workspaces by selecting File, Switch Workspace in the IDE.
Folder Structure WPF projects are organized into several different folders by default, which you can navigate through using the Project Explorer view (the main folders are shown in Figure 1.10). Doubleclicking on a file in the Project Explorer view opens it in an editor, provided there is an editor for
18
Chapter 1
Introduction to WebSphere Portlet Factory
that file type available. The default links and folders displayed in the Project Explorer are discussed in the following sections (please note that earlier versions of WPF may have a slightly different file structure).
Figure 1.10
Default WPF folders shown in the Project Explorer view.
Models
This folder holds all of the models in your project. Note that this folder is actually the same as the WebContent/WEB-INF/models directory, so files stored in either of these directories display in both locations. Profiles
This folder contains all of the profile sets in your project (profile sets have a .pset extension and can be opened in a Profile Set Editor by double-clicking the appropriate profile set in the Project Explorer view). As with models, this folder is actually the same as a folder under the WebContent directory (WebContent/WEB-INF/profiles), so if you modify any of your profile sets, both directories will reflect the changes.
Introduction to the WebSphere Portlet Factory Designer
19
WebContent/WEB-INF/work/source This folder contains all of the Java source code for your project. The WPF designer automatically compiles any .java file that is saved in this directory and reports any errors in the Problems view (the corresponding .class files are stored in the WebContent/WEB-INF/work/classes directory).
JRE System Library The JRE system library contains all of the libraries required for the Java Runtime Environment (JRE), and is not directly accessible from the Project Explorer view. You should switch to the Package Explorer view if you want to view the contents of the JRE system library.
WebContent The WebContent folder stores all the design artifacts in your project that are directly servable to clients (that is, they can be accessed directly in a Web browser), with the exception of the contents of the WEB-INF directory. An index.jsp file in the WebContent directory enables you to browse through all the models in your project and run them from a Web browser (note that this artifact exists only in the development WAR, so you don’t have to worry about end users browsing through your models in a production environment). Additionally, the WebContent directory usually contains your web.xml and portlet.xml files, which are descriptors used to define your application to the target server. You don’t have to edit these files directly, as WPF will edit them for you when your application is regenerated.
TIP You can make changes to the web.xml file yourself, but be aware that parts of the file are automatically updated by WPF. The parts that WPF automatically updates are clearly marked, so you shouldn’t run into any problems. The portlet.xml file, however, is completely rewritten every time your application is rebuilt, based on settings in your Portlet Adapter builder—so any direct changes you make to the portlet.xml file are lost. Several options are available if you want to make changes to the portlet.xml file that are not covered by the Portlet Adapter builder. First, you can manually edit the portlet.xml file after it has been created, although this is not recommended as you need to do it every time the application is rebuilt. Second, you can modify the configuration files in the WebContent/WEB-INF/bin/deployment directory of your project. Both approaches can be supplemented with a third option, which involves using a custom builder to update the portlet.xml file. Creating a custom builder is covered in Chapter 15.
The WebContent directory contains three main directories: • A factory directory, which contains core WPF resources, such as HTML pages • A META-INF directory, which stores meta information about your project • A WEB-INF directory, which contains all of the design elements that aren’t directly servable to clients (such as Java classes, models, and profiles)
20
Chapter 1 Introduction to WebSphere Portlet Factory
The WEB-INF directory usually contains most of the files in your project and is split up into a number of subdirectories (shown in Table 1.1).
Table 1.1 WEB-INF Subdirectories Directory Name Description bin
The bin directory contains various batch files you can use to assist in the development process, such as obscure.bat (which encrypts passwords for you).
builders
Contains builder definitions used in WPF.
classes
Contains deployable class files. You don’t need to directly change anything in this folder.
clientLibs
Contains deployable JAR files.
config
This directory contains configuration files for your project. Configuration files can be used to configure everything from logging settings to server settings. They are discussed further in Appendix B, “Portlet Factory Properties.”
factory
Contains core WPF design artifacts that are not directly servable to clients, such as XML data definitions and SQL scripts.
lib
Contains all of the JAR files used by WPF.
logs
In addition to more traditional Java logging methods, WPF has its own logging mechanisms that incorporate log4j logging. The results of these logging processes are stored in the logs directory. For more information on using logs in WPF, see Chapter 14, “Error Handling, Logging, and Debugging.”
manifests
Contains a manifest of all the files in your project.
models
This directory is the same directory as the models directory that displays at the top level of your project hierarchy in the Project Explorer view; it stores all of the models in your project.
profiles
This directory is the same directory as the profiles directory that displays at the top level of your project hierarchy in the Project Explorer view; it stores all of the profile sets in your project.
script_templates
Contains any .jst templates in your application.
work
The work/source folder contains all of your Java source code (the same folder as the one that displays at the top level of your project hierarchy), and the work/classes folder contains all the compiled versions of these files.
Building Your First Portlet
21
TIP By default, the source folder is not displayed under the work directory, so if you want to edit source code, use the WebContent/WEB-INF/work/source folder at the top level of your project hierarchy. Note also that you shouldn’t edit the class files directly, as they are automatically overwritten by the WPF Designer every time you save a Java source file.
Building Your First Portlet This section walks you through the process of creating a simple Hello World! portlet, deploying it to an application server and portal server, and then testing it. If you can successfully complete the portlet discussed in this example, then you will understand enough about the fundamentals of WPF that you shouldn’t have any problems with building, testing, or deploying in future chapters. Before you proceed, you need to decide which server(s) you are going to deploy to. You will then link to these server(s) when you set up your deployment configurations. If you have both local and remote servers available, you need to make a choice about which ones are the best to use. Local servers are certainly more convenient for testing purposes, but may not be practical if you’re using a slow machine or have limited server licenses. Instructions for both remote and local deployment are discussed over the course of the example. The example in this chapter uses both types of deployment configuration (application server and portal server). For the application server deployment configuration, you will see how to point to a local WAS CE server or WebSphere Application Server, and for the portal server deployment configuration, you will see how to point to a local WebSphere Portal 6 server (although the deployment process is fairly similar across different server versions). Note that because a portal server is actually a normal application server with a portal framework, you can actually use a portal server for both deployment configurations; however, you can’t use an ordinary application server for your portal server deployment configuration (as it doesn’t have a portal framework). If you need more information on setting up your development environment (installing WPF, configuring your portal server, and so on), please refer to Appendix A for more information.
Creating a Project This section walks through the process of creating a new Hello World! project in WPF, and discusses how to set up your deployment configurations. This project contains a simple model that will be surfaced to a portal server as a portlet. To create a project, first select File, New, WebSphere Portlet Factory Project in the WPF Designer to start the Create Portlet Factory Project wizard. If this option is unavailable, select File, New, Other, select WebSphere Portlet Factory Project from the WebSphere Portlet Factory category, and then press Next.
22
Chapter 1 Introduction to WebSphere Portlet Factory
Type in a name for the project (such as WPFSandpit), and press Next when you are finished. Note that the project you create in this section can be reused in the other chapters of this book, so you should name the project accordingly. The next screen (shown in Figure 1.11) enables you to specify feature sets to add to your project. Feature sets give you access to additional sets of builders and can be added to a project at any time. You use some of these feature sets elsewhere in this book, but for now, don’t select any feature sets and press Next.
Figure 1.11
Specifying feature sets for your project.
The next screen displays the Java build settings for your project (for more information on using Java in your portlets, see Chapter 8. For now, press Next to accept the default settings. The next screen enables you to specify your deployment configurations.
Setting Up an Application Server Deployment Configuration If you installed WPF with WAS CE, then you already have an application server deployment configuration called myWASCE1. Select this configuration if you have it, and skip to the “Setting Up a Portal Server Deployment Configuration” section later in this chapter. If you don’t have an application server deployment configuration, you need to create one.
Building Your First Portlet
23
To create an application server deployment configuration, press the Add button in the application server deployment section of the screen. The New Deployment Configuration window displays. Give your new configuration an appropriate name without using any spaces (such as ApplicationServer6). Type a description for your configuration (such as Deployment configuration for WAS 6), and then select a server from the Server Type dropdown (such as WebSphere Application Server 6.x if you are using WebSphere Portal 6.0 as your target server). After you select a server, some extra options should display below the server type dropdown. Every application server has a directory for its installed applications called installedApps. Type in the location of the installed applications directory for your application server in the Installed Applications Dir box. Then, type in the hostname for your application server in the Server Host box (you can use localhost if the server is on the same machine as WPF) and the port on which you access applications on the server in the Server Port box. If your application server is readily available and running, enable the checkbox for Specify Deployment Credentials, enter the WAS server name, and enter the username and password of a user who has access to deploy applications on the server. This gives you the option of deploying your application directly from the IDE; if you do not select it, you need to deploy it manually on the application server (this is covered in the “Manual Deployment” section).
WARNING If you use WebSphere Portal Server in your application server deployment configuration, you should specify the name of the portal server for the WAS Server for deployment dropdown (WebSphere_Portal by default) rather than the name of the application server (server1 by default).
Press OK to save your application server deployment configuration. Note that if you have enabled the Specify Deployment Credentials checkbox, your connection to the server may be automatically tested when you press the OK button. You can also test your connection by pressing the Test Server Connection button, although WPF will still test your settings when you try to save the configuration. Depending on your server’s security settings, WPF might prompt you for a username and password (you can use the same username and password you just specified). After you have connected, you should see a success message, as shown in Figure 1.12.
WARNING Testing your application server connection may yield a success message even when the server port is wrong. If you are unsure about the server port, make sure you can access applications on the server from the port you’ve specified. Some of the default server ports are: WebSphere Portal 6: WebSphere Portal 5.1: WebSphere Application Server 6: WebSphere Application Server 5.1:
10038 9081 10000 9080
24
Chapter 1 Introduction to WebSphere Portlet Factory
Figure 1.12
Message to indicate a successful test of the server connection.
Setting Up a Portal Server Deployment Configuration The next step is to set up a portal server deployment configuration. To do this, first press the portal server Add button from the Deployment Configuration screen in the Create Portlet Factory Project wizard. The New Deployment Configuration window displays. Give your new configuration an appropriate name, such as PortalServer6 (don’t use any spaces). Then, type a description for your configuration (such as Deployment configuration for WPS 6), and select a server from the Server Type dropdown (such as WebSphere Portal 6.x). Some extra options should display under the server type dropdown. Leave Java Standard selected as your Portlet API—this means that any portlets you build will be based on the JSR-168 specification, which is an industry standard set of APIs for building Java portlets.
WARNING If you are using a version of WebSphere Portal server prior to version 5.1, you might need to set the Portlet API to WebSphere Native (deprecated), as WebSphere Portal servers prior to version 5.1 do not support the Java Standard API by default. To deploy to WebSphere Portal 5.0 or earlier, you also need to use WPF 5.x instead of WPF 6.x.
If your portal server is accessible through the file system, you can deploy to it directly from the IDE. To set this up, type the root directory for the portal server in the WP Root box (for example, C:\Program Files\IBM\WebSphere6\PortalServer). Enable the checkbox for Specify Deployment Credentials, and then specify a username and password for a user who has access to deploy portlets to the server. The Admin URL field should automatically point to the config section of your portal server (this is the portal hostname, followed by the port number, followed by /wps/config), and the JRE Home field should automatically point to the bin directory where your Java Runtime Environment (JRE) is installed (change these fields if they point to the wrong locations). Press OK when you are finished, and your username and password will automatically be tested against the portal server specified.
Building Your First Portlet
25
If you want to deploy to a server that is not accessible through the file system, disable the Specify Deployment Credentials checkbox. The label for the field WP Root will change to ‘War location’, and you should specify where you would like to store the deployable WAR file in this field. After the WAR file has been created, you need to manually deploy it to the remote server (this is covered in the next section, “Manual Deployment”). Press OK to save your portal server deployment configuration. If you clicked the Specify Deployment Credentials checkbox, WPF automatically tests your settings to see if the connection is valid—upon completion, you should see the same success message shown earlier in Figure 1.12.
TIP A number of errors can occur when you try to save a deployment configuration. The most common errors are caused by incorrect passwords or insufficient access to deploy portlets to the target server. Note also that an invalid port number can cause the portal server deployment configuration test to fail, although this is not the case for the application server deployment configuration.
The Deployment Configuration dialog should now contain details for each of your deployment configurations. Press Finish when you are satisfied with your settings. A dialog box then prompts you to confirm whether you would like to deploy your project. Select Yes, which creates deployable WAR files for your deployment configurations and (if you clicked either of the Specify Deployment Credentials checkboxes) also deploys them automatically. This process may take anywhere up to several minutes depending on the speed of your machine and the speed of the servers involved. Depending on your server’s security settings, WPF may also prompt you for a username and password for your application server (you can use the same username and password specified when you set up the application server deployment configuration). Note that the WAR file for the application server deployment configuration is stored inside an EAR file. Either file can be deployed when not deploying to a portal server, but it’s easier to deploy using the EAR file because you don’t have to specify a context for your application during the installation process. When WPF has finished setting up your project, creating your WAR files, and deploying them to any target servers, control is returned to the IDE.
TIP You should check the Problems view in your IDE after you’ve set up your deployment configurations to ensure your application has deployed correctly. One of the most common errors at this stage is specifying a username and password for the portal server administrator (if prompted for it in the deployment process), whereas what is required is the username and password for the application server administrator (although these settings could refer to the same user in many cases). Note that if you are connecting to a remote server from WPF, you will receive an error indicating that the application needs to be deployed. Instructions for manually deploying an application are discussed in the “Manual Deployment” section.
26
Chapter 1 Introduction to WebSphere Portlet Factory
You have now successfully created a WPF project and set up both your application server deployment configuration and portal server configurations. If you enabled the Specify Deployment Credentials checkboxes, then you also have deployed WAR files to each server (a development WAR to the application server and a portlet WAR to the portal server). In the future, if you want to set or modify your deployment configurations for an existing WPF project, you can do this from the WebSphere Portlet Factory/Deployment Info section of the Project Properties dialog, which you can open by selecting the root folder of your project, and then selecting Properties from the Project menu in the menu bar at the top of the IDE. If you set up automatic deployment, you can skip to the “Testing the Application” section. Otherwise, you need to manually deploy your application, which is discussed in the next section, “Manual Deployment.”
Manual Deployment If you did not enable the Specify Deployment Credentials checkbox in either of your deployment configurations, you need to manually deploy your application to that server(s). Manual deployment of the development WAR (created via the application server deployment configuration) and the portlet WAR (created via the portal server deployment configuration) are discussed in the following sections.
Application Server Deployment Configuration To manually deploy an application to an application server, first open the application server administration console in a Web browser (for example, http://myApplicationServer. com:10001/ibm/console). The administration console can also be opened from the Start menu. Expand the Applications section and click Install New Application, as shown in Figure 1.13. In the Local file system box, enter the path to the EAR file you created earlier, which contains the development WAR file (or use the Remote file system box if you can’t access the file locally). By default, this EAR file will have been created in the installableApps directory on the application server when you set up your deployment configurations (in the example, the directory X:\ibm\WebSphere\profiles\wp_profile\installableApps is used). Press Next, and then press Yes if prompted to display non-secure items. You can accept the defaults on the next few screens by pressing Next at each screen (until you are asked to press the Finish button). The first screen contains settings for bindings and mappings, the second screen contains basic deployment options, and the third screen enables you to map modules in your application to particular servers. The fourth screen contains settings for virtual host mappings, followed by a screen containing settings for mapping security roles to users and groups. The final screen contains a summary of your previous settings. Review these settings, and then click Finish to install the application. You will see a success screen similar to the one displayed in Figure 1.14. Click the ‘Save to master configuration’ link to save the new settings. On the confirmation screen that displays, press Save.
Building Your First Portlet
Figure 1.13
Installing a new application through the administration console.
Figure 1.14
Message received after successfully installing an application.
27
You have now installed the application, but you need to start the application before it can be run. To do this, click Enterprise Applications from the sidebar and scroll through the list of applications to find the HelloWorld application (it will have a red cross next to it, which indicates that the application is currently stopped). Enable the checkbox next to the HelloWorld application and press the Start button (as highlighted in Figure 1.15) to start the application. A success message will display, and you should be able to run the application. Note that it is required you go through this process only once for each development WAR file. You can set up your project so that it automatically updates the deployed WAR by opening the Project Properties dialog box, selecting WebSphere Portlet Factory Project Properties, and then typing in the directory where your application is deployed into the Development WAR location field (also, you need to enable the checkbox for automatically synchronizing the dev WAR). This causes changes in your project to automatically update the deployed application whenever you save resources in the IDE. After you have this configured, you can also force a synchronization of the files in your project with the deployed WAR by right-clicking the root folder of your project in the IDE, clicking on Application Server WAR, and then selecting ‘Synchronize (copy) modified files’. Synchronizing the development WAR removes any errors in the Problems view associated with the development WAR not yet being deployed.
28
Chapter 1 Introduction to WebSphere Portlet Factory
Note, however, that certain changes (such as removing files from an application) may require a rebuild as opposed to synchronization. You can rebuild your development WAR by right-clicking the root folder of your project in the IDE and selecting Application Server WAR, Build WAR for dev testing.
Figure 1.15
Starting the HelloWorld application.
Portal Server Deployment Configuration Although it is possible to use the administration console to deploy the portlet WAR, this section describes how to use the portal’s administration interface, which is quicker and slightly easier than using the administration console (but you can’t use it to deploy to an ordinary application server). To manually deploy an application for a portal server deployment configuration, log in to the portal as an administrator, and then open the Administration page. Click Web Modules under the Portlet Management heading (see Figure 1.16) to open the Manage Web Modules portlet. Click the Install button on the Manage Web Modules portlet, and then enter the path to your portlet WAR file in the Web Modules portlet. By default, this WAR file has have been created in the directory specified in the WAR location field in the portlet deployment configuration (X:\Program Files\IBM\WebSphere6\PortalServer\installableApps\HelloWorld.war was used in the example). Press Next. A list of the portlets that this application contains is listed on the next screen. Press Finish to install your application. You should see a success message at the top of the Manage Web Modules portlet, as shown in Figure 1.17. Your portlet WAR file is deployed, started, and ready to use (there is no need to manually start the application).
Building Your First Portlet
Figure 1.16
Opening the Web Modules page.
Figure 1.17
Success message after installing the HelloWorld application.
29
Note that it is required you go through this process only once for each portlet WAR file. You can set up your project so that it automatically updates the deployed WAR by opening the Project Properties dialog, selecting WebSphere Portlet Factory Project Properties, and then typing in the directory where your application is deployed into the Portlet WAR location field (also, you need to enable the checkbox for automatically synchronizing the portlet WAR). This causes changes in your project to automatically update the deployed application whenever you save resources in the IDE. After you have this configured, you can also force a synchronization of the files in your project with the deployed WAR by right-clicking the root folder of your project in the IDE, clicking on Portal Server WAR, and then selecting Synchronize (copy) modified files. Synchronizing the portlet WAR removes any errors in the Problems view associated with the portlet WAR not yet being deployed. Note, however, that certain changes (removing files or actions that affect the portlet.xml file, such as adding portlets to an application) require a rebuild as opposed to synchronization. You can rebuild your portlet WAR by right-clicking the root folder of your project in the IDE and selecting Portal Server WAR, Build Portlet WAR. After you have deployed your application manually to the appropriate server(s), you should proceed to the next section, which discusses how to test your application.
30
Chapter 1 Introduction to WebSphere Portlet Factory
Creating a Model You should now add a model to your WPF project, which will be used to test your application and deployment configurations. A Portlet Adapter builder will be added to the model. This means that when the model is deployed to a portal server, it will be available as a portlet (a screenshot of the portlet is shown later in Figure 1.22). When run on an ordinary application server, the model runs as a standalone Web application rather than as a portlet (as shown in Figure 1.20). The differences between these two scenarios become clearer when you test the application in the next section. To add a model to your project, select File, New, WebSphere Portlet Factory Model in the WPF Designer to start the Create Portlet Factory Project wizard. If this option is unavailable, select File, New, Other, and then select WebSphere Portlet Factory Model from the WebSphere Portlet Factory category. Press Next. On the next screen, select the WPF project you created earlier and press Next. The next screen lists different types of models WPF can automatically create for you. Note that the list of model types you see on this screen will differ depending on the feature sets you have enabled in the project. The model types on this screen have a particular default configuration of builders best suited to a particular purpose, although you can always add these builders manually as well. Chapter 3, “Using Data from a Relational Data Source,” and Chapter 4, “Using Domino Data,” discuss how you can use some of the model types on this screen to automatically create models tailored to accessing and displaying data. Select the Main and Page option to create a basic model that simply displays a page to the screen, and then press Next. Then select Simple Page from the Page Settings screen and press Next again. The Simple Page option causes WPF to create a default page for you, so you don’t have to supply your own. Give your model the name testDeployment, and make sure it is created in the directory models/chapter01. Press Finish to build your model. This creates a new model called testDeployment in your WPF project and opens the model in the Model Editor and Outline view. To surface this model as a portlet on a portal server, you need to add a Portlet Adapter builder to the model. To do this, open the Builder Palette by pressing the icon in the Outline view in your IDE. Select Portlet Adapter from the list as shown in Figure 1.18, and then press OK. This adds a Portlet Adapter builder to your model and opens it in the Model Editor. You need to give the Portlet Adapter a name, portlet title, and portlet description. The name is the name of the Portlet Adapter builder as it displays in Outline view, and the portlet title is the name of the portlet as it displays in the portal. The portlet description displays on several configuration screens in the portal and can be used as a search key to find the portlet. Enter helloWorld for the name and Hello World! for the portlet title. For the portlet description, type This portlet tests my deployment configurations. (Note that you can’t use any spaces in the name, but you can use spaces in the portlet title and portlet description.)
Building Your First Portlet
Figure 1.18
31
Selecting the Portlet Adapter builder.
TIP Note that the portlet title is blank by default, so any time you add a Portlet Adapter builder to a model, make sure you enter a value for the Portlet Title field. If you leave the Portlet Title field blank, the name of the model is used for the portlet title (which is usually undesirable).
Save the Portlet Adapter builder call by pressing Ctrl+S. Select Yes when prompted to save the modified builder calls. Your project now contains a model called testDeployment, and it is ready to be tested.
Testing the Application In this section, you test your Hello World application—first as a standalone Web application run directly from the IDE (which tests the deployed application linked to the application server deployment configuration), and then as a portlet running on a portal server (which tests the deployed application linked to the portal server deployment configuration). First, you need to set up a run configuration, which contains basic settings for how you want to run your applications from the IDE. To begin setting up a run configuration, select the Run option from the Run menu in your IDE. This opens the Run dialog, as shown in Figure 1.19.
32
Chapter 1
Figure 1.19
Introduction to WebSphere Portlet Factory
The Run dialog.
Double-click on the WebSphere Portlet Factory Model category. This creates a new configuration called New_configuration, which displays under the WebSphere Portlet Factory Model heading.
TIP
You don’t need to use the Run menu every time you want to run your application.You can execute the last run configuration you used by pressing the button in your IDE.
Change the name of the configuration to something more meaningful (WPF run configuration, for example), and press the Run button to preview your application. This runs the currently active model (testDeployment, in this case) in your default Web browser. If your browser displays the output shown in Figure 1.20, the deployed application described by your application server deployment configuration is working correctly.
Building Your First Portlet
Figure 1.20
33
Testing your application on an application server.
TIP
If you don’t see the output in your Web browser, make sure the application server is running and your application server deployment configuration settings are correct. Also, if you didn’t enable the Specify Deployment Credentials checkbox in your application server deployment configuration, make sure that your application installed without any errors and has been started (see the previous section, “Manual Deployment,” for an explanation of how to do this).
The next step is to test the deployed application described by the portal server deployment configuration. To do this, you need to rebuild the deployed application on the portal server. If you enabled the Specify Deployment Credentials checkbox in the portal server deployment configuration, changes to your project will, in most cases, automatically update the deployed application, and there will be no need to rebuild. However, there are exceptions to this: If you remove files from your project or modify the portlet.xml file (which you do indirectly when you add portlets or rename portlets), you need to rebuild. The best way to rebuild your application (provided you use the Specify Deployment Credentials option for your portal server configuration) is to right-click the HelloWorld project root folder in your IDE, and then select Portal Server WAR, Build Portlet WAR. This rebuilds both the deployable portlet WAR file and the installed portlet application. If you’re not using the Specify
34
Chapter 1 Introduction to WebSphere Portlet Factory
Deployment Credentials option, you need to manually redeploy (which is covered in the “Manual Deployment” section of this chapter). Note that you can rebuild your deployed portlet application at any time by right-clicking on the project root folder and selecting Portal Server WAR, Build Portlet WAR (provided you are using the Specify Deployment Credentials option for your portal server configuration).
WARNING Some versions of WebSphere Portal Server 6.0.x do not recognize rebuilt applications. If you use WebSphere Portal 6.0.x and you find that the deployed application does not update after a rebuild, you may need to restart the application.You can restart an application from the administration console or by restarting the server.
After your deployed portlet application has been rebuilt, you need to add the Hello World! portlet to a page in the portal so you can see it. To do this, log in to the portal as an administrator, and then open the Portlet Palette by navigating to the target page in the portal and pressing the icon in the top-right corner of the portal (note that the location of this icon may differ if you use a custom theme or a version of WebSphere Portal other than version 6.0). After the Portlet Palette opens, type Hello World! in the Search box and press the magnifying glass icon. The Hello World! portlet should display as shown in Figure 1.21.
Figure 1.21
Finding the Hello World! portlet.
Click and drag the Hello World! portlet onto the page where you would like it displayed. The portlet should display as shown in Figure 1.22. Press the icon again to close the Portlet Palette.
Important Points
Figure 1.22
35
The Hello World! portlet.
Summary In this chapter, you learned about the basic building blocks of a WPF application: builders, models, and profiles, and how the Factory generation engine works behind the scenes to assemble executable applications (called WebApps) based on the builder calls in your models. You also learned about the different types of WAR files you can produce from WPF (development WAR, portlet WAR, and deployment WAR) and the corresponding deployment configurations needed to deploy these WAR files to target application servers or portal servers. Finally, you learned about how to use the WebSphere Portlet Factory perspective in the WPF Designer, and created, deployed, and tested a simple Hello World! portlet. Now that you understand the fundamentals of WPF, you should be able to start building any of the other examples discussed in this book. Learning WPF largely consists of familiarizing yourself with the various builders, so spend some time experimenting with the builder calls available and working through the examples in this book that interest you. WPF is an invaluable development tool with a broad range of potential applications, and the more you use it, the more easily and quickly you will build your own portlets. The next chapter, “Providing and Consuming Services,” describes how to incorporate services into your portlets using the service provider/consumer pattern.
Important Points • Portals are user interfaces that interact with information and processes and consist of one or more portlets. Portlets are displayed to end users on pages in a portal. • WebSphere Portlet Factory (WPF) is a wizard-based development tool that facilitates rapid portlet development. • Builders, models, and profiles are the basic building blocks of WPF applications. Builders are wizard-based interfaces that automate common development tasks. Models are collections of builder calls. Models are often deployed as portlets to a portal server. Profiles are used to facilitate functionality based on different contexts, roles, or settings, and they can be used to generate multiple copies of an application from the same code base.
36
Chapter 1 Introduction to WebSphere Portlet Factory
• When you open, save, or apply changes to a model, a WPF mechanism known as the Factory generation engine creates one or more executable versions of your model called WebApps. WebApps consist of artifacts like Java code, HTML pages, and XML files, and are built according to the inputs in your builder calls. • Portlet Factory applications can be packaged into three different types of WAR files: a development WAR, a portlet WAR, and a deployment WAR. The development WAR contains artifacts that assist in the development process and must be deployed to an application server. The portlet WAR must be deployed to a portal server and can take advantage of the portal server’s functionality. Models in a portlet WAR that contain a Portlet Adapter builder can be run as portlets. The deployment WAR cannot utilize portal functionality and does not contain the development artifacts included in the development WAR. Deployment WARs are useful for deploying non-portal applications to a production server and must be deployed to an application server.
C
H A P T E R
2
Providing and Consuming Services
Service Oriented Architecture (SOA) is becoming an increasingly prevalent part of portlet design. A pattern I will refer to as the “service provider/consumer pattern” is employed to implement SOA in WebSphere Portlet Factory applications—in this chapter, you learn what this pattern is and how to use it. You also use the pattern to build a simple portlet that retrieves a staff roster, and you learn about how you can stub services to help test your portlets. By the end of the chapter you will understand the advantages provided by the service provider/consumer pattern, and you will be able to implement it effectively in other scenarios. The files used in this chapter are available for download from ibmpressbooks.com/title/ 9780137134465 under the Chapter 2 folder (instructions for copying these files into your project are included in a readme.txt file in the same folder); however, to increase your understanding of the topics discussed, it is recommended that you create these files yourself by following the example in this chapter. The following topics are covered in this chapter: • The service provider/consumer pattern • Creating a service provider • Creating a service consumer • Creating a stub service • Applying the service provider/consumer pattern
The Service Provider/Consumer Pattern In WPF applications, the service provider/consumer pattern is used to implement SOA, which makes your applications more modular and extensible. (For information on some of the terms used in discussing this pattern, see the sidebar “Service Terminology.”) Applications built using
37
38
Chapter 2 Providing and Consuming Services
the service provider/consumer pattern are loosely coupled; this means that the services that they consist of can be modified with little or no effect on the rest of the applications. For example, a service connecting to a SQL server database can be modified to connect to a DB2® database without needing to modify design elements that interact with the service. Services in WPF applications can do anything from retrieving and updating employee data to running sales reports. In general, services are usually employed to model high-level business processes, and often integrate with backend data sources or other applications. The service provider/consumer pattern consists of a service provider, which provides a service to one or more service consumers. The functionality of the service is defined through one or more functions or operations, which are then executed or consumed by the service consumer. A result is then usually returned back to the consumer and displayed on the screen. In WPF, this process is normally achieved by using separate models for the service consumer and service provider, so that you can then modify the service provider without needing to modify the consumer. Figure 2.1 shows a diagram of the relationship between the service provider and service consumer.
Service Provider Model
Service Consumer Model
Operation 1
Service Call 1
Operation 2
Service Call 2
Service Consumer Model Service Call 2
Figure 2.1
The service provider/consumer pattern.
In the first part of this chapter, you build a service provider model to provide a staff roster service. The service will contain a single operation, which retrieves the roster from an XML variable. The roster consists of a five-day working week, with each day assigned to a particular staff member. In the later section of the chapter, “Creating a Service Consumer,” you add another model to the project and surface it to a portal server as a portlet. This portlet will consume the operation provided by the service provider and display the roster to the screen. The “Creating a Stub Service” section discusses how you can test your services with stub data, and the final
Creating a Service Provider
39
section of this chapter provides suggestions for applying the service provider/consumer pattern in other scenarios.
SERVICE TERMINOLOGY A number of standard terms are used when discussing services. These are defined as follows: Service—A reusable component that performs specific functions on behalf of consumers. They are usually platform independent, and are loosely coupled to each other and to the application(s) calling the service. Services are black boxes in that consumers do not know anything about how they work, but only about the function the services provide and the format of the data to be sent to and from the services. Services are most commonly used in WPF to retrieve or update data in a backend data source, but they can also be used to do anything from finding out the weather to processing online forms. Service Oriented Architecture (SOA)—A broad term used to denote the planning or end result of employing services in systems and applications. Using an SOA promotes component reuse at a higher level of abstraction than that normally used in ordinary classes and functions. An SOA is generally more flexible than traditional architectures (in that modifications or extensions to a component in an SOA have minimal impact on the rest of the system). Operation—Services provide one or more operations, which form the interface used by consumers to interact with the service. Operations sometimes define input and output variables, which the consumer then uses to communicate with the service. The example in this chapter contains one operation, retrieveRoster, which is used by consumers to get access to a staff roster. This operation takes no input variables (to retrieve the roster; it is enough to simply call the operation) and returns an XML roster to the consumer of the service. Service provider—Refers to a method, model, or application that offers a service. The example in this chapter contains a single provider: a model called rosterService, which provides access to a staff roster stored in an XML variable. Service consumer—Broadly used to denote a method, model, or application that calls (or consumes) an operation on a service. The example in this chapter contains one consumer: a model called roster that consumes the retrieveRoster operation and then displays the results (a staff roster) to the screen in a portlet. Web service—A special type of service used over a network. Chapter 9, “Using Web Services and Manipulating XML,” walks through an example application that explains how to provide and consume Web services in WPF.
Creating a Service Provider Before you proceed with this section, you should be sure you have a WPF project that can be used to contain the service provider model and service consumer model that you create in this chapter. If you have a project from Chapter 1, “Introduction to WebSphere Portlet Factory,” or from
40
Chapter 2 Providing and Consuming Services
another chapter if you are not reading them in order, you can use it; otherwise, you should create a new WPF project (see Chapter 1 for more information on how to do this). The project will be published as a WAR file and deployed to a portal server, and you should also deploy the application to a local application server for testing. You can use the portal server if it is running on your local machine; otherwise, it is recommended that you use the IBM WebSphere Application Server Community Edition server (WAS CE) that comes with WPF. After you have a project set up, you need to add a model to the project for your service provider. This model uses the following builders to provide its functionality: • Service Definition • Variable • Simple Schema Generator • Action List • Service Operation These will be built and discussed in this order. In the example in this chapter, the Action List, Simple Schema Generator, and Service Definition builders must precede the Service Operation builder call; otherwise, you will receive errors in the Problems view, indicating that certain resources could not be found.
WARNING Although the order of builder calls is usually not important, occasionally the order of your builder calls can create errors or warnings in your application. These can occur when you try to access the results of another builder call when that builder call has not yet generated its associated code. Although you will not normally run into problems here, as a best practice, you should always list referenced builder calls before the builder calls that reference them. If you’re not sure what order to put your builder calls in, just keep an eye on the Problems view, as any error or warning messages associated with builder call order will show up there as soon as you add a builder call in the wrong place. These messages will be similar to “…is not available,” “…does not exist,” or “…did not evaluate to any fields.”
Creating a Model Create a new model called rosterService in your project, under the folder WEB-INF/models/ chapter02. The model should be based on the Empty template, which creates a model in your project that contains no builder calls. Note that the New Model Wizard has an option to create a Database Service Provider; you will use this option in Chapter 3, “Using Data from a Relational Data Source,” to create a service provider for accessing and manipulating a relational data source. For more information on creating models, see the example in Chapter 1.
Creating a Service Provider
41
Defining the Service
You now need to add some functionality to the rosterService model. To do this, first add a Service Definition builder call to the model by selecting Service Definition from the Builder Palette (you can open the Builder Palette by clicking the icon in the Outline view) and pressing OK. After you have a service definition builder call, fill in the settings in the Model Editor, as shown in Figure 2.2. Note that the Make Service Public option is checked, which makes the service accessible to the consumer model you will create in the next section. Notice the Generate WSDL option is not enabled. WSDLs are used for communicating with Web services, and you can enable this option to enable your service as a Web service. Web services are discussed in Chapter 9, “Using Web Services and Manipulating XML.” Note also that the Add Testing Support option and Generate main options are checked. Together, these options add test pages to your model (which you will use to test the operation on your service that you create later) and add a main action list. Note that every model that has an action list called main (usually added via the use of the Action List builder) is directly executable. In this case, then, checking the main option enables you to access the automatically generated service test pages by running the rosterService model. (Note, however, that the main action list is created behind the scenes so you won’t be able to actually see it in Outline view.) When you are done entering the appropriate settings, press Ctrl+S to save the builder call.
Figure 2.2
Configuring the Service Definition builder call.
42
Chapter 2 Providing and Consuming Services
Adding Roster Data The next step is to add the roster that is retrieved by the service. There are a number of ways to do this; in this example, you create an XML variable, and then generate a schema based on this variable, using a Simple Schema Generator builder call. The schema defines the structure of the data retrieved from the retrieveRoster operation (you create the operation a little later). You can do this in reverse as well; that is, create the schema first and then generate the XML data based on the schema (created with a Schema builder call). The first approach is useful if you don’t know how to write XML schemas (or you don’t have an XML schema authoring tool), because WPF will write the schema for you. Add a Variable builder call to the model and change the builder call’s name to rosterData. Display the Choose Variable Type dialog by pressing the … (ellipsis) button next to the Type input. After the Choose Variable Type dialog is open, select XML and press OK. In the Initial Value field, type in the XML shown here: <Week xmlns=”http://com.ibm.serviceExample”> <Monday xmlns=””>Bob Sam <Wednesday xmlns=””>Sally FredGreg
This XML document defines elements for a five-day week, and it also assigns a staff member to each day. The xmlns attribute of the Week element defines a namespace for the XML, which differentiates the Week element from Week elements in other namespaces. In the Advanced section of the Variable builder call, select ‘Read-only: shared across all users’ for the State and Failover input, and then save the builder call. By default, separate variables are created in memory by the Variable builder for each user, so changing this setting ensures that only a single roster variable is used across all users. This setting also ensures that the variable is stored and recovered in the event that the application fails. Add a schema to your model by selecting Simple Schema Generator from the Builder Palette, and then press OK. Enter the name getRosterSchema for the schema, and type the name rosterData into the Sample Data field so that the rosterData Variable will be used as the basis for the schema. Save the builder call when you are finished. After the builder call is saved, a schema called getRosterSchema is automatically created for you and available to use in other builder calls in the model (you will link to the schema later from a Service Operation builder call to define the structure of the roster). Note that every time you make a change to the structure of the rosterData Variable builder call, the schema getRosterSchema is updated accordingly (alternatively, if you want changes to the Variable builder call to be restricted to the structure specified in the schema, you should use a Schema builder call rather than the Simple Schema Generator).
Creating a Service Provider
43
Adding an Action to Retrieve a Roster The next step is to create an Action List builder call. This builder call defines a series of actions to perform for the service operation getRoster. Add an Action List builder call by selecting Action List from the Builder Palette in the Outline view and then pressing OK. Name the Action List returnXMLRoster. The Action List performs one simple action: It returns the value of the XML roster to the calling function (in this case, it is the consumer of the getRoster operation). To add this action, open the Select Action dialog by pressing the ellipsis button next to the first action in the action list. From the Select Action dialog, select Return from under the Special folder. In the Set Return Value dialog that follows, press the ellipsis button to bring up the Choose Reference dialog. Select Week from the Variables/rosterData folder, as shown in Figure 2.3, and then press OK. This specifies that the data you would like to return is in the variable created by the Variable builder you added earlier. The appropriate action now displays in the Set Return Value dialog. Press OK to return to the action list.
Figure 2.3
Specifying a return value in the Choose Reference dialog.
The last step for the action list is to change the return type of the Action List builder to IXml. IXml is an interface used by WPF to interact with XML. When working with XML vari-
ables in WPF, you should always use the IXml type because there is no XML type as such. For information on how to manipulate XML using the IXml interface, refer to the example in Chapter 9, “Using Web Services and Manipulating XML.” Save the builder call when you are finished.
Adding a Service Operation The final builder call to add to your service provider is a Service Operation builder call. Service operations are the interfaces used by consumers to communicate with your service. In this example, you add only one service operation to your service, called getRoster. This operation calls the action list you created earlier, which then returns the roster to the caller of the operation.
44
Chapter 2 Providing and Consuming Services
The reason why you need both a Service Operation builder call and an Action List builder call is because the Service Operation builder call can only link to other methods or action lists; you can’t perform the ‘return’ function directly from the Service Operation builder call. Add a Service Operation builder call by selecting Service Operation from the Builder Palette and pressing OK. Fill out the Service Operation Properties section of the Service Operation builder call as shown in Figure 2.4. This declares that the operation is part of the rosterService service, is called getRoster, and will run the action list returnXMLRoster when consumed. The operation description allows you to see what the operation does when linking to it in a Service Consumer model.
Figure 2.4
Configuring the Service Operation builder.
Next you must define what consumers of the service need to send to the operation (Operation Inputs) and what they will receive in return (Operation Results). No inputs are required, so change the Input Structure Handling input under the Operation Inputs heading to ‘No inputs.’ Under the Operation Results heading, select the Specify Result Schema option to explicitly specify a schema for the results, then type in getRosterSchema/Week for the Result Schema input. Type Five day roster for the Result Description, and make sure the Result Field Mapping input is set to Automatic. This will automatically map the results of the operation into the Week element of the roster schema you created earlier. Save the builder call when finished. You have now successfully created and configured your service provider model, which you will test in the next section.
Testing the Service Provider Now that you’ve finished creating your service provider, you can test to see if the getRoster operation is working correctly. Because you enabled testing support in the Service Definition builder call, WPF automatically embeds testing facilities into the rosterService model for you. To test the service, run the rosterService model from the WPF Designer by clicking the icon on the toolbar. This runs the currently active model (rosterService) with the last run configuration you used. If you have not set up a run configuration before, you will be prompted to do
Creating a Service Consumer
45
so—create a new configuration under the WebSphere Portlet Factory Model category. (If you want more information on setting up a run configuration, see the “Testing the Application” section in Chapter 1.) After you run rosterService, you should see the index test page in your default Web browser; it should be similar to the image shown in Figure 2.5. This screen lists all of the operations available on the rosterService service.
Figure 2.5
Index test page from the rosterService model.
To test the getRoster operation, click the getRoster link. You should see the roster displayed, as shown in Figure 2.6. If not, check that you don’t have any errors showing in the Problems view, and make sure you have changed the Return Type field on the Action List to IXml (otherwise, you will see only a blank table when you test the getRoster operation).
Figure 2.6
Testing the getRoster operation.
At this stage, you have a WPF project that contains a service provider model called rosterService. The next section outlines how to build a service consumer model to consume the getRoster operation and display the results in a portlet.
Creating a Service Consumer This section describes how to add a second model to your project, which consumes the getRoster operation provided by the rosterService model. The consumer is surfaced to a portal server as a portlet and displays the roster returned from the service operation (a screen shot of the portlet is shown in Figure 2.8, which appears later in the chapter). There are two ways you can create service consumers in WPF: The first is to use the List and Detail Service Consumer option in the new model wizard, and the second is to create the service consumer manually. A list in WPF is a collection of basic data across one or more records (usually displayed in a table or chart), whereas detail is data specifically related to a single record
46
Chapter 2 Providing and Consuming Services
(which usually contains data not available in the list). The List and Detail Service Consumer option give you everything you need in this example. The service consumer model in this section uses the following builders to provide its functionality: • Portlet Adapter • Service Consumer • View & Form These are added automatically using the WPF New Model Wizard.
Creating a Model Start the New Model Wizard (select File, New, and then select WebSphere Portlet Factory Model from the WPF Designer menu), and then specify the WPF project containing your service provider on the next screen. Press Next. On the next screen, select the Detail Service Consumer option for the model template and press Next. The List and Detail Service Consumer automatically surfaces your model as a portlet via a Portlet Adapter builder call. Type the name of the portlet on the next screen as roster (this name will also be used to name the other builder calls in your model). Also, specify the service provider chapter02/rosterService that you created in the previous section. Press Next when you are finished.
TIP It’s a good idea not to use the word ‘portlet’ in your model names, as you may decide to remove the portlet functionality later (or add it back in), and you don’t want to have to rename all of the elements in your model every time this happens.
You can now specify the service operation that is used to provide your list data (for instance, the roster). Select getRoster and press Next. The next screen contains settings for the detail part of the data. There is no detail data in this example, as you are displaying only the list (the roster), so deselect the ‘Create link to details’ checkbox. Using the detail option is described in Chapter 3, “Using Data from a Relational Data Source,” and Chapter 4, “Using Domino Data.” Press Next to continue. Give your model the name roster, and then store the model in the models/chapter02 folder. This is the name of the model as it is in the IDE. Note that although the model name and portlet name are the same in this example, it is possible to use different names for each field. When you are done, press Finish to build your model. This creates a new model called roster in your project and opens the model in the Model Editor and Outline view. The roster model contains three builder calls: a Portlet Adapter, a Service Consumer, and a View and Form. The Portlet Adapter
Creating a Service Consumer
47
builder converts the model into a portlet that can be viewed inside a portal. (Note that if you make any changes to this builder call—including the changes you are about to make—you need to rebuild the deployed application before you can see the results of the changes. For more information on this process, see the example in Chapter 1). The Service Consumer builder consumes the operation getRoster on the rosterService service, and the View and Form builder displays any list and detail data in the model to the screen. The View and Form builder contains a number of options for manipulating how the data is displayed; although these options are discussed in this chapter, they are covered in more detail in Chapter 5, “Customizing Portlet Appearance.”
Configuring the Portlet Adapter The builder calls don’t require any configuration in this example, except for two small changes to the Portlet Adapter builder call. Open the Portlet Adapter builder call and change the Portlet Title to Roster and change the Portlet Description to This portlet displays the staff roster to the screen. Save the builder call when you are finished. The Portlet Title identifies the portlet inside the portal (it is actually displayed in the title bar of the portlet), and the Portlet Description displays on various administration screens inside the portal.
TIP Note that the Portlet Title is blank by default, so any time you use the List and Detail Service Consumer option when creating a new model (or, any time you add a Portlet Adapter builder to a model), be sure you enter a value for the Portlet Title field. If you leave the Portlet Title field blank, the name of the model will be used for the portlet title (which is usually undesirable).
Your project now contains a service provider model and service consumer model. The service provider provides a service called rosterService with a single operation called getRoster. The service consumer should be able to consume the operation and display the results (a roster) to the screen, which you test in the next section.
Testing the Service Consumer To test your service consumer from the WPF Designer, make sure the roster model is the currently active model in the Model Editor (for instance, check that you haven’t closed it or switched back to editing the rosterService model). Run the roster model from the WPF Designer, and you should see the roster display in your default Web browser, as shown in Figure 2.7. After you have tested the service consumer model, you should rebuild the application and test it on the portal server as a portlet (for instructions on how to do this, see the example in Chapter 1). After you have added the portlet to a page in the portal, your portlet should display as shown in Figure 2.8.
48
Chapter 2 Providing and Consuming Services
Figure 2.7
Testing the roster model.
Figure 2.8
The Roster portlet.
Creating a Stub Service A stub service is a service that contains dummy data. You normally use a stub service when you are accessing or manipulating data in a backend data source, and the data source is not readily available (for example, perhaps you are developing in a test environment that cannot authenticate with the data source). When stubbing a service, you normally create a stub model, which is basically a copy of your service provider model that contains dummy data (and doesn’t require connectivity to the data source). You can create a stub model using inputs in the Interface and Stub Models section of the Service Definition builder, shown in Figure 2.9 (you can also create a stub model via a Service Stub builder call, which contains the same options provided by the Interface and Stub Models section of the Service Definition builder). The Interface Model can be used to specify a service interface, which you use when you have multiple service models that have a common interface (with the same operations), and the Stub Model input is the name and location of your stub model. Pressing the Generate Stub Model button regenerates the stub model specified in the Stub Model input.
Applying the Service Provider/Consumer Pattern
Figure 2.9
49
Service stub options on a Service Definition builder call.
There is no need to use a stub service in the example in this chapter because you are not linking to a data source, and the data (the XML roster) is always readily available. For an example using a stub service, see Chapter 4, which discusses how to stub a Lotus Notes database, which will enable you to test the service even when you don’t have connectivity to a Domino server.
Applying the Service Provider/Consumer Pattern The service provider/consumer pattern demonstrated in this chapter can (and should) be used throughout your WPF applications, as it provides you with powerful features such as caching, inbuilt testing capabilities, and stub support. However, this is not to say that every single method in your applications should be accessed through a service. When considering a function as a potential candidate for a service, you should consider whether you will actually gain from providing the functionality through a service. Simple functions with limited potential for reuse are possible scenarios in which a service provider and consumer might actually make an application more complicated than it needs to be. Having said this, service provider and consumer models clearly delineate and separate functionality in the model tier of an application from the view tier (if you are unfamiliar with these terms, see the following sidebar “The Model View Controller Pattern”), which can make your applications clearer and easier to maintain.
THE MODEL VIEW CONTROLLER PATTERN The Model View Controller (or MVC) pattern is a commonly employed pattern in J2EE development, which separates out an application into three separate divisions (or tiers). In WPF, the content of these tiers can be contained in anything from a builder call, to a Java class, to an HTML file. Builder calls from different tiers are normally separated into different models, and these models are sometimes even stored in different subdirectories of the models folder for clarity. Model—A model is the data or business logic in an application (not to be confused with the model that is a collection of builder calls in WPF). WPF does not offer rich functionality for manipulating business logic out-of-the-box, so complicated business logic is sometimes farmed out to an external Java method and then accessed from a Linked Java Object builder
50
Chapter 2 Providing and Consuming Services
or Service Consumer builder (for more information on using Java in WPF, refer to Chapter 8, “Using Java in Portlets”). Data in WPF is usually accessed and manipulated via a Service Consumer builder. View—A view is the user interface of your application (HTML pages, UI controls, and so on). In WPF, many of the elements in the view tier are stored in the WebContent/factory directory, such as HTML templates and style sheets. Other custom controls (such as those added by Button and Page builder calls) are usually stored in their own “view” model. The view tier is also sometimes referred to as the presentation tier. Controller—A controller is a method that controls the execution flow in an application. The main action list is an example of a controller in WPF, as is the Factory controller servlet mentioned in Chapter 1.
You should also consider what the candidate function actually does. Services usually model higher-level functions or processes than traditional classes or modules, and often correspond directly with a particular business function or process (although this doesn’t necessarily have to be the case). So, for example, you might use the service provider/consumer pattern to handle some of the following functionality and processes: • Accessing or manipulating a backend data source (for example, updating a Lotus Domino database). • Kicking off a business process (for example, lodging an application form). • Calling a shared Java method, EJB call, or Linked Java Object (LJO). • Performing business logic (for example, deciding if a customer is eligible for a special offer). Note that the example in this chapter can also be built without using the service provider/ consumer pattern because the service just runs an action list that returns the value of an XML variable. However, using a service here provides a number of advantages: It clearly separates the model and view tiers of your application; it removes several builder calls from the service consumer model, making it clearer; any future models in the same application will be able to use the rosterService service as well; and any future modifications to the rosterService service will not require changes to the roster model (provided that the name of the service operation doesn’t change and the schema it uses remains intact). As a general rule, if a function is to be accessed from several models or if it provides some business function or process, it is usually a good idea to employ the service provider/consumer pattern. In the context of the example in this chapter, then, potential extensions could include a setRoster operation to the rosterService service, so that a roster manager can change the roster, or a sendRoster operation, which might email the roster to a division manager.
Important Points
51
Summary In this chapter, you learned how to create service providers, consume a service from a portlet, and build applications in WPF based on the service provider/consumer pattern. This pattern is an excellent way to build applications in WPF because it helps keep your applications modular and separates your model tier from your view tier. You also created, deployed, and tested an application based on this pattern, which contained a service provider model and a service consumer model. The service consumer model was surfaced to a portal server as a portlet. You also learned about service stubs and potential scenarios in which the service provider/consumer pattern can be used. The next chapter, “Using Data from a Relational Data Source,” describes how to use the service provider/consumer pattern to interact with a relational data source from a portlet.
Important Points • The service provider/consumer pattern is used extensively in WPF applications, particularly to interact with backend systems. Using the service provider/consumer pattern encourages separation of the presentation and model tiers in an application. • A service provider is a design element (usually a model) used to offer shared, high-level functionality to one or more applications. This functionality is offered through a series of operations on the service provider. • A service consumer is a design element (such as a model) that calls the operations on a service provider. • The Service Definition and Service Operation builders are normally used to create a service provider model. The service in this model is then usually consumed via a Service Consumer builder. • The Simple Schema Generator builder can be used to automatically generate XML schemas from an XML document in WPF. • Stub services are services that contain dummy data and are often used in WPF to test data services when there is no connectivity to a database.
This page intentionally left blank
C
H A P T E R
3
Using Data from a Relational Data Source
One of the most common uses for WebSphere Portlet Factory (WPF) is to surface functionality for interacting with relational data through a portlet. The subject of this chapter is how you can use some of the default WPF builders to perform basic data operations on a relational data source and provide this functionality through a portlet. By the end of this chapter, you will understand how to use the WPF builders to access and manipulate a relational data source, and you will have built a sample contact portlet to demonstrate this functionality. Each of the files in this chapter is available for download from ibmpressbooks.com/title/9780137134465 under the Chapter 3 folder (instructions for copying these files into your project are included in a readme.txt file in the same folder); however, to increase your understanding of the topics discussed, it is recommended that you create these files yourself by following the example in this chapter. Before you begin the example in this chapter, you need to create the contact database to which the portlet will connect. Appendix A, “Setting Up Your Environment,” gives instructions for creating the contact database on either a DB2 or SQL Server. The following topics are covered in this chapter: • Using data services • Creating a service provider • Creating a contacts portlet
Using Data Services Data from a backend data source is normally accessed and manipulated via a service provider model, which users interact with through a service consumer. The service consumer model is often surfaced to a portal server as a portlet (for more information on this pattern (and services in general) refer to Chapter 2, “Providing and Consuming Services”).
53
54
Chapter 3 Using Data from a Relational Data Source
In WPF, data from a service provider is retrieved in two parts: a list (sometimes called a view) and detail. In the context of a relational data source (such as an SQL server database or DB2 database), a list is a snapshot of records from a table, and the detail is an individual row in the table. A list usually contains only select information about each record (contact name, company, and phone number, for example), whereas the detail often provides more in-depth information (such as address, contact email, and fax number). WPF offers several out-of-the-box builders to facilitate this functionality, including numerous UI builders and backend connectivity builders. In this chapter, you use some of these builders to create a service provider model for accessing contact data (first name, last name, company, address, phone number, email, and fax number) in a relational database. The service provides several operations, which let users create, read, update, and delete records in the database. These operations are consumed from a service consumer model, which are surfaced to a portal server as a portlet.
Creating a Service Provider In this section, you build a service provider model to provide access to the contact data in the TESTDB database. Note that before you proceed with this section, you need to set up the TESTDB database as outlined in Appendix A. Also, you need a WPF project, which can house the service provider model (and the contacts portlet that you create later). If you have a project from a previous chapter, you can use it; otherwise, you should create a new WPF project (for more information on creating projects, see Chapter 1, “Introduction to WebSphere Portlet Factory”). The project will be published as a WAR file and deployed to a portal server, and you should also deploy the application to a local application server for testing. You can use the portal server if it runs on your local machine; otherwise, it is recommended that you use the IBM WebSphere Application Server Community Edition server (WAS CE) that comes with WPF. After you have a project set up, you need to add the service provider model. This model uses the following builders to provide its functionality: • Service Definition • SQL Call (x5) • Service Operation (x5) These are added automatically using the WPF new model wizard.
Creating a Model Open the new model wizard by selecting File, New, WebSphere Portlet Factory Model from the File menu. When the dialog appears, select your WPF project and press Next. You then see a list of different types of models that WPF can create for you automatically. You can create service providers in WPF in two ways: The first is to let WPF create one for you (which you can do by using the Database Service Provider option in the Select Model wizard), and the second is to
Creating a Service Provider
55
create the service provider manually. The Database Service Provider option is a useful shortcut if you are connecting to a backend database, because it automatically populates your model with useful builder calls, and the manual option is useful if you want to use builder calls other than those populated by the wizard. Select the Database Service Provider option and press Next.
Defining the Service The next screen enables you to define the service. Give the service the name contactsService. If you enable the Generate WSDL checkbox, a WSDL document is automatically created for your service, enabling it as a Web service. The Generate WSDL checkbox can also be found on the Service Definition builder call, so you can enable or disable your service as a Web service at any time (Web services are discussed in Chapter 9, “Using Web Services and Manipulating XML”). Leave the Generate WSDL checkbox unchecked and press Next.
Specifying the First Operation You can now specify the first operation for the service. You will define five operations in this example: one to retrieve a list of contacts, one to retrieve details for an individual contact, one to create a new contact, one to update an existing contact, and one to delete a contact. The first operation enables you to read details on a particular contact. In the SQL DataSource field, specify the TESTDB data source (jdbc/TESTDB). If this data source does not display, make sure you have completed the “Configuring a JDBC Resource” section in Appendix A. Also, check to see that you have restarted the application server after adding the data source. In the SQL Statement field, specify the following SQL statement: SELECT ID, FIRSTNAME, LASTNAME, COMPANY, PHONE FROM “ADMINISTRATOR”.”CONTACTS”
This SQL retrieves a list of records from TESTDB, but displays only the ID, FIRSTNAME, LASTNAME, COMPANY, and PHONE columns (so that the list is easier to read). Press Next when finished. On the next screen, type getContactsList as the Operation Name and select ‘Multiple rows’ for the Operation Returns input. Make sure the ‘Create another operation’ checkbox is enabled. These settings define the name of the operation as it displays to consumers and ensures that multiple rows are returned from the operation. Also, the ‘Create another operation’ checkbox declares that you would like to create more operations for the service, which causes the wizard to prompt you for more operation details on the next screen. Press Next to continue.
Specifying the Second Operation Now, create the second operation. Select jdbc/TESTDB for the SQL DataSource input as before, and then type the following SQL into the SQL Statement input: SELECT * FROM “ADMINISTRATOR”.”CONTACTS” WHERE ID = ?
56
Chapter 3 Using Data from a Relational Data Source
This SQL returns every column (ID, FIRSTNAME, and so on) for a particular contact, where the ID matches the value of a certain parameter. This information is eventually displayed from the service consumer when a user clicks on a particular ID. Press Next when you are finished. Notice the use of the ? symbol—this is used in WPF to denote the value of a parameter, which you will now define. Fill in the options on the next screen, as shown in Figure 3.1 (but don’t press the Next button yet). For this operation, only a single row is returned. Notice the contents of the Operation Inputs section; this defines the name of the ‘?’ parameter. An ID is passed to the operation when it is called, which is then used to retrieve details for that particular contact. When you’ve filled out this screen so that it appears as shown in Figure 3.1, enable the box to create another operation (the ID column might disappear depending on the version of WPF you are using, but this won’t affect the operation). Press Next when you are finished.
Figure 3.1
Configuring the second service operation.
Specifying the Third Operation You can now specify a third operation. Select jdbc/TESTDB for the SQL Datasource, and then type the following SQL as a single line into the SQL Statement input:
This SQL creates a new contact with the values of the parameters passed in. (Note: There is no need to set the ID column, as it is automatically generated.) Press Next when you are finished. Fill out the next screen as shown in Figure 3.2, and then press Next. Checking the ‘Create another operation’ box locks the parameters in and prevents you from editing them, so make sure you check this box after you have entered in the parameters. Note that there is no need for output from this operation, as the operation simply updates the TESTDB database. In addition, the order of the parameters is important—as specified in the SQL call, the value of the first parameter is assigned to the FIRSTNAME column, the second is assigned to LASTNAME, and so on.
Figure 3.2
Configuring the third service operation.
Specifying the Fourth Operation
To enter the fourth operation, select jdbc/TESTDB for the SQL Datasource, and then type the following SQL line into the SQL Statement input: UPDATE “ADMINISTRATOR”.”CONTACTS” SET FIRSTNAME = ?, LASTNAME = ?, COMPANY = ?, ADDRESS = ?, PHONE = ?, EMAIL = ?, FAX = ? WHERE ID = ?
58
Chapter 3 Using Data from a Relational Data Source
This SQL updates an existing contact with the values of the parameters passed in for a contact with the ID specified. (Note: There is no need to set the ID column, as it was automatically generated when the contact was created and there is no need to change it.) Press Next when you are finished, and then fill out the next screen, as shown in Figure 3.3. Press Next when you are finished. The order of parameters on this page is important.
Figure 3.3
Configuring the fourth service operation.
Specifying the Fifth Operation You can now specify the fifth and final operation on the service. Select jdbc/TESTDB for the SQL Datasource, and then type the following SQL into the SQL Statement input: DELETE FROM “ADMINISTRATOR”.”CONTACTS” WHERE ID = ?
Creating a Service Provider
59
This SQL deletes an existing contact from TESTDB that matches the ID specified in the parameter. Press Next when finished, and then fill out the inputs on the next screen, as shown in Figure 3.4. Press Next when you have filled out the appropriate inputs.
Figure 3.4
Configuring the fifth service operation.
Give your model the name contactsService and make sure it is created in the directory models/chapter03. Press Finish to build your model. This creates a new model called contactsService in your WPF project and opens the model in the Model Editor and Outline view. You have now finished creating your service provider, which you will test in the next section. Spend a few minutes exploring the builder calls WPF has automatically added to the contactsService model. The Service Definition builder call defines general properties for the entire service, and a Service Operation and SQL Call builder call have been added for each operation specified in the WebSphere Portlet Factory Model wizard. The SQL Call builder is discussed in the following sidebar, “The SQL Call Builder.”
60
Chapter 3 Using Data from a Relational Data Source
THE SQL CALL BUILDER Several low-level SQL builders are available in WPF. That is, they are builders used for a specific purpose, as opposed to high-level builders which amalgamate functionality from several other builders. The SQL DataSource builder, for example, is used to connect to a relational data source, and the SQL Statement builder is used to execute an SQL statement on a relational data source. The SQL Call builder used in this chapter is a high-level builder that amalgamates the key functionality of these other SQL builders. The top section of the SQL Call builder enables you to specify a link to a JDBC resource. Any data sources you connect to need to have the jdbc/ prefix to get picked up by WPF. The other inputs on the page are used to configure the data source specified in this section. The Database Explorer section of the builder can be used to browse tables and stored procedures in a database, which can then be used to generate SQL statements. These SQL statements can be copied into the middle section of the builder, which is used to store a SQL statement that can be executed from other builders. For example, in this chapter, the SQL specified in the middle section of each SQL Call builder is executed via operations on the service provider. Note that when using the SQL inputs on the builder, there is no need to use the escape character \ in front of quotes—just type in the quotes directly (as in the example). The Result Set Handling section defines characteristics of the results returned from the SQL statement specified in the SQL Statement input (if a SELECT statement was specified) and enables you to transform these results into an XML document. XML documents are used extensively in WPF, as they provide a simple and powerful mechanism for interacting with data (manipulating XML in WPF is discussed in Chapter 9). The Method Names section is used to specify custom names for the methods used to interact with the SQL Call builder. This is useful if you want to rename methods for clarity or if you want the method names to conform to a coding convention. The Events, Statistics, and Logging section enables you to log various events and statistics from the builder (logging in to WPF is discussed in Chapter 14, “Error Handling, Logging, and Debugging”). The Builder Override Definition section can be used to override inputs in the SQL Call builder with inputs from other SQL builders. Although the SQL Call builder provides most of the functionality you need when working with relational data sources, the other SQL builders provide several additional options that you might find useful in certain scenarios. For example, the SQL Statement builder enables you to specify the maximum number of rows to return for a SQL statement, which you can’t do with the SQL Call builder.
Testing the Service Provider By default, WPF automatically generates test pages for your service provider (which you can disable from the Testing Support section of the Service Definition builder call). This enables you to test the service provider directly from the IDE.
Testing the Service Provider
61
To test the service, run the contactsService model from the WPF Designer by clicking the icon on the toolbar. This runs the currently active model (for instance, contactsService) with the last run configuration you used. If you have not set up a run configuration before, you are prompted to do so—create a new configuration under the WebSphere Portlet Factory Model category. If you want more information on setting up a run configuration, refer to the “Testing the Application” section in Chapter 1. After you run contactsService, you should see the index test page in your default Web browser, as shown in Figure 3.5. This screen lists all the operations available on the contactsService service.
Figure 3.5
Index test page from the contactsService model.
Testing the retrieveContactsView Operation
To test the getContactsList operation, click the getContactsList link. You should see a list of contacts, as shown in Figure 3.6. If not, ensure you don’t have any errors showing in the Problems view in the IDE and ensure that you have completed all the steps in the previous section correctly. Also, make sure the connection to the TESTDB data source works on the application server (you can test this from the administration console, which is described in the “Configuring a JDBC Resource” section of Appendix A). Press Back to return to the index page.
Figure 3.6
Testing the getContactsList operation.
62
Chapter 3 Using Data from a Relational Data Source
TIP If your data source is large (over 10,000 rows), it might be too slow to retrieve the entire result set every time you access the data.You can cache your data by adding a Cache Control builder to your model and pointing it to the result set.You can also perform partial fetches using the Service Operation Builder and display these results across several pages. Pagination is discussed in more detail in Chapter 5, “Customizing Portlet Appearance.”
Testing the getContactDetail Operation To test the getContactDetail operation, click the getContactDetail link. The screen that follows enables you to specify parameters for the operation. Enter 1 as the ID and press the Submit Query button. You should see details for the contact who has an ID of 1 (that is, Bob Jones), as shown in Figure 3.7. Press Back when you are finished. Note that in this example, no validation is used— validating data from an input screen is covered in Chapter 11, “Field Validation, Formatting, and Translation.”
Figure 3.7
Testing the getContactDetail operation.
TIP If you open the Service Definition builder call and enable the Specify Default Inputs box in the Testing Support section, you can specify default values for your testing so that you don’t need to retype them every time. For example, if you type 1 for getContactDetailQueryInputs.ID field for the getContactDetail operation, this ID is used when testing the operation unless another ID is specified.
Creating a Contacts Portlet
63
Testing the createContact Operation To test the createContact operation, click the createContact link. The screen that follows enables you to specify parameters for the operation, which are the values used to create the new contact. Enter in some sample data and press the Submit Query button. When the screen reloads, it will display nothing but a Back button—press this to continue. If the contact was added correctly, you should be able to run the getContactsList operation again and see the new contact displayed at the bottom of the list.
Testing the setContact Operation To test the setContact operation, click the setContact link. The screen that follows enables you to specify parameters for the operation, which are the values used to update a particular contact. Enter in the ID of the contact you created in the previous step, in addition to some new data for that contact, and press the Submit Query button. If the operation worked correctly, you should be able to run the getContactsList operation again and see that the contact is listed with the details you just entered. Note: Although the test page requires you to reenter each field, these fields are automatically populated when you edit a contact from the service consumer.
Testing the deleteContact Operation To test the deleteContact operation, click the deleteContact link. Enter in the ID of the contact you would like to delete (such as the ID of the contact you just created and updated), and then press the Submit Query button. When the screen reloads, it displays nothing but a Back button; press this to continue. If the contact was deleted correctly, you should be able to run the getContactsList operation again and see that the contact has been removed. Close the contactsService model when you’ve finished testing it. You now have a service provider called contactsService, which provides create, read, update, and delete functions for the TESTDB database. The next section walks you through creating a service consumer model to consume the operations offered by this service and provides an interface to these operations in a portlet.
Creating a Contacts Portlet This section describes how to add a second model to your project, which defines a user interface to consume the various operations offered by your service provider. This service consumer model is then surfaced to a portal server as a portlet. For more information on the service consumer/ provider pattern in WPF, see Chapter 2, “Providing and Consuming Services.” Figure 3.21 later in this chapter shows a screenshot of the finished portlet. The service consumer model uses the following builders to provide its functionality: • Portlet Adapter
• Comment (x2)
• Service Consumer
• Button (x2)
• View & Form
• Input Form
• Action List (x2)
64
Chapter 3 Using Data from a Relational Data Source
Note that the model developed in this section uses only basic display options; the focus is more on consuming services that access and manipulate a relational data source. For more information on arranging output in portlets, see Chapter 5.
Creating a Model To create the service consumer model, select File, New, WebSphere Portlet Factory Model from the File menu to bring up the WebSphere Portlet Factory Model dialog. On the next screen, select your WPF project and press Next. WPF then displays a list of different types of models that can be created for you automatically. You can create service consumers in WPF in two ways: The first is to let WPF create one for you (which you can do by using the List and Detail Service Consumer option in the wizard), and the second is to create the service consumer manually. The List and Detail Service Consumer option gives you everything you need in this example, so select it in the wizard and press Next. The List and Detail Service Consumer automatically surfaces your model as a portlet via a Portlet Adapter builder call. Specify the name of the portlet on the next screen as contacts (this name will also be used to name the other builder calls in your model). Also, select the service provider chapter03/contactsService you created in the previous section. Press Next when finished. You are then required to specify the service operation that is used to provide the list of contacts. Select getContactsList and press Next to enter in the settings for the detail part of the data. Fill out the boxes, as shown in Figure 3.8, and press Next. This converts all of the values in the ID column in the contact list into clickable links. When a user clicks on the link, the ID is passed to the getContactDetail operation and the results displayed on the screen. The next screen enables you to configure the value of the ID parameter to be sent to the getContactDetail operation. Accept the defaults and press Next. The default value for the ID parameter takes the ID value from the contact clicked by the user. Finally, give your model the name contacts and make sure it is created in the directory models/chapter03. This is the name of the model as it appears in the IDE. Note: In this example, although the model name and portlet name are the same, it is possible to use different values for each name. When you’ve entered the name, press Finish to build the model. This creates a new model called contacts in your WPF project and opens the model in the Model Editor and Outline view. The contacts model contains three builder calls: a Portlet Adapter, a Service Consumer, and a View & Form. The Portlet Adapter builder converts the model into a portlet, which can be viewed inside a portal. (Note: If you make any changes to this builder call—including the changes you are about to make—you need to rebuild the deployed application before you can see the results of the changes. For more information on this process, see the example in Chapter 1.) The Service Consumer builder consumes the operation getContactsList on the contactsService service, and the View & Form builder displays any list and detail data in the model to the screen. The View & Form builder also enables you to do some basic manipulation of how the data is displayed (the View & Form builder is discussed in more detail in Chapter 5).
Creating a Contacts Portlet
Figure 3.8
65
Specifying a detail operation.
Configuring the Portlet Adapter The Portlet Adapter builder call requires two small changes before it can be used. Double-click the Portlet Adapter builder call to open it in the Model Editor, and then change the Portlet Title to Contact and the Portlet Description to Interface for interacting with the TESTDB database. Save the builder call when finished. The Portlet Title identifies the portlet inside the portal (it will actually be displayed in the title bar of the portlet), and the Portlet Description appears on various administration screens inside the portal. At this stage, the service consumer model is using only two of the five operations provided by the service provider—that is, you can read a list of contacts from TESTDB, and you can also view details on an individual contact by clicking on their ID. Preview the contacts model now from your IDE, which will give you some understanding of what the wizard creates by default. The next few sections describe how to surface functionality for the create, update, and delete operations. Note that at the completion of each of the following sections, you can test your changes directly from your IDE.
Adding Update Functionality To add functionality from the setContact operation, double-click on the View & Form builder call in the Outline view. Scroll down to the Update Page Support section, and then tick the Create Update Page checkbox. Some new options appear underneath—fill out the Update Method and
66
Chapter 3 Using Data from a Relational Data Source
Update Next Action inputs, as shown in Figure 3.9. Note that you don’t need to type these in directly; you can select them from the Select Action dialog, which you open by clicking the ellipsis button to the right of the builder input.
Figure 3.9
Configuring update support on the View & Form builder.
The Update Method defines what happens when new data is submitted (it will run the setContact operation in this case), and the Update Next Action defines a post update action (in this case, the contactsView_ShowResults method created by the View & Form). The contactsView_ ShowResults method is used to refresh the contacts list with any changes and redisplay the contacts list—something that is not done automatically. You don’t always need to define a post update action, but it can be useful if you want to perform additional actions after an update. Save the builder call when finished.
TIP The View & Form builder creates three pages in your model: contactsView_ViewPage contactsView_DetailsPage contactsView_UpdatePage The name of the View & Form builder call is used as a prefix for each page. The ViewPage page contains the contact list. The DetailsPage is the page used to display detail information on an individual contact, and the UpdatePage is the page used to edit a contact.You can place controls on these pages by specifying one of them in the Page input on a builder that creates UI elements. Placing controls on pages is discussed in Chapter 6, “Adding Basic User Interface Controls to Your Portlets.”
An Edit button is now displayed on the detail page, which lets you edit the current contact and update the TESTDB database accordingly.
Adding Delete Functionality To add functionality from the deleteContact operation, first add a Comment builder call to the service consumer model. This comment isn’t strictly necessary, but it will help to organize your model and make it clearer. Name the Comment Delete and save the builder call. Now, add
Creating a Contacts Portlet
67
another action list to the service consumer model. This action list will define what happens when a user wants to delete a contact. Name the Action List deleteContact, and then press the ellipsis button next to the first action to open the Select Action dialog. The first action that you define assigns the ID of the currently selected contact to the argument that is passed in to the delete operation. Select Assignment as shown in Figure 3.10.
Figure 3.10
Specifying the first action for deleteContact.
In the Make Assignment dialog that follows, press the ellipsis button next to the Target box and select ID from the list of the deleteContact arguments, as shown in Figure 3.11. This specifies the argument that will be set.
Figure 3.11
Selecting the target value.
68
Chapter 3 Using Data from a Relational Data Source
Next, press the ellipsis button next to the Source box and select ID from the currently selected contact, as shown in Figure 3.12. The value of this field is used to set the value of the target ID specified in the previous step. Press OK when you are finished.
Figure 3.12
Selecting the source value.
For the second action in the action list, select deleteContact from the Select Action dialog (it then appears in the Action List as DataServices/contacts/deleteContact). This actually calls the deleteContact operation, passing in the value of the parameter that was set in the previous step. Press OK when you are finished. For the third action in the action list, select contactsView_ShowResults from the Select Action dialog. This is the method created by the View & Form builder that refreshes the contacts list after the delete operation is performed, and then returns the user to the contacts view. Press OK when finished and save the builder call. Now, add a Button builder call to the service consumer model. The button is placed on the details page of each contact and runs the deleteContact action list when clicked (which deletes the currently selected contact). Fill out the Button builder call, as shown in Figure 3.13. Notice that the Action Type input is set to ‘Link to an action’; this runs the delete operation without submitting the page (submitting the page is undesirable in this case, as this would try to
Creating a Contacts Portlet
69
save the current contact). Also, take note of the contents of the Page Location section of the builder call—the button will be placed on the contactsView_DetailsPage page (the contacts details page) after the update_button tag. The update_button tag is where the Edit button appears on the page, so the Delete button appears to the right of it. Save the builder call when finished. A Delete button is now displayed on the detail page, which lets you delete the current contact and update the TESTDB database accordingly.
Figure 3.13
Configuring the Delete button.
Adding Create Functionality To add functionality from the createContact operation, first add a Comment builder call to the service consumer model and name it Create. Save the builder call, and then add an input form for the create function by adding an Input Form builder call to the model. Input forms are used in WPF to enable users to enter in data. Note that an input form is automatically created by WPF for the update function in the View & Form builder call, but you need to manually create an input form for the create function (both forms are slightly different). Fill out the input form, as shown in Figure 3.14. This displays a form for entering in contact information based on the input schema defined for the createContact service operation. When the form is submitted, the createContact operation is consumed and the contactsView_ShowResults action list runs. Also, expand the Advanced section and uncheck the Generate Main checkbox, which prevents the builder from automatically generating a main Action List (you are already using the one created by the View & Form).
70
Figure 3.14
Chapter 3 Using Data from a Relational Data Source
Configuring the Input Form builder call.
The final modification to make to the Input Form is to add a Cancel button to the form so that users can navigate back to the view page when they’re finished. To do this, enter the text Cancel into the Input Cancel Text input, and then specify contactsView_ViewPage for the Input Cancel Action input. This creates a Cancel button with the label Cancel, which returns the user to the contactsView_ViewPage page when clicked. Save the Input Form when you are finished. By default, the inputs on the input form do not automatically clear when the form is loaded, so if you create two contacts, you will see that the input values are still left over from the first contact. This is undesirable in the present example, so you will now add an action list to clear the input form, and then display the input form afterward. This action list is then run by a button you will create later. Add an Action List builder call to the model, and name it resetAndOpenCreateContactPage. For the first action, select Assignment from the Special section of the Select Action dialog. Leave the source blank (as you want to assign a null value) and enter in the input you would like to clear into the target field (which, in this case, is the parent element of all the inputs used by the Input Form: Variables/contactsCreateContactInputs/createContactQueryInputs, shown in Figure 3.15). This sets all the inputs in the inputs variable to null values. You now need one more action in the Action List (to reshow the input form), so select createContactForm_ ShowInputPage from the Select Action dialog for the second action. You now need to add a button to the main view page so that users have a means to call up the input page you just created. Add a Button builder call to your model, and then fill out the builder call, as shown in Figure 3.16. The button displays the label ‘Create contact’ and shows the input page when clicked.
Creating a Contacts Portlet
Figure 3.15
Configuring the resetAndOpenCreateContactPage action list.
Figure 3.16
Configuring the Create button.
71
Notice that you need to change the Action Type input to read ‘Link to an action’ (rather than the default setting, ‘Submit form and invoke action’). If you don’t change this input, the submit
72
Chapter 3 Using Data from a Relational Data Source
action is called when you press the Create button (which, in this case, causes an error, as the contacts list page doesn’t have a form to submit). Also, notice the location of the button control—at a new HTML tag called createContact, which displays immediately before the data tag (the data tag displays the results of the view operation on the contacts service). For more information on placing controls on forms, see Chapter 6. Save the builder call when finished. Your service consumer model is now ready to test and deploy to a portal server, which is covered in the next section.
Testing the Contacts Portlet Your project now contains both a service provider model (offering create, read, update, and delete functions on the TESTDB data source) and a service consumer model to consume the operations on that service. In this section, you test that the service consumer can access the service provider and that it can correctly consume each operation on the service. You then rebuild the portlet application and view your service consumer model as a portlet. Make sure the contacts model is the currently active model in your IDE. Run the contacts model from the WPF Designer, and the contacts list should display in your default Web browser, as shown in Figure 3.17.
Figure 3.17
Testing the contacts list from the contacts model.
Click on a contact ID to open the detail page for that contact. For example, clicking Haley Smith’s ID should return the page shown in Figure 3.18.
TIP In this example, the list and detail are surfaced in the same portlet, but sometimes these are split into two separate portlets (one for the list and one for the detail). Clicking on a document in the list portlet opens the document in the detail portlet. This enables the list to always be accessible (you don’t have to cycle back and forth between the list and the detail), and the list and the detail can be shuffled around the page as the user desires. Furthermore, these portlets can also be used as components in other applications; for example, clicking on a supplier in the list portlet might bring up a report for that supplier in a chart contained in another portlet. Intercommunication between portlets is discussed in Chapter 7, “Communicating Between Portlets.”
Creating a Contacts Portlet
Figure 3.18
73
Viewing a contact from the contacts model.
Click the Edit button to revise the current contact’s details. For example, clicking the Edit button for Haley Smith should return the page shown in Figure 3.19. Change some of the contact’s details and press Submit, and you should see the contact record updated in the contacts list. From the main view page, press the Create contact button. The input form should appear, as shown in Figure 3.20. Enter the details for a new contact and press Submit. You should see the contact added to the bottom of the contacts list and be able to open the contact’s record to view his details.
Figure 3.19
Editing a contact from the contacts model.
74
Figure 3.20
Chapter 3 Using Data from a Relational Data Source
Creating a contact from the contacts model.
Open a contact again by clicking an ID in the contacts list, and then press the Delete contact button. You should return to the contacts list and see that the contact was deleted. Close the contacts model when you’ve finished testing it. You should now rebuild your application on the portal server (for instructions on how to do this, see the example in Chapter 1), after which you can view the contacts model as a portlet. After you have added the portlet to a page in the portal it should appear, as shown in Figure 3.21.
Figure 3.21
The Contacts portlet.
Summary In this chapter, you learned how to interact with a relational data source from WPF. You created a simple WPF application that contained two models: a service provider and a service consumer. The service provider contained several operations that provided create, read, update, and delete functionality for the TESTDB data source, and the service consumer enabled users to consume these operations from a portlet. The portlet developed in this chapter is just a starting point for interacting with backend data. For more information on how to manipulate the presentation of your data (including how to paginate your data sets), see Chapters 5 and 6.
Important Points
75
The next chapter, “Using Domino Data,” discusses how to interact with data from Domino databases, which are non-relational data sources.
Important Points • Service consumers access data as a list and detail. • In the context of relational data, a list is a snapshot of records from a table, which usually contains only select information from each of these records. The detail offers more in-depth information on a particular record. • The SQL Call builder is a high-level amalgamation of several other SQL builders in WPF, and it enables you to access a backend SQL data source and run SQL statements against it. • The View & Form builder is used in a service consumer to configure general options for interacting with relational data, such as specifying the associated service operations for displaying the list and detail.
This page intentionally left blank
C
H A P T E R
4
Using Domino Data
As the prevalence of portal technologies increases, being able to portalize a Lotus Notes application—that is, transition Domino data access and functionality from an existing Notes application into a portal-based application—is becoming an increasingly important skill for developers to have. This chapter describes how you can access and manipulate Domino data in a portlet. By the end of the chapter, you will know how to use the service provider/consumer pattern to interact with a Domino database, and you will have created a sample application to demonstrate this approach. You will also learn how to create a stub service to test your service consumer when you’re not connected to a Domino server. Both the models used in this chapter, in addition to the Notes database, are available for download from ibmpressbooks.com/title/9780137134465 under the Chapter 4 folder (instructions for copying these files into your project are included in a readme.txt file in the same folder). You should copy the Notes database into your Domino data directory as per the instructions in the readme.txt file; however, it is recommended that you try to create the models yourself to increase your understanding of the topics discussed in this chapter. Also, before you begin the example in this chapter, you need to configure your Domino server so that WebSphere Portlet Factory (WPF) can communicate with it (this is covered in the “Configuring Lotus Domino” section in Appendix A). The following topics are covered in this chapter: • Portalizing Notes functionality • Configuring your environment • Creating a service provider • Creating a suppliers portlet • Using a stub service
77
78
Chapter 4 Using Domino Data
Portalizing Notes Functionality Although portals should not be seen as a replacement for the Notes client, they can streamline common business processes performed in Notes (such as sending emails, recording timesheets, or displaying sales reports) because the interfaces used to access a portal (usually Web browsers) are much more lightweight and accessible than a typical Notes client. Because of the comparative simplicity of the interface, portalized Notes applications should not attempt to be monolithic applications, but should provide a handful of important functions useful to the functioning of the business. For example, an email portlet should contain an interface for sending and receiving mail, in addition to opening and adding attachments to email messages, but it does not need to contain an interface for designing stationery (which arguably wouldn’t add enough value to the application to offset the added complexity). Keep in mind that portlets are only a small part of a larger context (the portal), and therefore they need to be designed in a certain way to increase their usability. For example, tabs in a portlet should be avoided wherever possible, because most portals already use a tab-based navigation system, and multiple navigation points can become confusing for end users. WebSphere Portlet Factory provides several out-of-the-box tools to facilitate connectivity to Domino. This chapter aims to show how you can use these tools to quickly and easily surface a preexisting Notes application (a database of suppliers, which you can download from ibmpressbooks.com/title/9780137134465) as a portlet in WebSphere Portal. Interactivity between WPF and Domino is set up through the use of a service provider model, which is then consumed from a service consumer model. The service consumer model is surfaced to a portal server as a portlet. This chapter assumes that you are familiar with basic service terminology and the service provider/ consumer pattern; if this is not the case, please refer to Chapter 2, “Providing and Consuming Services.” Also, this chapter focuses on providing Domino functionality in a portlet, rather than formatting Domino data. For information on some of the different ways to arrange Domino data in a portlet, see Chapter 5, “Customizing Portlet Appearance,” and for more on extending the user interface, see Chapter 6, “Adding Basic User Interface Controls to Your Portlets.”
TIP When building portlets based on Notes applications, it is important to remember that you should normally port across only key functionality. Large, complex portlets can become unwieldy when used in the context of a portal, because portals contain other portlets and applications (often on the same page) that can quickly add to the complexity of the interface. Of course, there might be good reasons for why you might still port across a Notes application in its entirety. In these scenarios, it is sometimes more effective to put the original Notes application into an iFrame portlet rather than build the portlet in WPF from scratch. iFrames provide you with a mechanism for embedding HTML documents (such as those created by Web-enabled Notes databases) within the portal, and can be implemented in WebSphere Portal 6 using the Web Clipping Editor portlet. The main disadvantage of using iFrame portlets is that they are not well integrated into the portal; the portal is only aware of the iFrames, rather than the applications within them, and the applications cannot easily communicate with other portlets.
Configuring Your Environment
79
The example application discussed in this chapter contains two models: The first is a service provider, and the second is a service consumer. The first section of this chapter describes how to set up the service provider, which provides create, read, update, and delete functionality for a Notes database containing supplier information. The second section covers the service consumer, which enables end users to interact with the service provider and is surfaced to a portal server as a portlet. Finally, the last section of the chapter walks you through the process of creating a stubbed service to test your service consumer when you’re not connected to the Domino server.
Configuring Your Environment This section walks you through setting up your environment to connect to a Domino server. Note that if you have not configured your Domino environment to connect to WPF, you should complete the “Configuring Lotus Domino” section of Appendix A before you proceed with this section. Before you can access the supplier database in Notes, you need to copy the database into the data directory of your Domino server (the database can be downloaded from the Chapter 4 folder at ibmpressbooks.com/title/9780137134465). The supplier database is a basic Notes database and contains the following design elements: • A Supplier form (for adding and editing suppliers) • A Stock Item form (for adding and editing suppliers) • A Return form (for adding and editing information on returned stock items) • Views for displaying documents created with these forms (Suppliers, Stock and Returns) • Two lookup views (lookupSuppliers and lookupReturns) • An agent called AddSupplierToMMDataSourceFromWPF (for adding the current supplier’s address and contact details to a text file, which can be used as a data source for running mail merges) Be sure you open the database in a Notes Designer client to see how the agent, forms, and views are constructed. If you open the supplier database in a Notes client, you can see that the form contains a button to add the current supplier to a mail merge data source (a file called supplierRecord.dat on the user’s C drive). However, fields are the only things that come across from a Notes form into a WPF form, so the button is not accessible from WPF. If you want to display the button in WPF, you need to use a Button builder. For the code that is executed when the button is pushed, you need to recode the procedure in a Java method (using the Java Domino API) or put the code into a Notes agent and run the agent from WPF. Chapter 16, “More Techniques for Domino,” has more information on Notes agents in WPF and describes how to set up the AddSupplierToMMDataSourceFromWPF agent to run from a button on the service consumer.
80
Chapter 4 Using Domino Data
Before you proceed, you should make sure you have a WPF project, which is used to contain the service provider model and service consumer model that you create in this chapter. If you have a project from another chapter, you can use it, except you need to add the Lotus Collaboration feature set to the project (to do this, open the Project Properties dialog and select Lotus Collaboration Extension from under the Integration Extensions category in the WebSphere Portlet Factory Project Properties/Feature Info section). Otherwise, you should create a new WPF project with the Lotus Collaboration Extension feature set (for more information on creating projects, see Chapter 1, “Introduction to WebSphere Portlet Factory”). The project will be published as a WAR file and deployed to a portal server, and you should also deploy the application to a local application server for testing. You can use the portal server if it is running on your local machine; otherwise, it is recommended that you use the IBM WebSphere Application Server Community Edition server (WAS CE) that comes with WPF.
Configuring the Domino Properties File After you have a project with the Lotus Collaboration feature set, you need to configure your Domino connection settings. Because you are using the Lotus Collaboration Extension feature set, a Domino connection properties file (called default_domino_server.properties) is added to your project under the WebContent/WEB-INF/config/domino_config folder. Properties files are used to store your Domino server connection settings in WPF, and you can have multiple properties files to cater for different scenarios. For example, you might have one properties file for a local Domino server and one properties file for a remote Domino server. Each Domino builder has an input to specify the properties file used by that builder.
TIP If you copy a Domino properties file from one project to another, you need to open it and save it in the new project before it can be used.
To configure the default Domino properties file, double-click the default_domino_ server.properties file from the WebContent/WEB-INF/config/domino-config folder in the HelloWorld project to open it. Change the ServerName property to point to your Domino server. Note that you must specify the port you are using to connect to Domino (63148 is the default DIIOP port). If you are using a local Domino server with default port settings, you should be able to accept the default server entry. You also need to change the UserName and Password properties to correspond to an account that has at least Editor access to any databases you want to use in WPF.
Configuring Your Environment
81
TIP Technically, the user you specify in the configuration file needs only read access to the database so that you can access the database at design time. At runtime, you can use a different user to authenticate with Domino (such as the user currently accessing the application) by changing the Runtime Credentials field on the Domino Data Access builder. Specifying an editor in the configuration file provides less security and might not be suitable in a production environment; however, it can make testing easier, particularly if you haven’t set up an alternative authentication mechanism (such as Lightweight Third-Party Authentication).
Save the properties file when you are done editing it.
Testing the Connection to Domino Now that you’ve set up the properties file, you should test the connection to Domino from WPF. When you create your service provider model using the Domino Service Provider wizard, you know whether the connection works because the wizard returns errors when the connection isn’t configured properly. However, WPF also comes with a test page you can use to test the connection before you start building the service provider model, which can make your applications easier to debug (because you don’t have to restart the wizard every time you want to test the connection). To use the test page, open the WebContent/factory/util/domino_test.jsp file in your project using a Web browser. The URL should be similar to the following: http://localhost:10038/DominoExample/factory/util/testDominoConnection.jsp substituting ‘localhost’ for the name of the application server specified in your application server deployment configuration. You should see a test page, as shown in Figure 4.1.
TIP You need to have the HTTP task running on your Domino server to use this test page. If you are using only the DIIOP task, you can either temporarily start the HTTP task, or test your connection by creating a Domino Data Access builder in a WPF model. After you’ve added a Domino Data Access builder, press the Get Databases and Views button—if you don’t receive any error messages, and you can see a list of databases in the Database Name list (which you can open by pressing the ellipsis button next to the Database Name builder input), you can successfully connect to Domino.
Fill out your connection settings, and then press the Test Connection button. If you have configured your settings correctly, when the page reloads, you should see a Got a connection to the IOR message, and a list of available databases on your server should be displayed.
82
Chapter 4 Using Domino Data
Figure 4.1
Domino connectivity test page.
TIP If you cannot connect to the Domino server successfully, make sure that both the DIIOP and HTTP tasks are started, your credentials are correct, and a firewall is not blocking access to the Domino server.
Creating a Service Provider The service created in this section provides five operations, which let users create, read, update, and delete records in the Domino database (there are two read operations). These operations will then be consumed by a portlet, which you create in the next section. The service provider model created in this section use the following builders to provide its functionality: • Domino Data Access • Service Definition • Service Operation (x5) These are added automatically using the WPF new model wizard.
Creating a Model To add a service provider model to the project, select File, New, WebSphere Portlet Factory Model from the File menu to bring up the WebSphere Portlet Factory Model dialog. On the next
Creating a Service Provider
83
screen, select your WPF project and press Next. The next screen lists different types of models WPF can create for you automatically. You can create service providers in WPF in two ways: using one of the options in the Select Model wizard (such as Database Service Provider) or creating the service provider manually. Because you are using the Lotus Collaboration Extension feature set, you also have a third option: the Domino Service Provider, which specifically caters to Domino databases. Select the Domino Service Provider option and press Next.
Defining the Service You now need to define the service. Give the service the name suppliersService. If you check the Generate WSDL box, a WSDL document is automatically created for your service, enabling it as a Web service. The Generate WSDL checkbox can also be found on the Service Definition builder call, so you can enable or disable your service as a Web service at any time. Web services are discussed in Chapter 9, “Using Web Services and Manipulating XML.” Leave the box unchecked for this example. The bottom section of the screen enables you to specify a Domino connection properties file (which you configured and tested in the previous section). Accept the default setting and press Next.
TIP If you get errors at this stage of the wizard, it means the settings in your properties file are not allowing you to connect to the Domino server. Make sure the username, password, and hostname in the properties file are correct, you have an active network connection between WPF and Domino, and you have configured your Domino environment as described in Appendix A.
On the next screen, you have to specify the database you want to connect to and the Notes view you want to use for displaying list data. Select Suppliers.nsf for the Database Name, and Suppliers for the View Name. Note that you should not have to type in either of these values manually; the Database Name should be available from the Choose Notes Database dialog (which you can open by clicking the ellipsis button next to the Database Name field), and the View Name should be available from the View Name drop-down list. If you cannot see the database, make sure you copied the Suppliers.nsf file to the data directory of the server you are accessing. If you cannot see the view, make sure the user specified in your properties file has at least reader access to the database (the Suppliers.nsf database available from the download site has a default access of Manager, so there shouldn’t be an access problem unless you change the Access Control List [ACL] or you use a different database with restricted access). Press Next when you are finished.
Specifying Operations To specify operations for the service provider, check all the checkboxes currently on the screen; this will enable you to retrieve data from the Suppliers view and read, edit, and create documents
84
Chapter 4 Using Domino Data
from the Suppliers view. The delete operation is not automatically created, so you have to add it manually when the WebSphere Portlet Factory Model wizard has finished. When you check a box for one of the document operations, an additional drop down will appear at the bottom of the screen with the label ‘Form To Use For Document Schema’. Select the Supplier form from the drop-down list. This setting specifies that the read, create, and edit document operations use the Supplier form. The default operation names are long, so modify them as shown in Figure 4.2. Press Next when you are finished specifying your service operations.
Figure 4.2
Specifying your service operations.
Name the model suppliersService and make sure it is created in the models/chapter04 directory. Press Finish to build your model. When the wizard finishes, a new model called suppliersService is created in your project and opens in the Model Editor and Outline view. Spend a few minutes going over the builder calls that were automatically added to the model by WPF. The Service Definition builder call defines general properties for the entire service, and the Domino Data Access builder call defines the connection to Domino, in addition to how the Domino data is used (such as reading and creating
Creating a Service Provider
85
Notes documents). This functionality is then accessed by the Service Operation builder calls in your model, which define the public interface to the service (that is, the operations on the service). The action taken when an operation is called is specified in the Action To Call inputs on the Service Operation builder calls. After the operations have been defined, service consumers can call these operations to get access to the Domino data configured in the Domino Data Access builder call (see “The Domino Data Access Builder” sidebar for more information on the Domino Data Access builder).
Adding a Delete Operation The WebSphere Portlet Factory Model wizard did not automatically create an operation for deleting documents, so the next step is to make this functionality available through a service operation. To do this, first add a Service Operation builder call to the suppliersService model by selecting Service Operation from the Builder Palette and then pressing OK (you can open the Builder Palette by clicking the icon in the Outline view).
THE DOMINO DATA ACCESS BUILDER The Domino Data Access builder specifies settings for interacting with documents and views in a Domino database. In the top part of the builder, you need to specify a database that you want to connect to and the properties file that defines the link to the server where the database resides. The Runtime Credentials input in this part of the builder enables you to configure how user authorization is calculated when accessing the database—the default value of ‘Use regen credentials specified above’ uses the ID specified in the properties file. The View Support section of the builder contains settings for a view in the database that you want to use for your list information in WPF.You can use these settings to exclude documents from any data returned from Domino. For example, the Rows to Include input enables you to specify that only certain rows should be included (such as rows that display a category title), and the Category input specifies that only a certain category of documents should be displayed (based on the first categorized column in the view). There is also a Search String input in this section, which lets you search the full text on the results returned from the view. Documents that don’t fall within the search results are not returned for operations that retrieve view data. Searches in this box have the following syntax: field [fieldName] [relation] [value] where fieldname is the name of the field to search for, value is the value of all or part of the field, and relation is a relation between the field and the value, either ‘=’ or ‘contains’. For example, the search string field supplierCode contains Til would return all the documents with a supplierCode containing the text Til. The search string field supplierCode=Til107 would return all of the documents with a supplierCode of Til107. These searches can also be concatenated; for example, the search string field supplierCode contains Til AND field supplierName contains Kingdom would return all documents with a supplierCode containing the text Til and a supplierName containing the text Kingdom.
86
Chapter 4 Using Domino Data
The Document Support section of the builder specifies that you want to use a particular form to access and manipulate Notes documents from the View. The Optional Schema Settings section enables you to modify the schemas that WPF automatically creates for your Domino data (which you can view under the WebApp/Schemas folder on the WebApp Tree tab when editing a model). The Connection Override section lets you override the connection properties set in the connection properties file specified at the top of the builder call. The Advanced section contains several useful options for configuring the connection to Domino. For example, the Fetch Subsets of Data input can lead to faster page loading times when displaying paginated data, because you can specify to only retrieve the documents displayed on the current page (instead of all documents in the view). Also, the Use HTTP Access input can be used to force WPF to communicate with Domino over HTTP (rather than DIIOP); however, this works only when you don’t have a search string or category specified and you have the Enable View Support box checked. The Domino Data Access builder requires additional builders (such as the View & Form builder call used later in this chapter) to control how the data is presented. The Domino View & Form builder, which is an amalgamation of the Domino Data Access and View & Form builders, is another builder that can access Domino data. The Domino View & Form builder is easier to implement than a service-based approach using the View & Form builder, and it also provides additional functionality (such as automatically building a create page for your data). However, the Domino View & Form builder contains inputs that affect the presentation and model tiers of your application, which ultimately makes your application more difficult to maintain (if you are unfamiliar with these terms, please refer to the “The Model View Controller Pattern” sidebar in Chapter 2). As a result, the Domino Data Access builder should be preferred wherever possible. Adding a Domino Data Access builder call to a model will also give you access to a number of methods for accessing and manipulating Domino data. For example, open up a Service Operation builder call in the suppliersService model and press the ellipsis button next to the Action To Call input to open the Select Action dialog. Under the Methods folder, there should be a subfolder called suppliersServiceView containing a series of methods created by the Domino Data Access builder call (some of the more useful methods are discussed in Chapter 16). Several of these methods are also accessible from the DataServices folder in the Select Action dialog, which you use in the example in this chapter.
The Domino Data Access builder already provides a method for deleting Notes documents, and you can run this method from the service operation. Open the Service Operation Properties section of the Service Operation builder and change the Data Service input to suppliersService. Change the Operation Name input to deleteSupplierDocument and the Action to Call input to suppliersServiceView.deleteDocument. This creates an operation called deleteSupplierDocument that runs the deleteDocument method provided by the Domino Data Access builder (for instance, suppliersServiceView.deleteDocument). After you have filled out the Service Operation Properties section, you should make sure the inputs and outputs for the operation are correct. Service operations use inputs and outputs to define how service consumers communicate with them (for more information on basic service terminology and how services are used more generally, see Chapter 2).
Creating a Service Provider
87
In the Service Inputs section of the builder call, select ‘Use structure from called action’ for the Input Structure Handling input and make sure Input Field Mapping is set to Automatic. This specifies that the structure of the inputs to the operation is defined by the called action, which, in this case, is the deleteDocument method provided by the Domino Data Access builder. It also automatically maps inputs from the Service operation to the deleteDocument method. The deleteDocument method takes one argument, which is the universal ID (UNID) of the document that you want to delete. The inputs for the deleteDocument method are defined in an XML schema automatically created by the Domino Data Access builder call.
TIP You can have a look at the XML schemas in your model (both those that you have created and those that WPF has created for you) by switching to the WebApp Tree tab when editing a model in the Model Editor. All the schemas used in the current WebApp are listed under WebApp/Schemas. Additionally, you can look up the inputs and outputs of a method under the WebApp/Variables folder. Using both of these folders, you can get a better understanding of how the methods automatically created by WPF are defined. For example, the readSupplierDocument method created by the Service Operation builder call will have a corresponding variable for its inputs, called readSupplierDocumentInputs. Selecting this variable in the WebApp/Variables folder will tell you the schema (suppliersServiceView_ document) and the element (InputUNID) for this variable.You can then look up this schema under the WebApp/Schemas folder and browse to the InputUNID element to see what inputs the operation is expecting.
After you have configured the Service Inputs section of the builder, you need to specify the outputs (if any) that you want to use for the operation. In this case, no output is required, because you just want to delete a particular document from the database (similarly, the updateSupplierDocument operation also requires no output, because you just want to update a document based on the inputs passed in to the operation). Notice, however, that the other operations in the model do return results to the caller; for example, the readSupplierDocument operation returns all the fields on the Supplier form for the UNID specified in the inputs to the operation. The structure of the results returned from methods defined by the Domino Data Access builder call is defined in schemas created by the Domino Data Access builder call (which is why, in these cases, the Result Structure Handling input is set to ‘Use structure from called action’). To specify that no outputs are used, change the Result Structure Handling input in the Operation Results section to ‘No results’ and make sure the Result Field Mapping is set to Automatic. Save the builder call when you are finished. Your service provider is now finished and ready to test.
88
Chapter 4 Using Domino Data
Testing the Service Provider By default, WPF automatically generates test pages for your service provider (which you can disable from the Testing Support section of the Service Definition builder call). This enables you to test the service provider directly from the IDE. To test the service, run the suppliersService model from the WPF Designer by clicking the icon on the toolbar. This runs the currently active model (for instance, suppliersService) with the last run configuration you used. If you have not set up a run configuration before, you are prompted to do so—create a new configuration under the WebSphere Portlet Factory Model category (if you want more information on setting up a run configuration, see the “Testing the Application” section in Chapter 1). After you run suppliersService, you should see the index test page in your default Web browser, as shown in Figure 4.3. This screen lists all the operations available on the suppliersService service.
Figure 4.3
Index test page from the suppliersService model.
Testing the readSupplierView Operation To test the readSupplierView operation, click the readSupplierView link. You should see a list of contacts, as shown in Figure 4.4. If not, check that you don’t have any errors showing in the Problems view in the IDE and that you have completed all the steps in the previous section correctly.
Figure 4.4
Testing the readSupplierView operation.
Testing the Service Provider
89
Notice that the four columns defined in the Notes view are displayed, in addition to two columns that WPF adds to every view by default: isDocumentRow and UNID. The isDocumentRow column denotes whether a row corresponds to a document in Notes, and the UNID column denotes the UNIDs for each document displayed. These fields are excluded from the service consumer’s output with a Data Column Modifier builder call. Copy a UNID from one of the documents (by highlighting it and pressing Ctrl+C), because you need a sample UNID for the next test. Press Back to return to the index page.
Testing the readSupplierDocument Operation To test the readSupplierDocument operation, click the readSupplierDocument link. The screen that follows enables you to specify parameters for the operation. Paste in the UNID you copied to the clipboard in the previous step, and then press the Submit Query button. You should see details for the supplier with the ID specified. Fred Jackson’s details, for example, are shown in Figure 4.5. Note that the supplier rating is not displayed, because this field is set as ‘Computed for display’ on the Notes form (so it doesn’t actually exist on the Notes document). Chapter 16 discusses how to add the supplier rating to the WPF detail and update pages. Press Back when you have finished testing the readSupplierDocument operation.
TIP If you open the Service Definition builder call and check the Specify Default Inputs box in the Testing Support section, you can specify default values for your testing so that you don’t need to retype them every time. For example, if you copy a UNID and paste it into the InputUNID.UNID field for the readSupplierDocument operation, this UNID is used when testing the operation unless another UNID is specified.
Figure 4.5
Testing the readSupplierDocument operation.
90
Chapter 4 Using Domino Data
Testing the updateSupplierDocument Operation To test the updateSupplierDocument operation, click the updateSupplierDocument link. The screen that follows enables you to specify parameters for the operation, which are the values used to update a particular supplier. Paste in the same UNID you copied to the clipboard earlier, in addition to some new data for that supplier, and press the Submit Query button. If the operation worked correctly, you should be able to run the readSupplierView operation again and see that the supplier is listed with the details you just entered. (Note: Although the test page requires you to reenter each field, these fields are automatically populated when you edit a supplier from the service consumer.)
TIP When you save a Notes document from WPF, the inputs are validated based on the validation formulas on the Notes form. In this example, the SupplierName field requires a value with a length of at least three characters, and the supplierCode field cannot be null. So, if you don’t meet these requirements, you get an error when you try to save the form. You can turn off this validation by calling the setComputeWithFormEnabled method of the Domino Data Access builder and passing in the value ‘false’ as an argument. Validation is discussed in more detail in Chapter 11, “Field Validation, Formatting, and Translation.”
Testing the createSupplierDocument Operation To test the createSupplierDocument operation, click the createSupplierDocument link. The screen that follows enables you to specify parameters for the operation, which are the values used to create the new supplier (you don’t need to type in a UNID—this is assigned automatically). Enter in some sample data and press the Submit Query button. When the screen reloads, it displays the UNID of the new document just created in Notes. Copy this UNID onto the clipboard, because you use it in the next test. Press the Back button to continue. If the supplier was added correctly, you should be able to run the readSupplierView operation again and see the new supplier.
Testing the deleteSupplierDocument Operation To test the deleteSupplierDocument operation, click the deleteSupplierDocument link. Paste in the UNID of the supplier you created in the previous step to delete that supplier, and then press the Submit Query button. When the screen reloads, it displays nothing but a Back button—press this to continue. If the contact was deleted correctly, you should be able to run the readSupplierView operation again and see that the contact has been removed. Close the suppplierService model when you have finished testing it.
Creating a Suppliers Portlet
91
You now have a working service provider model called suppliersService. The next section walks through creating a service consumer model to consume the operations offered by this service and provides an interface to these operations in a portlet.
Creating a Suppliers Portlet This section describes how to add a second model to your project, which offers a user interface to consume the various supplier operations provided by your service provider. This service consumer model is then surfaced to a portal server as a portlet. A screenshot of the portlet is shown later in Figure 4.18. In this section, you use the following builders in the suppliers model: • Portlet Adapter • Service Consumer • View & Form • Comment (x3) • Action List (x2) • Button (x2) • Input Form • Data Field Modifier Note that the model developed in this section uses only basic display options; the focus is more on how to surface functionality to consume services that interact with a Domino database. For more information on arranging output in portlets, see Chapter 5 and Chapter 6.
Creating a Model To create the service consumer model, select File, New, WebSphere Portlet Factory Model from the File menu to bring up the WebSphere Portlet Factory Model dialog. On the next screen, select your WPF project and press Next. The next screen lists different types of models WPF can create for you automatically. You can create service consumers in WPF in two ways: the first is to let WPF create one for you (which you can do by using the List and Detail Service Consumer option in the Select Model wizard), and the second is to create the service consumer manually. A list in WPF is a collection of basic data across one or more records (usually displayed in a table or chart), whereas detail is data specifically related to a single record (which usually contains data not available in the list). The List and Detail Service Consumer gives you everything you need in this example, so select it and press Next. The List and Detail Service Consumer automatically surfaces your model as a portlet via a Portlet Adapter builder call. Specify the name of the portlet on the next screen as suppliers (this
92
Chapter 4 Using Domino Data
name is also used to name the other builder calls in your model). Also, specify the service provider chapter04/suppliersService you created in the previous section. Press Next when you are finished. You now need to specify the service operation that is used to provide the list of suppliers. Select readSupplierView and press Next. On the next screen, fill out the boxes, as shown in Figure 4.6, and then press Next. These settings are for the detail part of the data, and they convert all the values in the Supplier_Name column in the contact list into clickable links. When a user clicks on the link, a value identifying the supplier is passed to the readSupplierDocument operation, and the results are displayed to the screen.
Figure 4.6
Specifying a detail operation.
You now need to configure the value that will uniquely identify each supplier for the readSupplierDocument operation. Make sure the settings appear, as shown in Figure 4.7, and then press Next. This uses the Notes UNID to identify each supplier. Finally, give your model the name suppliers and make sure it is created in the directory models/chapter04. This is the name of the model as it appears in the IDE. Note: In this example, although the model name and portlet name are the same, it is possible to use different names for each field. When you’re done, press Finish to build your model. This creates a new model called suppliers in your WPF project and opens the model in the Model Editor and Outline view.
Creating a Suppliers Portlet
Figure 4.7
93
Specifying a detail operation.
Configuring the Portlet Adapter The suppliers model contains three builder calls: a Portlet Adapter, a Service Consumer, and a View & Form. The Portlet Adapter builder converts the model into a portlet, which can be viewed inside a portal (note that if you make any changes to this builder call, including the changes you are about to make, you need to rebuild the deployed application before you can see the results of the changes. For more information on this process, see the example in Chapter 1). The Service Consumer builder consumes the operation readSupplierView on the suppliersService service, and the View & Form builder displays any list and detail data in the model to the screen. The View & Form builder also enables you to do some basic manipulation of how the data is displayed (the View & Form builder is discussed in Chapter 5). The builder calls don’t require any configuration in this example, except for two small changes to the Portlet Adapter builder call. Double-click the Portlet Adapter builder call to open it in the Model Editor, and then change the Portlet Title to Suppliers and the Portlet Description to Interface for interacting with the Suppliers.nsf database. Save the builder call when you are finished. The Portlet Title identifies the portlet inside the portal (it is actually displayed in the title bar of the portlet), and the Portlet Description appears on various administration screens inside the portal. At this stage, the model uses only two of the five operations provided by the service provider—that is, you can read a list of suppliers from the suppliers database and view details on an individual supplier by clicking on their ID. Test the suppliers model now from your IDE (by pressing the button), which gives you some understanding of what the wizard creates by default.
94
Chapter 4 Using Domino Data
Next, you need to add builder calls to the model to surface functionality for the create, update, and delete operations. Note that at the completion of each of the following sections, you can test the added functionality directly from your IDE. Testing every operation on the consumer together is discussed in the “Testing the Service Consumer” section at the end of this chapter.
Adding Update Functionality To add functionality from the updateSupplierDocument operation, double click on the View & Form builder call in the Outline view. Scroll down to the Update Page Support section, and then check the Create Update Page box. Some new options appear underneath. Bring up the Select Action dialog for the Update Method input, and then select the updateSupplierDocument operation from the DataServices section (it appears in the input as DataServices/suppliers/update SupplierDocument). The Update Method defines what happens when new data is submitted (it runs the updateSupplierDocument operation in this case). For the Update Next Action input, select suppliersView_ShowResults. This input defines a post update action, and, in this case, it will refresh the suppliers list with any changes and return the user to the suppliers list—something that is not done automatically.
TIP The View & Form builder creates three pages in your model: suppliersView_ViewPage suppliersView_DetailsPage suppliersView_UpdatePage The name of the View & Form builder call is used as a prefix for each page. The ViewPage page contains the supplier list. The DetailsPage is the page used to display specific information on an individual supplier, and the UpdatePage is the page used to edit a supplier. You can place controls on these pages by specifying one of them in the Page input on a builder that creates UI elements. For more information on placing controls on forms, see Chapter 6.
Save the builder call when you are finished. An Edit button is now displayed on the detail page, which lets you edit the current supplier and update the Suppliers.nsf database accordingly.
Adding Delete Functionality To add functionality from the deleteSupplierDocument operation, add a Comment builder call to your model. Name the Comment Delete and save the builder call. This Comment isn’t necessary, but it helps to organize your model and make clear the purpose of the builder calls that follow it in the Outline view. Add another Action List to your model. This action list defines what happens when a user wants to delete a supplier. Name the Action List deleteSupplier, and then press the ellipsis
Creating a Suppliers Portlet
95
button next to the first action to open the Select Action dialog. The first action that you define assigns the UNID of the currently selected supplier to the argument that is passed to the delete operation. Select Assignment from the Special heading in the Select Action dialog, and then, in the Make Assignment dialog that follows, press the ellipsis button next to the Target box and select arg1 from the list of the deleteSupplierDocument arguments, as shown in Figure 4.8. This specifies the argument that will be set.
Figure 4.8
Selecting the target value.
Next, press the ellipsis button next to the Source box and select UNID from the currently selected contact, as shown in Figure 4.9. The value of this field will be used to set the value of the argument specified in the previous step. Press OK when you are finished. For the second action in the action list, select deleteSupplierDocument from the Select Action dialog (or type DataServices/suppliers/deleteSupplierDocument). This consumes the deleteSupplierDocument operation, passing in the value of the argument that was set in the previous step. Press OK when you are finished. For the third action in the action list, select suppliersView_ShowResults from the Select Action dialog (or type suppliersView_ShowResults). This refreshes the suppliers list after the delete operation has been performed, and then returns the user to the suppliers view. Press OK when you are finished, and then save the builder call. Next, add a Button builder call to your model. The button is placed on the details page of each contact and runs the deleteSupplier action list when clicked (which deletes the currently selected supplier). Fill out the Button builder call, as shown in Figure 4.10. Notice that the Action Type input is set to ‘Link to an action’; this runs the delete operation without submitting the page (submitting the page is undesirable in this case, as this would try to save the current supplier). Also, take note of the contents of the Page Location section of the builder call—the button is placed on the suppliersView_DetailsPage page (that is, the suppliers details page) after the update_button tag. The update_button tag is where the Edit button displays on the page, so the Delete button displays to the right of it.
96
Figure 4.9
Figure 4.10
Chapter 4 Using Domino Data
Selecting the source value.
Configuring the delete button.
Creating a Suppliers Portlet
97
Save the builder call when you are finished. A Delete button is now displayed on the detail page, which lets you delete the current supplier and update the Suppliers.nsf database accordingly.
Adding Create Functionality To add functionality from the createSupplierDocument operation, add a Comment builder call to your model and name the Comment Create. Save the Comment builder call. Then, add an input form for the create function by adding an Input Form builder call. The Input Form builder is used in WPF to enable users to enter in data (an input form is automatically created for the update function, but you need to manually create an input form for the create function). Name the builder call createSupplierForm and change the Input Submit Operation input to DataServices/ suppliers/createSupplierDocument. Change the Input Next Action input to suppliersView_ShowResults. These changes display a form for entering in contact information based on the input schema defined for the createSupplierDocument service operation. When the form is submitted, the createSupplierDocument operation is consumed, and then suppliersView_ShowResults runs. Uncheck the Generate Main input in the Advanced section; this prevents the Input Form from generating another Action List called main (you are already using the one created by the View & Form). The final modification to make to the Input Form is to add a Cancel button to the form so that users can navigate back to the view page when they’ve finished. To do this, enter in the text Cancel into the Input Cancel Text input, and then specify suppliersView_ViewPage for the Input Cancel Action input. This creates a Cancel button with the label Cancel, which returns the user to the suppliersView_ViewPage page when clicked. Save the Input Form when you are finished. By default, the inputs on the input form do not automatically clear when the form is loaded, so if you create two suppliers, the input values are still left over from the first supplier. This is undesirable in this scenario, so you will now add an action list to clear the input form, and then display the input form afterward. This action list is then run by a button you will create later. Add an Action List builder call and name the action list resetAndOpenCreate SupplierPage. The first action in the Action List needs to set all the inputs to null values, and the second action will display the input form. Open the Select Action dialog for the first action and select Special/Assignment from the Select Action dialog. This enables you to assign a source value to a target field. Leave the source blank (because you want to assign a null value) and enter in the parent element of the inputs into the target field, as shown in Figure 4.11. Press OK to accept the selection and then OK again to accept the action. Save the builder call. You now need to add a button to the main view page so that users have a means to call up the input page you just created. Add a Button builder call to your model, and then fill out the Button builder call, as shown in Figure 4.12. The button displays the label Create supplier and shows the input page when clicked.
98
Chapter 4 Using Domino Data
Figure 4.11
Document inputs for the create page.
Figure 4.12
Configuring the create button.
Creating a Suppliers Portlet
99
Notice that you need to change the Action Type input to read ‘Link to an action’ (rather than the default setting ‘Submit form and invoke action’). If you don’t change this input, the submit action is called when you press the create button (which, in this case, causes an error, because the contacts list page doesn’t have a form to submit). Also, notice the location of the button control— at a new HTML tag called createSupplier, which appears immediately before the data tag (the data tag displays the results of the view operation on the contacts service). For more information on placing controls on forms, see Chapter 6. Save the builder call when you are finished.
Removing Unwanted Fields All the functionality the service provider offers is now consumed by the service consumer; however, if you tested the consumer model, you would notice that several unwanted fields (UNID and IsDocumentRow) clutter up the output. To remove these fields, you should add a Data Field Modifier builder call to your project (the use of this builder call is discussed in more detail in Chapter 5). To remove the unwanted fields from your consumer model’s output, add a Comment builder call to your model and name the Comment Modifiers. Save the builder call when you are finished. Add a data field modifier to the suppliers model and name it hideUnwantedFields, and then check the Hide Element box. This removes most of the other inputs on the form and enables you to specify in the Fields input a series of fields that you want to remove. Enter in the UNID and IsDocumentRow fields for each page in your application, as shown in Figure 4.13.
Figure 4.13
Configuring the data field modifier.
100
Chapter 4 Using Domino Data
TIP The Data Field Modifier should be placed after the Input Form and View & Form builder calls, because it modifies the output they create. If you put the data field modifier first, you get a series of warnings in the Problems view stating that the field selectors for each field you want to modify did not evaluate to any fields.
When you’ve finished configuring the data field modifier, save your changes. Your service consumer model is now ready to test and deploy to a portal server.
Testing the Service Consumer Your project now contains a service provider model and a service consumer model. The service provider enables you to access and manipulate the Suppliers.nsf Domino database, and the service consumer provides a user interface for interacting with the operations on that service. In this section, you use a local application server to test that the service consumer can access the service provider, and that it can correctly consume each operation on the service. You then rebuild the portlet application and view your service consumer model as a portlet. To test your service consumer using a local application server, first make sure the suppliers model is the currently active model in your IDE and run the suppliers model from the WPF Designer. After you have run the suppliers model, the suppliers list should appear in your default Web browser, as shown in Figure 4.14. You can change the titles of the columns and specify the names for each column by using another Data Column Modifier builder call (this is discussed in Chapter 5).
Figure 4.14
Testing the suppliers list from the suppliers model.
Click on a supplier name to open the detail page for that supplier. For example, clicking Tile Kingdom should return the page shown in Figure 4.15.
Testing the Service Consumer
Figure 4.15
101
Testing the supplier detail from the suppliers model.
TIP As with the example in Chapter 3, “Using Data from a Relational Data Source,” the list and detail here are surfaced in the same portlet. Sometimes, the list and detail are split into two separate portlets (one for the list and one for the detail), an approach that is discussed in more detail in Chapter 7, “Communicating Between Portlets.”
Click the Edit button to edit the current suppliers details. For example, clicking the Edit button for Tile Kingdom should return the page shown in Figure 4.16. Change some of the suppliers details and press Submit, and you should see the supplier record updated in the suppliers list. Notice that there is no selector for the stockSupplied field, so you need to type in the stock manually. Chapter 16 discusses how to replace the stockSupplied field with a pre-populated select box.
Figure 4.16
Editing a supplier.
Press the Create supplier button from the main suppliers view to open up the create supplier input page. Enter in some new supplier details (such as those shown in Figure 4.17) and press Submit. Note that you don’t need to type a UNID, because this is assigned automatically. The new supplier should now appear in the suppliers list.
102
Chapter 4 Using Domino Data
Figure 4.17
Creating a new supplier.
Open a supplier again by clicking its name in the suppliers list, and then press the Delete supplier button. You should be returned to the suppliers list and see that the supplier was deleted. Close the suppliers model when you’ve finished testing it. You should now rebuild your application on the portal server (for instructions on how to do this, see the example in Chapter 1), after which you can view the suppliers model as a portlet. After you have added the portlet to a page in the portal, it should appear, as shown in Figure 4.18.
Figure 4.18
The Suppliers portlet.
Using a Stub Service In the context of WPF, a stub service is a service containing preset, stubbed data that doesn’t require connectivity to the data source. To build a stub service, you would normally create a copy of your service provider called a stub model. Stub models provide several benefits to the developer: • You can work disconnected from the data source. This often provides more convenient working conditions, and it is usually faster than working when connected to the data source. • You can test WPF applications (including update and create operations) without affecting the data source.
Using a Stub Service
103
• Stub models make it easier for multiple people to perform unit tests on the same data source, because you can specify test values without affecting test values for others. See the “Building a Stub Service” section of Chapter 2 for more general information on stub models in WPF. In this section, you create a stub model based on the suppliersService model. You then point the suppliers model to the stubbed model instead of the original suppliersService model, which enables you to test the suppliers model when you don’t have access to the Supplier.nsf database.
TIP It is recommended that you specify default values for the operations that require arguments, because this causes the sample data for these operations in your stub service to use real data from the data source for their results.You can set default values for operations from the Service Definition builder call by checking the Specify Default Inputs box in the Testing Support section.
Creating a Stub Model To create a stub model for the suppliersService service, open the suppliersService model in the WPF Designer, and then open the Service Definition builder call. Expand the Interface and Stub Models section and type in suppliersStub in the Stub Model input. Press the Generate Stub Model button to create a new stub model called suppliersStub, based on the suppliersService model (this could take a minute or so, depending on the speed of your machine). You can save the builder call, which keeps the name of the stubbed model if you want to recreate it. Open the suppliersStub model by double clicking on the suppliersStub model in the Project Explorer view. Spend a few minutes going over the builder calls in the suppliersStub model. Notice that the five service operations from the suppliersService model have been retained, and each operation now has a corresponding Method builder call. These method calls are run from the service operations and take the place of the Domino calls in the suppliersService model. If an operation returns results, these results are retrieved from a Variable builder call named after the operation. All the operations have corresponding Variable builder calls, with the exception of the deleteSupplierDocument and updateSupplierDocument operations (because they don’t return any values). Notice that the Variable builder calls are all in XML and are based on schemas specified in Schema builder calls displayed at the top of the Outline view. You shouldn’t change the schemas because they are based on the Domino data retrieved from the Domino Data Access builder call, although the Variable builder calls can be freely modified to test different scenarios for your application.
104
Chapter 4 Using Domino Data
By default, the suppliersStub model doesn’t have a main action list, so it can’t be run directly from the IDE (although, you can link to it from your service consumer). It is sometimes useful to run the stub model directly (such as when you are changing variable values and you want to see whether you have entered them correctly), so if you want to do this, open the Service Definition builder call in the suppliersStub model, and scroll down to the Testing Support section at the bottom of the builder call. Check the Add Testing Support box, which automatically checks the Generate Main box. This generates test pages for your stub model and enables you to run the model directly from the WPF Designer. Click the icon on the toolbar to test the stub model using your default browser. You should see the same index page as the one displayed for the ordinary suppliersService service model and be able to run each service operation as you did there (except you are now working with dummy data rather than Domino data).
Using the Stub Model There are two ways to use the stub model from your service consumer. The first is to open the suppliers model and then open its Service Consumer builder call. Change the Provider Model input to chapter04/suppliersStub and save your changes. The suppliers model now uses the suppliersStub model as its service provider, and you don’t need to change anything else in the model. The second method is to use a service mapping registry so that you don’t have to modify the models themselves. To do this, create a copy of the WEB-INF/config/service_mappings/mappings.xml.example file called suppliersMappings.xml, and paste it into the same directory. This file contains settings for mapping services in your project, but is not used by default as it doesn’t have an XML extension. The file includes examples for mapping different parts of your project. Modify the contents of the file so that it reads as follows: <ServiceMappings> <UseStub />
Save the file when you are finished, and then close and reopen the suppliers model to make sure it is using the latest service mappings. This automatically uses the stub services in your service consumers so that you can work disconnected from the Domino server. If you want to use the ordinary service again, simply comment out the ForAllServices elements in the service mapping file (or, change the extension of the file to something other than xml). You have now successfully created a stub model for the suppliersService model. Note that if you intend on completing any of the extensions to the suppliersService model covered in Chapter 16, you should switch the Provider Model input on the Service Consumer builder call back to suppliersService, because in Chapter 16 you will add new operations to the service (if desired, you can stub these new operations after they have been created).
Important Points
105
Summary In this chapter, you learned how to connect to a Notes database from a WPF portlet and how to provide create, read, update, and delete functionality for Domino data. The application built in this chapter provided this functionality via a service provider model and service consumer model. The content of this chapter provides only a basic introduction to what you can do with Domino from WPF. Using WPF, you can also do such things as run Notes agents, evaluate formulas, and display categorized views from your portlets. For more information on using Domino from WPF, see Chapter 16. The next two chapters discuss how to work with the presentation of your data. Chapter 5 focuses on how to lay out data on the page—adding, removing, and moving data, in addition to changing how it is displayed. Chapter 6 focuses on how you can add extra controls to the page, which add functionality and improve the overall appearance and usability of the application.
Important Points • You need to use the Lotus Collaboration Extension feature set to get access to the Domino builders in WPF. • The Runtime Credentials field on the Domino Data Access builder can be used to control how WPF authenticates with Domino. • WPF projects contain artifacts for automatically testing connectivity to Domino and testing operations on the Domino service provider. • When you save a Notes document from WPF, the inputs are validated based on the validation formulas on the Notes form. This can be turned off via the setComputeWithFormEnabled method of the Domino Data Access builder. • Stub models are useful for testing services because they don’t require data source connectivity. They can be created from the Interface and Stub Models section of the Service Definition builder.
This page intentionally left blank
C
H A P T E R
5
Customizing Portlet Appearance
WebSphere Portlet Factory (WPF) provides several techniques for customizing marked-up content in a portlet. This chapter focuses on the various ways in which these techniques can be used, and, by the end of the chapter, you will have a good knowledge of when and how they should be used. To demonstrate these techniques, you will also build a simple data portlet that displays a list of assets for an asset management system. The portlet enables users to drill down in the list to get more detail on each asset. It is not possible to update asset information in the example, although the techniques discussed in this chapter could be applied just as easily to update functions as to display functions. The example application discussed in this chapter utilizes one model and two HTML files, which are available for download from ibmpressbooks.com/title/9780137134465, under the Chapter 5 folder (instructions for copying these files into your project are included in a readme. txt file in the same folder). However, to increase your understanding of the topics discussed in this chapter, it is recommended that you create these files yourself by following through the example. You need to build the test portlet in the first two sections of this chapter before any of the other sections can be completed; however, the rest of the sections in this chapter can be completed in any order, and you can skip sections if you are already familiar with the techniques they discuss. The following topics are covered in this chapter: • Customizing portlet appearance
• Data modifiers
• Creating a service provider
• Working with Web pages
• Creating an assets portlet
• Cascading style sheets
• Pagination
107
108
Chapter 5 Customizing Portlet Appearance
Customizing Portlet Appearance In addition to traditional methods of customizing the appearance of Web content, such as writing and editing HTML pages, JSPs, and style sheets, WPF also provides a series of prepackaged builders that help automate the process of shifting around and manipulating marked-up portlet content. These builders provide a robust, wizard-based interface for laying out portlet pages, while letting you leverage more traditional Web artifacts like HTML pages and JavaScript code. Web artifacts can be linked to WPF builders via inputs on the builders themselves (such as the inputs on the View & Form builder that designate HTML templates), so most WPF developers use a combination of traditional Web artifacts and WPF builders to produce appropriate interfaces for their applications. Some builders deal specifically with the arrangement of data on a page, and several highlevel builders encapsulate functionality from both the presentation tier (components controlling the appearance of a portlet) and model tier (the data and business logic) of an application. Wherever possible, it is recommended that you use the builders that do not mix the presentation tier with the model tier. This enables you to use the service provider/consumer pattern in your applications, which loosen the coupling between your data and your front end and ultimately make your applications easier to maintain (for more information on the service provider/consumer pattern, see Chapter 2, “Providing and Consuming Services”). The example in this chapter uses the service provider/consumer pattern to take some basic asset data for an asset information system and surface it in a portlet. The first two sections of this chapter focus on constructing the asset portlet using the View & Form builder, and the remaining sections focus on how the UI of this portlet can be extended with other WPF techniques. The View & Form builder used in this chapter provides a useful and powerful starting point for the interface in the example, because the sample data is based on a list and detail that the View & Form is perfectly suited to (for more information on the list and detail components of a portlet, see Chapter 2). However, note that the View & Form is just one way of creating an interface for your data. For example, the Data Page is another commonly used alternative to the View & Form and is particularly useful for displaying XML data (several sample applications in this book use the Data Page; see, for example, Chapters 7, 9, 11, and 12). For more information on some of the different data display techniques available in WPF, see the sidebar “Display Data in WPF” later in this chapter. Also, the Rich Data Definition is an important and powerful builder commonly used to control the display of your data and, in particular, to define formatting, validation, and translation for fields. The use of the Rich Data Definition is discussed in Chapter 11, “Field Validation, Formatting, and Translation.”
Creating a Service Provider To provide data to your assets portlet, you should create a service provider model. This model contains a list of asset information in an XML document and is retrieved from the test portlet you create in the next section. One operation is used, which retrieves the list data. The detail data is extracted from this by the assets portlet.
Creating a Service Provider
109
Before you proceed with this section, you need a WPF project to house your service provider model and the assets model you will create later. If you have a project from a previous chapter, you can use that; otherwise, you should create a new WPF project. For more information on creating projects, see Chapter 1, “Introduction to WebSphere Portlet Factory.” The project is published as a WAR file and deployed to a portal server, and you should also deploy the application to a local application server for testing (you can use the portal server if it is running on your local machine. Otherwise, it is recommended that you use the IBM WebSphere Application Server Community Edition server (WAS CE) that comes with WPF. After you have a project set up, you need to add the service provider model. The service provider uses the following builders to provide its functionality: • Service Definition • Schema • Variable • Action List • Service Operation They are built and discussed in this order. Note that although the order of builder calls is usually not important, occasionally, the order of your builder calls can create errors or warnings in your application. In this example, the Service Operation builder call needs to follow the Schema, Action List, and the Service Definition builder call, or you get errors claiming that there are missing resources.
Creating a Model Create a new model called assetsService in your project under the folder WEB-INF/models/ chapter05. Because you will store your data in an XML variable rather than getting it from a database, you need to create the service provider manually. To do this, the model should be based on the Empty template, which creates a model with no builder calls in it. For more information on creating models, see the example in Chapter 1.
Defining the Service Add a Service Definition builder call by selecting Service Definition from the Builder Palette (you can open the Builder Palette by clicking the icon in the Outline view) and pressing OK. Name the Service Definition assetsService and check the Add Testing Support box in the Advanced section. This lets you run the model and test whether your service operation is working correctly. Save the builder call when finished.
Adding an Asset Schema The next builder call to add is a Schema, which defines the asset data returned from the service. Schemas are useful for outlining the structure of your data sets, and without one you won’t be
110
Chapter 5 Customizing Portlet Appearance
able to use the Row Details Support section of the View & Form builder call (which displays the detail portion of your portlet). Add a Schema builder call and name the schema assetsSchema. Select ‘Specify explicitly as builder input’ for the Schema Source Types input. Next, enter in the following XML into the Schema Source input. Note that this XML is also available for download from ibmpressbooks. com/title/9780137134465 under the Chapter 5 folder: <xsd:schema xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:tns=”http://wpf.ibm.com/2002/10/assetsSchema” targetNamespace=”http://wpf.ibm.com/2002/10/assetsSchema”> <xsd:element name=”Assets”> <xsd:complexType> <xsd:sequence> <xsd:element ref=”tns:Asset” minOccurs=”1” maxOccurs=”unbounded” /> <xsd:element name=”Asset”> <xsd:complexType> <xsd:sequence> <xsd:element ref=”tns:Name” minOccurs=”1” maxOccurs=”1” /> <xsd:element ref=”tns:Type” minOccurs=”1” maxOccurs=”1” /> <xsd:element ref=”tns:Description” minOccurs=”1” maxOccurs=”1” /> <xsd:element ref=”tns:Location” minOccurs=”1” maxOccurs=”1” /> <xsd:element name=”Name” type=”xsd:string” /> <xsd:element name=”Type” type=”xsd:string” /> <xsd:element name=”Description” type=”xsd:string” /> <xsd:element name=”Location” type=”xsd:string” />
Save the builder call when finished.
Adding Asset Data Create some test data based on the structure defined in the assetsSchema. To do this, add a Variable builder call to the model and change the name to assets. Open the Choose Variable Type dialog
Creating a Service Provider
111
for the Type input by pressing the ellipsis button to the right of the input and select the assets element in the schema you just created (assetsSchema/assets). Press OK to accept the variable type. You should now see that the Namespace input has been filled in with the namespace from your schema. Now, enter the following XML into the Initial Value input and save the builder call when finished. Note that this XML is also available for download from ibmpressbooks.com/title/ 9780137134465 under the Chapter 5 folder. This XML defines four sample assets based on the assetsSchema schema, which you can use for testing the techniques discussed in this chapter. Subcontractor AgreementContractEmployee contract for subcontractors.Z:\Documents\Contracts\SubContractorAgreement.docWebSphere Portlet Factory 6.0.2SoftwareWebSphere Portlet Factory install files.Z:\Software\IBM\WPS6.0.1Francis Millingtons resumeResume Francis Millingtons latest resume. Z:\Documents\Resumes\FrancisMillingtonsResume.doc Bob Hines’ resumeResumeBob Hines’ latest resume.Z:\Documents\Resumes\BobHinesResume.doc
112
Chapter 5 Customizing Portlet Appearance
Adding an Action to Retrieve a List of Assets Add an Action List builder call to the model and name it getAssetsList. This action list returns the contents of the assets variable you just created and is called from the Service Operation builder call you will create next. However, you need to create the action list first or you get errors in your project indicating missing resources. Change the Return Type of the builder call to IXml. (IXml is a WPF interface used to manipulate XML and is used because there is no XML data type in WPF as such. For more information on the IXml interface, see Chapter 9, “Using Web Services and Manipulating XML.”) Then, open the Select Action dialog for the first action in the Actions input. You can do this by pressing the ellipsis button to the right of the first row in the Actions input. From the Select Action dialog, select Special/Return to return a value to the caller of the Action List, and then select the assets variable (Variables/assets/Assets). Press OK to accept the action, and then save the builder call when you’ve finished.
Specifying the getAssetsList Operation The final builder call to add to the service provider is a Service Operation. Add the Service Operation builder call now and name it getAssetsList. Point the Data Service input to the Service Definition builder call created earlier (assetsService) and specify the Action To Call as getAssetsList (this is the action list you just created). You must specify the inputs and outputs for the Service Operation. To do this, expand the Operation Inputs section and select ‘No inputs’ for the Input Structure Handling input (because no inputs are required; the operation just needs to return the contents of the assets Variable). For the Operation Results section, select ‘Specify result schema’ for the Result Structure Handling, which lets you specify the schema of your results, and then select the Assets element of the assetsSchema schema for the Result Schema input (assetsSchema/Assets). Make sure the Result Field Mapping input is set to Automatic. This specifies the structure of the results returned from the operation and makes sure that the results from the getAssetsList action are automatically mapped into this schema.
TIP The ‘Use structure from called action’ option in the Operation Inputs and Operation Results sections take the structure of the data from whatever action you have specified in the Action To Call input. You can only use this option when the structure is either not XML or is an XML document where the schema is explicitly specified. In the example, you have specified IXml as the return type for the action list, so the schema is not explicitly specified.
Your service provider is now ready to test.
Testing the Service Provider
113
Testing the Service Provider
Because you have opted to add testing support in the Service Definition builder call, WPF will automatically generate test pages for your service provider, which you can disable from the Testing Support section of the Service Definition builder call. This enables you to test the service provider directly from the IDE. To test the service, run the assetsService model from the WPF Designer by clicking the icon on the toolbar. This runs the currently active model (for instance, assetsService) with the last run configuration you used. If you have not set up a run configuration before, you are prompted to do so—create a new configuration under the WebSphere Portlet Factory Model category (if you want more information on setting up a run configuration, see the “Testing the Application” section in Chapter 1). After you run assetsService, you should see the index test page in your default Web browser, as shown in Figure 5.1. This screen lists all the operations available on the assetsService service.
Figure 5.1
Index test page from the assetsService model.
To test the getAssetsList operation, click the getAssetsList link. You should see a list of assets, as shown in Figure 5.2. If not, check that you don’t have any errors showing in the Problems view in the IDE and that you have completed all the steps in the previous section correctly. Press Back to return to the index page.
Figure 5.2
Testing the getAssetsList operation.
114
Chapter 5 Customizing Portlet Appearance
Close the assetsService model when you’ve finished testing it. You now have a service provider called assetsService, which provides a list of asset information.
Creating an Assets Portlet In this section, you build an assets portlet to access the assetsService service, which you can use to experiment with some of the different techniques available for customizing the appearance of information in a portlet using WPF. The data used in the example is retrieved from an XML document retrieved from the service consumer (a screenshot of the portlet is shown later in Figure 5.6). The model uses the following builders to provide its functionality: • Portlet Adapter • Service Consumer • View & Form
Creating a Model Create a new model called assets in your project under the folder WEB-INF/models/chapter05. To do this, the model should be based on the Empty template, which creates a model with no builder calls in it. For more information on creating models, see the example in Chapter 1.
Adding a Portlet Adapter The first builder call to add is a Portlet Adapter, which surfaces your model as a portlet when the application is deployed to a portal server. Add a Portlet Adapter builder call now, name it assets, and then change the Portlet Title to Assets and the Portlet Description to This portlet displays a list of asset information and enables you to view details on each asset. This surfaces the assets model as a portlet called Assets. Save the builder call when
finished.
Consuming the Service Add a Service Consumer builder call to the model, which consumes the assetsService service provider you created in the previous section. Name the Service Consumer assets and point the Provider Model input to chapter05/assetsService. Save the builder call.
Displaying Assets Data You need a way to display the assets data in the portlet. In the present example, the View & Form builder is used, because it will provide functionality for drilling down on a row in the data set (that is, the detail). Add a View & Form builder call to the model. The View & Form builder call provides options for displaying information in a variable based on the results of a specified Java method. In this case, the method is the getAssetsList operation on the assets service. Name the builder call
Creating an Assets Portlet
115
assetsDataViewAndForm and bring up the Select Action dialog for the View Data Operation input. Select getAssetsList from the DataServices/assets category and press OK. You can leave the default values for the View Page HTML and HTML Template File inputs—these are discussed later in the “Working with Web Pages” section of this chapter. You have now defined the list portion of your data—the list of assets—but have not specified how the details for each asset should be accessed. To do this, check the Create Link To Details box in the Row Details Support section, and then fill out the Row Details Support section, as shown in Figure 5.3. This enables you click on the Name of an asset to bring up that asset’s details. These details are taken from the currently selected asset in the assets XML variable returned from the service provider. Save the builder call when finished.
Figure 5.3
Configuring the Row Details Support.
For more information on the View & Form builder call, see the following “About the View & Form Builder” sidebar.
ABOUT THE VIEW & FORM BUILDER Displaying data from a backend data source in WPF is normally achieved via the View & Form builder. After you have a link to a data source through a service provider, you can then use the View & Form builder to provide an interface for users to interact with the data source via the service. The View & Form builder is particularly useful for interacting with data in a list and detail format, and, because this builder enables you to interact with a service provider in another model, the View & Form builder also encourages a service-oriented approach to your applications. The View & Form builder is split up into several sections. The first section, View Page Options, contains inputs for configuring the list portion of your data. You designate the variable that contains your data in the View Variable input, and then specify a method to retrieve the list information from this variable in the View Data Operation input. The remaining inputs
116
Chapter 5 Customizing Portlet Appearance
in this section let you paginate the results and specify HTML files that define how the results are displayed. The View Page HTML input defines the base page for displaying view data, which must contain a tag named data. The contents of this tag is replaced by WPF based on a design specified in the HTML template defined in the HTML Template File input. HTML templates are covered in the “Working with Web Pages” section of this chapter. The Input Page Options section enables you to create an input page for entering in parameters for the view method, which is specified in the View Page Options section. You can also specify various configuration options for the input page, such as defining an HTML base page and template and enabling schema validation. The Row Details Support section contains inputs for configuring detail data by providing a page that displays details for a row when a link for that row is clicked. Check the Create Link to Details box to enable this section. Note that to use this section, your variable must be defined by a schema (otherwise, you get errors showing up in the Problems view). You don’t have to explicitly create a schema if you’re working with a variable retrieved from a WPF data service because in these cases, WPF automatically creates the schema for you. The Details Link Column input in the Row Details Support section specifies the column that users can click on to obtain detail information, and the Details Link Text input lets you override the values in this column with custom values. You can also specify an HTML base page and template and control how the results are obtained (for example, from a service operation or directly from the currently selected row in the data set).You can also specify text for a Back button, which takes the user back to the list page when clicked. The Update Page Support section lets you configure options for an update page for your data, which lets users edit your data. To use this functionality, a method must be available to handle the logic that is run when the page is submitted. These methods are normally created in service providers.You can also specify post- and pre-update logic in this section and declare an HTML base page and template to manipulate the appearance of the form. The Enable Update Validation checkbox lets you enable schema-based validation, and the last three inputs enable you to change the text of the navigation buttons used on the update form. The Label Translation and Sample HTML section lets you define a resource bundle for the View & Form, which you can use to dynamically set field labels. You can also create sample HTML pages from this section, which enable you to preview what the view (or list) page and detail page will look like. These sample pages are a good place to start if you want to modify the base HTML pages used by the View & Form builder. For example, if you generate a sample View Page, WPF creates a page for you that has all the tags that the page depends on. You can then rearrange these tags to your liking and use the edited page in the View Page HTML input so that it is the base page for the list portion of your data. Finally, the Advanced section lets you generate a main action list. A model with an action list called main is executable as a portlet or standalone Web application. The main action list created by the View & Form builder calls a method to retrieve the data that is displayed for the list portion of the builder, and then displays that data to the screen. Note that the main action list created by the View & Form builder is not actually visible in the Outline view, although you can see it in the WebApp Tree tab of the Model Editor (under the Methods section).
Creating an Assets Portlet
117
You now have an assets model that you can use to experiment with the techniques described in the remainder of this chapter. The next section explains how to test the assets portlet.
Testing the Assets Portlet To test the assets portlet, first run the assets model from the WPF Designer. After you have run the assets model, you should see a list of four assets in your default Web browser, as shown in Figure 5.4.
Figure 5.4
Testing the assets model.
Click on the name of an asset to test whether the detail component of the View & Form builder is working. For example, if you click on Bob Hines’ resume, you should see the output in Figure 5.5. The detail portion of your data normally contains more fields than that displayed in the list portion; however, in this case, the same amount of fields are displayed in both. The “Data Modifiers” section of this chapter discusses how you can remove fields from the list and detail portions of your portlet.
Figure 5.5
Displaying detail information for Bob Hines’ resume.
Note that you can and should test your application regularly as you progress through this chapter, especially at the end of each section. Aside from enabling you to see the output your builders produce, this also makes it easier to troubleshoot any errors that arise. Be aware, however, that if you test in the middle of a section, you may get errors related to missing components that haven’t been built yet. The text prompts you to test your application at opportune testing points. After you have tested the assets model, you should rebuild your application on the portal server (for instructions on how to do this, see the example in Chapter 1). Then, you can view the
118
Chapter 5 Customizing Portlet Appearance
assets model as a portlet on the portal server. After you have added the Assets portlet to a page in the portal, it should appear similar to Figure 5.6.
Figure 5.6
The Assets portlet.
DISPLAYING DATA IN WPF When dealing with list and detail data, the View & Form builder is an ideal way to display the data because it comes with numerous options specifically catering to a list and detail. The View & Form builder can also be used as part of a service-based design (that is, when your data is available via a service provider and accessed from a service consumer) because it doesn’t mix the presentation of your application with your data. The View & Form builder simply links to data available in the model; it doesn’t define where this data comes from or how it should be retrieved. The View & Form builder is discussed in detail in the later section titled “View & Form.” However, several alternatives are available that might be more suitable in certain circumstances. This section describes some of the more commonly used alternatives for displaying data in WPF. The Data Page Builder—The Data Page builder is the primary alternative to the View & Form and maps data from a variable or XML schema onto an HTML page. The interfaces for both builders are similar, and if you understand one, you can understand the other. The Data Page does not provide functions for a list and detail per se, but it does enable you to implement both read and edit functions for a dataset. You would use a Data Page builder instead of a View & Form for the Data Page-specific functionality that the View & Form doesn’t expose, such as more control over field validation, or if your data was not in the form of a list and detail. As a result, data pages are particularly useful for displaying input pages or results pages. For example, you might define the fields for a form in an XML schema, and then display these inputs on an HTML page using a Data Page builder. The Data View Builder—The Data View builder is an older builder included for backward compatibility. It is not recommended that you use this builder, because it mixes the presentation tier of your application with the model tier (that is, the data access and the user interface are all controlled in the same builder).
Pagination
119
The Domino View & Form Builder—The Domino View & Form builder combines the functionality of the View & Form builder and a builder used to interface with Domino data called the Domino Data Access builder. The Domino View & Form builder makes it easier to create an interface for accessing and manipulating Domino data and provides additional functionality (such as automatically building a create page for your data). However, the Domino View & Form builder also tightly couples the presentation of your portlet to the data it displays because they’re both defined in the same builder, so you can’t provide the data via a service. Ideally, you should be able to change the data tier in your application without necessarily affecting the presentation tier, because this makes your applications easier to maintain. Therefore, it is recommended that you use only the Domino View & Form builder for testing purposes and not deploy models containing this builder into production environments. In production environments that communicate with Domino, a service-based approach using a combination of the View & Form builder and Domino Data Access builder should be preferred. An example using this approach is given in Chapter 4, “Using Domino Data.”
The remainder of this chapter discusses the various options available in WPF for arranging information in a portlet.
Pagination The process of viewing large numbers of records and documents in a portlet is made easier with the use of automated pagination features in WPF. Using WPF, you can automatically convert long lists of data into separate pages, which can then be cycled through using buttons at the bottom of each page. Each page is viewed by reloading the list page of your data set, except only a certain part of the data is displayed. In WPF, pagination is implemented using a paging assistant. A paging assistant provides several useful methods for paginating data, such as returning page counts, and can be linked to from other WPF builders to do such things as placing navigation controls on the screen. A paging assistant can be explicitly added (using the Paging Assistant builder) or added automatically using one of the builders with support for pagination, such as the View & Form builder. If you are using the View & Form builder (as in the present example), the easiest way to implement pagination is to check the Paged Data Display box of the View & Form builder. This reveals another input called Rows per page, which you can use to configure the size of each page, and it also automatically gives you access to a paging assistant in your model. You can cycle through each page using several buttons automatically created by WPF. Figure 5.7 shows an example of what the default pagination controls look like using the View & Form builder call in the assets application. Although the View & Form builder already provides some pagination capabilities, you can customize these by using other WPF builders. The next example shows you how you can do this in the assets application.
120
Chapter 5 Customizing Portlet Appearance
Figure 5.7
Testing the pagination capabilities of the View & Form builder.
The following builders are added in this section: • Visibility Setter • Paging Buttons
Turning on Pagination Turn on pagination in the View & Form builder call as just described and set the page length to 3, which causes your data to appear in pages of three records (you don’t have many records in your data set, so if you set it to more than three, you won’t be able to see the pagination in use). Save the builder call when finished.
Modifying the Paging Buttons If you preview your application now, you should see the output shown in Figure 5.7. Using the View & Form, you don’t have much control over the navigation controls, so to modify these controls, you should replace the default buttons using a Paging Buttons builder call. A Paging Links builder is also available, but buttons are used in this example. Before you do this, however, you can hide the navigation controls created by the View & Form by using a Visibility Setter. Visibility Setters can be pointed to a particular tag on an HTML page and hide the contents of that tag based on a condition you specify. Add a Visibility Setter builder to your model and call it hideView&FormNavigation. Point the Page input to the view page (hideView&FormNavigation) and the Tag input to the navigation controls used by the View & Form (paging_buttons). Note that you can find out the names for tags used by WPF builders if you switch to the WebApp Tree tab in the Model Editor and navigate through the Page section. In this example, you want the navigation buttons to be always hidden, so make sure the Visibility Rule input is set to Always Hide. Save the builder call when finished. You no longer have any navigation controls in your portlet, so add a Paging Buttons builder to your model to regenerate these buttons. Fill out the inputs, as shown later in Figure 5.8. Notice the pagingAssistant created by the View & Form is referenced in the Assistant Name input, which specifies the pagination assistant used by the buttons. Also, note that the buttons are inserted after the data tag, which is used by the View & Form to place the view results onto the page. Save the builder call when finished.
Pagination
Figure 5.8
121
Configuring the Paging Buttons builder call.
WARNING The Paging Assistant shouldn’t be used in conjunction with the Category View builder. The Category View builder is intended as an alternative to pagination, and if you are using the Category View, you need to change the structure of your view so that its contents can fit within a single portlet page. By default, the Check For New Data and Preserve Location checkboxes are marked. This will mean that every time data is fetched from the source variable, a check is performed to see if it has changed. If it has, the displayed data is updated. Also, whenever data is updated, the current page location is retained, rather than being reset to zero.
The Paging Buttons builder contains several sections that let you customize the appearance of the buttons as they appear on the page. Modify the Range Text Format input so that it reads Page {page} of {page_count}, and then insert the text ‘Go to’ (with a trailing space) into the Alt Text input under the First Button Images, Prev Button Images, Next Button Images, and Last Button Images sections. Save the builder call when finished. You have successfully configured pagination in the assets application. Now, if you test the assets application, you should see the pagination controls shown in Figure 5.9. Notice that if you hover the mouse over each navigation button, ‘Go to’ appears at the start of each help text pop-up message.
122
Chapter 5 Customizing Portlet Appearance
TIP When you paginate data from a backend data source, you don’t necessarily want to refresh the data set from the backend every time you change pages, because it slows down the portlet. To stop the portlet from checking the backend for changes on each page refresh, uncheck the Check for New Data box on the Paging Assistant builder (although note that with this setting disabled, the display is refreshed only when the portlet is loaded). If you’re using a Service Operation builder call, you can also restrict the data (records or documents) retrieved in each fetch so that only the data required for the current page is retrieved. To do this, select Page Enable for the Page Enable Options input in the Paging Options section of the Service Operation builder. Note that if you’re using Domino data, you can also fetch paginated data by checking the Fetch Subsets of Data box in the Advanced section of the Domino Data Access builder.
Figure 5.9
The assets model with added pagination controls.
Data Modifiers To modify how data appears on a page, WPF provides numerous builders, commonly referred to as data modifiers. These modifiers are discussed next. An example at the end of this section demonstrates how you can use these modifiers in the assets application.
The Data Column Modifier Builder The Data Column Modifier builder is arguably the most useful builder for manipulating the appearance of columns in a table. Using the Data Column Modifier, you can reorganize columns; hide columns; add new columns, including a counter column, which shows the number of each row in the data set; change column widths and headings; and specify sortable columns. You can also specify an HTML template for the table itself, and you can even paginate the data in the table, although the pagination options available are more limited than those offered by the Paging Assistant, Paging Buttons, and Paging Links builders.
Data Modifiers
123
TIP
Using WPF, you can configure pagination in numerous places. However, all these places use the same pagination mechanism, so it doesn’t matter which one you decide to use. However, as a general rule, you should specify paging in one place, and ideally specify only one layer of navigation controls, as pagination settings defined in more than one place can lead to problems. Also, although it is technically possible to use multiple layers of navigation controls (such as when you use the Paging Buttons and Paging Links builders together), it can be difficult to maintain and use.
Figure 5.10 shows some sample settings for the assets view columns in the assets portlet.
Figure 5.10
Example view column settings.
The Add Counter Column checkbox adds a column to the page displaying a counter, and the Add Columns input lets you add extra columns not included in the data set referenced by the builder. The Manage Columns input provides settings for each column as seen in the table at the bottom of Figure 5.10. These columns are explained briefly: Column Name—The name of the column as it occurs in the data set. Status—Whether the column is displayed or hidden. Column Heading—The heading label for the column. Column Width—The width of the column in pixels. Alignment—How the column data is aligned (left, right, center, or default, which makes no adjustment to how column data is displayed). Column Sort Type—Whether the column is sortable. In addition to these configuration options, the Data Column Modifier also contains a ‘Settings for Add and Delete Row Operations’ section. This section lets you generate a delete button
124
Chapter 5 Customizing Portlet Appearance
for each column and create methods for adding and deleting rows. Note that these methods only add and remove entries from the variable displayed. If you want to update a data source, you either need to use existing methods from other builders or write your own. The example at the end of this section describes how you can use the Data Column Modifier to manipulate the appearance of the assets view in the assets portlet.
The Data Hierarchy Modifier Builder The Data Hierarchy Modifier builder is used to group data on a page. In the example at the end of this section, you see how you can sort the fields on the assets detail page into two separate sections: Basics and Details. As with the Data Column Modifier, you can also reorganize, rename, and hide elements on a page.
The Data Field Modifier Builder The Data Field Modifier enables you to manipulate the appearance and behavior of individual fields on a form, and it gives you much more control over field properties than what you have with the Data Column Modifier and Data Hierarchy Modifier. Although, with the exception of being able to specify HTML templates, you can’t modify the layout of fields using the Data Field Modifier. The Data Field Modifier builder is split up into several sections, which are discussed below. Aside from the Properties section common to all WPF builders, which lets you enable or disable the builder, enter comments, and specify a sorting category, the top part of the Data Field Modifier (shown in Figure 5.11) enables you to specify one or more target fields for the builder to which all the inputs in the builder apply. You can then generate labels for these fields and flag them as hidden or repeating fields if desired (if you leave these inputs set to ‘Do not change,’ the underlying settings of the field is used).
Figure 5.11
Data Field Modifier builder settings.
Data Modifiers
125
The next section of the builder is either Field Settings or Container Settings, depending on the type of target fields specified. The Field Settings section lets you specify a lookup table for the target fields and designate whether the target fields are data entry fields, view only fields, or action fields. Action fields are fields that can perform an action when you click on them, such as opening a page or running a script. The type of field determines the remaining options in this section—for example, Figure 5.12 shows the Field Settings section for a mandatory, user-editable text input box. You can also use the Field Settings section to specify whether the user is required to enter a value for the field when it is part of an input form and set a default value for the field when the model is first loaded for each user.
Figure 5.12
The Field Settings section of the Data Field Modifier.
The Container Settings section (shown in Figure 5.13) lets you add HTML controls and labels for the contents of the container, in addition to control the direction that the contents display in. You can also use the Container Settings section to enable paging for the contents. Note that this section displays only if the fields specified in the List of Fields input are containers (that is, they contain child fields).
Figure 5.13
The Container Settings section of the Data Field Modifier.
The Formatting section displays only if you have individual fields specified as target fields, and it lets you specify their validation, translation, and formatting settings. Figure 5.14 shows an example specifying that target fields are formatted as dates. Manipulating field formatting, validation, and translation formulas is covered in Chapter 11.
126
Figure 5.14
Chapter 5 Customizing Portlet Appearance
The Formatting section of the Data Field Modifier.
The next section is the Attribute Settings for Fields section, shown in Figure 5.15. As with the Formatting section, this section shows only if you have individual fields specified as target fields. You can use this section to specify HTML attributes for the target fields, such as class and width.
Figure 5.15
The Attribute Settings for the Fields section of the Data Field Modifier.
The HTML Class Settings section lets you specify an HTML template for the fields, as shown in Figure 5.16. HTML templates are discussed in the “Working with Web Pages” section of this chapter.
Figure 5.16
The HTML Class Settings section of the Data Field Modifier.
Data Modifiers
127
The Form Layout Builder The Form Layout builder enables you to create newspaper-style columns for your detail pages. This gives you added flexibility in how you organize your data, but should be used with caution— keep in mind that portlet interfaces are normally small, and if users have to scroll in a Web browser, they are more accustomed to scrolling vertically than horizontally. The following example shows how you can use all four of the builders in the assets application. The following builders are added in this section: • Comment • Data Column Modifier • Data Hierarchy Modifier • Data Field Modifier • Form Layout
Adding a Data Column Modifier Add a Comment builder call to the model and name it Data Modifiers. This Comment will make it easier to organize the builders in your model. Save the Comment builder call when finished. Now, add a Data Column Modifier builder call to the assets model. In this example, you use the Data Column Modifier to modify the appearance and functionality of the view page. Press the ellipsis button next to the Container Field input, which opens the Data Field Page Chooser dialog. Select the asset row from the view page, as shown in Figure 5.17. Some extra inputs now appear on the Data Column Modifier builder, which you can use to customize each asset row.
Figure 5.17
Selecting the Container Field.
Scroll down to the bottom of the ‘Settings for Add and Delete Row Operations’ section, where the columns on the view page are listed. Change the Status column for the Description and
128
Chapter 5 Customizing Portlet Appearance
Location fields to ‘Hide’, which hide these columns from the view page. Users can still see this information when they open an asset’s detail page. Change the Column Width columns for Name and Type to 200 and 60 respectively, which set the width of these columns to a fixed number of pixels. (Note: The numbers in this column do not represent the numbers of characters in a field, so they are constant no matter what fonts and font sizes are used.) Setting column widths keeps the table width constant when switching between pages. If you don’t specify these settings, column widths automatically adjust depending on the length of the values in the columns. Now, change the Column Sort Type column for the Name and Type columns to ‘Case Insensitive String’. This converts the column headings into hyperlinks. Users can now sort the information in the view in ascending/descending alphabetical order by alternatively clicking the header links. The columns in your Data Column Modifier should now appear, as shown in Figure 5.18. Save the builder call when finished.
Figure 5.18
Configuring the Data Column Modifier builder call.
Adding a Data Hierarchy Modifier Add a Data Hierarchy Modifier builder call to your model. In this example, you use the Data Hierarchy Modifier to split up the assets detail form into two sections: Basics and Details. Press the ellipsis button next to the first row in the Fields input, which opens the Data Page Field Input dialog. Select the Asset for the details page, as shown in Figure 5.19, and then press OK. The box at the bottom of the Data Hierarchy Modifier is now populated with the fields on the asset detail form. Type in Basics,Details in the New Group Names input, and then press the Enter key on your keyboard. This creates two new sections in the field list at the bottom of the screen, which you can use to organize your data. Each group has a start and end tag, and everything within these tags is contained within that specific group. Drag and drop the start and end tags so that they appear, as shown in Figure 5.20, and then save the builder call.
Data Modifiers
129
Figure 5.19
Selecting the Asset for the details page.
Figure 5.20
Configuring groups for the details page.
Adding a Data Field Modifier Add a Data Field Modifier builder call to the assets model. In this example, you use the Data Field Modifier to set the class HTML attribute of the Name field on the details page so that it appears in a larger font. Press the ellipsis button next to the first row in the Fields input, which opens the Data Page Field dialog. Select the Name field on the details page, as shown in Figure 5.21, and then press OK. Some new inputs are added to the Data Field Modifier, which enables you to manipulate various aspects of the Name field on the detail form.
130
Figure 5.21
Chapter 5 Customizing Portlet Appearance
Selecting the Name field on the details page.
Scroll down to the Attribute Settings for Fields section and add an attribute called class with the value OutputDataCell. Because the Name element already has a class attribute specified, make sure you also set the Overwrite Rule input to Overwrite HTML value, or the existing class attribute is retained. The new value for the class attribute, OutputDataCell, refers to a class specified in the gridtable.css style sheet, which is located in the WebContent/factory/html_templates directory in your project. This new class increases the font size of the field. Style sheets are discussed in the “Cascading Style Sheets” section at the end of this chapter. Save the builder call when finished.
Testing the Data Modifiers Because the Form Layout builder alters the appearance of the groups created by the Data Hierarchy builder, you should test your application now before you add the Form Layout builder call. When you run the assets application, you should see the page shown in Figure 5.22. Notice that you can click on a column heading to sort the information in the table on that column. If you cycle between pages, the width of the table stays constant.
Figure 5.22
Page layout after the Data Column Modifier.
If you click on an asset to open the details page, you see that the page has been split into two separate sections called Basics and Details, and that the Name field stands out from the other fields on the page because it is printed in a larger font. For example, if you click on the link for Francis Millington’s resume, you should see the details page shown in Figure 5.23.
Data Modifiers
Figure 5.23
131
The details page for Francis Millington’s resume.
If you view the page source for the details page (you can do this in Internet Explorer by selecting Source from the View menu) and scroll down to where the Name field appears, you see that the Name field has a class attribute of OutputDataCell as follows: <SPAN name=”Name” class=”OutputDataCell”>Francis Millington’s resume
Adding a Form Layout The last data modifier to add is the Form Layout builder. Add a Form Layout builder call to your model and name it assetsFormLayout. In this example, you use the Form Layout builder to modify the data on the assets detail page so that it appears in two columns instead of one. In the Fields input, specify the Asset node on the detail page, as shown in Figure 5.24. Change the Number of Columns input to 2 and save the builder call.
Figure 5.24
Configuring the Form Layout builder.
Open the Data Field Modifier builder call by double-clicking on it in the Outline view. Notice that the majority of the inputs on the builder call have been hidden—this is because the Name field as specified in the Fields input has been renamed by the Form Layout builder, so the Data Field Modifier no longer recognizes the old reference. Press the ellipsis button next to the name field specified in the Fields input and select the new name field, as shown in Figure 5.25. Save the builder call when finished.
132
Figure 5.25
Chapter 5 Customizing Portlet Appearance
Changing the Name field.
You will notice some warnings in the Problems view, which are caused by the Data Field Modifier not being able to access the results of the Form Layout builder because the Form Layout comes after the Data Field Modifier in the Outline view. To remove these warnings, click and drag the Form Layout builder in the Outline view so that it comes before the Data Field Modifier. Save your model when finished. If you test the assets application now and open an asset’s detail page, you see that the data is listed in two separate columns. For example, if you open the detail for WebSphere Portlet Factory 6.0.2, you should see the output shown in Figure 5.26.
Figure 5.26
The details page for WebSphere Portlet Factory 6.0.2.
Working with Web Pages Web pages form the basis of every WPF portlet. When you generate your application, WPF takes the Web pages it contains and builds them into a JSP, which is then accessible as a portlet. Much of the Web page creation process is automated in WPF; however, working directly with a markup language (such as HTML) can give you more control over the appearance of your applications, even if it is just to make small additions to the pregenerated code.
Working with Web Pages
133
When using Web pages in WPF portlets, it is important to remember that they are accessed within the context of a portal (for instance, as a table cell within an HTML table). So, although your models can be run as standalone Web applications, they also need to be formatted so that they will display correctly when placed into table cells on a portal page. You can still use , , and tags in WPF, because WPF generates the content for these tags appropriately when outputting content to a portlet, but just make sure you don’t push other portlets off the page by making your pages too large. Also, don’t rely on users being able to cycle back and forward between your pages unless you include some sort of navigation controls, because portals are unsuited to navigation that isn’t a part of the portal or portlets themselves. If you do implement your own navigation, keep it simple, because the portal will likely have its own navigation, and you don’t want to clutter the interface. Also, make sure that all of your navigation occurs within the portlet itself; don’t create pop-up pages that open outside of the context of the portal. This sort of page will not be subject to the navigation system used in the portal, which could cause confusion for end users. You can view what your model’s generated pages will look like by navigating to the Pages section of the WebApp Tree tab. Although you can’t change these pages from the WebApp Tree tab, there are several other ways to change the pages in your application, which are discussed next.
HTML Builders Pages stored in HTML files (files with a .html extension) can be referenced from a WPF application by using the Imported Page builder, provided that the HTML file exists within the file structure of the application. Alternatively, you can also use the Page builder to type in an HTML page directly into a builder, which is useful for small HTML fragments that don’t need to be shared across projects. If there is a possibility that you will reuse the HTML, you should use HTML files instead, because they are easier to share between projects. After you have an HTML page in WPF, you can then display the page by typing in the name of the Imported Page or Page builder into an action list or Java method. For example, in an empty WPF model, you could add a Page builder call named page1, and then add an Action List builder call named main. The main action list would run every time the application starts. You could then type in page1 in the main action list, which would display the page when the main action list runs. After this has been done, you could insert elements from other HTML pages or builders onto the page created by the Page builder, giving you complete control over what is displayed and how it is displayed.
TIP Many of the builders in WPF (such as the View & Form and Data Page) use HTML files as base pages to display their output. WPF requires that certain tags exist on these pages when it places controls onto the form, so if you want to modify them, it is best to use the existing base HTML pages as a starting point. Many of the HTML-generating builders come with a Create Sample HTML button for this purpose.
134
Chapter 5 Customizing Portlet Appearance
You can insert HTML content onto a page through the use of named tags (<span> and tags are the most commonly used). Builders that produce HTML output can be used to replace the contents of these tags, or place controls in a certain position relative to the tags (before, after, wrapped to the left of an enclosing table, and so on). For example, let’s say you type in the following span tag into an HTML page: <span name = dataPage>
You then create a Data Page builder call in your model. Now, you can set the Location for New Tags input to dataPage and the Location Technique to On Named Tag, which replaces the dataPage span tag with the contents of the data page. Every builder in WPF that creates HTML has similar inputs, which enables you to insert the results of that builder onto a page. Because you can also replace input tags, you can replace any inputs on an HTML page with custom WPF inputs.
TIP Note the closing tag in the example. All elements that need to be closed should be explicitly closed off in a separate tag. For example, the tag <span name = dataPage /> is not considered closed.
In addition to the WPF builders that produce generated HTML output, there is also an HTML builder that you can use to insert an explicitly specified HTML fragment (specified inside the builder) at a particular span or input tag. Alternatively, you can also use the Inserted Page builder, which inserts an entire HTML page (which you might have specified using a Page or Imported Page builder) inside another page.
TIP Although it is also possible to use the Model Container builder to insert the output of an entire model onto an HTML page, you should only use this process if you explicitly need functionality provided by the model specified in the Model Container. If you just want to display an HTML page, you should use one of the other HTML builders instead.
When specifying HTML events in WPF, you can either code them directly into an HTML page or you can use the HTML Event Action builder. Using the HTML Event Action builder offers some advantages over coding events directly into HTML pages because it makes it easier to interact with other WPF methods and builders, and several configuration options are given to help streamline the process.
Working with Web Pages
135
JSP Builders As with HTML pages, JSPs can be imported into an application with the Imported Page builder and inserted into other pages via the Inserted Page builder (although, you cannot use the Page builder for JSPs). JSPs can also be imported using the JSP Proxy Page builder. A restriction of using JSPs in WPF is that you can’t set response headers or use forwarding, because JSPs are invoked using the include command, which doesn’t allow these actions. If you want to redirect users to a certain page, you should use the Java processPage method of the WebAppAccess object, as shown here (using Java in WPF is covered in Chapter 8, “Using Java in Portlets”): webAppAccess.processPage(page2);
Three JSP-specific builders are used in WPF. The first of these is the JSP Directive builder, which lets you insert JSP directives onto a page. JSP directives let you do such things as access classes and tag libraries on a page. The JSP Proxy Page builder, aside from letting you import JSPs into your project, lets you intercept calls to these JSPs with methods or action lists specified in WPF. This is useful to replace the functionality of JSPs without breaking any links in your application. For example, instead of displaying a JSP error page, you might choose to run a logging script in WPF, and then display an alternative HTML page. Using the JSP Proxy Page builder, you can do this without modifying any of the references to the original JSP. Finally, the JSP Tag builder provides an easy interface for inserting JSP tags onto a page. The builder lets you specify a tag library, and then provides inputs for the attributes associated with the tag that you specify.
JavaScript As in ordinary HTML development, a JavaScript script can be explicitly included inside an HTML page, which gives you access to that script elsewhere on the page. Some of the default HTML templates (such as gridtable.html) include embedded JavaScript scripts, and several JavaScript script files are created by default in every WPF application (stored under the WebContent/factory/clientjavascript directory). These script files let you do such things as display calendar and people pickers on a form and provide functionality for some of the Ajax builders (Ajax is discussed in Chapter 13, “Using Ajax and Dojo”). If you want to write your own scripts, you should store them in a different directory to the default JavaScript directory so that you do not mix up the default scripts with your own (for example, you might use WebContent/chapter05/clientjavascript). These scripts can then be accessed from other HTML pages or builders. You can also specify JavaScript in-line, inside builders that let you execute actions, such as the Button builder and Action List builder. There is also a Client JavaScript builder, which lets you insert client-side JavaScript at a specified tag on a page. However, wherever possible, you should store your scripts in the project’s file structure rather than specifying them in-line, because this makes it easier to share the scripts between projects.
136
Chapter 5 Customizing Portlet Appearance
When using JavaScript in WPF, you can access WPF artifacts from within your scripts. This lets you do things such as retrieve values from WPF variables and execute WPF actions and Java methods. You can do this using the following syntax: ${WPFArtifactType/WPFArtifactName}
WPFArtifactType is a design artifact in the WebApp (such as a Method or Variable) and WPFArtifactName is the name of the artifact. You can find out the types and names of artifacts by browsing the WebApp Tree tab or looking at the entries in a Select Action dialog. The text of the WPF call in the JavaScript is substituted with any results returned by running the artifact in WPF. For example, to run a method called getAssetID that returns a String value, you might use the following script: var assetID = ‘${MethodCall/getAssetID}’;
Notice that the method call is enclosed in single quotes. The reason for this is that the result of calling getAssetID is substituted for the method call, so without the quotes, the line might read something like this when loaded in a browser: Var assetID = AB1234;
This will cause a syntax error, because AB1234 is a String literal and not a variable name. Be aware, also, that these actions are evaluated when the scripts load, so if you’re running WPF actions that display pages, the pages are inserted into your HTML as the JavaScript is loaded. New to WPF 6.0.1 is support for the JavaScript Dojo toolkit, and WPF comes with several builders to make it easier to integrate Dojo into your applications. The Dojo toolkit gives you access to numerous powerful interface improvements, such as a rich text editor and dragging and dropping capabilities. The DOJO builders are covered in more detail in Chapter 13. As in ordinary Java portlet development, JavaScript should be used with caution in WPF development. Different browsers often have different JavaScript implementations, so minimizing the amount of JavaScript in your applications can help to mitigate potential usability problems. Also, the more JavaScript in your application, the harder it is to extend your applications to other markups besides HTML. This might not be much of an issue for you, but JavaScript can definitely help to improve users’ experience of your application. Just make sure to take the appropriate precautions; for example, if your application is going to be accessed through different browsers, it might help to check for different browser versions when using JavaScript. Also, in some circumstances, it might be useful to test whether JavaScript is enabled when the page loads, and then provide a non-JavaScript version of the page when necessary.
HTML Templates HTML templates contain standard page structures and formatting for an application, and are used extensively in WPF to modify the output for some of the page automation builders (the builders that produce HTML for you). HTML templates are used by these builders to determine how your user interface should be generated. You don’t necessarily have to change these templates, and
Working with Web Pages
137
doing so can be complicated; so it is recommended that you use style sheets and the WPF data modifier builders, such as the Data Field Modifier, as an alternative to modifying HTML templates wherever possible. However, HTML templates can provide you with that extra degree of control in your user interface, and they can also be shared easily between applications.
TIP HTML templates in WPF can be profiled so that different templates are used depending on the context in which your portlet is used. For more information on profiling in WPF, see Chapter 12, “Profiling Portlets.”
You can think of an HTML template as a set of rules for defining how different types of HTML fragments should be laid out and formatted. These HTML fragments are defined in templates using constructs. A construct is an abstract type that WPF matches to a particular type of HTML fragment and contains one or more elements (or even other constructs) that define how the HTML should be laid out and formatted. When generating pages, WPF will try and match HTML fragments with constructs in the template, and if a match is found, the construct’s structure and formatting is applied to the HTML fragment. For example, a construct might define how to display HTML fragments that contain dates, and another construct might define how to display tables. If multiple matches for an HTML fragment are found, the most specific construct is used. So, if an HTML fragment matches a generic date construct and a date construct for tables, and the fragment is in a table, the construct for dates in tables are used. Constructs are specified in an HTML template using the following syntax:
Everything that falls in-between the start and end tag defines how the construct is displayed and structured. Each template is split into two sections: a display section and a data entry section. If a form contains at least one data entry field on it, the data entry section of the template is used; if not, the display section is used. As a best practice, it is recommended that you don’t try and build an HTML template from scratch, but rather take a copy of another HTML template, modify it, and link to the copied version from your builders. WPF provides several master HTML template files by default, which are located in the WebContent\factory\html_templates directory of your project. The example at the end of this section demonstrates how you can use this approach to implement a custom HTML template in the assets application. Some of the page automation builders that use an HTML template are listed here, along with the name of the HTML template used. Note that in addition to inputs for HTML templates, these builders usually have an additional input for page HTML. The difference between page HTML and HTML templates is that the page HTML presents a container for the HTML output
138
Chapter 5 Customizing Portlet Appearance
produced by the builder and contains a tag named ‘data’; WPF replaces the contents of this tag with the output generated by the builder using the HTML template to structure and format the output. Using both inputs together, you can get considerable control over how WPF displays data and still take advantage of WPF’s automation features. Also, because the templates are stored in HTML files, you can easily share them between different applications. View & Form The View & Form builder used in this chapter has several inputs for specifying base HTML pages, and it also has inputs for HTML templates in the View Page Options and Row Details Support sections. By default, the gridtable.html template is used for both templates to define the structure and appearance of the view page and detail page. Breadcrumbs This builder uses the breadcrumbs.html template to add breadcrumb navigation to an HTML page.
TIP If you open up one of the default templates, you will notice that they already contain sample data. This data is just used to preview the template and is removed when the template is actually used by WPF (remember that WPF automatically flushes the contents of any elements in an HTML template where the word Container is used in the name attribute).
Data Page This builder also uses the gridtable.html template to control how it creates HTML. Data Column Modifier, Data Field Modifier, and Data Hierarchy Modifier All these builders contain inputs for specifying HTML templates, but no templates are used by default. Some useful templates you can use as starting points are the columnar.html and columnar_styles.html templates, which each display a basic table containing four columns. The first column is for a field label (for instance, Name), the second is for indicating whether the field is mandatory, the third is for the actual field value or input, and the fourth is for a validation message (for instance, ‘This field requires a numeric value’). The templates are identical, except columnar.html takes its styles from the template_compatible.css style sheet, and columnar_ styles.html has all its styles hard-coded into the HTML. The following example demonstrates how you can use some of the HTML manipulation techniques discussed. The example adds some HTML to the view page and manipulates the HTML used to display the paging buttons. A custom HTML template is also used to manipulate the layout of the detail page. The following builders are added in this section: • Comment • HTML
Working with Web Pages
139
Adding HTML First, add a Comment builder call to the model and name it Using HTML. Save the builder call. This builder serves as an organizational divide in your model between the builders in the previous section and the HTML builder used in this section. Now, add an HTML builder call to your model. This builder call is used here to insert instructions for opening the detail page after the OuterTable tag, which places the message at the bottom of the screen. Edit the HTML builder so that it appears, as shown in Figure 5.27, and save the builder call when finished.
Figure 5.27
Configuring the HTML builder call.
Modifying the Paging Buttons HTML Make a copy of the data_paging_buttons.html file, which you can find under the WebContent/ factory/pages directory of the assets project, and name the copy custom_data_paging_ buttons.html. This page contains the HTML for the page navigation buttons that appear at the bottom of the data table. Modify the HTML as shown by the bolded sections in the following code, and then save the file when finished. This removes the style class for the wrapper surrounding each button, which removes the border from the pagination controls, and centers these controls in the middle of the screen.
Open the Paging Buttons builder call in the Builder Call Editor and scroll down to the Advanced section. Change the HTML Page input to /factory/pages/custom_data_ paging_buttons.html, which uses the newly created HTML as the base HTML page for the navigation buttons. Save the builder call when finished.
Modifying the gridtable HTML Template Make a copy of the gridtable.html HTML template, which you can find under the WebContent/ factory/html_templates directory of the assets project, and name the new copy custom_ gridtable.html. This new template is used to change the layout of the assets detail page. Open the custom_gridtable.html file by double-clicking on it in the Outline view.
Working with Web Pages
141
Notice that the file is split into two sections: a display page section and a data entry page section. Because the detail page doesn’t contain any data entry fields, you should work with the display page section, the section at the top of the template delineated by the comment . Scroll down to the LabelContainer construct, which appears as follows:
This construct displays section labels and contains an element called DisplayGroupLabel, which defines the way section labels are displayed for all HTML output that uses the current template. Center the contents of the DisplayGroupLabel element by adding an align attribute, as shown in the following bolded code:
Scroll down to the DataContainer construct. This construct contains an element called DisplayField, which defines each table row contained in the DataContainer construct. Each row is made up of two cells: a label cell and an output data cell. Modify the label cell so that it contains text that is left aligned and succeeded by a space character, which ensures that there is a space between the text and the edge of the cell. You can do this by modifying the bolded parts of the DataContainer construct, as shown here:
<span name=”FieldLabel” class=”label”>Field Label
<span name=”FieldElement” class=”outputData”>Field Value
Save the custom HTML template.
Using the HTML Template Open the View & Form builder and scroll down to the Row Details Support section. Edit the HTML Template File input in this section so that it points to your new, customized HTML template. Save the builder call when finished.
142
Chapter 5 Customizing Portlet Appearance
Test the assets application now by opening the view page in a Web browser. You should see the output shown in Figure 5.28. Notice the added prompt at the bottom of the data table on the view page and the modified pagination controls. If you open a detail page for an asset, you should see the modified layout shown in Figure 5.29.
Figure 5.28
Testing the HTML modifications to the assets view page.
Figure 5.29
Testing the HTML modifications to the assets detail page.
Cascading Style Sheets Cascading Style Sheets (CSS) is a language that enables you to customize the appearance of individual elements on a markup page, such as HTML or XML. For example, you can use CSS to set the font size of a heading in a portlet or change the layout of a table. CSS is cascading because your style directions for a page are cascaded into a single style sheet, based on certain priorities. A style with a higher priority takes precedence over a style with a lower priority. These priorities are specified, in order of highest to lowest: 1. Style specified in an HTML element 2. Style specified inline in the HEAD tag of an HTML page 3. External CSS file linked to from an HTML page 4. Default style in Web browser When creating portlets in WPF, cascading style sheets can be implemented in several ways. Some of the builders that create presentation components have an input to specify a style sheet that applies to the output produced by the builder; for example, the Data View builder has an input
Cascading Style Sheets
143
called Stylesheet URL for this purpose. This is equivalent to specifying a style sheet in the HEAD tag of an HTML page. However, when a style sheet input is not available (as in the case of the View & Form builder), you should use the Style Sheet builder. This builder lets you designate a style for either an entire page by placing it in the HEAD tag or for part of a page by placing it in a particular HTML element. The style in the builder can be defined in an external .css file (usually in a servable directory under the WebContent directory), imported from an external file, or typed directly into the Style Sheet builder. As a third alternative, you can also manually insert CSS statements directly inside HTML pages as you would in an ordinary Web application. For example, you might manually specify an external style sheet using the following HTML:
Style sheets can be used to modify standard HTML elements, such as body and head. For example, the body element can be styled as shown here: body { color: #000000; font-family: Arial, Helvetica, sans-serif; font-size: 12px; }
You can also create custom style classes, which can then be assigned to particular elements. For example, you could create a tableHead class for your table headings, as shown here: /** for table head cells**/ .tableHead { background-color: #DDDDDD; padding: 3px; }
You would then associate HTML elements with this style by setting the class attribute of each element. For example, to specify that a table head element called ColumnHeader should use the tableHead style, you would define the element on an HTML page as follows:
This technique applies when you are modifying your own HTML pages directly and when you are working with HTML automatically created by WPF. For example, you can modify the style sheet used by the builder’s HTML template by making a copy of the HTML template and its associated style sheet, modifying the copied style sheet, pointing the copy of the HTML template to the new style sheet, and then referencing the new HTML template in your builder. Alternatively, you can also use a Data Field Modifier builder call to change the attributes of fields when they are placed on the page.
144
Chapter 5 Customizing Portlet Appearance
Keep in mind that when working with portlets in WPF, you are working with a table cell within an HTML table, which is how portlets are displayed in a portal. As a result, the portal can define its own styles, as can other portlets within the portal, and these styles might conflict with the styles in your portlet. Make sure you thoroughly test your portlet styles as they will appear in the portal before rolling them out into production environments. The following example shows how to add a Style Sheet builder call to the assets model. This style sheet is used on the asset detail page and overrides the NewsColumnWrapper and label styles used for the newspaper column container and the field labels, respectively. For more information on the default styles, see the “Commonly Used Styles” sidebar in this chapter. The changes will specify that the background around the newspaper columns is white instead of dark gray and that the label headings should be bolded. The following builders are added in this section: • Comment • Style Sheet
Adding a Style Sheet Add a Comment builder call to the model and name it Style Sheets. Save the builder call. Then, add a Style Sheet builder call to your model, and name the stylesheet assetsStyle. Change the Location Type input to ‘In HEAD Tag’ and select assetsDataViewAndForm_ DetailsPage from the Page dropdown. This will place the style sheet reference at the top of the asset detail page in the HEAD tag, which enables all elements on the detail page to use it (although the style sheet is inaccessible on the view page). The other option for the Location Type input, ‘Explicit page location,’ lets you place the style sheet at a specific tag, so that the style sheet is only available to elements contained within that tag. By default, the Style Sheet builder expects you to explicitly specify the style sheet in the CSS Source field, although you can modify the CSS Source Type input to point to a style sheet stored in a different location. Fill out the CSS Source input with text shown here and save the builder call when finished. This overwrites the NewsColumnWrapper and label classes with the new custom styles. .NewsColumnWrapper { background-color: #FFFFFF; } .label { font-weight:bold; } #datapage { position:relative; height:420px;
COMMONLY USED STYLES A few of the more commonly used default classes in WPF are listed here. These classes are all defined in the gridtable.css file, which is stored in the WebContent/factory/html_ templates directory.You can check what styles are being used for WPF-produced content by viewing the source HTML under the Page folder of the WebApp on the WebApp Tree tab. displayPageTable—This class defines the table that wraps each data page. dataEntryPageTable—This class defines the same table when the data page is in edit mode. gridTable—This class is also used in data page tables, and defines the background color and borders of the table. tableHeadRow, tableHead, and tableHeadText—These classes define the style for the heading row of a data table, the individual heading cells, and heading text, respectively. tableRowOdd, tableRowEven—These classes define the styles for odd and even rows, respectively. outputData, outputDataCell—These classes define styles for input fields or displayed values in a data table, and for the cells that contain them. sectionLabel, sectionLabelCell—These classes define styles for section labels and the cells that contain them. Section labels are used to label groups of input fields and displayed values in a data table. label, labelCell—These classes define styles for labels used to identify input fields and displayed values in a data table, in addition to the cells that contain them. requiredPrompt—This class defines the style of mandatory fields on a form. errorMessage—This class defines the style used by error messages produced by the default WPF validation mechanism. Validation is discussed in Chapter 11.
145
146
Chapter 5 Customizing Portlet Appearance
If you test your application now, you should see that the style of the details page has changed. Figure 5.29 showed the details page for Subcontractor Agreement before the style sheet changes, and Figure 5.30 shows the same details page after the changes (you can toggle between both styles by alternately disabling and enabling the Style Sheet builder call).
Figure 5.30
After adding style sheet changes.
Summary In this chapter, you learned how to arrange and manipulate the appearance of data in a portlet using WPF. You created a simple WPF application to surface information from an asset management system, which contained a model that was made available as a portlet. In the course of customizing the interface of this portlet, you used several WPF builders and learned about how HTML templates are used. The next chapter discusses how you can improve your portlet interfaces by adding various controls to the page, such as buttons and links.
Important Points • WPF can use HTML, XML, JSP, and JavaScript to produce its output. WPF developers normally use a combination of builders and traditional Web artifacts (for instance, style sheets) to control how this information is arranged and displayed in a portlet. • Data can be paginated in WPF using a paging assistant, which is automatically created by several builders. To create an interface for users to cycle between pages, a Paging Buttons or Paging Links builder should be used in conjunction with the paging assistant. • The View & Form and Data Page are commonly used to display data on the screen. The View & Form is particularly well suited to data that is in a list and detail format. • The Data Column Modifier, Data Hierarchy Modifier, and Data Field Modifier are commonly used to manipulate the properties of fields on a page. The Data Field Modifier gives you a lot of control over individual fields, and the Data Column Modifier and Data Hierarchy Modifier are more useful for organizing fields on a page. The Rich Data Definition builder is also commonly used for controlling the display of data, and is particularly useful for field validation, formatting, and translation. The Rich Data Definition builder is discussed in Chapter 11.
Important Points
147
• It is recommended that HTML pages and JSPs are inserted into models using the Imported Page builder, which makes your pages easier to share between projects. The Page builder is useful for building small HTML pages that don’t need to be shared. You can view the generated pages for a model by navigating to the WebApp Tree tab of the Editor window. • Builders that produce HTML output can insert content in relation to named tags on a page. <span> tags are the most commonly used tag for this purpose. • Builders that produce HTML output usually have two types of HTML input: one for page HTML and the other for a HTML template. Page HTML is the surrounding HTML for the output of a builder, and an HTML template is used to arrange and format the results of the builder inside the page HTML. • Cascading Style Sheets can be implemented in WPF using the Style Sheet builder and by configuring certain builders that support this functionality, such as the Data View builder. More traditional methods for defining styles, such as adding style sheet references to an HTML page directly, are also available.
This page intentionally left blank
C
H A P T E R
6
Adding Basic User Interface Controls to Your Portlets
User Interface (UI) controls such as text boxes, buttons, and checkboxes are an integral part of any portlet, as they are the means by which people interact with your applications. This chapter discusses how you can expedite the process of adding basic UI controls to portlets via the prepackaged builders in WebSphere Portlet Factory (WPF). By the end of this chapter, you will be aware of some useful techniques for automating UI development, and you will also have constructed a simple survey form to demonstrate these techniques. The survey form will be surfaced to a portal server as a portlet. The model used in this chapter is available for download from ibmpressbooks.com/title/ 9780137134465 under the Chapter 6 folder (instructions for copying this file into your project are included in a readme.txt file in the same folder); however, to increase your understanding of the topics discussed in this chapter, it is recommended that you create the models yourself by following the example in this chapter. Two image files are also used in this example; you can download these images from the images directory in the Chapter 6 folder or, alternatively, you can use your own image files (the dimensions for the images are given later in the chapter). The following topics are covered in this chapter: • User interface controls in WPF • Creating a survey portlet
User Interface Controls in WPF A number of builders in WPF can be used to automate the process of adding and manipulating UI controls on a portlet page. These builders automatically insert particular UI elements at specified tags in your portlets (the <span> tag or tag is normally used), and WPF provides you with a graphical interface for manipulating the settings of these controls. This automation makes your
149
150
Chapter 6 Adding Basic User Interface Controls to Your Portlets
portlet interfaces easier to maintain and can help to standardize the appearance of HTML in your portlets. For further tweaking of these controls, and to add controls that are not provided by the default WPF builders, you can also declare and configure the controls directly in the HTML (as you would in ordinary portlet development). If you find that you are often adding the same custom controls to your pages, then you may find it useful to encapsulate these controls in a custom builder, which will enable you to automate the use of these controls to varying degrees. Custom builders are discussed in more detail in Chapter 15, “Performance and Process Optimization.” Because there are several options available to you when adding UI controls in WPF (prepackaged builders, HTML controls, custom builders, and combinations of all three), there are usually multiple ways to achieve the same task. There is no real right or wrong way of going about designing a UI in WPF, but it is still a good idea to come up with a standard procedure for interface design (particularly when multiple people work on the same projects). As a best practice, it is generally better to use prepackaged WPF builders wherever possible, as this requires the least development effort to implement, is easy to maintain due to the wizard-based interface of the builders, and the builders have already been tested by the WPF development team. When you need UI functionality that is not provided by the default builders, you can add it directly into the HTML as required. If you think this functionality can be automated to some degree, you can include the functionality in a custom builder, but keep in mind that the time required to develop one of these builders is significantly more than the time taken to add the controls directly into the HTML. However, it is only a once-off development effort, after which the benefits are that your controls will be easier to maintain and you can standardize the way your HTML is created. Also, for more complicated controls, using a custom builder rather than manually manipulating HTML can help to minimize human error and will often save you time in the long run. If you have completed the examples in the book up to this point, you already have seen some of the ways you can automatically add UI controls to a page using WPF (using, for example, the View & Form builder). WPF also contains builders for adding basic UI controls to a page, and the example application in this chapter demonstrates the use of the basic UI control builders. The example utilizes one model, which displays a list of survey questions and allows these questions to be submitted. Once submitted, a results page is shown and the user’s inputs are displayed on the screen. The example in this chapter is a convenient way to demonstrate WPF’s basic UI builders, but note that when working with large forms, it is often easier to define your form fields in an XML document, and then use a Data Page and Rich Data Definition to create form fields based on the XML. You can still use the builders discussed in this chapter to customize these fields (by overwriting the contents of the desired tags), and also to create additional UI controls not related to the form fields themselves (such as OK buttons and site navigation links). The Data Page is discussed in Chapter 5, “Customizing Portlet Appearance,” but first appears in an example in Chapter 7, “Communicating Between Portlets.” The use of the Rich Data Definition and Data Page together is demonstrated in Chapter 11, “Field Validation, Formatting, and Translation.” Also, for
Creating a Survey Portlet
151
information on more advanced UI control builders than the ones in this chapter, see Chapter 13, “Using Ajax and Dojo.”
Creating a Survey Portlet In this section, you build a portlet to test some of the UI controls available in WPF. The portlet consists of two screens. The first is a survey form for a fictional product (the mysterious product X) and enables users to submit their answers to the portal server (a screen shot of this portlet is shown later in Figure 6.5). The answers are not processed or validated in any way, but they are displayed to the user on the second screen, which is a confirmation page to let the user know that the survey was submitted successfully. Validating, translating, and formatting are covered in Chapter 11. Before you proceed with this section, you need a WPF project to house the survey model. If you have a project from a previous chapter, then you can use it; otherwise, you should create a new WPF project (for more information on creating projects, see Chapter 1, “Introduction to WebSphere Portlet Factory”). The project will be published as a WAR file and deployed to a portal server, and you should also deploy the application to a local application server for testing. You can use the portal server if it runs on your local machine; otherwise, it is recommended you use the IBM WebSphere Application Server Community Edition server (WAS CE) that comes with WPF. After you have a project set up, you need to add a model for the survey portlet. For the first page in the survey form, you need the following builders: • Portlet Adapter
• Radio Button Group
• Action List (x2)
• Checkbox Group
• Comment
• Text Area
• Page
• Check Box
• Text Input
• Hidden Input
• Calendar Picker
• Variable
• Select
• Button
Note that although the order of builder calls is usually not important, occasionally the order of your builder calls can create errors or warnings in your application. The order of the builder calls used in this chapter can be changed if desired; the present order is intended to group builder calls according to the page they belong to, in roughly the same order that they will be used on the page.
Creating a Model Create a new model called survey in your project, under the folder WEB-INF/models/chapter06. The model should be based on the Main and Page template, using a Page Type of Simple Page. For more information on creating models, see the example in Chapter 1. After it is created,
152
Chapter 6 Adding Basic User Interface Controls to Your Portlets
your model should have two builders in it: an Action List called main and a Page called page1. The Action List runs automatically whenever the model is run and opens the page1 Page.
Modifying the Page Open the Page builder. Delete the contents of the Page Contents (HTML) input and enter the following HTML instead:
This creates a survey form that has a table consisting of six questions and six answers. The answers are span tags that will be replaced with UI controls from WPF. A seventh answer appears outside the table—this is replaced with a checkbox. The checkbox is outside the table because its text is long and would otherwise drag out the width of the first table column. Note that there is no question included for answer7, as the question is included as part of the checkbox field. Finally,
Creating a Survey Portlet
153
the submittedDate span tag is used to place a hidden control (which is submitted with the form but won’t be displayed to the user), and the submitButton is replaced with a button from WPF that enables users to submit their answers. Save the builder call when you are finished.
TIP There are numerous ways to build forms using WPF. Many of the other chapters in this book use the various data display builders in WPF for this purpose, such as the Data Page and View & Form builders, which help to automate the process of form construction. The approach adopted in this chapter (that is, explicitly specifying fields on an HTML page) demonstrates an alternative approach that may be preferable in certain scenarios, such as when you don’t have a data set to base your field inputs on or when you want to control the layout of your fields directly in the HTML. Note that you can also change the layout of generated HTML using the data display builders (see Chapter 5 for more information about this), but explicitly specifying fields in the HTML might be quicker for simple forms that are not intended to be reused elsewhere. Note that the techniques used in this chapter can also be used to replace the results of the data display builders (by specifying the appropriate tags that you want to overwrite).
Adding a Portlet Adapter The first builder call to add is a Portlet Adapter, which surfaces your model as a portlet when the application is deployed to a portal server. Select Portlet Adapter from the Builder Palette (you can open the Builder Palette by clicking the icon in the Outline view) and pressing OK. Name the Portlet Adapter survey, change the Portlet Title to Survey Form, and then change the Portlet Description to This portlet displays a survey form for Product X. This surfaces the survey model as a portlet called Survey Form. Save the builder call when you are finished.
Organizing Builders for page1 Add a Comment builder call to the model and name it page1. This helps separate out the builder calls in your model and denotes the purpose of the builder calls that follow it. Save the builder call so that it displays in Outline view, and then drag it so that it displays above the page1 builder call. Also, drag the Portlet Adapter builder to the top of the model, and save the model when you are finished. The next few builder calls contain functionality for the first page in the model (that is, page1, the survey form itself).
Adding a Text Input The next step is to add UI controls to the form, so that users can answer the survey questions. The first control is an input for an answer to the first question, “What is your email address?” The answer is a single line of text, so you can use the Text Input builder to place a text box onto the
154
Chapter 6 Adding Basic User Interface Controls to Your Portlets
screen. Add a Text Input builder call to the model and name it answer1. Specify the Page as page1 and the Tag as answer1, and then save the builder call.
Adding a Calendar Picker The second control is an input for the answer to the first question, “When did you start using Product X?” The answer is a date value, so the input field requires a text box with a calendar picker. The user can either type in the date manually or use the calendar picker to choose a date. When a user clicks on the calendar picker, a JavaScript calendar is temporarily displayed. From it, the user can select a particular date. The user’s selection is then sent back to the underlying form. Add a Calendar Picker builder call to the model and name it answer2. Specify page1 and answer2 as the Page and Tag in the Page Location section, and then expand the PopUp Calendar section. The JavaScript calendar can be accessed through two different buttons: The first is labeled with an ellipsis (…), and the second is labeled with an image (by default, the image is a calendar, although you can change it from the Button image input. Note that you can also change the images used when the button is hovered over and when it is pressed). Change the Button Type input to image and save the builder call.
Adding a Drop-Down Select Box The next question, “How many times a week do you use product X?,” requires that the user choose from a preset group of answers. To handle this, a drop-down box will be added to the form with the Select builder. Add a Select builder call to the model and name it answer3. Specify the Page as page1 and the Tag as answer3. In the Select Data input, enter the following commaseparated list of values: Less than 3,4-6,7 or more
This adds three possible options to the drop-down for answer3. Note that the Lookup Table input enables you to retrieve this information via a lookup table rather than specifying it directly inside the builder call. For an example of how to use lookup tables, see Chapter 4. Save the builder call when you are finished.
Adding a Radio Button Group Now, add a Radio Button Group builder call to the model. This provides a group of radio buttons for the answer to question 4, “How do you rate product X?” The possible answers are Terrible, Poor, Average, Good, and Excellent. Name the builder call answer4, and specify page1 and answer4 as the Page and Tag, respectively. For the Radio Group Input field, enter the following comma-separated list of values: Terrible,Poor,Average,Good,Excellent
Finally, scroll down to the Selected Value input at the bottom of the builder call and type Average (the Average option is then selected by default). Save the builder call when you are
finished (note that it is also possible to implement this solution using the Radio Button builder,
Creating a Survey Portlet
155
which gives you more control over how the radio buttons are laid out on the page. For more information on the Radio Button builder, see the “The Radio Button Builder” sidebar).
THE RADIO BUTTON BUILDER To implement radio buttons, you can either create them by using a Radio Button Group builder or utilize existing radio buttons (that is, ones already defined in the HTML) by using a Radio Button builder. The advantage of the Radio Button builder over the Radio Button Group builder is that you can specify the format of the radio buttons inside the HTML. For example, to implement radio buttons for the answer to question 4, you could specify the HTML for the radio buttons along the following lines:
Terrible Poor Average Good Excellent
After this is done, you can add a Radio Button builder to your model, pointing the Tag to answer4. Note that you can modify how the options appear simply by modifying the HTML (for example, you can make the options display in two columns by inserting another table cell, or on separate lines by inserting tags).
Adding a Checkbox Group The next builder call to add is a Checkbox Group, which provides a group of checkboxes for answers to the question, “When do you use product X?” The possible answers are Walking, Running, Cycling, and Swimming. Add a Checkbox Group builder call to the model and name it answer5. Enter the Page and Tag as page1 and answer5, respectively. For the Checkbox List Data input, enter the following comma-separated list of values: Walking,Running,Cycling,Swimming
Save the builder call when you are finished. (Note that you can also add a single checkbox to a form using the Checkbox builder. This will be done for answer7 later in this section.)
Adding a Text Area You now need a text box for the user to enter some additional comments (answer6). Because the answer may take up several lines, you should use the Text Area builder rather than the Text Input builder, as this will enable you to place a text box with several rows (scroll bars will be displayed if the user types in more characters than what can be displayed in the text box at one time).
156
Chapter 6 Adding Basic User Interface Controls to Your Portlets
Add a Text Area builder call to the model and name it answer6. Specify the Page and Tag as page1 and answer6. Now expand the Attributes section and enter 50 for the Cols input and 3 for the Rows input. This creates a text input with three rows of 49 characters (the space for the 50th character will be taken up with a vertical scroll bar). Change the Wrap input to Virtual; this wraps the text in the text box when the user types more than 49 characters, but won’t insert any new line characters in the text that is submitted to the server when the user submits the form. Save the builder call when you are finished.
Adding a Checkbox For the final answer input, add a Checkbox builder call to the model and name it answer7. This adds a single checkbox control to the form, which the user can enable if he wants to receive information about special offers and promotions. Enter page1 and answer7 for the Page and Tag inputs, and then type I would like to receive information about future promotions and special offers for the Label input. Save the builder call when you are finished.
Adding a Hidden Input The next builder call adds a hidden input to the form to hold the value of the date and time at which the form was created for a given user. Although this input will not be displayed on the screen, it is still submitted when the form is submitted. This is particularly useful for sending diagnostic information to the server that is unnecessary to display on the screen. Add a Hidden Input builder call to the model and name it createdDate. Specify the Page and Tag as page1 and createdDate, and then type the following line for the Value input: ${Java/java.util.Calendar.getInstance().getTime()}
This is inline Java code that populates the hidden input with the results of a method call, accessed via a Java utility class called Calendar. For more information on using Java in WPF, see Chapter 8, “Using Java in Portlets.” Save the builder call when you are finished.
TIP You can also hide inputs by setting the type HTML attribute of the field to hidden (using an Attribute Setter or Data Modifier builder). This has the same effect as the Hidden Input builder, although the type attribute is not supported by every WPF control (such as the text box created by the Text Area builder). Alternatively, you can use the Visibility Setter builder to hide fields. The Visibility Setter enables you to hide tags based on certain conditions (such as a particular value being true); however, fields hidden by a Visibility Setter are not accessible as request inputs (that is, the values of these fields are not submitted when the form is submitted). Visibility Setters can also be used in conjunction with partial page refreshing to dynamically hide and show parts of a form without reloading the entire page (for an example of how to do this, see Chapter 13).
Creating a Survey Portlet
157
Adding a Variable to Store User Inputs Now, add a Variable builder call to the model and name it formInputs. Change the Type input to String and save the builder call. This builder call creates a String variable called formInputs, which you can use to store the user’s submitted values (and then display them on the confirmation page).
Defining Actions for Submitting a Survey Next, add an Action List builder call to the model. This Action List defines the sequence of actions to execute when the survey form is submitted. Two actions are required: The first is set the value of the formInputs variable based on a text representation of the user’s answers, and the second action displays the confirmation page. Change the name of the builder call to submitAction, and then open the Select Action dialog by clicking the ellipsis button to the right of the first row in the Actions input. Select Assignment from the Special menu (as shown in Figure 6.1), and then press OK. This lets you assign a particular source value to a target field. Specify Variables/formInputs as the Target. For the source value, you should specify a string representation of the request inputs, which you can do by using another inline Java statement: ${Java/webAppAccess.getRequestInputs().toString()}
Press OK to accept the assignment and return to the Action List builder call.
Figure 6.1
Setting the first action for the submitAction Action List.
158
Chapter 6 Adding Basic User Interface Controls to Your Portlets
TIP The inline Java statement in this section represents all of the request inputs in a concatenated string. This is useful for debugging purposes, but it is cumbersome if you want to be able to access and process individual inputs. You can access an individual request input by using the following syntax, where requestInput is the name of a request input (for example, answer1): ${Inputs/requestInput}
For the second action in the Action List, type page2. This opens the confirmation page, which you create in the next section. Save the builder call when you are finished.
Adding a Button The last builder call to add for page1 is a Button builder call, which gives users a means to submit the form. Add a Button builder call to the model and name it submitButton. Specify page1 and submitButton as the Page and Tag, and then type Submit for the Label input. Change the Action input to submitAction; this causes the Action List created in the previous step to be run when the button is pushed. Notice that the Action Type input is set to ‘Submit form and invoke action,’ which will cause the values of the input fields to be sent to the request object so that they are accessible in the submitAction Action List. Save the builder call when you are finished. You have now finished the interface for page1.
Organizing Builders for page2 Add another Comment builder call to the model and name it page2. The next few builder calls contain functionality for the second page in the model, which displays a confirmation message to the user. The users’ inputs from the first page will also be displayed. To create the interface for the confirmation page, you need to add the following builders: • Comment • Page • Image • Text • Link • Image Button
Adding a Confirmation Page Add a Page builder call to the model and name it page2. This creates a page called page2 that can accessed when the survey form is submitted. The page displays a confirmation message and shows the user’s answers from the first page.
Creating a Survey Portlet
159
Delete the contents of the Page Contents (HTML) input, and then enter the following HTML in its place:
Thank you!
Your answers have been submitted.
<span name=”submittedImage”>
Your answers: <span name=”results”>
<span name=”back”>
Save the builder call when you are finished. Notice that this page contains several span tags; these tags will be overwritten with various UI controls in the next few steps.
Adding an Image Builder The first control to add to page2 is an image that will display under the confirmation text. The user will not be able to interact with the image, as it is intended to simply improve the appearance of the page. If you want users to interact with images, use the Image Button builder, which you add at the end of this section. Before you can display an image in the model, you need to add the image file to your project. Create a directory called images in the WebContent directory of your project, and then copy in the back.jpg and submitted.jpg files (you will use the back image later in this section). These files are available for download from the images directory under the Chapter 6 folder at ibmpressbooks.com/title/9780137134465. If you can’t download these files, simply create your own images using the same filenames, and put them into the WebContent/images directory. (The back.jpg should be fairly small—about 14×14 pixels. The submitted.jpg should be larger, as it is a centerpiece for the confirmation screen. The image used in the example is 116×153 pixels.) Add an Image builder call to the model and name it submittedImage. Enter the Page and Tag as page2 and submittedImage, respectively, and then specify the Image Source input as /images/submitted.jpg. Save the builder call. This adds the submitted.jpg image to page2 at the submittedImage tag.
160
Chapter 6 Adding Basic User Interface Controls to Your Portlets
Displaying Submitted User Inputs Now add a Text builder call to the model. This builder call places the values of the user’s inputs on the survey form into a static, non-editable text field on page2. Name the builder call displayResults, and then specify page2 and results as the Page and Tag inputs. For the text input, specify ${Variables/formInputs}, which displays the contents of the formInputs variable in the text field. Save the builder call.
Adding a Link Add a Link builder call to the model and name it backLink. This places a link on the page that enables users to return to the survey form. Change the Page and Tag inputs to page2 and back, respectively, and then type Return to the question page for the Link Text. Specify page1 as the Action, and change the Action Type input to ‘Link to an action.’ This simply runs the appropriate action (opening page1) without submitting the form (as there is no form on page2, only on page1). Save the builder call when you are finished.
Adding an Image Button Finally, add an Image Button builder call to the model. This can be used to place a clickable image button next to the back link, so that users can click on the image or the link to return to the survey form. Change the name of the Button to backButton, and then change the Location Technique input in the Page Location section to ‘Relative to named tag.’ This enables you to place the button before the backButton tag. Specify page2 and back for the Page and Tag inputs, and then change the Placement input to ‘Before.’ Type the New Tag Name as backButton. Under the Image Sources section, type /images/back.jpg for the Image input to link to the image file you added earlier. Note that you can also specify separate images in this section for when the mouse is hovered over, and for when the button is clicked. Leave the defaults for these inputs, and then enter page1 into the Action input at the bottom of the builder call. This returns to the survey form when the button is pressed. Save the builder call when you are finished. The survey portlet is now complete. The next section describes how you can test the various UI controls in the survey portlet from your IDE.
Testing the Survey Portlet To test the survey portlet, run the survey model from the WPF Designer by clicking the icon on the toolbar. This runs the currently active model (survey) with the last run configuration you used. If you have not set up a run configuration before, you will be prompted to do so—create a new configuration under the WebSphere Portlet Factory Model category (if you want more information on setting up a run configuration, see the “Testing the Application” section in Chapter 1). The Product X survey form should load as shown in Figure 6.2.
Testing the Survey Portlet
Figure 6.2
161
The Product X survey form.
Enter your email address into the first field, and then enter a date value into the second input by pressing the Calendar icon to the right of the text box. You should see a popup JavaScript calendar, as shown in Figure 6.3. After you select a date and press OK, it should update the underlying form with that date.
Figure 6.3
The Calendar control.
162
Chapter 6 Adding Basic User Interface Controls to Your Portlets
Press the arrow in the drop-down box for question 3 (“How do you rate product X?”), and you should see a list of three options available. Select an option and proceed to the next question. Rate product X on the scale provided for question 4, and then click one or more boxes for question 5 (“When do you use product X?”). After you have finished this, enter some text into the Additional Comments text box. Finally, enable the checkbox to receive information about future promotions and special offers. Press the Submit button when you are finished. This submits all of your answers and copies them into the formInputs variable. You should now see a confirmation page, as shown in Figure 6.4. Notice that all of the answers from the first page are displayed, including an additional input for createdDate (this was the hidden input you created on the survey form). The layout of the page is very basic (and not overly pretty), but it does display the values entered using the UI controls on page1. As mentioned earlier in the chapter, if you wanted to process these inputs, you could use the ${Inputs/ requestInput} syntax for each input; for example, the action ${Inputs/answer2} would retrieve the user input for answer 2. Also, note that for large numbers of fields, it is usually easier to define your data in an XML document, and then display a form to edit this data using WPF builders like the Data Page and Rich Data Definition (the Data Page is first used in Chapter 7, and the use of the Data Page and Rich Data Definition together is discussed in Chapter 11).
Figure 6.4
The confirmation page.
Click the ‘Return to the question page’ link to return to the survey form. When the survey form loads, press the Submit button again. All of the inputs are blank, but you just want to test the Image button on the confirmation page to ensure it will also return you to the survey form page. When the confirmation page loads, click on the back image next to the ‘Return to the question page’ link. You should be returned to the survey form. After you have tested the survey
Summary
163
model, you should rebuild your application on the portal server (for instructions on how to do this, see the example in Chapter 1), after which you will be able to view the survey model as a portlet on the portal server. After you have added the survey portlet to a page in the portal, it should display as shown in Figure 6.5.
Figure 6.5
The survey portlet.
Summary In this chapter, you learned about some of the different builders in WPF that can be used to add UI controls to portlets, and you also built a simple survey portlet using these builders. The portlet consisted of a survey form, which displayed a number of questions about Product X, and a confirmation page, which displayed when the survey form was submitted. Later in this book, you look into some of the other ways that WPF can be used to create UI controls. In particular, the Rich Data Definition builder (discussed in Chapter 11) can be used to
164
Chapter 6 Adding Basic User Interface Controls to Your Portlets
add controls for fields taken from an XML document, and the Ajax and Dojo builders that come with WPF can be used to add more powerful UI controls to your applications (these controls are discussed in Chapter 13). It is not necessary that you complete the chapters in this book in order, so you can skip ahead to these chapters if you want. The next chapter, “Communicating Between Portlets,” discusses some of the different ways to send information between portlets in WPF.
Important Points • Although you can add UI controls to an HTML page in the same way that you can in ordinary Web development, you can also use WPF builders to add and configure some of the more commonly used controls. • The Calendar Picker builder can be used to add a text field with a popup JavaScript calendar to a portlet page. • The Text Input and Text Area builders can be used to add text boxes to a page. The Text Input builder should be used when only one line of text is required; the Text Area builder can be used for multiple lines of text. Also, the Text builder can be used to place static text at a given location on a page. • The Select builder places a drop-down text box onto a page. • The Radio Button Group builder can be used to place radio buttons onto a portlet page. The Radio Button builder can be used to replace existing radio buttons with WPF radio buttons; this is useful because it lets you manipulate the appearance of the radio options in the underlying HTML. • The Checkbox Group builder can be used to place a list of checkboxes onto a page. The Checkbox builder places a single checkbox onto a page. • The Hidden Input can be used to place an invisible input onto a form. The values of hidden inputs are still submitted when the underlying form is submitted, but they are not displayed to the user. • The Button builder places a clickable button onto a page. Also, the Link builder can be used to place a hyperlink on a portlet page. • The Image builder can be used to place an image on a page. The Image Button builder can be used to place a clickable image onto a page, which executes a particular action when clicked.
C
H A P T E R
7
Communicating Between Portlets
The ability to send information from one portlet to another adds considerable flexibility to your portlet applications, in both the way they are designed and in the way they are used. This chapter outlines the numerous ways that you can implement inter-portlet communication using WebSphere Portlet Factory (WPF). By the end of this chapter, you will understand the strengths and weaknesses of each approach and will have built a library loans portlet application to demonstrate them. Each of the models in this chapter is available for download from ibmpressbooks.com/title/ 9780137134465 under the Chapter 7 folder (instructions for copying these files into your project are included in a readme.txt file in the same folder); however, to increase your understanding of the topics discussed, it is recommended that you create the models yourself by following through the example in this chapter. Note that the example in this chapter does not go into detail about the service/provider consumer pattern; for more information on this pattern and on services in general, refer to Chapter 2, “Providing and Consuming Services.” The following topics are covered in this chapter: • The benefits of inter-portlet communication • The WebSphere Property Broker • Creating a project • Creating a service provider • Creating a list portlet • Creating a detail portlet
165
166
Chapter 7 Communicating Between Portlets • Configuring the WebSphere Property Broker • Alternative communication methods • When to use inter-portlet communication
The Benefits of Inter-Portlet Communication Working inter-portlet communication into your applications has several benefits from both a design perspective and a usability perspective. Some of the key benefits are listed below. Extensibility Inter-portlet communication makes your applications easier to extend and plug into other applications. For example, a portlet that displays a list of loans for a library database might allow users to click on a particular loan, and then send a loan ID off to other portlets to perform an appropriate action. One portlet might be used to display details for the loan, and another portlet might display a chart of overdue loans for the borrower. In this example, you could develop additional portlets that interact with either portlet, without having access to the source code for the loans portlets. Maintainability Inter-communicating portlets are easier to maintain because you can develop or modify a portlet without making changes to others, assuming that you do not change the nature of the content that is communicated. For example, you can change the user interface and services in the loans list portlet without worrying about affecting the loan detail portlet. Usability Because the interface for applications that use inter-portlet communication can be spread across several portlets, inter-portlet communication can be beneficial for users. Depending on their access rights, users can drag and drop these portlets around the portal page to suit their personal preferences, and they can add and remove portlets to further customize the interface to their application. The example application discussed in this chapter demonstrates inter-portlet communication for a simple library system between a portlet listing library loans, and another portlet displaying details on an item in the list. The application utilizes three models: a list model, a detail model, and a service provider model. The list model is surfaced to a portal server as a portlet and displays the list of loans. The detail model is also surfaced as a portlet and displays details for a loan selected in the list model. Finally, the service provider provides the list and detail data for the list and detail models, which is stored in an XML document.
The WebSphere Property Broker JSR-168 portlets do not natively support inter-portlet communication, but portlets can still communicate with each other in WebSphere Portal Server using a mechanism called the WebSphere
Creating a Service Provider
167
Property Broker. The Property Broker is the preferred method of inter-portlet communication in WebSphere Portal, and it is the first communication method demonstrated in this chapter.
TIP JSR-168 is a standard set of Java APIs for developing portlets.You can and should specify your WPF portlets as JSR-168 by selecting Java Standard for the Portlet API setting in your portal server deployment configuration. The Property Broker receives properties from portlets, such as a loan ID or success flag, and then publishes these properties for the rest of the portal to use. Target portlets can then access these properties and define actions to respond to property changes. After you have deployed your portlets in WPF, you then use the portal administration interface to set up a link between a property in a source portlet and a property in a target portlet. This process is known as wiring two portlets together.
The WebSphere Property Broker can facilitate communication between portlets in different applications, and also between portlets that were built using different development tools. For example, a WPF portlet can send information to a standard Java JSR-168 portlet, and vice-versa. This makes it easy to maintain and extend your applications, and other developers can write portlets that communicate with your portlets without needing access to the source code. The first few sections in this chapter focus on using the WebSphere Property Broker to set up inter-portlet communication between a library list portlet and a library detail portlet. The “Alternative Communication Methods” section then outlines some potential alternatives for communicating between portlets.
Creating a Service Provider To provide data to your list and detail portlets, you should create a service provider model. This model will contain operations to retrieve a list of loans and retrieve details on a particular loan, which is consumed by the list and detail models. The service provider is the singular access point for loans information in your application, and it makes your application easier to maintain. Before you can begin the steps in this section, you need a WPF project, which houses the service provider model and the other models in this chapter. If you have a project from a previous chapter, you can use that. If you don’t, you should create a new WPF project. For more information on creating projects, see Chapter 1, “Introduction to WebSphere Portlet Factory.” The project is published as a WAR file and deployed to a portal server, and you should also deploy the application to a local application server for testing. You can use the portal server if it is running on your local machine. If not, it is recommended that you use the IBM WebSphere Application Server Community Edition server (WAS CE) that comes with WPF. After you have a project set up, you need to add the service provider model. The service provider uses the following builders to provide its functionality:
168
Chapter 7 Communicating Between Portlets • Service Definition • Variable • Simple Schema Generator • Action List • Method • Service Operation (x2)
Note that although the order of builder calls is usually not important, some of the builder calls in this chapter need to be in a specific order. This is because some of the builder calls require certain things to be built by the time their build methods run when the WebApp is regenerated. In this example, the Variable builder call must precede the Simple Schema Generator builder call to create a schema based on the variable built by the Variable builder call, and the Simple Schema Generator must precede the Service Operations because the Service Operations require the schema to use for their results. Also, the Action List must precede the first Service Operation, and the Method must precede the second Service Operation, or you will get errors indicating that these resources are unavailable.
Creating a Model Create a new model called loansService in your project under the folder WEB-INF/models/ chapter09. Because you will store your data in an XML variable, rather than get it from a database, you need to create the service provider manually. To do this, the model should be based on the Empty template, which creates a model with no builder calls in it. For more information on creating models, see the example in Chapter 1.
Defining the Service Add a Service Definition builder call to the model by selecting Service Definition from the Builder Palette. You can open the Builder Palette by clicking the icon in the Outline view. Then, press OK. Name the builder call loansService and expand the Testing Support section at the bottom of the builder call, and then enable the box for Add Testing Support. WPF now automatically generates test pages for the operations in your service every time the model is regenerated. Save the builder call when you are finished.
Adding Loan Data The next step is to add your sample data for the service provider. Add a Variable builder call to the model and name it loans. Change the Type input to XML and enter in the following XML into the Initial Value input: 00001
Creating a Service Provider
169
The Gullible TravelerMalcolm Johnson11/05/200700002A Traveling AuntSamuel Russell11/11/200700003An Interminable JourneyJoseph Jones12/22/2007
Save the builder call when you are finished. Next, add a Simple Schema Generator builder call to the model. This builder call is used to automatically generate a schema based on the Variable builder call, which is then used to define the structure of the result sets for the Service Operation builder calls. Name the builder call loansSchema, and for the Sample Data input, select ‘loans’ from the drop-down list. This is the variable you created earlier. Save the builder call when you are finished.
Adding an Action to Retrieve a List of Loans Now, add an Action List builder call to the model. The Action List defines the action to be called when the retrieveLoansList service operation is consumed, which you create in a later step. Name the Action List getLoans and change the return type to IXml. IXml is a WPF interface used to access and manipulate XML. For more information on IXml, see Chapter 9, “Using Web Services and Manipulating XML.” Open the Select Action dialog for the first row in the Actions input, which you do by clicking on the ellipsis button on the first row of the Actions input. Select Special, Return, then select the loans Variable from under the Variables section and press OK. The action should appear in the Action List as follows: Return!${Variables/loans/Loans}
When the Action List is called, this action returns the entire Loans variable created by the Variable builder. Save the builder call when you are finished.
170
Chapter 7 Communicating Between Portlets
Specifying the getLoansList Operation Next, you create the first operation for the loansService service. This operation returns a list of loans (by calling the getLoansList Action List). Add a Service Operation builder call to the model and make sure the Service Operation Properties section is expanded. Select loansService for the Data Service input, which links the operation to the loansService service created by the Service Definition builder call. The Operation Name input defines how the operation is referred to by consumers. Change this input to getLoansList—there is no problem with this being the same name as your Action List. The Action To Call input defines what happens when the operation is consumed. This input should be changed to point to your Action List (getLoansList). Note that the Operation Description input is left blank, because it appears only to service consumers when the service provider is defined as a Web service. Make sure ‘No inputs’ is selected for the Input Structure Handling input in the Operation Inputs section of the builder call. This specifies that the operation has no inputs—consumers have only to specify that they want to consume the operation to retrieve the loans list. Expand the Operation Results section of the builder call and select Specify Result Schema for the Result Structure Handling input. This enables you to manually specify the structure of the results to be returned from the operation. For the Result Field Mapping input, select the topmost element in the Loans schema (Loans). The Loans element contains a list of Loan elements and is returned from the getLoansList Action List. The input should read loansSchema/Loans when you are finished. Make sure the Result Field Mapping input is set to Automatic, which automatically maps results from the called action to the result returned from the service operation. Save the builder call when you are finished.
Adding a Method to Retrieve a Specific Loan Add a Method builder call to the model and name it getLoanFromID. This method returns a particular loan based on a loan ID argument and is called whenever the getLoanDetail operation is consumed. A Method builder call is used, rather than an Action List, because some IXml manipulation is required to link loanIDs to loans. Expand the Arguments section of the builder call and add a single argument called loanID of type String. This argument corresponds to a particular loan, and you access it from the method defined in the Method Body Input. Change the Return Type of the builder call to IXml, and then enter in the following code into the Method Body input: { //cycle through loans variable to get each loan IXml loans = webAppAccess.getVariables().getXml(“loans”); for (IXml loan = loans.getFirstChildElement(); loan !=null; loan = loan.getNextSiblingElement()) {
Testing the Service Provider
171
//look for match on loanID element if ( loan.getText(“Loan/LoanID”).equals(loanID) ) //match found, return the current loan return loan; } //no match found, return empty loan return XmlUtil.create(“Loan”); }
This code cycles through each of the loan elements in the loans list, and checks to see whether the loan ID of the element matches the loan ID passed in to the method. For more information on the use of IXml, see Chapter 9. Save the builder call when you are finished.
Specifying the getLoanDetail Operation The final builder call for the service provider is another Service Operation builder call. This builder call defines a second operation for the loansService service, which displays details for a particular loan (by calling the getLoanFromID Method). Add a Service Operation builder call to the model and point the Data Service input to loansService. Change the Operation Name to getLoanDetail, and the Action To Call to getLoanFromID. This creates a new operation on the loansService service called getLoanDetail, which runs the getLoanFromID method when consumed. In the Operation Inputs section of the builder call, select ‘Use structure from called action’ for the Input Structure Handling input and make sure the Input Field Mapping input is set to Automatic. This specifies that the inputs to the operation are defined by the getLoanFromID Method, and should be automatically mapped to the inputs in getLoanFromID. Fill out the Operation Results section of the builder call the same as the Operation Results section in the previous Service Operation builder call, which specifies that the structure of the results to be returned from the operation is defined by the Loan element in the Loans schema. Save the builder call when you are finished. You have now finished building your service provider. The next section discusses how you can test your service provider before moving on to create the list portlet and detail portlet.
Testing the Service Provider Because you enabled test support for your service, WPF automatically generates test pages for your service provider, which you can disable from the Testing Support section of the Service Definition builder call. This enables you to test the service provider directly from the IDE. To test the service, run the loansService model from the WPF Designer by clicking the icon on the toolbar. This
172
Chapter 7 Communicating Between Portlets
runs the currently active model (for instance, loansService) with the last run configuration you used. If you have not set up a run configuration before, you are prompted to do so—create a new configuration under the WebSphere Portlet Factory Model category. If you want more information on setting up a run configuration, see the “Testing the Application” section in Chapter 1. After you run loansService, you should see the index test page in your default Web browser, as shown in Figure 7.1. This screen lists all the operations available on the loansService service.
Figure 7.1
Index test page from the loansService model.
To test the getLoansList operation, click the getLoansList link. You should see a list of loans, as shown in Figure 7.2. If not, check that you don’t have any errors showing in the Problems view in the IDE and that you have completed all the steps in the previous section correctly. Press Back to return to the index page.
Figure 7.2
Testing the getLoansList operation.
To test the getLoanDetail operation, click the getLoanDetail link. The screen that follows asks you to enter in an ID of a loan you would like to retrieve details for. Enter in 00001 as the ID and press the Submit Query button. You should see some details for the loan that has an ID of 00001 (that is, The Gullible Traveler), as shown in Figure 7.3. Press Back when you are finished. Close the loansService model when you’ve finished testing it. You now have a service provider called loansService, which provides a list of loans and details on individual loans. The next few sections walk through the process of creating consumers for the loansService service in the form of a list portlet and detail portlet.
Creating a List Portlet
Figure 7.3
173
Testing the getLoanDetail operation.
Creating a List Portlet
This section describes how to add another model to your project, which is surfaced to a portal server as a portlet. The portlet consumes the service created in the previous section, and displays a list of loans to the user. When a loan in the list is clicked, the ID of the loan is sent to the WebSphere Property Broker, which can then be read by other portlets. A screenshot of both the list and detail portlets is shown later in Figure 7.15. The model uses the following builders to provide its functionality: • Portlet Adapter • Service Consumer • View & Form • Cooperative Portlet Source Creating a Model
To build the list portlet, select File, New, WebSphere Portlet Factory Model from the File menu to bring up the WebSphere Portlet Factory Model dialog. On the next screen, select your WPF project and press Next. The next screen lists different types of models WPF can create for you automatically. You can create service consumers in WPF in two ways: the first is to let WPF create one for you, and the second is to create the service consumer manually. The builders you use to create the list portlet in this section are fairly standard, so you can opt for WPF to create them for you. Select List and Detail Service Consumer from the Service Consumers category and press Next. Specifying the Service
The next screen asks you define a portlet name and service provider model for the consumer. The portlet name is also used as a prefix when the wizard names some of the builder calls in your model. Name the portlet loans and select chapter07/loansService as the service provider model. Click Next to continue.
174
Chapter 7 Communicating Between Portlets
Specifying List and Detail Operations You now need to specify an operation to retrieve view data—the list portion of your portlet. Select getLoansList from the drop down and press the Next button to edit the detail portion of the portlet. Although the list portlet in this chapter is intended to be used as a list portlet only, defining the detail portion gives the loansList model the flexibility to be run as a list and detail portlet together if a separate detail portlet is unavailable. Fill out the settings on this screen, as shown in Figure 7.4. This converts the loanID column values into clickable links that run the getLoanDetail operation when clicked. Press Next when you are finished.
Figure 7.4
Configuring the detail portion of the loansList model.
Now, you need to define a uniquely identifying parameter that is used to open a loan when a loan ID is clicked. Enter in the loan ID from the loan on the currently selected row, as shown in Figure 7.5, and click the Next button. Name the model loansList and make sure it is created in the directory models/chapter07, and then press Finish. This creates a new model called loansList in your WPF project, and opens the model in the Model Editor and Outline view. The loansList model contains three builder calls: a Portlet Adapter, a Service Consumer, and a View & Form.
Creating a List Portlet
Figure 7.5
175
Configuring input overrides for the loansList model.
Configuring the Portlet Adapter The Portlet Adapter builder converts the model into a portlet, which can be viewed inside a portal. Note that if you make any changes to this builder call, including the changes you are about to make, you need to rebuild the deployed application before you can see the results of the changes. For more information on this process, see the example in Chapter 1. The Service Consumer builder makes the operations in the loansService service available in the current model, and the View & Form builder displays the list and detail data in the model to the screen. You need to make two small changes to the Portlet Adapter builder call. Double-click the Portlet Adapter builder call to open it in the Model Editor, and then change the Portlet Title to Loans List and the Portlet Description to List of loans. Save the builder call when you are finished. The Portlet Title identifies the portlet inside the portal—it is actually displayed in the title bar of the portlet—and the Portlet Description appears on various administration screens inside the portal.
Defining the Portlet as a Cooperative Source The final step in building the list portlet is to add a Cooperative Portlet Source builder call. This builder call defines the structure and content of the information to send to the Property Broker (for instance, the loan ID). A Cooperative Portlet Target builder call is then used in the detail portlet to receive the loan ID from the Property Broker.
176
Chapter 7 Communicating Between Portlets Add a Cooperative Portlet Source builder call to the loansList model and then name it
openLoanDetail. Change the Type input to Property Broker Link, which replaces the links cre-
ated by the View & Form builder with links that send the loan ID to the Property Broker. Note that the links are replaced only when the model is viewed as a portlet and the Property Broker is correctly configured; otherwise, the links created by the View & Form builder call are retained.
TIP The Type input gives you four options for specifying a communication method. The first two, C2A Single Action and C2A Multiple Action, define click-to-action tags that can be placed on the page. Click-to-action is a feature of the WebSphere Portlet API, which has been deprecated in favor of the JSR-168 standard and is provided for backward compatibility only. The remaining two options define methods for publishing properties to the Property Broker. Whereas the Property Broker Link option automatically creates links on the page to communicate with the Property Broker, the Property Broker Action option creates an action that can be called from any control, such as a link, button, and so on. For more information, read the “Property Broker Action” section in this chapter.
In the Page location section of the Cooperative Portlet Source builder call, specify loansView_ViewPage as the Page and LoanID as the tag. This replaces the ID links created by the View & Form builder call with new links that publish information to the Property Broker. For portlets to communicate via the Property Broker, they need to use the same namespace. In the example in this chapter, the Cooperative Portlet Source and Cooperative Portlet Target both need to have the same namespace defined. Leave the default value for the Namespace input. The Output Definitions section defines the information that you send to the Property Broker. Fill out this section, as shown in Figure 7.6. Note that the Value input should read ${Variables/LoanLoopVar/Loan/LoanID}. This declares that whenever a loan ID link is clicked, the loan ID of the selected loan should be sent as a String called loanID to the Property Broker. Save the builder call when you are finished.
Figure 7.6
Configuring output definitions for the Cooperative Portlet Source builder call.
Your project now contains a list portlet. The next section describes how to test this portlet from your IDE.
Creating a Detail Portlet
177
Testing the List Portlet
Although you cannot test the communicative capabilities of the list portlet at this stage, because there is no detail portlet to communicate with, you should run the loansList model from the IDE to see if you are getting any errors. To do this, run the loansList model from the WPF Designer. You should see a list of loans in your default Web browser, as shown in Figure 7.7.
Figure 7.7
List of loans from the loansList model.
Notice that the ID of each loan is a clickable link. If you click on the ID link for a particular loan, you should see some details for that loan. For example, clicking on the ID for A Traveling Aunt should return the details shown in Figure 7.8.
Figure 7.8
Testing the getLoanDetail operation.
Note that currently the list portlet functions as both a list and detail portlet, because the View & Form builder call provides links that make it possible to open details on a loan from the loans list. However, when you deploy the loansList model as a portlet and configure the Property Broker, these links are replaced with links from the Cooperative Portlet Source builder call. When the links from the Cooperative Portlet Source builder call are used, the detail page in the list portlet cannot open when a loan ID is clicked. Close the loansList model when you’ve finished testing it. You now have a list portlet that provides a list of loans by consuming the loansService service. The next section describes how to create a detail portlet that displays details for a loan clicked in the list portlet. Creating a Detail Portlet
This section describes how to build a detail portlet, which communicates with the list portlet created earlier. As with the previous portlet, this portlet consumes the loansService service to obtain its loan information. Details for loans selected in the list portlet are brought up in the detail portlet via the Property Broker—a screen shot of the portlet is shown later in Figure 7.15.
178
Chapter 7 Communicating Between Portlets The model uses the following builders to provide its functionality: • Portlet Adapter • Service Consumer • Page (x2) • Action List • Data Page • Cooperative Portlet Target • Event Handler
Creating a Model The list model is a good starting point for building the detail portlet, so make a new copy of the list model by selecting the loansList model in the Project Explorer view, and then pressing Ctrl+C, then Ctl+V. Name the new model loanDetail when prompted. Double-click the loanDetail model to open it for editing. Before you add builder calls to communicate with the loansList portlet, you should first modify the loanDetail model so it does not conflict with the loansList model when it is deployed as a portlet. To do this, open the Portlet Adapter builder call in the loanDetail model, change the Name input to loanDetail, and then change the Portlet Title input to Loan Detail. This ensures that there are no conflicts between the list and detail portlets when you deploy them. Also, change the Portlet Description to Loan details. Now delete the Cooperative Portlet Source builder call from the loanDetail model, because in the current example the loanDetail model is a target of communication, not a source. Also, delete the View & Form builder because you are using a Data Page instead. You can reconfigure the View & Form so that it displays only detail information, but, in this case, it is easier to use a Data Page.
TIP A single portlet can be both a target and a source of communication. For example, you might want a modification in the detail portlet (say, to the date of a particular loan) to be reflected in the list portlet. In this case, both portlets are sources and are both are targets; an ID is sent from the list portlet to the detail portlet, and then a refresh request is sent back to the list portlet after the detail portlet is updated. Note also that a single portlet can define multiple sources and multiple targets.You might want to define multiple sources for a portlet if the portlet publishes more than one piece of information, and multiple targets are useful when you want to receive multiple properties from the Property Broker.
Creating a Detail Portlet
179
Adding a Default Message Page Note that when the loanDetail portlet loads, it should not display any loan information, but rather a message indicating that no loan is currently selected. To bring this about, you need to add another Page builder call to the model. Add a new Page builder call, and name the builder call defaultPage. Enter in the following HTML into the Page Contents (HTML) input:
No loan selected.
This displays the text No loan selected. in the Loan Detail portlet. Save the builder call when you are finished.
TIP You should use only the Page builder for small snippets of HTML specific to your application. Any HTML that you might want to share between projects should be included in an HTML file and then imported into your model using the Imported Page builder.
Adding a main Action Now, add an Action List builder call to the model. Call the action list main, which causes the action list to execute whenever the model is run. Enter defaultPage for the first action in the Actions input and save the builder call. The loanDetail model now displays the defaultPage whenever it runs.
Adding an Interface for Loan Details Add another Page builder call to the model; this creates an HTML page that displays whenever loan details are to be displayed for a loan. Change the Name of the builder call to detailsPage, and then enter in the following HTML into the Page Contents (HTML) input: <span name=”loanDetails”>
180
Chapter 7 Communicating Between Portlets
This page has a single span tag on it (loanDetails) that is overwritten with loan details using a Data Page. Save the builder call when you are finished, and add a Data Page builder call to the model to add the loan details. Type the name detailsPage for the Data Page, and then open the Select Action dialog for the Variable input. Select the Loan element from the results of the getLoanDetail operation, as shown in Figure 7.9, and press OK. This causes the Data Page to display the results of the getLoanDetail operation.
Figure 7.9
Selecting the Loan element.
Next, point the Page in Model input to detailsPage and change the Page Type to View Only so that there are no data entry controls inserted onto the page. Finally, specify detailsPage for the Location for New Tags input and save the builder call.
Defining the Portlet as a Cooperative Target The next step is to define the loanDetail model as a target for inter-portlet communication. To do this, add a Cooperative Portlet Target builder call to the model. Change the Event Name input to openLoanDetail, and then fill out the Input Definition section, as shown in Figure 7.10. These settings define the communication inputs and should be the same as the outputs defined in the Cooperative Portlet Source builder call in the loansList model. Keeping these settings identical makes it easier to understand what is being sent where, and prevents possible type mismatches. Leave the Output Definition section blank. There are no outputs for this builder call, because nothing is sent back after the loanDetail portlet receives the loan ID. Notice the namespace at the bottom of the builder call. This builder must be the same as the namespace defined in the Cooperative Portlet Source builder call, or else the two portlets cannot communicate (you can leave the default value). Enter in Displays loan detail for the Caption and save the builder call. Note that this caption describes what happens when the property changes, whereas the caption in the Input Definition section describes the property itself.
Creating a Detail Portlet
Figure 7.10
181
Configuring the Cooperative Portlet Target builder call.
Handling an Inter-Portlet Communication Event You now have an event called openLoanDetail, which you later link to a loan ID being clicked in the loansList model. However, you haven’t defined anything to happen when the openLoanDetail event is triggered. To do this, you need an event handler. Add an Event Handler builder call to the model and call it handleOpenLoanDetail. Select the openLoanDetail event from the Event Name drop-down box and notice that a String argument called loanID has been added in the Arguments section. This loanID is the same loanID as that specified in the Cooperative Portlet Target builder call, which means that you can now access the loan ID from the openLoanDetail event. Change the Return Type input to void, because there is no need to return any information to the caller of this event. You simply want to perform some actions, which you specify in the Actions input. Open the Select Action dialog for the first action and find the getLoanDetail operation under the Methods section. Notice that there are two versions of the operation. Select the one that enables you to specify arguments, as shown in Figure 7.11, and press OK. A Define Method Call Arguments dialog appears; select the loan ID from under the Variables section in the Select Action dialog, which should then populate the value ${Arguments/loanID} to the underlying dialog. Press OK when you are finished to accept the new action.
Figure 7.11
Adding the loansGetLoanDetailWithArgs method.
182
Chapter 7 Communicating Between Portlets
For the second action, select the details page from under the Page section of the Select Action dialog. It appears as ‘detailsPage’. The actions are now complete: The first action consumes the getLoanDetail operation on the loansService service, passing in the loan ID as a parameter. This loads only the information into a results variable, so the second action is necessary to display the results. Save the builder call when you are finished. Your project now contains a detail portlet. You will run a preliminary test on the detail portlet in the next section, and then test it in full after it has been deployed to a portal server and the Property Broker has been configured.
Testing the Detail Portlet At this point, you should test that there are no obvious problems in the loanDetail model by previewing it from your IDE. You should see the message shown in Figure 7.12 displayed on the screen.
Figure 7.12
Testing the loanDetail model.
After you have configured the Property Broker later in this chapter, you can do a full test of the communication between the list and detail portlets. Before you do this, however, you should rebuild your application on the portal server. For instructions on how to do this, see the example in Chapter 1. Then, you can view the list and detail models as portlets. After you have added the portlets to a page in the portal, they should appear, as shown in Figure 7.13.
Figure 7.13
The loans application portlets.
Your application has now been successfully deployed to the portal server, although the portlets contained in the application cannot communicate with each other yet. The next section discusses how to set up this communication using the WebSphere Property Broker.
Configuring the WebSphere Property Broker
183
Configuring the WebSphere Property Broker
When setting up inter-portlet communication via the WebSphere Property Broker, some configuration is required to link or wire portlets together after they have been deployed. This section walks through the process of configuring the list portlet to send a loan ID to the detail portlet, which then displays details for the selected loan. At this point, note that the portlets are not wired together, so if you click on a loan in the list portlet, the details are opened within the same portlet, and the detail portlet still displays the No loan selected. message. To configure the Property Broker, first log in to the portal as a user who has access to wire portlets together. You need a user with Privileged User access or higher to the portal page that is to contain the loan portlets. Navigate to where you added the Loan Detail and Loans List portlets and open the Page Menu for the page. You can do this in WebSphere Portal 6, for example, by moving the cursor to the right of the page title until a small arrow icon is revealed, as shown in Figure 7.14. Click the arrow to open the Page Menu, and then select Edit Page Layout.
Figure 7.14
Opening the Page menu.
Click on the Wires tab, which should be the last tab on the Page Layout page. If you can’t see the Wires tab, check that you are logged in as a user with at least privileged user access to the current page. This page lets you define inter-portlet communication for the portal. You can even define communication from one page to another. Note that you only define communication for portlets that have been added to a page in the portal. In this step, you add a row to the Wires page to define the communication between the list and detail portlet. Select Loans List from the Source portlet dropdown, and then select loanID in the Sending dropdown. These values are taken from the Cooperative Portlet Source builder call, although they can be defined using IDEs other than WPF (WPF applications can communicate with non-WPF applications, and vice-versa). The current page is automatically selected as the target page. Select Loan Detail for the Target portlet, and set the Receiving dropdown to ‘Displays loan detail, loanID’. This is the action caption and input name defined in the Cooperative Portlet Target builder call. The final drop-down box gives you a choice of creating the wire as personal or public. A personal wire is accessible only by the current user, whereas a public wire is accessible to all users. Leave the default setting and press the icon to add the wire to the portal. Wait for the page to reload and press the Done button to return to the portal. You have now configured communication between the list and detail portlet.
184
Chapter 7 Communicating Between Portlets
Testing Inter-Portlet Communication When you press the Done button on the Wires page, you are returned to where the list and detail portlets are displayed on the portal. To test that the communication is working correctly, click on a loan in the Loans List portlet. When the page reloads, a loan should be opened in the Loan Detail portlet, and the Loans List portlet should still display the loans list, rather than the loan detail, as it did previously. For example, clicking on 00003 should bring up loan details for The Interminable Journey, as shown in Figure 7.15.
Figure 7.15
Testing inter-portlet communication in the portal.
You have now successfully created, deployed, and tested an application consisting of intercommunicating portlets. However, the method employed in this section is only one of several ways to set up inter-portlet communication. Some alternatives to this approach are discussed in the next section.
Alternative Communication Methods Although the WebSphere Property Broker is a highly flexible and versatile method of interportlet communication, there are some situations where other approaches might provide more value. For example, the Property Broker is available only in WebSphere Portal, so if you transfer your portlets to another type of portal server, you need to modify the communication technique used. Second, communicating via the Property Broker requires that a wire must be set up between two portlets before they can communicate; this creates extra configuration steps and leaves more of the configuration process open to human error—although, it can also be regarded as a good thing, because it gives end users more control over the communication process. There are several alternative options available for setting up inter-portlet communication in WPF, and each has its own strengths and weaknesses. These methods are described next.
Alternative Communication Methods
185
Property Broker Action Property Broker Actions, as opposed to Property Broker Links, let you define your communication as actions rather than as clickable links. The advantage of this approach is that you have more control over how the action is run—whether it be programmatically or from a UI control; however, the disadvantage is that more WPF configuration is required than when using Property Broker Links. The communication on the Property Broker is no different—that is, you still publish properties in the same way. Property Broker Actions are not really an alternative method of communication to Property Broker Links, because they are really just Property Broker Links that don’t produce a link in the UI. However, Property Broker Actions can be quite useful, and because the configuration process is sufficiently different, an example is included. The following builder is added to the loansList model in this section: • Link
Modifying the Cooperative Source To alter the communication in the loans application to use Property Broker Actions, first open the Cooperative Portlet Source builder call in the loansList model. Change the Type input from Property Broker Link to Property Broker Action. This creates an action to publish the loan ID to the Property Broker, but it does not create the corresponding trigger for the action, such as a link. Save the builder call when you are finished.
Adding a Link and Configuring Communication To trigger the Property Broker Action, add a Link builder call to the loansList model. Name the Link loanIDLink. In the Page Location section of the builder call, fill out the Page input as loansView_ViewPage and the Tag input as LoanID. This replaces the loan ID links created by the View & Form builder call with the new links defined by the Link builder. Select pbAction_openLoanDetail for the Action input, which triggers the Property Broker Action when a loan ID is clicked. Notice that two arguments with the pbAction_openLoan Detail_Arg prefix are automatically added to the Input Mappings input in the Arguments section of the builder call. You need to replace the values of these arguments. The first argument to any Property Broker Action must be the name of the action (that is, pbAction_openLoanDetail). This argument is used by the portal to associate the Property Broker call with a particular Property Broker action. The argument is added automatically when using Property Broker Links, but needs to be added manually when using Property Broker Actions. Rename the argument to ACTION_NAME, and then type the name of the action (pbAction_openLoanDetail) as the value. Note that the name and value of the argument must be written as specified, or the interportlet communication cannot work. Note that the Evaluate Arguments input must be set to ‘As the page is rendered’; if it is not, the links cannot be generated correctly. Because the Cooperative Portlet Source builder call is expecting an argument, you must also change the second argument for each link created by the Link builder call. To do this, change
186
Chapter 7 Communicating Between Portlets
the second argument to point to the currently selected loan, and change the name of the argument to loanID so that it is easier to identify, as shown in Figure 7.16. Note that the value of the argument is the same as the value of the argument specified in the Cooperative Portlet Source builder call. In the next step, you remove the value from the Cooperative Portlet Source builder call so that the value from the Link builder call is used instead. Save the builder call when you are finished.
Figure 7.16
Adding an argument to the Link builder call.
Open the Cooperative Portlet Source builder call and delete the Value input for the loanID output. The value for the loanID output is now taken from the Link builder call. You have now finished configuring the loansList model. No changes are required to the loanDetail model, because the parameter published to the Property Broker is the same as it was before. Note that the ACTION_NAME parameter does not need to be specified in the Cooperative Portlet Source builder call. Rebuild your portlet application by right-clicking on your project and selecting Portal Server WAR, Build Portlet War. You don’t need to reset or modify the wire, because the structure of the communication hasn’t changed. The only thing that has changed is how you actually trigger the event to send to the Property Broker. To test the new communication method, navigate to the loans portlets in the portal and click on a loan in the Loans List portlet. The corresponding loan should open in the Loan Detail portlet. You have now successfully configured the loans application to use Property Broker Actions rather than Property Broker Links. Again, note that this change doesn’t change the type of communication, but merely how you use it in WPF.
WPF Event Model When using the WPF event model, communication between portlets occurs by triggering and handling an event. This approach is easy to use and configure: First, you declare an event in a source and target portlet, and then you trigger, or fire, the event in the source portlet. Finally, you handle the event in the target portlet. Events can be fired and targeted to a particular portlet,
Alternative Communication Methods
187
or they can be fired in such a way that they can be accessed by any other portlet in the same WAR file. You can trigger events using the WPF event model in two ways: on the server and on the client. Both methods enable you to access and manipulate resources on the server, such as reading and updating data from a service provider, but triggering events on the client also enables you to perform partial page refreshes. Partial page refreshes improve the speed of your application, and users appreciate that they can interact with your portlet without having to constantly reload the entire portal page. Partial page refreshes are covered in more detail in Chapter 13, “Using Ajax and Dojo.” Note that you can only pass parameters to events when using server-side events, which is why both types of events are sometimes used together; for example, you might use a server-side event to update data in a Lotus Notes database, and then use a client-side event to partially refresh the page with this data. The example at the end of this section uses only a server-side event. Be aware that when using the WPF event model approach, all inter-communicating portlets need to be contained in the same WAR file, which also means that they all need to be built with WPF. As a result, the WPF event model is most useful for setting up inter-portlet communication when interoperability with other applications is not a concern. The following builders are added in this section: • Event Declaration (x2)
Defining an Event To alter the communication in the loans application to use the WPF event model, the first step is to define an event, which is triggered every time a user clicks on a loan ID. Add an Event Declaration builder call and change the Event Name input to openLoanDetail. Add an argument called loanId of type String to the Arguments section so that any triggers for the event need to pass in a String value when they trigger the event. This String corresponds to the loan ID of the loan that the user clicks on. The event is triggered any time details for a loan are opened. Save the builder call when you are finished.
Triggering the Event Now that you have finished configuring the event declaration, you should modify the loansList model to trigger the event. Open the Link builder call in the loansList model and change the Action input to fireopenLoanDetail. You can select this from the Select Action dialog. If the fireopenLoanDetail action does not appear, make sure you have saved your Event Declaration. The fireopenLoanDetail action fires the openLoanDetail event instead of publishing the loan ID to the Property Broker.
188
Chapter 7 Communicating Between Portlets
TIP If you want to fire an event to a specific target, you can use the fireTargeted trigger. For example, to fire an event to the loanDetail model so that it can’t be accessed by other models, select fireTargetedopenLoanDetail instead of fireopenLoanDetail. You are prompted for the target of the event, and you can specify four possible targets: ${Java/WebAppAccess.EVENT_TARGET_ALL}) This fires the event to all models in the current application. ${Java/WebAppAccess.EVENT_TARGET_PARENT}) This fires the event to the parent model of the current model, which is any model that is used to load the current model (for example, if you’re using a Linked Model builder call). ${Java/WebAppAccess.EVENT_TARGET_SELF}) This targets the event to the current model. ${Java/WebAppAccess.getModelInstance(targetModel)}) This targets the event to the targetModel model. You should substitute the text targetModel with the actual name of the target model—for example, loanDetail.
Notice that an argument called fireopenLoanDetail_Arg1 has been added underneath the Arguments section of the builder call. This argument has been created by WPF to cater for the loan ID that is supposed to be passed to the event, but you still have two arguments left over from when you configured the Property Broker Action. The loanID argument left over from the Property Broker Action is the argument you want to pass to the event when it is fired, so remove the other two arguments from the Input Mappings input. The Arguments section should now appear, as shown in Figure 7.17.
Figure 7.17
Setting arguments for the Link builder call.
You have now finished configuring the Link builder call. Disable the Cooperative Portlet Source builder call as it is not being used, but you might want to use it again later. You can disable builder calls by right-clicking on them and selecting Disable. Save the model when you are finished.
Alternative Communication Methods
189
Handling the Event Now, you need to configure the loanDetail model to handle the openLoanDetail event. To do this, first open the loanDetail model and disable the Cooperative Portlet Target builder call, because you are no longer using the Property Broker (don’t save the model yet). Copy the Event Declaration builder call from the loansList and paste it into the loanDetail model. This is done so that both models are using the same event. Because the event name in your Event Declaration is the same as the event name in your Cooperative Portlet Target builder call, no changes are required to the Event Handler builder call; it processes the openLoanDetail event as it is defined by the Event Declaration now that the Cooperative Portlet Target is disabled. Save the model to complete the configuration of your application to use inter-portlet communication via events. To test the new communication method, rebuild your application and navigate to the loans portlets in the portal. Click on a loan in the Loans List portlet, and the corresponding loan should open in the Loan Detail portlet.
Shared Variables Shared variables are perhaps the quickest and easiest way to implement inter-portlet communication. Shared variables can be stored in one of four ways: in the HTTP session, in the HTTP request, in the JVM where the current WebApp is running, or using a custom Java class. Variables stored in the JVM are accessed as static variables (the same copy is used across all instances of the portlet). To create or read a shared variable from a model, add a Shared Variable builder and designate a variable to share. Note that variables stored in the HTTP session increase the amount of memory consumed by the session, and variables stored in the HTTP request have limited scope. Also, variables in the JVM cannot be accessed outside the JVM (for instance, across a cluster), and custom variable storage methods require additional Java development. Provided you keep these concerns in mind, however, shared variables are a powerful and easy-to-configure method of inter-portlet communication. In the previous section, it was necessary to pass the loanID from the event trigger to the event handler via an argument to the event. In this section, you use a shared variable in place of the loanID argument so that when you change the loanID variable in the loansList portlet, it is read in the loanDetail portlet and used to update the loan information. Note that in this example, an event is still used to access loan details for the loan ID stored in the shared variable. This is because even though the loan ID is shared between both models, the details portlet won’t know that it is supposed to refresh the other loan details unless an event is fired that causes this to happen. Because changes to a shared variable do not in themselves trigger any events, shared variables are often used in conjunction with the WPF event model. The following builders are added in this section: • Variable (x2) • Shared Variable (x2) • Action List
190
Chapter 7 Communicating Between Portlets
Removing the Event Argument To alter the communication in the loans application to use the shared variable approach, first remove the argument from the Event Declaration builder call in both the loansList and loanDetail models, because you are now storing the loan ID in a shared variable. The argument is listed in the Arguments input of the Event Declaration. Save both instances of the builder call when you have removed the arguments.
Adding a loanID Variable Next, add a Variable builder call to the loansList model and call it loanID. Change the Type input to String and save the builder call. This builder call holds the value of the currently selected loan ID and is used in both models, so copy and paste the builder call into the loanDetail model. Save the model when you are finished. By default, each model uses its own instance of the variable created by the Variable builder call. To share the variable, you need to add a Shared Variable builder call to each model. Add a Shared Variable builder call to the loansList model first, and then name the builder call loanIDShared. Change the Variable input to loanID. The Scope input enables you to specify where the shared variable will be stored. As discussed earlier, you have four options when setting a shared variables scope: Session, Request, Application, and Custom. Make sure this input is set to Session, which means that the variable value persists even when other links on the page are clicked. Then, change the Unique ID input to loanID and save the builder call. Make a copy of the Shared Variable builder call and paste it into the loanDetail model. Save the model when you are finished. Creating these Shared Builders shares the loanID variable in the HTTP session using the text loanID as an identifier. The identifier can then be used from other processes and portlets to access the shared variable from the HTTP session. When referring to the variable from WPF, however, you need only to reference the name in the Variable builder call.
TIP When referring to a shared variable in other builders, refer to the Variable builder call rather than the Shared Variable builder call.
Configuring Actions for loansList The next step is to set up two actions in the loansList model. The first action changes the value of the loanID variable when a loan ID is clicked, and the second action fires the new openLoanDetail event without any arguments. To add these actions, add an Action List builder call to the loansList model and call it selectLoan. Enter in an argument called loanID of type String in the Arguments input, and then change the Return Type input to void to prevent the Action List from returning a value.
Alternative Communication Methods
191
For the first action, select Assignment from under the Special heading in the Select Action dialog. For the Target variable, select the loan ID variable (Variables/loanID), and for the Source variable, select the loanID argument from the Action List (${Arguments/loanID}). The Make Assignment dialog should appear, as shown in Figure 7.18. Press OK when you are finished to accept the action.
Figure 7.18
Configuring the Make Assignment dialog.
For the second action, fire the openLoanDetail event by selecting fireopenLoanDetail from the Select Action dialog. Save the builder call when you’ve finished adding both actions.
Running selectLoan Open the Link builder call and change the Action input to selectLoan. This runs the selectLoan action list whenever a loan ID is clicked. WPF assumes that you want to set a new argument for the selectLoan action list, so it automatically adds an extra argument to the Input Mappings input. You can delete the extra argument, because the previous loanID argument will suffice. Save the model when you are finished.
Using the Shared Variable in the loanDetail Model You need to configure the loanDetail model to use the shared variable instead of the argument in the old openLoanDetail event. To do this, open the handleOpenLoanDetail Event Handler builder call and change the first action in the Actions input to use the loanID variable instead of the loanID argument, which no longer exists. The new action should read loansGetLoanDetailWithArgs(${Variables/loanID}). Save the builder call when you are finished. To test the new communication method, rebuild the application and navigate to the loans portlets in the portal, and then click on a loan in the Loans List portlet. The corresponding loan should open in the Loan Detail portlet. You have now successfully configured the loans application to use shared variables for its inter-portlet communication.
Click-to-Action (C2A) Click-to-action (C2A) facilitates inter-portlet communication through the use of portlet menus that the user can configure. The C2A builders have been deprecated as of WPF 6.0, because they
192
Chapter 7 Communicating Between Portlets
rely on the now deprecated WebSphere Portlet API, rather than the JSR-168 standard, so you should avoid using C2A as your inter-portlet communication mechanism.
When to Use Inter-Portlet Communication When developing portlets, in WPF or otherwise, spreading an application’s functionality across several inter-communicating portlets can help to provide a more customizable interface and ultimately produce more extensible applications. However, some scenarios are more conducive to inter-portlet communication than others. For example, you might not want to separate the list and detail portions of a data access function if they are going to be used on a page that already contains a list and detail portlet, because multiple list and detail portlets on the same page can clutter and complicate the interface. Similarly, if a list portlet depends on another portlet on the page for its information, you might want to include the list as a second page in that portlet, rather than as a separate portlet. The decision as to whether to combine functions into a single portlet is also influenced by how many other portlets you expect will be used on the same page and how much space they will take up. If you expect screen real estate to be scarce, perhaps you need to cater to 800x600 resolutions, and if you expect multiple portlets to be used at once, it might be best to economize and combine several functions into a single portlet wherever possible, rather than implement the same functions using inter-portlet communication. Having said this, inter-portlet communication is a powerful tool for portlet developers to improve the usability of their applications and should be used wherever possible. As a general rule, potentially reusable, high-level functions, such as the list and detail components of a data portlet, are the best candidates for inter-communicating portlets, because they can be easily incorporated into other applications. The information sent between each portlet is fairly simple— usually just a uniquely identifying key for a particular record or document—which reduces the information sent between the server and the client, and therefore also speeds up your application. It is also the sort of information that could be meaningful to other functions or processes. Clicking on an item in a list portlet, for example, could trigger other portlets on the page to open charts, reports, or edit forms for that item.
Summary In this chapter, you learned about the different approaches to implementing inter-portlet communication in WPF, in addition to the strengths and weaknesses of each approach. You also created a library loan system that consisted of a service provider and two portlets, which demonstrated each approach. When you clicked on a loan in the list portlet, it caused details on that loan to be opened in the detail portlet. Both portlets retrieved their data from the service provider. The next chapter, “Using Java in Portlets,” discusses how to utilize Java code in your WPF applications.
Important Points
193
Important Points • WPF portlets can communicate with each other using the WebSphere Property Broker, shared variables, or the WPF event model. Each approach has its own strengths and weaknesses. • Communication via the WebSphere Property Broker is facilitated by publishing properties to a WebSphere Portal Server mechanism known as the Property Broker, which then routes these properties off to other portlets. Implementing communication using the Property Broker offers considerable flexibility, because it means that your portlets can communicate with portlets in other WARs, or even portlets built using environments other than WPF. Also, because WPF provides builders to automate the communication configuration, you don’t need to write any code to implement the communication. However, configuration in the portal is required before this mode of communication will work. • Shared variables can be configured to use a number of different stores and are perhaps the quickest and easiest way to implement inter-portlet communication in WPF. However, unless your portlets are all in the same WAR file, you need to write code to retrieve the variable values. This approach is best suited to storing one or two small pieces of information used across an entire WPF application. • The WPF event model also offers a quick and easy approach to inter-portlet communication, and gives you the added benefit of a prepackaged WPF builder to handle communication events without regenerating the entire portal page. This approach has limited extensibility because all communicating portlets need to be contained in the same WAR file to be used effectively, and they need to be developed in WPF; however, it is well suited to WPF applications where events in one portlet trigger events in another.
This page intentionally left blank
C
H A P T E R
8
Using Java in Portlets
Although WebSphere Portlet Factory (WPF) automates much of the Java development required to build portlet applications, you can certainly fine-tune and extend your WPF applications with a little well-placed Java code. This chapter explains how you can work Java into your portlet applications and walks through the creation of a simple portlet demonstrating some common uses for Java. By the end of this chapter, you will have a good understanding of the different approaches available for extending your applications with Java, as well as knowledge of the benefits and drawbacks of each approach. The example application discussed in this chapter utilizes one model and several Java source files, which are available for download from ibmpressbooks.com/title/9780137134465, under the Chapter 8 folder. (Instructions for copying these files into your project are included in a readme.txt file in the same folder.) However, to increase your understanding of the topics discussed in this chapter, it is recommended that you create the files yourself by following the example. The following topics are covered in this chapter: • Java development considerations • Java methods • Java Application Programming Interfaces • Java beans • Creating a Java bean and bean manager • Creating a service provider • Creating a shopping cart portlet • Java archives
195
196
Chapter 8 Using Java in Portlets
Java Development Considerations If you look under the covers of a WPF application, you can see that Java is used extensively to implement each builder call; indeed, the Web application executed on the portal server is itself a Java application. You can view the Java code created by WPF from the WPF Designer by opening the WebApp Tree tab for a particular model and viewing the contents of the Method and Linked Java Object headings (see Figure 8.1). You cannot edit this Java from the WebApp Tree tab, but you can influence how it is created (or overwrite it entirely) by modifying the inputs in builder calls or by adding your own Java methods to the model.
Figure 8.1
Viewing a model’s Java methods.
There is nothing wrong with developers using Java in their WPF applications per se; Java undoubtedly improves your applications in certain scenarios, and in others, it is indispensable (such as in the implementation of complicated business logic). In most cases, however, the WPF builders are preferred to Java code as they are less error prone and easier to maintain. It also usually takes much longer to write a Java class than it does to implement the same functionality using builders.
Java Development Considerations
197
TIP If you have sections of Java code that are frequently used across more than one application, you can include them inside a custom builder in WPF. This makes it easier for nonJava developers to interact with the code, helps enforce development standards, and reduces human error. Custom builders are discussed in Chapter 15, “Performance and Process Optimization.”
Although it is important to keep the use of Java to a minimum, you should pay particular attention to minimizing the amount of Java used in presentation-level components (that is, those that are directly related to the look and feel of your portlet). A common problem in ordinary JSP development, for example, is the inclusion of inline Java scripts on the page. This increases the complexity of the page and also increases the skills required to understand it. Unless your Java code is performing some very basic calculation directly relevant to the presentation of data, you should abstract your Java code out into separate classes, and then surface them to your presentation components via a service provider. Java can be particularly helpful in several areas of development, however: • Processing and business logic—Although the Action List builder can be used to define program execution steps (and can also implement basic conditional statements), Java will give you much more control over how the logic in your application is handled. You cannot, for example, implement do…while or for…next loops using only the default WPF builders. You can, however, use conditional statements. See the Conditional Statements in Action Lists sidebar for instructions on how to do this. For anything but the most basic program logic, then, it is likely that you will need to use Java to implement at least some of the logic in your application. • Action Lists can become difficult to read and understand if you are using large numbers of actions—In these cases, it is often preferable to use Java code.
CONDITIONAL STATEMENTS IN ACTION LISTS To insert a conditional statement into an Action List builder, bring up the Select Action dialog (by clicking the ellipsis button next to one of the actions in the Actions input) and navigate to the Special, Conditional category. The if, else, and endif statements you find there can be used to create conditional statements. Conditional statements in WPF must take the following form (note that you can also nest conditional statements inside other conditional statements): IF ([condition]) THEN [run action] !ELSE [run alternative action] !ENDIF
198
Chapter 8 Using Java in Portlets
Note that conditional statements can make your Action Lists difficult to read, so if you find that you are making extensive use of these statements (particularly if you are nesting conditional statements inside other conditional statements), you should consider using a Java method instead.
• Manipulating XML documents—Again, a number of builders in WPF can manipulate XML (the Transform family of builders, for example), but the Java IXml interface provides you with a greater degree of control. An example of manipulating XML using the IXml interface is given in Chapter 9, “Using Web Services and Manipulating XML.” • Interfacing with other Java frameworks—If your application needs to interact with other Java classes (say, for example, to process security information for the current user), you might need to write some Java methods to encapsulate this behavior. This is particularly true when using frameworks that make heavy use of Java beans; however, be aware that you can surface functionality from Java beans using builders as well. An example using the default WPF builders is given later in this chapter. The rest of this chapter explains some of the different methods available for adding Java code to WPF applications. At the end of the chapter, a simple test application is created to demonstrate these techniques.
Java Methods You can insert Java methods into WPF applications in a number of ways. This section describes the relative benefits and drawbacks of each approach. Note that Java methods can also be encapsulated in JARs and Java beans, which are discussed in separate sections later in the chapter.
Inline Java Java statements can be used to evaluate and return expression results for builder inputs that accept indirect references. Indirect references are where the input contains a reference to another variable or function that provides the input’s value, such as the Actions input in the Action List builder, or the Text input in the Text Input builder. For example, when you specify a parameter for an action in an Action List builder, an additional Java category will show up in the Choose Reference dialog (see Figure 8.2). The values under this category will give you access to particular Java objects, with the exception of the category, which you can use to insert the results of a Java expression into the action list. All inline Java statements are in the form ${Java/[Java expression]}. For example, the expression: ${Java/webAppAccess.getModelName()}
could be used to return the name of the current model. (The webAppAccess object is a memory resident object you can use to access aspects of the WebApp and is discussed in the “Java Application Programming Interfaces” section later in this chapter.)
Java Methods
Figure 8.2
199
The Java category.
WARNING Each inline Java statement should be a single Java expression; you cannot separate parts of an inline Java statement using semi-colons. Note that you do not have to close off an inline Java statement with a semi-colon. Also, do not use curly braces ({ and }) inside an inline Java statement, as they are used to delineate the boundaries of the entire statement.
Inline Java should be used with caution because it can make action lists difficult to read and maintain. However, it can be useful for debugging, especially when used in conjunction with the SystemOut action from the Select Action dialog. The following line, for example, could be used to write the name of the current model to the console: SystemOut!${Java/webAppAccess.getModelName()}
200
Chapter 8 Using Java in Portlets
TIP The Special, Java category in the Choose Reference dialog enables you to access several commonly used Java objects. Of particular use in this category is the UserID object, which returns the name of the current user, and the RequestInputs object, which returns a string containing all of the values the user has entered onto the form. The HttpServletRequest and HttpServletResponse options can be used to gain access to the javax.servlet.http. HttpServletRequest and javax.servlet.http.HttpServletResponse objects. The use of these objects is discussed in the “Java Application Programming Interfaces” section later in this chapter.
Action Lists The Action List builder in WPF enables you to run Java commands and methods in the same way that you can from many of the builder inputs in WPF, only it also enables you to include more than one of these actions in a sequence. As a result, Action Lists are very useful for organizing execution flow in your application. For example, an Action List might be used to set the value of a variable, then call a Java Method (passing in the variable as a parameter), and then display a results page. Action Lists also enable you to use limited conditional statements (as discussed in the “Conditional Statements in Action Lists” sidebar earlier in this chapter). Although you will no doubt need to make extensive use of Action Lists in your WPF applications, you should try to keep them fairly simple (that is, using as few actions as possible) to maintain their readability. If you find that your Action Lists have so many actions that they are difficult to understand, you should look at the Method builder and Linked Java Object (LJO) builders as possible alternatives (both of these approaches are discussed in the following sections).
The Method Builder If your Java methods are a bit more involved than a single expression, you might want to use the Method builder to contain your Java code. The Method builder enables you to encapsulate a Java method within a single set of curly braces (you cannot define multiple methods, or any classes, in a Method builder). You can then call this Java method from other builders or Java methods. You can also specify a list of arguments, a return type, and a list of imports for the method (note that you do not need to import any of the WPF classes or any of the Java core classes). You can see a list of all the methods in the current WebApp by opening a model, selecting the WebApp Tree tab, and navigating to the WebApp, Methods section. The Method builder can also be used for overwriting functionality created by other WPF builders. Although overwriting builder functionality is error prone and should be used with caution, it also gives you that extra degree of control when you find that simply modifying the results of a builder is not sufficient. For example, you might look on the WebApp Tree tab and notice that WPF has created a method for you called page1_SaveData, which handles the save process for page1. This method might provide some functionality that you don’t want, or that you want to
Java Methods
201
modify in some way. To modify the method, then, you can give your Method builder the same name as the method listed in the WebApp Tree, and then enable the Rename Existing Input box. Copy and paste the method from the WebApp Tree into your own method, and then change the code to your liking. You do need to be aware of a number of caveats when using the Method builder. The first is that the Method builder does not have all of the features of the standard Java editor used in Eclipse. Also, Java Methods are harder to share between developers when they are contained in Method builders. These factors combine to make maintenance of Method builders more difficult than maintaining traditional Java files; as a result, the Method builder should only be used for simple methods that won’t be shared between models or projects. A good implementation of the Method builder, for example, would be encapsulating logic for other Java calls. Also, due to its convenience, the Method builder is well suited to quick and simple Java methods. You should, however, minimize the amount of Method builders used in an application, and if you intend to distribute your code between models or projects (or your methods are quite long), you should consider using a Linked Java Object builder instead (LJOs are discussed in the next section).
WARNING If you receive the message “sun.tools.javac.Main has been deprecated” when executing a Java method, the Java Virtual Machine (JVM) has been unable to run your Java code. Check the error message carefully for more precise details and make sure no errors are listed in the Problems view in the WPF Designer.
The numerous Java snippets included in the “Java Application Programming Interfaces” section of this chapter are ideal candidates for Method builder code. The example included in the “Java Beans” section of this chapter also demonstrates how you can use the Method builder to interact with an LJO and return a Java object to another builder call.
The Linked Java Object Builder The final option available for inserting Java methods into a WPF application is the Linked Java Object (LJO) builder. An LJO links to an actual Java .class file in your project, and makes its methods available to other builders and Java methods in the model. You can see a list of all the LJOs in your project by opening a model, selecting the WebApp Tree tab, and navigating to the WebApp, Linked Java Objects section. LJOs have several advantages over the Method builder and inline Java code: • They are easier to share between developers. • They enable you to take advantage of all the Eclipse Java Editor features and shortcuts. • You can make use of pre-existing Java files.
202
Chapter 8 Using Java in Portlets
One disadvantage of using an LJO is that you need to manually write code to import Java libraries and define classes (tasks that you don’t have to perform when using the Method builder). However, these tasks are relatively straightforward and shouldn’t pose any problems for those who have any familiarity with Java. You can use two locations when inserting Java files into your project. If you have (or want to create) .java files that you plan on editing, insert the files into the source folder in your project. By default, the source folder is the WebContent/WEB-INF/work/source directory of your project. A link to this folder is also available under the root folder of your project in the Project Explorer view. Whenever you save a Java file in this directory, the Java compiler creates (or updates if it already exists) a binary version of the file (located in the WebContent/WEB-INF/work/classes directory by default) that can then be linked to from an LJO or executed from other Java code. If, however, you don’t want to modify the source code and just want to use a precompiled version of a Java class, you can insert it into the WebContent/WEB-INF/classes directory or WebContent/ WEB-INF/lib directory. Files stored in these directories are not automatically updated when you save source files elsewhere in the project.
TIP The dynamic loading of class files is indispensable in development and is enabled by default in WPF. Only classes in the WebContent/WEB-INF/work/classes directory are loaded dynamically.
The example at the end of the “Java Beans” section of this chapter demonstrates how you can use an LJO to create a factory class, which you use to access and manipulate customer information stored in a Java bean.
Java Application Programming Interfaces When writing Java methods in WPF, you can use a number of Application Programming Interfaces (APIs) that interact with WPF artifacts. Some of the more useful APIs used in WPF are listed in this section.
TIP You can bring up the Javadoc for WPF APIs by navigating to the IBM WebSphere Portlet Factory Designer, Reference, API documentation section in the WPF Designer help.
When using Java APIs in the WPF Designer, you need to make sure the Java compiler knows where to find all of the relevant API classes. Classes in the WPF foundation libraries (such as WebApp and IXml) are automatically found, as are all of the basic classes in the Java runtime
Java Application Programming Interfaces
203
library (such as System). However, other classes need to be qualified (you can see which classes are automatically imported by scrolling to the top of the WebApp, Methods section in the WebApp Tree tab of the Model Editor). To qualify classes when using the Method builder, you can either type the fully qualified name of the class every time you use it (for instance, javax.naming.Context), or you can type just the class name (for instance, Context) and then specify the classes that you want to access in the Import List input (for instance, javax.naming.*). Similarly, when using LJOs, make sure you import the appropriate classes using the import command (for instance, import javax.naming.*).
com.bowstreet.WebApp.WebAppAccess The WebAppAccess interface is arguably the most useful interface available in WPF, as it provides access to a running instance of a WebApp—that is, the executed product of your builders and Java methods (see Chapter 1, “Introduction to WebSphere Portlet Factory,” for a discussion of the WebApp in WPF). Methods defined in Method builders and inline Java calls automatically have access to an object called webAppAccess (which implements the WebAppAccess interface), giving you access to the model that contains the method. To use this object in LJO methods, however, you need to pass the webAppAccess object into the desired methods as a parameter.
TIP If you want to use the webAppAccess object in an LJO, you need to declare an argument of type WebAppAccess as the first argument in an LJO method. When the LJO method is called from your model, WPF automatically passes the webAppAccess object as the first argument.
To access the WebApp for a linked model (that is, a model accessible to your model via a Linked Model builder call), use this command: webAppAccess.getLinkedModelInstance(“linkedModelBuilderCall”);
where linkedModelBuilderCall is the name of a Linked Model builder call in the current model that links to the WebApp that you would like to access. To get access to a WebApp outside the scope of the current model (that is, one that is not available through a link in the current model), use this command: WebAppAccess remoteWebAppAccess = ¯webAppAccess.getModelInstance(“modelPath/ModelName”, “”, ¯false);
where the first parameter is the path and filename of the model you would like to access (relative to the WebContent/WEB-INF/models directory). The second parameter is the profile set and profile combination that you would like to apply to the model (for example, Customer!Admin could be used to specify the Admin profile in the Customer profile set; alternatively, specify an empty
204
Chapter 8 Using Java in Portlets
String (“”) to use the default settings). The third parameter is a Boolean value determining whether the WebAppAccess object is created as a singleton (a singleton is a class that can only be instantiated once per JVM, which prevents multiple copies of the class from being created). A description of some of the more useful methods in the WebAppAccess interface, along with examples of how to use these methods with the webAppAccess object, are described in Table 8.1. The full Javadoc for the WebAppAccess class is available in the WPF Designer help.
Gets a reference to the javax.servlet.http.HttpServlet Request object for the current user request. This object is useful for things like getting cookies and session information.
Gets a reference to the javax.servlet.http.HttpServlet Response object for the current user request. This object is useful for things like setting page statuses and adding cookies.
java.lang.String value = requestInputs.getInputValue (“ID”); com.bowstreet.webapp.util. UserInfo getUserInfo()
Used to get a handle to user information, such as the user ID.
com.bowstreet.webapp.util. UserInfo userInfo = webAppAccess.getUserInfo(); java.lang.String id = userInfo. getUserID();
com.bowstreet.webapp. Variables getVariables()
Used to get all of the input values for the current request.
com.bowstreet.webapp. Variables variables = webAppAccess.getVariables(); java.lang.String value = variables.getVariable(“ID”).get Value();
com.bowstreet.WebApp getWebApp()
Gets a reference to the shared WebApp from which all WebApp instances for a particular model are created. Using this interface, you can retrieve such things as method lists and data service lists, and also add these elements dynamically.
Used to invoke executable WPF artifacts, such as methods, actions, action lists, pages, or linked models.
//to call an executable WPF artifact webAppAccess.processAction (“builderName”); //to consume a service operation webAppAccess.processAction (“serviceCallName.invoke”);
void processPage(java. lang.String name)
Displays the output for a particular page. Note that multiple processPage methods can be used to display several pages on the same screen, one after the other.
//for a page in the same model webAppAccess.processPage (“page1”); //for a page in a different model webAppAccess.processPage (“linkedModelName.page1”);
com.bowstreet.webapp.Variables The Variables interface provides access to the variables created by the Variable builder, enabling you to both get and set variable values. You can get access to the variables in a WebApp by using the getVariables method of the WebAppAccess class. To get the value of a String variable called id, you can use the following line: java.lang.String value = ¯webAppAccess.getVariables().getString(“id”);
To set the variable’s value, you can use this line: webAppAccess.getVariables().setString(“id”, “newIDValue”);
Note that each variable data type has its own get and set methods, which are listed in Table 8.2. You can also retrieve the data type of a variable as a String, using a line similar to the following: java.lang.String variableType = ¯webAppAccess.getVariables().getVariable(“id”).getType();
Java Application Programming Interfaces
Table 8.2 Data Type
207
Variable Get and Set Methods
Get Method
Set Method
Boolean
getBoolean()
setBoolean()
Double
getDouble()
setDouble()
Float
getFloat()
setFloat()
Int
getInt()
setInt()
Iterator
getIterator()
none
Long
getLong()
setLong()
Object
getObject()
setObject()
String
getString()
setString()
Xml
getXml()
setXml()
com.bowstreet.webapp.RequestInputs The Variables interface is useful for getting variable values stored in WPF, but cannot be used to get the values or names of items on a form (such as the data entry fields created by a Data Page builder). For this purpose, you need to use the RequestInputs interface. The following code snippet shows you how to iterate through the inputs on a form, and then write the names and values of these inputs to the SystemOut.log file: java.util.Iterator names = ¯webAppAccess.getRequestInputs().getInputNames(); while(names.hasNext()) { //input name String inputName = (String)names.next(); System.out.println(“Input name: “ + inputName); //input value String inputValue = ¯webAppAccess.getRequestInputs().getInputValue(inputName); System.out.println(“Input value: “ + inputValue); }
208
Chapter 8 Using Java in Portlets
com.bowstreet.util.IXml and com.bowstreet.util.XmlUtil Another commonly used Java API, the IXml interface can be used to perform basic manipulations on XML documents. The IXml interface is discussed in more detail in Chapter 9. The other commonly used XML interface is XmlUtil, which provides some additional functionality for dealing with XML (particularly for creating an XML document, parsing XML structures, and finding an element within an XML document). For example, to create a new XML document called Orders, you can use the following code: try { IXml orders = XmlUtil.create(“Orders”); } catch (java.io.IOException e) { e.printStackTrace(); }
Notice that the code is enclosed in a try/catch block. This handles any IOExceptions thrown by the create method. To create the same XML document based on a parsed text string, you can use this code: try { IXml orders = XmlUtil.parseXml(“”); catch (java.io.IOException e) { e.printStackTrace(); }
To find an XML element called Item in the orders XML document, you can use this command: List results = XmlUtil.findElements(orders, ¯“Orders/Sales/Item”)
where Sales/Item is an XPath expression (you must specify a valid XPath for the element you are looking for inside the XML structure).
TIP You can print IXml objects directly to the console, without needing to explicitly convert them to Strings first. For example, you can print the orders IXml variable to the console by using this line: System.out.println(orders);
Java Application Programming Interfaces
209
javax.servlet.http.HttpServletRequest and javax.servlet.http.HttpServletResponse The HttpServletRequest and HttpServletResponse interfaces are not specific to WPF, but they can be quite useful in WPF development. In particular, you can use the HttpServletRequest interface to get objects (such as shared variable values) from the user’s current session, as shown in the following: String objectValue = ¯webAppAccess.getHttpServletRequest().getSession().get ¯“objectName”);
And you can add objects into the session as follows: webAppAccess.getHttpServletRequest().getSession().put ¯(“objectName”, “objectValue”);
This can give you a way to share user-specific variables between models (and even separate applications), but you should use this feature sparingly to minimize your application’s memory consumption. You can also remove objects from the session as follows: webAppAccess.getHttpServletRequest().getSession().remove ¯(“objectName”);
The HttpServletResponse interface is particularly useful for adding cookies for a user. For example, to add a cookie to store the ID of a purchased item, you can use the following code: String id = “00001”; Cookie cookie = new Cookie(“Purchase”, id); webAppAccess.getHttpServletResponse().addCookie(cookie);
To access the cookie, you would then use the getCookies method of the HttpServletRequest interface: Cookie[] cookies = ¯webAppAccess.getHttpServletRequest().getCookies(); for(i=0; i < cookies.length; i++) { Cookie cookie = cookies[i]; if (cookie.getName().equals(“Purchase”)) { //cookie object now holds reference to Purchase cookie } }
210
Chapter 8 Using Java in Portlets
Java Beans Java beans are Java classes that fulfill certain criteria and are intended to encapsulate a particular subset of functionality. The major criteria that a Java class should satisfy to be considered a Java bean are as follows: • The class must have a no argument constructor (a constructor is a method that is executed when a class is instantiated). • The private properties of the class are exposed through public getter and setter methods. For example, a property called name would require a getName and setName method. You can convert an ordinary Java class into a Java bean simply by making sure the class adheres to the criteria previously specified. Usually the class also implements the java.io.Serializable interface, which handles the storage of bean data (this is referred to as persistence). Java beans are commonly used in Java development to represent business objects (that is, entities that are part of the structure of the business, such as orders, customers, and stock). In WPF, you don’t necessarily need to use Java beans because you have all of the prepackaged data access builders available to directly access backend data sources. However, there are cases where it might be preferable to use Java beans. For example, you might be connecting to a data source that is not natively supported by WPF, you might want to execute some code before performing data functions, or you want to reuse existing data implementations in Java beans. In these scenarios, you need to create an interface to the Java bean from WPF. To use a Java bean in WPF, you can import the bean class into the WEB-INF/work/source or WEB-INF/work/lib directory, add a Linked Java Object builder call to link to the class, and then use one or more Data Page builders to create a UI for the bean. You can also automatically create a UI for collections of beans using the Bean Master Detail builder. However, this builder should be used with caution because it mixes the presentation layer of your application with your data layer (that is, it is used to control both your data and how it is presented to end users). Ideally, Java beans should be hidden behind a service provider, so they can be changed without affecting the presentation of your application. The next few sections describe how to build a simple shopping cart application, which stores the name and quantity of purchased items for a particular user in a Java bean. An ID is automatically assigned to each item in the cart, which can also be used to edit and delete cart items. A combination of Linked Java Objects, Action Lists, and Method builders is used to interact with the Java bean.
Creating a Java Bean and Bean Manager The first step in creating your shopping cart portlet is to create a Java bean to interact with individual shopping cart items and another class to manage collections of these beans in a shopping cart. Before you proceed with this section, you need a WPF project to house the models and Java classes for the shopping cart portlet. If you have a project from a previous chapter, you can use
Creating a Java Bean and Bean Manager
211
that; otherwise, you should create a new WPF project (for more information on creating projects, see Chapter 1). The project will be published as a WAR file and deployed to a portal server, and you should also deploy the application to a local application server for testing. (You can use the portal server if it is running on your local machine; otherwise, it is recommended that you use the IBM WebSphere Application Server Community Edition server (WAS CE) that comes with WPF.)
TIP When creating a WPF project, the Java Settings screen in the wizard enables you configure settings that affect the use of Java in your application. The first tab on this screen, Source, enables you to configure folder locations for all the editable Java source code in your project. Java source files in the default source directory (WebContent/WEB-INF/work/source) are automatically compiled into the WebContent/WEB-INF/work/classes directory whenever they are saved. The Projects tab lets you access resources from other projects on the workspace, and the Libraries tab lists all of the JARs that will be added to your project when it is created. Finally, the Order and Export tab lets you do two things: specify files that will be available to other projects when the project is imported, and specify the order in which JAR files are accessed in your project. If you have the same class defined in multiple JARs (as sometimes happens), you should put the JAR containing the correct class higher in the list than the JARs containing the incorrect classes (although you should avoid defining the same class in more than one JAR if possible). You generally don’t need to change anything on the Java Settings screen unless you are referencing other projects, or you are using a customized set of JAR files.
Creating a Shopping Cart Item Bean Create a Java source file for a shopping cart item Java bean by clicking File, New, Class in the WPF Designer. For the package, type com.ibm (substituting your own company name for ibm), specify ShoppingCartItem as the Name, and then click Finish. The package com.ibm is used throughout this chapter, but in each case you should substitute your own company name for ibm. You now need to edit the contents of the new Java class so they read as shown in the following example. The fastest way to do this is to add the bolded code manually, and then right-click on the source file in the Project Explorer view. Select Source, Generate Getters and Setters, which opens the Generate Getters and Setters dialog. After this dialog is open, select the boxes to create getters and setters for the id, name, and quantity properties, and then click OK. This should automatically add in the get and set methods in the following code. Note that you can also copy this code from the ShoppingCartItem.java file, which is available for download from ibmpressbooks. com/title/9780137134465, under the Chapter 8 folder. package com.ibm; public class ShoppingCartItem implements java.io.Serializable
212
Chapter 8 Using Java in Portlets
{ //properties private String id; private String name; private int quantity; //no argument constructor public ShoppingCartItem() { } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getQuantity() { return quantity; } public void setQuantity(int quantity) { this.quantity = quantity; } }
The bean has three private properties: id, name, and quantity, which are changed via separate get and set methods. The bean also has a constructor with no arguments, which enables you to create new shopping cart items. Notice also that the bean implements the java.io.Serializable interface; this enables the bean data to be serialized (that is, persisted). The data is only serialized until the user’s session is terminated (although it is possible to persist data beyond the lifetime of the user’s session).
Creating a Java Bean and Bean Manager
213
In the Problems view, you see the following warning (unless you have filtered out these warnings in your IDE preferences): The serializable class ShoppingCartItem does not declare a static final serialVersionUID field of type long
This message declares that you should set an identifying field for the class, so that when the class is modified, the Java serialization engine will recognize different versions of the same class. To add this field, place the cursor somewhere inside the line that begins public class ShoppingCartItem and press Ctrl+1. Select Add Generated Serial Version ID from the context menu. You should see an ID field added to the top of the class, similar to the following: /** * */ private static final long serialVersionUID = ¯-7554150284752194927L;
Save the ShoppingCartItem bean when you are finished.
Creating a Shopping Cart Item Manager Now you need to create a class to manage collections of ShoppingCartItem beans for each user. This new class holds an ArrayList of shopping cart item beans called shoppingCart. Create a new Java class called ShoppingCartItemManager in the com.ibm package (substituting ibm for your own company name), and fill out the class as shown in the following (note that you can copy this code from the ShoppingCartItemManager.java file, available for download from ibmpressbooks.com/title/9780137134465, under the Chapter 8 folder): package com.ibm; import java.util.ArrayList; import java.util.Iterator; public class ShoppingCartItemManager { //properties private static ArrayList shoppingCart = new ArrayList(); private static int idCounter=0; //get method for a shopping cart public static ArrayList getShoppingCart() { return shoppingCart;
214
Chapter 8 Using Java in Portlets
} //create blank shopping cart item public static ShoppingCartItem createShoppingCartItem() { return new ShoppingCartItem(); } //add newly created item to shopping cart public static void addShoppingCartItem(ShoppingCartItem ¯toAdd) { //increment Id counter and set Id of new item idCounter++; toAdd.setId(Integer.toString(idCounter)); //add new item shoppingCart.add(toAdd); } //retrieves a particular item from the shopping cart public static ShoppingCartItem getShoppingCartItem(String ¯toGet) { Iterator i = shoppingCart.iterator(); while (i.hasNext()) { ShoppingCartItem item = (ShoppingCartItem)i.next(); if (item.getId().equals(toGet)) { return item; } } return null; } //updates a particular item in the shopping cart public static void updateShoppingCartItem(ShoppingCartItem ¯toUpdate) { Iterator i = shoppingCart.iterator(); while (i.hasNext())
Creating a Java Bean and Bean Manager
{ ShoppingCartItem item = (ShoppingCartItem)i.next(); if (item.getId().equals(toUpdate.getId())) { item.setName(toUpdate.getName()); item.setQuantity(toUpdate.getQuantity()); return; } } } //delete method for a shopping cart item (can be used with ¯Bean Master Detail builder) public static void deleteShoppingCartItem(ShoppingCartItem ¯toDelete) { shoppingCart.remove(toDelete); } //delete method for a shopping cart item public static void deleteShoppingCartItem(String toDelete) { Iterator i = shoppingCart.iterator(); while (i.hasNext()) { ShoppingCartItem item = (ShoppingCartItem)i.next(); if (item.getId().equals(toDelete)) { shoppingCart.remove(item); return; } } } //deletes every item in the shopping cart public static void clearShoppingCart() { shoppingCart.removeAll(shoppingCart); idCounter = 0; } }
215
216
Chapter 8 Using Java in Portlets
This class holds an ArrayList of ShoppingCartItem objects, called shoppingCart, as well as a counter that is used to generate IDs for each item. These IDs are then used to look up particular items in the shopping cart. Separate methods are provided for retrieving, adding, editing, and deleting items from the shopping cart. Note that each method and property in the class is declared as static, which means that they apply to the class as a whole rather than to a particular instance of the class. (However, note that each user accesses a different copy of the class, which also means that each user has his own shopping cart). In fact, you don’t even need an instance of the class to call these methods, which makes the job of calling each method a little simpler. Save the file when you are finished.
TIP There is nothing preventing you from using non-static methods in Java code that is accessed from WPF. When using non-static methods, keep in mind that you need to create an instance of the class before it can be used (for instance, ShoppingCartItemManager s = new ShoppingCartItemManager). Also, to create instances of a class, you need to declare a constructor method (as done in the ShoppingCartItem class).
Your IDE automatically creates compiled .class files of your java files whenever you save them. If you have any errors in the Problems view at this point, check that you have entered the Java in this section correctly. The next section describes how to create an interface for these classes via a WPF service provider model.
Creating a Service Provider In this section, you build a service provider model to interface with the Java classes created in the previous section. The service provider will have operations for clearing and viewing the cart, and viewing, adding, updating, and deleting an item from the cart. To achieve this, the following builders are used: • Service Definition • Linked Java Object • Java/XML Converter • Comment (x6) • Method (x5) • Service Operation (x6) Note that although the order of builder calls is usually not important, some of the builder calls in this chapter need to be in a specific order. This is because some of the builder calls require certain things to be built by the time their build methods run (when the WebApp is generated). In
Creating a Service Provider
217
the following example (as with other examples that use the same builders), the Service Definition builder call must come before the Service Operation builder calls, and the Service Operation builder calls must come after the Methods and Action Lists that they access (otherwise, you will get errors indicating that these resources are unavailable).
Creating a Model Create a new model called shoppingCartService in your project, under the folder WEBINF/models/chapter08. The model should be based on the Empty template, which creates a model with no builder calls in it. You now need to add a number of builder calls to the model to make it a service provider.
Defining the Service The first step is to add several general purpose builders that are used across each operation in the service. Add a Service Definition builder call to the model by selecting Service Definition from the Builder Palette and then clicking OK (you can open the Builder Palette by clicking the icon in the Outline view). Name the builder call shoppingCartService, and expand the Testing Support section of the builder call. Finally, enable the Add Testing Support checkbox and save the builder call when you are finished.
Adding a Linked Java Object Now add a Linked Java Object builder call to the model and name it shoppingCartItemManager. For the Class Name, select com.ibm.ShoppingCartItemManager from the Select Action dialog (swapping ibm for your own company name). This builder call can be used to refer to the methods of the ShoppingCartItemManager class within the shoppingCartService model. Save the builder call when you are finished.
Adding a Java to XML Converter Add a Java/XML Converter builder to the model and name it shoppingCartConverter. This builder call is used to convert bean data to XML (and vice-versa). Change the Reference Type input to Collection of Objects, as you will be working with a collection of shopping cart item beans. For the Data Reference input, you need to specify a variable or method that returns the collection of beans that you want to use. In this example, the collection is provided in the form of the shoppingCart ArrayList, which you can access via the LJO created in the previous step. To do this, open the Choose Reference dialog for the Data Reference input (by clicking the ellipsis button to the right of the input), and specify the getShoppingCart method as shown in Figure 8.3. Now select the com.ibm.ShoppingCartItem class for the Class Name input (by using the picker provided by the ellipsis button to the right of the field), and then in the Variable input, type the name shoppingCartVariable (once again, remember to swap ibm for your own company name). This creates a temporary variable called shoppingCartVariable to store the results of XML conversions.
218
Figure 8.3
Chapter 8
Using Java in Portlets
Selecting the getShoppingCart method.
Finally, expand the XML Options section and change the Top-level Tag Name input to ShoppingCart, and the Row Tag Name input to ShoppingCartItem. This defines XML element names when WPF creates XML from shopping item beans. Save the builder call when you are finished. Clearing a Shopping Cart
Add a Comment builder call to the model, and name it Clear shopping cart. This does not actually affect any of the other builder calls, but it does make your model easier to read. The next builder call in the model provides functionality for clearing all items from a shopping cart. Add a Service Operation builder call to the model and name it clearShoppingCart. Specify the Operation Name as clearShoppingCart, and then for the Action to Call input, open the Select Action dialog and select the clearShoppingCart Method of the shoppingCartItemManager LJO, as shown in Figure 8.4.
Creating a Service Provider
Figure 8.4
219
Selecting the clearShoppingCart method.
This runs the shoppingCartItemManager LJO’s clearShoppingCart method. Note that there is no need to create an instance of the shoppingCartItemManager, as you are accessing a static method. Now specify the Data Service as shoppingCartService. Because this operation doesn’t require any inputs or return any results, specify ‘No Inputs’ for the Input Structure Handling input in the Operation Inputs section and ‘No Results’ for the Result Structure Handling input in the Operation Results section. Save the builder call when you are finished. Viewing a Shopping Cart
Add a Comment builder call to the model, and name it View shopping cart. The next few builder calls in the model provide functionality for viewing the contents of a shopping cart. Add a Method builder call to the model and name it viewShoppingCart. Change the Return Type of the builder call to IXml, and then type the following code into the Method Body input:
This retrieves the shopping cart from the shopping cart item manager and stores it in the shoppingCartVariable variable. The ConvertToXML method of the Java/XML Converter builder is then run, which converts the contents of the shoppingCartVariable variable into XML (remember that this variable is specified in the Java/XML Converter builder itself, so there is no need to pass the variable as a parameter). The result is then returned to the caller. Save the builder call when you are finished.
TIP In WPF, many of the default builders provide methods that surface additional functionality, and these methods are of the following form: {BuilderCallName}{BuilderMethod} For example, the ConvertToXML method of the shoppingCartConverter builder call is run with the command shoppingCartConverterConvertToXML.
Add another Service Operation builder call to the model and name it viewShoppingCart. Specify the Data Service as shoppingCartService, and then type viewShoppingCart for the Operation Name. For the Action to Call input, select the viewShoppingCart method you just created from under the Methods section of the Select Action dialog. This operation doesn’t require any inputs, so specify No Inputs for the Input Structure Handling input. However, the operation should return an XML shopping cart object containing a sequence of shopping cart items. In some cases, you are able to use the result structure from the called action as the structure of the operation’s result, but you can’t do this when the action returns an XML document where the schema is not explicitly specified. In the example, you have returned a non-schema typed IXml object (you cannot return a schema typed variable from a Method builder call), so you need to manually specify the schema in the Service Operation builder call. Change the Result Structure Handling input to ‘Specify result schema’, and then select shoppingCartConverterSchema/ShoppingCart for the Result Schema input (this is the top-level
Creating a Service Provider
221
element created by the Java/XML Converter builder call). Save the builder call when you are finished.
Viewing a Shopping Cart Item Add a Comment builder call to the model and name it View item. The next few builder calls in the model provide functionality for viewing an individual item in the shopping cart. Add a Method builder call to the model and name it viewShoppingCartItem. Add an argument to the Arguments input in the Arguments section, called itemID of type String. Change the Return Type of the builder call to IXml, and then type the following code into the Method Body input: { //get shopping cart item com.ibm.ShoppingCartItem shoppingCartItem = ¯(com.ibm.ShoppingCartItem)webAppAccess.callMethod(“shopping ¯CartItemManager.getShoppingCartItem”, itemID); //return XML representation of shopping cart item return (IXml)webAppAccess.callMethod ¯(“shoppingCartConverterConvertObjectToXML”, ¯shoppingCartItem); }
This retrieves an item from the shopping cart based on the ID passed in to the Method builder call. The resulting shoppingCartItem bean is converted into XML using the ConvertObjectToXML method of the Java/XML Converter builder, and then returns this XML to the caller. Note that in this case, the shoppingCartItem has been specified as an argument to the ConvertObjectToXML method, rather than changing the value of the shoppingCartVariable variable and then using the ConvertToXML method with no arguments. This is necessary because the XML returned is an individual item, and the shoppingCartVariable XML holds a sequence of items. Save the builder call when you are finished. Add another Service Operation builder call to the model and name it viewShoppingCartItem. Specify the Data Service as shoppingCartService, then type viewShopping CartItem for the Operation Name. Select the viewShoppingCartItem Method for the Action to Call input as well. Specify ‘Use Structure from Called Action’ for the Input Structure Handling input, so that the operation takes a single String input as defined in the viewShoppingCartItem Method builder call. For the Result Structure Handling input, select Specify Result Schema and then select shoppingCartConverterSchema/ShoppingCartItem for the Result Schema input (this is the row-level element created by the Java/XML Converter builder call). Save the builder call when you are finished.
222
Chapter 8 Using Java in Portlets
Adding a Shopping Cart Item Add a Comment builder call to the model, and name it Add item. The next few builder calls in the model provide functionality for adding an item to the shopping cart. Add a Method builder call to the model and name it addShoppingCartItem. Add an argument to the Arguments input in the Arguments section, called toAdd of type IXml. Change the Return Type of the builder call to void, and then type the following code into the Method Body input: { //create new shopping cart item bean ShoppingCartItem shoppingCartItem = new ShoppingCartItem(); //convert ‘toAdd’ object to bean webAppAccess.callMethod(“shoppingCartConverterConvertObject ¯FromXML”, shoppingCartItem, toAdd); //add new bean to shopping cart webAppAccess.callMethod(“shoppingCartItemManager.addShopping ¯CartItem”, shoppingCartItem); }
This creates a new, blank shopping cart item, and then sets it to a Java representation of the toAdd IXml object using the ConvertObjectToXML method of the Java/XML Converter builder. The resulting bean is then added to the shopping cart via the shoppingCartItemManager. Notice in the preceding code that the ShoppingCartItem class has not been fully qualified as it has been previously. Save the builder call now, and you will notice that this causes an error in your application. To remove this error, expand the Import List section of the builder call and enter the class com.ibm.ShoppingCartItem into the Import List input (swapping ibm for your own company name). Save the builder call, and the error should be removed from the Problems view. Although it is really a matter of preference how you choose to reference classes (particularly with small and simple methods), keep in mind that as your methods get larger, fully qualified classes in the method body can make your code more difficult to read. Add another Service Operation builder call to the model and name it addShopping CartItem. Specify the Data Service as shoppingCartService, and then type addShopping CartItem for the Operation Name. Select the addShoppingCartItem Method for the Action to Call input, and then select ‘Specify input schema’ for the Input Structure Handling input. For the Input Schema input, select shoppingCartConverterSchema/ShoppingCartItem. This declares the structure of the operation’s input to be a single shopping cart item element, as defined by the row element of the Java/XML Converter builder call. For the Result Structure Handling input, select No Results. Save the builder call when you are finished.
Creating a Service Provider
223
Updating a Shopping Cart Item Add a Comment builder call to the model, and name it Update item. The next few builder calls in the model provides functionality for updating an item in the shopping cart. Add a Method builder call to the model and name it updateShoppingCartItem. Add an argument to the Arguments input in the Arguments section, called toUpdate of type IXml. Change the Return Type of the builder call to void, and then type the following code into the Method Body input: { //create new shopping cart item bean com.ibm.ShoppingCartItem shoppingCartItem = new ¯com.ibm.ShoppingCartItem(); //convert ‘toUpdate’ object to bean webAppAccess.callMethod(“shoppingCartConverterConvertObject ¯FromXML”, shoppingCartItem, toUpdate); //update shopping cart with new bean webAppAccess.callMethod(“shoppingCartItemManager.update ¯ShoppingCartItem”, shoppingCartItem); }
This creates a new shopping cart item, and then sets it to the value of the converted toUpdate IXml object passed in to the Method builder call. The shopping cart is then updated with the new item. Save the builder call when you are finished. Now add another Service Operation builder call to the model and name it updateShoppingCartItem. Specify the Data Service as shoppingCartService, and then type updateShoppingCartItem for the Operation Name input. Select the updateShoppingCartItem Method for the Action to Call input, and then select ‘Specify Input Schema’ for the Input Structure Handling input. After this is done, select shoppingCartConverterSchema/ShoppingCartItem for the Input Schema input. This declares the structure of the operation’s input to be a single shopping cart item element. For the Result Structure Handling input, select No Results. Save the builder call when you are finished.
Deleting a Shopping Cart Item Add a Comment builder call to the model, and name it Delete item. The next few builder calls in the model provide functionality for deleting an item from the shopping cart. Add a Method builder call to the model and name it deleteShoppingCartItem. Add an argument to the Arguments input in the Arguments section, called itemID of type String. Change the Return Type of the builder call to void, and then type the following code into the Method Body input:
This deletes an item from the shopping cart based on the ID passed in to the Method builder call. Save the builder call when you are finished. Now add the final Service Operation builder call to the model and name it deleteShoppingCartItem. Specify the Data Service as shoppingCartService, and then type deleteShoppingCartItem for the Operation Name. For the Action to Call input, select the deleteShopping CartItem method. Specify ‘Use Structure from Called Action’ for the Input Structure Handling input, so that the operation takes a single String input as defined in the deleteShoppingCartItem method builder call. For the Result Structure Handling input, select No Results. Save the builder call when you are finished. Your service provider is now finished and ready to test. Testing the Service Provider
Because you enabled testing support on the Service Definition builder call, WPF automatically generates test pages for your service provider when you run it in a browser. To test the service provider, first run the shoppingCartService model from the WPF Designer by clicking the icon on the toolbar. This runs the currently active model (such as shoppingCartService) with the last run configuration you used. If you have not set up a run configuration before, you are prompted to do so—create a new configuration under the WebSphere Portlet Factory Model category (if you want more information on setting up a run configuration, see the “Testing the Application” section in Chapter 1). After you run shoppingCartService, you should see the index test page in your default Web browser, as shown in Figure 8.5. This screen lists all of the operations available on the shoppingCartService service.
Figure 8.5
Index test page from the shoppingCartService model.
Testing the Service Provider
225
Testing the addShoppingCartItem Operation Because the shopping cart is empty by default, you need to test the operations in a particular order. First, test the addShoppingCartItem operation by adding a few items to the shopping cart. To do this, click the addShoppingCartItem link. You should see a data entry screen as shown in Figure 8.6. Enter a sample name and quantity, and then click Submit Query (note that there is no need to enter an ID, as it is automatically overwritten by the addShoppingCartItem method of the ShoppingCartItemManager class). The quantity should be a numeric value; otherwise, you will receive errors (note that the data type of these inputs are not validated—validation of data inputs is covered in Chapter 11, “Field Validation, Formatting, and Translation”). Click the Back button when you are finished, and add one or two more items to the cart.
Figure 8.6
Testing the addShoppingCart operation.
Testing the viewShoppingCart Operation To test that the items were added and that they can be display correctly, click the viewShoppingCart link. You should see a list of the items similar to that shown in Figure 8.7. If not, check that you don’t have any errors showing in the Problems view in the IDE, and that you have completed all of the steps in the previous section correctly. Click Back to return to the index page.
Figure 8.7
Testing that items can be added and viewed in a list.
Testing the viewShoppingCartItem Operation Click on the viewShoppingCartItem link, and enter the ID of one of the items you just created (the IDs are in ascending order, starting at 1). Click the Submit Query button and you should see output similar to that shown in Figure 8.8. Return to the index page.
226
Figure 8.8
Chapter 8 Using Java in Portlets
Testing that individual items can be viewed.
Testing the updateShoppingCartItem Operation Click the updateShoppingCartItem link and enter an ID of an existing item. Now enter a new Name and Quantity for the item (different to the previous values the item had) and click the Submit Query button. Return to the index page and run the viewShoppingCartItem operation. The list of items should reflect the new values of the item just updated.
Testing the deleteShoppingCartItem Operation To test the deleteShoppingCartItem operation, click the deleteShoppingCartItem link. Enter the ID of an already existing item and click the Submit Query button. Return to the index page and run the viewShoppingCartItem operation. The list of items should no longer display the item just deleted.
Testing the clearShoppingCart Operation For the final test, click on the clearShoppingCart link. When you return to the index page and run the viewShoppingCartItem operation, the list of items in the cart should now be empty. Close the shoppingCartService model when you’ve finished testing it. You now have a service provider that interacts with a collection of shopping cart item Java beans. The next section walks you through creating a service consumer model, which communicates with the shoppingCartService model and is surfaced to a portal server as a portlet.
Creating a Shopping Cart Portlet In this section, you build a shopping cart portlet to consume the shopping cart service. The portlet provides a user interface to interact with a shopping cart (that is, to view, edit, add, and delete shopping cart items). A screenshot of the portlet is shown later in Figure 8.18.
Creating a Model To add the shopping cart portlet, select File, New, WebSphere Portlet Factory Model from the File menu to bring up the WebSphere Portlet Factory Model dialog. On the next screen, select your WPF project and click Next. The next screen lists different types of models WPF can create for you automatically. You can create service consumers in WPF two ways: the first is to let WPF create one for you (which you can do by using the List and Detail Service Consumer option in the Select Model wizard), and the second is to create the service consumer manually. The List and Detail Service Consumer gives you everything you need in this example, so select it and click Next.
Creating a Shopping Cart Portlet
227
The List and Detail Service Consumer automatically surfaces your model as a portlet via a Portlet Adapter builder call. Specify the name of the portlet on the next screen as shoppingCart (this name is also used to name the other builder calls in your model). Also, specify the service provider shoppingCartService you created in the previous section. Click Next when you are finished. The next screen enables you to specify the service operation that is used to provide the list of shopping cart items. Select viewShoppingCart and click Next. On the next screen, make sure the Create Link to Details checkbox is enabled, and then select Id for the Details Link Column field. Change the Details Action Type to ‘Display Data from Another Operation in this Service’, and then enter viewShoppingCartItem into the Details Operation input. These settings convert all of the values in the ID column in the shopping cart item list into clickable links. When a user clicks on the link, a value identifying the item is passed to the viewShoppingCartItem operation, and the results are displayed to the screen. Click Next, and you should see some settings enabling you to configure the value that will uniquely identify each shopping cart item for the viewShoppingCartItem operation. Make sure the settings appear as shown in Figure 8.9, and then click Next. This uses the ID of the currently selected row to identify a shopping cart item when it is clicked.
Figure 8.9
Overriding inputs for each shopping cart.
228
Chapter 8 Using Java in Portlets
You now need to name your model. Type shoppingCart, and make sure the model is created in the directory models/chapter08. Click Finish. This creates a new model called shoppingCart in your WPF project, and opens the model in the Model Editor and Outline view.
Configuring the Portlet Adapter The shoppingCart model contains three builder calls: a Portlet Adapter, a Service Consumer, and a View & Form. The Portlet Adapter builder converts the model into a portlet, so that it can be viewed inside a portal (note that if you make any changes to this builder call—including the changes you are about to make—you need to rebuild the deployed application before you can see the results of the changes. For more information on this process, see the example in Chapter 1). The Service Consumer builder makes the operations in the shoppingCartService service available in the model, and the View & Form builder displays any list and detail data in the model to the screen. The View & Form builder also enables you to do some basic manipulation of how the data is displayed (the View & Form builder is discussed in Chapter 5, “Customizing Portlet Appearance”). The builder calls don’t require any configuration in this example, except for two small changes to the Portlet Adapter builder call. Double-click the Portlet Adapter builder call to open it in the Model Editor, and then change the Portlet Title to Shopping Cart and the Portlet Description to Used to interact with a shopping cart. Save the builder call when you are finished. The Portlet Title identifies the portlet inside the portal (it is actually displayed in the title bar of the portlet), and the Portlet Description appears on various administration screens inside the portal. You now need to add the following builder calls to the model to provide the portlet with its functionality: • Action list (x2) • Comment (x3) • Input form • Button (x3)
Implementing the updateShoppingCartItem Operation To add update support to the model, open the View & Form builder call, and expand the Update Page Support section. Enable the Create Update Page checkbox, and then specify DataServices/ shoppingCart/updateShoppingCartItem for the Update Method input that appears underneath the checkbox. This creates an update form for each item, and then executes the updateShoppingCartItem operation whenever an item is saved (by default, the input values on the form are sent in an XML object as the input to the operation). Also, change the Update Next Action input to shoppingCartView_ShowResults, which re-runs the viewShoppingCartItem operation to refresh the cart item list, and then displays the refreshed shoppingCartView_ViewPage page. Save the builder call when you are finished.
Creating a Shopping Cart Portlet
229
Because the View & Form builder does not automatically create an interface for adding new items, you need to add a separate builder call to the model for this purpose. Add an Input Form to the model and name it addItemForm. Enter the Input Submit Operation input as DataServices/ shoppingCart/addShoppingCartItem, and then specify shoppingCartView_ShowResults as the Input Next Action. This consumes the addShoppingCartItem operation when an item is saved using the input form, and then refreshes the shopping cart after the save is finished (which also returns control to the item list page). Also, expand the Advanced section at the bottom of the builder, and disable the Generate Main checkbox so that an extra main Action List is not created (you are already using the main Action List created by the View & Form). Leave the default settings for the rest of the builder call, and save the builder call when you are finished.
Implementing the addShoppingCartItem Operation Now add a Button builder call to the model, which provides a means for users to open the input form. Name the builder call addShoppingCartItem, and then fill out the Page Location section of the builder call as shown in Figure 8.10. This adds the button to a new tag called add_button after the back_button tag, which automatically appears at the bottom of the list screen (even though the back button itself is not visible).
Figure 8.10
Configuring the page location for the Add item button.
For the label of the button, enter Add item, and then change the Action Type to ‘Link to an Action.’ Specify the Action as addItemForm_InputPage, which opens the input page created in the previous step. Save the builder call when you are finished.
Implementing the clearShoppingCart Operation Add a Comment builder call to the model, and name it Clear cart. The next few builder calls in the model provide functionality for clearing the contents of a shopping cart. Now add another Action List builder call to the model, which you use to define the sequence of actions to be executed when the shopping cart is cleared. Name the Action List clearShoppingCart, and then specify DataServices/shoppingCart/clearShoppingCart as the first action. This clears the contents of the user’s shopping cart. Then specify shoppingCartView_ShowResults for the second operation, which refreshes the item list and displays the shoppingCartView_ViewPage page. Save the builder call when you are finished.
230
Chapter 8 Using Java in Portlets
Add a Button builder call to the model and name it clearShoppingCart. This button provides users with a means to access the clearShoppingCart Action List and is displayed immediately after the Add item button on the item view page. Fill out the Page Location section of the builder as shown in Figure 8.11.
Figure 8.11
Configuring the page location for the Clear cart button.
Type Clear cart for the Label input, and then change the Action Type to ‘Link to an Action.’ Specify the Action as clearShoppingCart, which executes the Action List created in the previous step. Save the builder call when you are finished.
Implementing the deleteShoppingCartItem Operation The final piece of functionality is the delete operation. Add a Comment builder call to the model, and name it Delete item. The next few builder calls in the model provide functionality for deleting an item from a shopping cart. Now add another Action List to the model and name it deleteShoppingCartItem. The first action in the Actions input needs to set the value of the itemID argument from the deleteShoppingCartItem operation to the ID of the currently selected item. To do this, click the ellipsis button on the first row of the Actions input and select Special, Assignment. For the Target variable, specify the itemID of the deleteShoppingCartItem operation, as shown in Figure 8.12. For the Source variable, specify the ID of the currently selected row as shown in Figure 8.13. Click OK to accept your settings and return to the Action List builder call. For the second action, specify DataServices/shoppingCart/deleteShoppingCartItem. This calls the deleteShoppingCartItem operation, using the value set in the previous action as an input. Finally, specify shoppingCartView_ShowResults for the third operation, which updates the shopping cart and displays the view page created by the View & Form builder. Save the builder call when you are finished.
Creating a Shopping Cart Portlet
Figure 8.12
Assigning a target field.
Figure 8.13
Assigning a source field.
231
232
Chapter 8 Using Java in Portlets
Add a Button builder call to the model, naming it deleteShoppingCartItem. This button provides users with a means to access the deleteShoppingCartItem Action List and is displayed immediately after the Back button on the detail page for each shopping cart item. Fill out the Page Location section of the builder as shown in Figure 8.14.
Figure 8.14
Configuring the page location for the Delete item button.
Type Delete item for the Label input, and then change the Action Type to ‘Link to an Action.’ Specify the Action as deleteShoppingCartItem, which executes the Action List created in the previous step. Save the builder call when you are finished. Your test portlet is now finished, and ready to test.
Testing the Shopping Cart Portlet To test the shoppingCart portlet, first run the shoppingCart model from the WPF Designer. After you have run the shoppingCart model, you should see an empty list of shopping cart items, as well as several buttons for interacting with the shopping cart (see Figure 8.15).
Figure 8.15
The default screen for the shoppingCart model.
Testing the Add Item Button Click on the Add Item button to add a new item to your shopping cart. Enter some sample data for the Name and Quantity on the input screen that follows (keep in mind that the ID is overwritten by the ShoppingCartManager class, so there is no need to enter one in) and click the Submit button. You are returned to the default list screen, and the new item should appear in the item list (similar to the screenshot in Figure 8.16). Add one or two additional items to the list for testing.
Creating a Shopping Cart Portlet
Figure 8.16
233
Adding a shopping cart item.
Testing the Edit Item Button Click on the ID column for one of the items you just created, and you should see a detail page for that item similar to that shown in Figure 8.17. Click the Edit button to bring up an edit page for the item (similar to the input page seen when adding a shopping cart item) and make a change to the Name or Quantity of the item (you can’t change the ID of the item). You are returned to the default list screen, and the modified item details appear in the item list.
Figure 8.17
Item detail page.
Testing the Delete Item Button To test the delete functionality, open one of the items you created and then click the Delete Item button. You are returned to the list screen, and the item no longer displays in the list.
Testing the Clear Cart Button To test that you can clear the entire contents of the shopping cart, click the Clear Cart button. The screen should refresh and the list should now be empty. After you have tested the shoppingCart model, you should rebuild your application on the portal server (for instructions on how to do this, see the example in Chapter 1), after which you are able to view the shoppingCart model as a portlet on the portal server. After you have added the shoppingCart portlet to a page in the portal, it should appear as shown in Figure 8.18. The next section discusses how you can package Java files in your WPF applications using JAR files.
234
Chapter 8 Using Java in Portlets
Figure 8.18
The Shopping Cart portlet.
Java Archives Java Archives (JARs) are class file libraries that your project can utilize to take advantage of certain functionality. A typical WPF project employs numerous JAR files, which are used behind the scenes to facilitate such things as logging, XML manipulation, and builder generation routines. You can also import your own JAR files into a WPF project. If you have a collection of Java classes that you would like to distribute to others (or simply want to package together for ease of use), JAR files are a convenient way to achieve this. You can access Java classes contained in JAR files directly from inside builder calls or other Java code.
TIP You can package Java source files into a JAR file by selecting Export from the File menu in the WPF Designer, and then choosing JAR from under the Java heading.
By default, WPF projects store all their JARs in the WebContent/WEB-INF/lib and WebContent/WEB-INF/work/lib directories. If you use the Project Explorer view to browse these directories, you can see that the only files that appear are the ones that are not on the build path. To view a list of all the JAR files that are on the build path (as well as their contents), use the Package Explorer view in the WPF Designer.
Importing JARs If you want to add your own JAR files to a project, you can put them in a number of places. The usual place for custom JARs is either in the WebContent/WEB-INF/lib directory or WebContent/WEB-INF/work/lib directory. You can use the WebContent/WEB-INF/lib directory if you do not plan to change your JAR files, or the WebContent/WEB-INF/work/lib directory for any JAR files that you plan to update regularly (timestamps on JAR files in this directory are automatically checked whenever you rebuild the model, so that you can dynamically load in new JARs during development). You can also use the WebContent/WEB-INF/clientLibs directory for JAR files that you don’t want to add to the server’s classpath (such JARs might already exist on the server and would only be used at design time).
Java Archives
235
TIP WPF JAR files used as part of your IDE are stored in the eclipse/com.bowstreet.designer. core_[WPFversionnumber]/lib directory in your WPF installation. An example of such a file is the Domino NCSO.jar file, which is used by the Domino Data Access coordinator to fetch a list of views in the builder UI.
To add a new JAR file to your WPF project, first copy the JAR file to the WebContent/ WEB-INF/lib directory inside the project. If you were going to be regularly updating the JAR file, you would want to copy it to the WebContent/WEB-INF/work/lib directory instead. JAR files in these locations are used when your application is deployed. If you copied your JAR from a location on the server (such as the shared/app directory on the portal), you should copy it to the WebContent/WEB-INF/clientLibs directory so that it is not added to the server’s classpath when deployed. Open the Project Properties dialog by right-clicking on your project folder and choosing Properties. Switch to the Java Build Path page, and then click on the Libraries tab. Click the Add JARs button to display the JAR Selection dialog, navigate down to where the JAR file is stored, select it, and click OK. Click OK to close the Project Properties dialog, and you should now be able to access the JAR within your application.
Excluding JARs from WAR Files WebSphere Portal installations have a shared repository for JAR files, which is located under the shared directory in your portal server’s root directory. If you are using JARs that contain functionality used across multiple applications, you might want to consider putting them in this directory. Java methods in your application are then able to access these JARs after they are deployed. When testing or compiling your applications, however, the portal’s shared directory is unavailable, and you will get errors when you try to compile code that accesses nonexistent JARs. To get around this problem, you can import the JARs into your project (as described in the previous section) but exclude the JAR files from the deployed WAR (so only one copy of the JARs will exist after the application has been deployed). This also reduces the size of your deployable application (as opposed to storing JARs in the WebContent/WEB-INF/clientLibs directory, which only prevents them from being added to the server’s classpath). To exclude a JAR file from the deployable application, first import the JAR file into your project as described in the previous section. Then copy the JAR file to the shared/app directory on your portal server and restart the portal server (your deployed application uses this JAR instead of the one currently in your project). After you’ve done this, open the .excludeFromServer file under the WebContent directory (by default you can’t see this file in the Project Explorer view, but you can see it by opening the Navigator view). Any entries in this file are excluded from the deployable WAR (all entries are relative to the project root folder).
236
Chapter 8 Using Java in Portlets Change the contents of the file so that it reads as follows: # Add any files that you want excluded from the deployed ¯server in this file. # All paths start at the application root. For example: ¯WEB-INF/lib/myJar.jar WEB-INF/lib/testJar.jar
where testJar.jar is the name of your JAR file. Save the contents of the file when you are finished, and deploy your application. To test that your change worked, rebuild your portlet application and open the deployable WAR when the process has finished. The specified JAR file should not be part of the WAR.
TIP If you have a JAR in your project that already exists in the portal’s shared directory and you want to use the version in your project rather than the version on the portal server, you need to configure the application’s classloader policy to ‘Parent last’. (It is ‘Parent first’ by default, meaning that the JARs in the portal’s shared directory take precedence.) To do this, open the administration console for the portal server (https://localhost:10039/ibm/console by default) and navigate to the Applications/Enterprise Applications page. Locate and click the deployed application, select Web modules, and then click on the WAR filename. Change the Class loader mode property to Parent Last, click OK, and then save your changes.
Summary In this chapter, you learned how to extend your WPF portlet applications with Java. You learned about the use of the Method and Linked Java Object builders, as well as how to use JARs and Java beans. You also learned about some of the APIs that are useful in manipulating WPF applications, particularly in relation to the Domino Java API and the webAppAccess object. Finally, you created a service provider to interact with a collection of Java beans, and a shopping cart model to communicate with this service. The shopping cart model was surfaced to a portal server as a portlet. Chapter 9 discusses how you can provide and consume Web services in WPF portlets, as well as how to manipulate XML using a combination of prepackaged builders and the Java IXml interface.
Important Points • You can view a WPF application’s Java code by opening the WebApp Tree tab for a particular model, and then viewing the contents of the Method and Linked Java Object headings.
Important Points
237
• Java statements can be used to return the results of Java expressions for inputs in a builder that accept indirect references (such as the Actions input in the Action List builder), using the following syntax: ${Java/[Java expression]}
where [Java expression] should be replaced with a Java expression that will be evaluated and returned to the builder input (for example: ${Java/webAppAccess. getModelName()}). • The Method builder enables you to insert Java code inside your WPF applications and should be used when you want to insert small snippets of Java (particularly those that are not going to be reused in other models). • The Linked Java Object (LJO) links to a physical Java class file in your project and makes the methods of this class available in a model. When implementing Java methods in WPF, the LJO should be preferred over the Method builder when long methods are used, or if the methods might be reused in more than one model. • Javadoc for the WPF APIs is available in the WPF Designer Help, under IBM WebSphere Portlet Factory Designer, Reference, API documentation. • To ensure the separation of your data and presentation tiers, Java beans should be made available via a service provider. The Bean Master Detail builder automatically creates a UI to interface with a Java bean, but it mixes data and presentation and should therefore only be used for testing purposes. • In WPF, some builders provide methods to surface further functionality. These methods are of the following form: {BuilderCallName}{BuilderMethod}
• Imported JARs can be stored in the WebContent/WEB-INF/lib directory, or under the WebContent/WEB-INF/work/lib directory if you intend to regularly change them.
This page intentionally left blank
C
H A P T E R
9
Using Web Services and Manipulating XML
Web services enable people to consume and provide services over a network. They are commonly used in distributed applications (that is, applications made up of components sitting in different runtime environments). When working with Web services in WebSphere Portlet Factory (WPF), as in other environments, XML is often used to communicate between the Web service and the consumer of the Web service. This chapter demonstrates how you can use Web services and XML documents in your WPF applications. By the end of this chapter, you will understand how to create and consume Web services in WPF and you will understand some of the methods available in WPF for interacting with XML. You will build a stock-ordering application that utilizes a Web service and demonstrates XML manipulation. This chapter does not cover basic service concepts (such as the service provider/consumer pattern), as these are discussed in Chapter 2, “Providing and Consuming Services.” You don’t necessarily have to read Chapter 2 before you read this chapter, but if you are completely new to services, you may find Chapter 2 a more appropriate starting point. The models discussed in this chapter are available for download from ibmpressbooks.com/ title/9780137134465 under the Chapter 9 folder (instructions for copying these files into your project are included in a readme.txt file in the same folder); however, to increase your understanding of using Web services and XML in WPF, it is recommended that you create the models yourself by following the example in this chapter. The following topics are covered in this chapter: • Web services in WPF • Creating an order stock Web service • Creating a service provider • Creating an order stock portlet • XML Transform builders
239
240
Chapter 9
Using Web Services and Manipulating XML
Web Services in WPF
A Web service is a service that is provided and consumed over a network, and there are a number of builders in WPF you can use to automate the process of creating and consuming these services. WPF applications use standards such as WSDL, SOAP, and XML when working with Web services, which means that WPF Web services can be provided not only to WPF applications, but to applications developed in other environments as well (for more information on some of the terminology used when discussing Web services, see the “Web Service Terminology” sidebar). Using WPF, you can also consume Web services developed in other environments, in addition to Web services developed in WPF. When providing or accessing Web services in WPF, you should use the service provider/ consumer pattern (this pattern is discussed in Chapter 2). The service provider is not a Web service itself, but acts as an intermediary between the service consumer and any Web services that are required in order to construct the service provider’s functionality. The provider used in the example in this chapter, for instance, calls a Web service to execute stock-ordering functions; the Order Stock portlet accesses these functions from the service provider. Using a service provider in this way enables you to abstract your service implementations from the presentation tier, which means that you can change the mechanics of your service providers (for example, by linking them to different Web services) without having to change the service consumers. Service providers also give you a number of testing features (such as service stubs) that you wouldn’t normally have if you just called Web services directly from your service consumers. A diagram demonstrating the process of consuming Web services through a service provider is shown in Figure 9.1 (note that the Web service in the diagram can be part of a WPF application as well).
WPF Application Request
Service Provider
Web Service Response
Response
Request
Service Consumer
Figure 9.1
Web service consumption in WPF.
Web Services in WPF
241
WEB SERVICE TERMINOLOGY A number of standard terms are used when discussing Web services; these are defined in the following: XML Namespace—An XML namespace provides a unique context for XML attributes and elements. Two XML elements with the same name but different namespaces are seen as different elements. Schemas—Schemas define the structure of XML documents and are often used in WPF to define variables, as well as inputs and outputs for services. WPF uses schemas based on the http://www.w3.org/2001/XMLSchema namespace. Schemas used in Web services are included in the WSDL for the service, which are then accessible to consumers of the service. SOAP—Simple Object Access Protocol (SOAP) is a standard protocol used to exchange information between Web services. Web services that use SOAP communicate via request and response messages. In the example, the ID of a stock item and a quantity of it to order are taken from the user, and assembled into an XML request object. This request object is then sent to the Web service via a SOAP message. After the Web service has finished processing the request, it assembles an XML response object and returns it to the consumer through another SOAP message. WSDL—Web Services Description Language (WSDL) is an XML document used to describe a Web service. Web services that communicate through SOAP usually provide a WSDL, so that consumers of the service know the structure of the requests and responses used, as well as the operations that the service provides.
In WPF, XML is normally used to structure communication with Web services. This is done by sending XML request messages to the Web service, which sends back XML response messages to the consumer. These XML structures are commonly defined by schemas, which help to ensure the integrity of the communication. In WPF, a combination of builder calls and Java are used to work with these requests and responses, and WPF provides its own Java interface (called IXml) to access and manipulate XML. In this chapter, you build two applications. The first contains a model to be published as a Web service, which places stock orders in a simple inventory system; the second application contains a portlet that consumes the Web service. The stock list is stored in an XML variable inside the Web service, and XML is also used to communicate with the Web service. The structure of this communication (a request object and a response object) is defined by several schemas in the Web service. The Web service application is deployed to an application server, and the portlet application is deployed to a portal server.
242
Chapter 9 Using Web Services and Manipulating XML
Creating an Order Stock Web Service In this section, you build a Web service to provide functionality for ordering stock. The Web service contains a single operation called orderStock, which takes two inputs: the ID of a stock item to order and a quantity to order. The operation, when consumed, orders the stock item in the quantity specified. To keep the example simple, an order consists of incrementing a stock counter for the item ordered by the quantity specified; if the item does not exist in the orders variable, then it is added. After you create the Web service, the next section of this chapter, “Creating a Service Provider,” provides the interface between the Web service and the portlet, and then the “Creating an Order Stock Portlet” section walks you through the process of creating a model to consume the operations in the service (this model is then surfaced to a portal server as a portlet). Before you begin the steps in this section, you should create a new WPF project, which will house the model that provides the functionality for the orders Web service. You should use a new project even if you already have a project in your workspace from a previous chapter, as this will show you how to use Web services that are not stored in the same project as your service provider and consumer (you can even run the Web service on a remote server if you have another application server available). The Web service runs as a standalone application on an application server rather than as a portlet application, as you don’t need any of the benefits provided by the portlet container. You can still run the application as a standalone Web application on the portal server if it is convenient for you, provided it runs on your local machine; otherwise, it is recommended you use the IBM WebSphere Application Server Community Edition server (WAS CE) that comes with WPF. Create the Web Service project, and set up its application server deployment configuration (for more information on this process, see Chapter 1, “Introduction to WebSphere Portlet Factory”). The project will be published as a WAR file and deployed to an application server. After you have a project set up, you need to add a model to the project. This model uses the following builders: • Service Definition • Schema (x2) • Variable (x2) • Method • Service Operation Note that the Service Definition and Method builder calls must both precede the Service Operation builder call in your model; if this is not the case, you will get errors indicating that there are missing resources.
Creating an Order Stock Web Service
243
TIP In WPF, there are two ways to make a model into a Web service: You can either enable the Generate WSDL checkbox on a Service Definition builder call or add a Web Service Enable builder call to your model. Generally speaking, the best approach is to use the checkbox on the Service Definition builder, as this builder gives you more facilities for testing the Web service, and it is quicker and easier to configure than the Web Service Enable builder.
Creating a Model Create a new model called orderStockWebService in your project, under the folder WEBINF/models/chapter09. The model should be based on the Empty template, which creates a model with no builder calls in it. For more information on creating models, see the example in Chapter 1.
Defining the Service The first builder call to add is a Service Definition, which defines a name for your service and publishes it as a Web service. To do this, select Service Definition from the Builder Palette (you can open the Builder Palette by clicking the icon in Outline view) and press OK. Change the name of the builder call to orderStockWebService, and then enable the Generate WSDL checkbox. Finally, expand the Testing Support section and select the checkbox for Add Testing Support, which enables you to test the service after you have created it. Save the builder call.
TIP The Service Definition builder automatically uses Document Literal SOAP encoding for service messages. Document Literal encoding provides the most interoperability with other Web services.
Defining the Request Object Now you need a schema for the XML data that is to be sent to the Web service (known as the request object). This schema defines the structure of all the inputs to a particular Web service operation, and it is included in the WSDL document that is generated from your Web service (if the term WSDL is unfamiliar to you, see the “Web Service Terminology” sidebar at the beginning of this chapter). You don’t need to worry about what this WSDL file consists of as WPF creates it for you, but you do need to know how to find it (which you will see how to do later in this section).
244
Chapter 9 Using Web Services and Manipulating XML
TIP In the example in this chapter, you create schemas directly, using the Schema builder call. However, you can also get WPF to automatically generate a schema based on the structure of a Variable builder call, by adding a Simple Schema Generator builder call to your model. The Simple Schema Generator is useful if you don’t know how to write XML schemas, but it requires you to first create Variable builder calls to base the schemas on. An example using the Simple Schema Generator is given in Chapter 2.
Add a Schema builder call to the model, then type the name orderStockRequestSchema for the Schema builder call. Select the ‘Specify explicitly as builder input’ option so that you can type the schema directly into the builder call. In the Schema Source box, type the following XML: <xsd:schema xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:tns=”http://com.ibm.orderStockRequest” targetNamespace=”http://com.ibm.orderStockRequest”> <xsd:element name=”OrderStockRequest”> <xsd:complexType> <xsd:sequence> <xsd:element ref=”tns:ID” minOccurs=”1” maxOccurs=”1” /> <xsd:element ref=”tns:Quantity” minOccurs=”1” maxOccurs=”1” /> <xsd:element name=”ID” type=”xsd:string” /> <xsd:element name=”Quantity” type=”xsd:int” />
This schema defines the OrderStockRequest object, which is passed into the Web service as a request. The OrderStockRequest element contains two subelements that are used as inputs: an ID and a Quantity, which correspond to the ID of the item to be ordered and the quantity to be ordered. These elements are referenced inside the <xsd:sequence> element, and are not defined until the bottom of the schema. Alternatively, you can define the inputs directly inside the <xsd:sequence> element, but the approach used above is usually easier to read, especially when your schemas contain multiple complex types.
Creating an Order Stock Web Service
245
Note also the schema definition at the top of the schema; the xmlns:xsd attribute defines a namespace for types prefixed by xsd, and the xmlns:tns defines a namespace for types prefixed by tns. The targetNamespace attribute declares the namespace for all elements defined in the schema (such as ID and Quantity). For more information on the terms namespace and schema, see the sidebar “Web Service Terminology.” When you are done editing the schema, save the builder call.
Defining the Response Object Next, you need to add another schema for the XML data that is to be returned from the Web service (known as the response object). This schema defines the structure of the results that are to be passed back to callers of the Web service, and it is also included in the WSDL document that is generated from your Web service. Add a Schema builder call to the Web service provider model and name it orderStockResponseSchema. Select the ‘Specify explicitly as builder input’ option as you did with the previous schema. In the Schema Source box, type the following XML: <xsd:schema xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:tns=”http://com.ibm.orderStockResponse” targetNamespace=”http://com.ibm.orderStockResponse”> <xsd:element name=”OrderStockResponse”> <xsd:complexType> <xsd:sequence> <xsd:element ref=”tns:Result” minOccurs=”1” maxOccurs=”1” /> <xsd:element ref=”tns:Fault” minOccurs=”1” maxOccurs=”1” /> <xsd:element name=”Result” type=”xsd:string” /> <xsd:element name=”Fault” type=”xsd:string” />
This schema defines the OrderStockResponse object, which is passed back from the Web service as a response. The OrderStockResponse object contains two subelements that are returned to the caller of the Web service: Result and Fault. Result holds the results of the order operation (how many items are currently on order for the ID specified in the orders variable), and Fault holds debugging information if an exception is raised. Save the builder call when you are finished.
246
Chapter 9 Using Web Services and Manipulating XML
Adding Order Data Now add a Variable builder call to the model. The variable created by this builder call holds an XML document containing all of the stock items currently on order. Name the variable orders and make it of type XML. In the Initial Value field, enter the following XML, which defines three items with a quantity of zero: <StockOrders xmlns=”http://com.ibm.orders”> 000010000020000030
In the Advanced section of the Variable builder call, select ‘Read-only: shared across all users’ for the State and Failover input, and then save the builder call. By default, separate variables are created in memory by the Variable builder for each user, so changing this setting ensures that only a single orders variable is used across all users. This setting also ensures that the variable is stored and recovered if the application fails.
WARNING The IXml class in WPF is not thread-safe, so, if multiple people are updating an XML variable at the same time, be sure your code ensures that only one thread at a time updates the variable.
Adding a Method to Place a Stock Order Add a Method builder call to your model. This builder call contains a Java method to place a stock order, which provides the functionality for your Web service orderStock operation. The method takes one input (the XML request object), processes the order based on this input, and then returns one output (the XML response object).
Creating an Order Stock Web Service
247
TIP
Because the code in this method is fairly long, it is sometimes placed in a Java source file instead of a Method builder call, and then linked to from your model via a Linked Java Object. This is useful if you like to use the more advanced editing features of your IDE (as the Method builder itself provides only limited formatting).
Name the builder call orderStock, and then enter one argument called orderStockRequest of type IXml, as shown in Figure 9.2 (IXml is a Java interface used to access and manipulate XML). This enables the method to access the orderStockRequest XML object that is passed into the function. Change the Return Type as well so that it is IXml.
Figure 9.2
Configuring the Method builder call.
In the Method Body field of the Method builder call, enter the following Java method (note that this method is also available for download from ibmpressbooks.com/title/9780137134465 under the Chapter 9 folder): { try { //get id and quantity from request object String id = orderStockRequest.findElement(“ID”).getText();
248
Chapter 9 Using Web Services and Manipulating XML
int quantity = ¯Integer.parseInt(orderStockRequest.findElement(“Quantity”).getText()); //set the total quantity that is on order for the item specified //this quantity will be increased if there are already orders for this item int onOrderTotal = quantity; //get orders variable and put all the elements from it called ‘item’ into a //list updating this list will update the orders variable IXml orders = webAppAccess.getVariables().getXml(“orders”); List ordersList = orders.getChildren(“Item”); //flag that denotes whether a stock item already exists boolean stockAlreadyExists = false; //check if an order for the specified stock item already exists in the list for (Iterator it = ordersList.iterator(); it.hasNext();) { //get order from orders list IXml order = (IXml)it.next(); //if there is already an order for the id specified... if (order.findElement(“ID”).getText().equals(id)) { //the order already exists, increment quantity in orders variable IXml quantityItem = order.findElement(“Quantity”); onOrderTotal = quantity+Integer.parseInt(quantityItem.getText()); quantityItem.setText(Integer.toString(onOrderTotal)); //flag that the item already exists stockAlreadyExists = true; //exit for loop break; } } //if the stock item doesn’t already exist in the orders variable if (! stockAlreadyExists)
Creating an Order Stock Web Service
249
{ //order doesn’t exist, create new order IXml newElement = orders.addChildElement(“Item”); newElement.addChildWithText(“ID”, id); newElement.addChildWithText(“Quantity”, Integer.toString(quantity)) ; } //place order into orders variable webAppAccess.getVariables().setXml(“orders”, orders); //assemble response object String resultString = “An order was successfully made for “ + quantity + “ ¯items of “ + id + “. ” + onOrderTotal + “ items of “ + id + “ are currently ¯on order.”; IXml responseXml = XmlUtil.create(“OrderStockResponse”); responseXml.addChildWithText(“Result”, resultString); responseXml.addChildWithText(“Fault”, “”); //return response object return responseXml; } catch (NumberFormatException e) { //an exception was raised, return fault information IXml errorXml = XmlUtil.create(“OrderStockResponse”); errorXml.addChildWithText(“Result”, “No result — exception raised.”); errorXml.addChildWithText(“Fault”, “An invalid quantity was specified.”); return errorXml; } catch (Exception e) { //an exception was raised, return fault information IXml errorXml = XmlUtil.create(“OrderStockResponse”); errorXml.addChildWithText(“Result”, “No result — exception raised.”); errorXml.addChildWithText(“Fault”, e.getMessage()); return errorXml; } }
250
Chapter 9 Using Web Services and Manipulating XML
Spend a few minutes going over the method to see how it works. (For information on how XML is manipulated in the code, see the “Using the IXml Interface” sidebar. Also, for information on the use of the webAppAccess Java object, see Chapter 8, “Using Java in Portlets”). Essentially, the method takes the ID and Quantity from the request object (which is passed into the Method builder call) and cycles through all of the orders in the orders variable to see if there is a match for the ID specified. If there is a match, then the Quantity specified is added to the Quantity of the matched order in the orders variable; otherwise, a new order is added to the orders variable, with the ID and Quantity specified in the request object. Finally, a response object is created and returned to the caller of the method, which contains the result of the order operation and any fault information. Note also that if an invalid value is specified for the quantity, a NumberFormatException is raised and the ‘An invalid quantity was specified’ message is returned in the Fault output. Any other exceptions are caught and returned as part of the fault output. Save the Method builder call when you are finished.
USING THE IXML INTERFACE The IXml interface is used in WPF to create, access, and manipulate XML. It offers numerous functions for carrying out common operations on XML documents. The XmlUtil class is sometimes used in conjunction with the IXml interface to perform XML manipulation (see Chapter 8 for more examples of using the XmlUtil class). In the example in this chapter, when the response object is built, the following code is used to create a variable in memory called responseXml, which holds a new XML structure called OrderStockResponse: IXml responseXml = XmlUtil.create(“OrderStockResponse”); Code using the addChildWithText method, as shown below, is then used to add child elements to this structure: responseXml.addChildWithText(“Result”, resultString); The previous code adds an element called Result that contains the value of the resultString variable. The following code is used to set the value of the XML variable created by the orders Variable builder call: webAppAccess.getVariables().setXml(“orders”, orders); The following code is used to retrieve the contents of the variable and store it in an IXml variable for further processing (note that any changes to the IXml orders variable also change the XML variable orders created by the Variable builder call): IXml orders = webAppAccess.getVariables().getXml(“orders”); To get a list of elements in an XML variable, you can use code similar to the following (used in the example): List ordersList = orders.getChildren(“Item”);
Creating an Order Stock Web Service
251
This gets all of the child elements matching a particular name (Item, in this case). This list can then be iterated through and processed as in the example. Additionally, the following code can be used to find the first instance of a particular XML element (Quantity) and store it in an IXml variable: IXml quantityItem = order.findElement(“Quantity”); You can then use the methods getText() and setText() to get and set the values of these elements, as shown in the two code fragments that follow: String id = orderStockRequest.findElement(“ID”).getText(); quantityItem.setText(Integer.toString(onOrderTotal));
Adding a Service Operation Now, add a Service Operation builder call to the model. Use this builder to define an operation for the Web service; in particular, you declare its request and response objects and what it actually does when it is run (that is, execute the orderStock method). Point the Data Service input to the orderStockWebService Service Definition, and then change the name of the Service Operation to orderStockItem. Specify orderStock for the Action to Call, and type the description Orders a stock item into the Operation Description input (this description is available to consumers of the operation). Specify the request object for the operation by expanding the Operation Inputs section, and making sure the Input Structure Handling input is set to ‘Specify input schema’. Now, select orderStockRequestSchema/OrderStockRequest for the Input Schema, and fill out the Input Description with the text stock item and quantity to order. The Input Field Mapping input should be set to Automatic. This uses the request schema you created earlier to define the structure of the request object to this operation, and the request inputs passed in will be automatically mapped into your schema.
TIP Note that the Service Operation builder enables you to take the structure of your inputs and outputs from the called action, which is to say that the structure will be taken from the inputs and outputs of the action specified in the Action To Call input.You can’t use this option if the action returns an XML variable that is not schema typed, which is the case with the method used in this example (it is declared as being of type IXml only). Later in this chapter, you set up a service operation that takes its inputs and outputs from the called action.
252
Chapter 9 Using Web Services and Manipulating XML
Specify the response object by expanding the Operation Results section. Select Specify Result Schema for the Result Structure Handling input, and then select orderStockResponse Schema/OrderStockResponse for the Result Schema (this is the response schema you created earlier). Type the description Text describing results of order, and any fault information if an exception was raised into the Result Description input, and save the builder call when you are finished.
TIP To iterate through an entire XML structure, you may find a recursive function useful. For example, the following code iterates through an XML structure passed into it as an argument, and prints the element name and value to the console. The function calls itself to process all of the sibling and child elements of each node in the hierarchy. Copy this code into a Method builder, rename the builder call to iterateThroughXml, and then add one argument called xml of type IXml: { for(IXml element = xml.getFirstChildElement(); element != null; ¯element = element.getNextSiblingElement()) { System.out.println(“XML element name: “ + element.getName()); System.out.println(“XML element value: “ + element.getText()); iterateThroughXml(webAppAccess, element.getFirstChildElement()); } }
You have now successfully created a stock-ordering Web service. The next section explains how you can test the service.
Testing the orderStockWebService Web Service To test the orderStockWebService Web service from your IDE, run your model from the WPF Designer by clicking the icon on the toolbar. This runs the currently active model (orderStockWebService) with the last run configuration you used. If you have not set up a run configuration before, you are prompted to do so—create a new configuration under the WebSphere Portlet Factory Model category (if you want more information on setting up a run configuration, see the “Testing the Application” section in Chapter 1). A testing interface should display, as shown in Figure 9.3, and it should list the operations in your service.
Creating an Order Stock Web Service
Figure 9.3
253
Testing your Web service.
To test whether the orderStockItem operation correctly orders a stock item when the item doesn’t exist in the orders list, click the orderStockItem link, and an interface for entering inputs into the operation (as shown in Figure 9.4) is displayed. Type an ID for an item that doesn’t exist in the orders XML document (for example, 00004) and a quantity to order (for example, 3), and then press the Submit Order button. The page reloads and will now contain nothing other than the Back button.
Figure 9.4
Ordering a new stock item.
To test whether the operation works when the stock item does exist in the orders list, click the orderStockItem link again and enter the same details as before. When you press the Submit Query button, you should see that the quantity for item 00004 has been incremented by the amount specified (3). Press Back when you are finished. Finally, to test whether exceptions are correctly handled, click the orderStockItem link again and leave the inputs blank. Press the Submit Query button and you should see that the response returned indicates that an exception was raised (see Figure 9.5).
Figure 9.5
Testing exception handling.
254
Chapter 9
Using Web Services and Manipulating XML
After you have finished testing the Web service, click the View WSDL link on the test page. You should see a WSDL document similar to that shown in Figure 9.6.
Figure 9.6
Displaying the WSDL for the orderStock service.
This WSDL document will be used in the service provider you create in the next section. As mentioned earlier, you don’t need to understand how the WSDL works; however, you do need to use the URL for the WSDL and paste it into the service provider (this is covered in the next section). Note that because WSDL and SOAP are commonly used Web service standards, you can use this Web service not only in WPF, but also in applications developed using environments other than WPF. After you run the Web service, it is accessible to all applications (WPF or otherwise) that have access to the application server you deployed the Web service provider application to.
Creating a Service Provider
255
TIP The Web service developed in this section is accessed from the development WAR. If you want to deploy this service to a production environment, you must create a deployment WAR, install it on an application server, and then run the application. The service is then accessible to all users with access to the server and won’t contain any unnecessary development artifacts included in the development WAR. Note, however, that the URL for the WSDL changes accordingly.
Creating a Service Provider Now that you have a Web service, you need another WPF project to house the models for the order stock portlet. Separating the Web service and order stock portlet into two separate projects means that they can be deployed separately, so that the Web service can be run independently of the portlet. If you have a project from a previous chapter, you can use it; otherwise, you should create a new WPF project (for more information on creating projects, see Chapter 1). The project will be published as a WAR file and deployed to a portal server, and you should also deploy the application to a local application server for testing (as with the previous project, you can use the portal server if it runs on your local machine; otherwise, you should preferably use WAS CE, but you can also use another application server such as Tomcat or WAS 6.x). After you have a project set up, you need to add two models to the project (a service provider and a service consumer). The service provider accesses the Web service directly, and the service consumer then calls this Web service via the service provider. The first model you create is the service provider. It uses the following builders to provide its functionality: • Service Definition • Web Service Call • Service Operation • Method
Creating a Model and Defining the Service Create a new model called orderStockService in your project, under the folder WEB-INF/ models/chapter09. The model should be based on the Empty template, which creates a model with no builder calls in it. After you have added the model to your project, add a Service Definition builder call to the model and name it orderStockService. Enable the Add Testing Support checkbox in the Testing Support section, and save the builder call.
256
Chapter 9 Using Web Services and Manipulating XML
Calling the Web Service Add a Web Service Call builder call to your model. This builder call is used to consume the operation on the Web service you created earlier in this chapter. Enter the name consumeStockService for the builder call, and then enter the URL for the WSDL for your Web service into the WSDL URL input (you can obtain this URL by pressing the View WSDL link from the Web service test page). This URL differs depending on your project name and the server you deployed the Web service to, but it should be similar to the following: http://localhost:10038/WebServiceExample/webengine/Chapter09/ orderStockWebService/Action!getWSDL
You should be able to type this URL into a Web browser and see the WSDL document (as shown earlier in Figure 9.6). After you put in the correct URL, you can press the Fetch WSDL button to fill in the Web Service Call builder call with the values from the WSDL. Note that you can easily consume any WSDL-based Web service simply by specifying the URL for the WSDL in the WSDL URL input. If a WSDL changes, be aware that you will need to refresh the Web Service Call builder call (which you can do by pressing the Fetch WSDL button and saving the builder call).
TIP The Service Call Type input in the Web Service Call builder enables you to connect to a number of different Web services. WSDL Web services are common and easy to use, but not all Web services publish WSDL documents (nor are they always made available to you). Aside from WSDL Web services, using WPF can also be used to call SOAP Web services that don’t publish WSDL documents. HTTP Web services can be used as well. Note that WPF provides a LOCAL option for accessing Web services that exist in the same project as your consumer, but to make your applications more extensible, it is recommended that you always use the service provider/consumer pattern when accessing local services.
If you cycle through some of the inputs in the Web Service Call builder call, you can see that they have been populated with values obtained from the WSDL. Enable the AutoCreate Input Vars input in the Request Parameters section, which automatically populates the request object with whatever values are sent in to the Web service call. Save the builder call when you are finished.
TIP The Service Information section of the Web Service Call builder call lists some basic information (e.g., descriptions and namespaces) on the Web service, and the Advanced section enables you to configure such things as a username and password to use when accessing the Web service (if one is required).You don’t need to configure any of these inputs in the current example.
Creating a Service Provider
257
Adding a Service Operation The last builder call for the orderStockService defines an operation for the service. Add a Service Operation builder call to the model and name it orderStockItem. Set the Data Service to orderStockService, and enter the Action to Call as DataServices/consumeStockService/execute (this line is used to execute the operation defined in the Web Service Call). Expand the Operation Inputs and Operation Results sections, and make sure you take each structure from the called action. This uses the appropriate schemas from the Web Service Call to define the structure of the request and response to the orderStockItem service (the same schemas are used). The field mapping in each section should also be set to Automatic. Save the builder call when you are finished.
TIP Although the request and response objects are automatically created in this example, you can also manually create them using IXml. For example, the following method can be used to construct a request object to send to the Web service: { //assemble request object IXml requestXml = XmlUtil.create(“OrderStockRequest”); requestXml.setAttribute(“xmlns”, ¯“http://com.ibm.orderStockRequest”); requestXml.addChildWithText(“ID”, ¯webAppAccess.getRequestInputs().getInputValue(“stockItem”)); requestXml.addChildWithText(“Quantity”, ¯webAppAccess.getRequestInputs().getInputValue(“stockQuantity”)); //return request object return requestXml; }
The service provider is now ready to test.
TIP If you’re browsing through the Select Action dialog opened from the service provider, you may notice that the consumeStockService Web service under the Methods section has a number of methods available, and one of these is the invoke method. When calling an operation on a Web service, you can use either the invoke method or the execute command (like in the example), as they both do the same thing.
258
Chapter 9 Using Web Services and Manipulating XML
Testing the Service Provider To test the service provider, run it from your IDE. The testing interface is exactly the same as for the Web service, with the exception that no link is provided to view the WSDL (because the service provider is not itself a Web service). Step through the same tests for the Web service and make sure you can access the Web service operation correctly. If you get any errors when testing the service provider, check that you have correctly configured the Web Service Call builder call and that you can access the WSDL document for the Web service in a browser.
TIP One of the most common error messages you can get when trying to access a Web service is as follows: The AXIS engine could not find a target service to invoke! targetService is null If you get this error, there is likely a mismatch between the structure of your request object and the request object expected. Check that the structure and namespace of your request object match those specified in the schema for the request object in the Web service.
Creating an Order Stock Portlet Now that your service provider is finished, you need to create a consumer for the service, which will be published to a portal server as a portlet. A screenshot of the finished product is shown in Figure 9.10. The order stock portlet uses the following builders to provide its functionality: • Action List (x2) • Page • Portlet Adapter • Service Consumer • Variable • Data Page (x2) • Button Note that the Page builder call should precede the Data Page builder call to improve performance; this is because the Data Page builder call references the results of the Page builder call, and putting the Page builder call first stops the WPF generation engine from making two passes of the builder calls in your model.
Creating an Order Stock Portlet
259
Creating a Model Create a new model called orderStock in your project, under the folder WEB-INF/models/ chapter09. The model should be based on the Main and Page template, using a Page Type of Simple Page. For more information on creating models, see the example in Chapter 1. After the model is created, it should have two builders in it: an Action List called main and a Page called page1. The Action List runs automatically whenever the model is run and will open the page1 Page.
Modifying the Page Open the Page builder, and change the contents of the Page Contents (HTML) input to the HTML listed in the following: Order Stock
The HTML contains three span tags that replaced later in this section using builders in WPF: The first (serviceRequest) is for the request inputs, the second (submitOrder) is for a button to submit the form, and the third (serviceResponse) is for displaying the results of the service. Save the builder call when you are finished.
TIP There are a number of different ways that you can work with HTML in WPF. You can, as in the example, use a Page builder; or you can include an external HTML file in the WebContent folder of your project, which you can then use in your model via an Imported Page builder call. You can also combine these methods using the Inserted Page builder to insert pages into other pages. The use of HTML in WPF is discussed in Chapter 5, “Customizing Portlet Appearance.”
260
Chapter 9 Using Web Services and Manipulating XML
Adding a Portlet Adapter The first builder call to add is a Portlet Adapter builder call, which surfaces the orderStock model as a portlet on the portal server specified in your deployment configuration. Add a Portlet Adapter builder call to the model, and then change the Name to orderStock. Also, change the Portlet Title to Order Stock and the Portlet Description to Allows users to order stock items. This surfaces your model to the portal server as a portlet called Order Stock. Save the builder call when you are finished.
Consuming the Service Add a Service Consumer builder call to the model. You use this model to make the operations in the orderStockService service available in your model. Change the name of the builder call to orderStock, and then specify the location of the provider in the Provider Model input (chapter09/ orderStockService). Save the builder call when you are finished.
Creating a Variable to Store the Request Now add a Variable builder call to the model. This builder creates a variable based on the request schema for the orderStockService service, and is sent to the service when users press the Submit Order button in your portlet. Later, you use this variable to build an interface for users to enter ID and Quantity information. Change the name of the variable to stockRequest, and then select the request schema from the service for the Type input (orderStockConsumeStockService_WSDLSchema_1/ OrderStockRequest). This automatically populates the Namespace input with the appropriate namespace taken from the schema. Leave the Initial Value input blank (as the variable will be set by users when they run the application and enter values for the ID and Quantity fields) and save the builder call.
Displaying the Interface Add a Data Page builder call to the model (for more information on the use of the Data Page builder, see Chapter 3, “Using Data from a Relational Data Source”). This Data Page is used to build an interface so that users can specify values for the order ID and Quantity. Change the name of the Data Page to displayServiceRequest, and change the Variable input to point to the variable you created earlier (Variables/stockRequest/OrderStockRequest). Change the Page Type input to Data Entry, so that text fields are automatically created for each element in the variable (ID and Quantity). Change the Page in Model input to page1, and the Location for New Tags input to serviceRequest, so that the results of the builder are displayed at the serviceRequest tag on page1. Save the builder call when you are finished. The next step is to add a Data Page builder call to display the results of calling the Web service. Add a Data Page to the model and name it displayServiceResponse. Select DataServices/orderStock/orderStockItem/results/OrderStockResponse for the Variable input and press
Creating an Order Stock Portlet
261
OK. This makes the Data Page display the results of executing the operation described by the consumeStockService builder call. Next, specify page1 for the Page in Model input, so that the results are displayed on the page created by the Page builder you created earlier, and specify ‘View Only’ for the Page Type (because there is no need to be able to edit the results). In the Created Element Settings section, specify serviceResponse in the Location for New Tags input, so that the results are displayed at the serviceResponse <span> tag, and then save the builder call.
Adding Submit Functionality Next, add an Action List builder call. This action list is run whenever the user submits an order by pressing the Submit Order button, and it contains three actions: first, it sets the request object for the orderStockItem operation from the OrderStockRequest variable (which contains the ID and Quantity values entered by the user); then it executes the orderStockItem operation; and finally, it displays the page created by the Page builder call (which then displays the results of calling the service on the screen). For the first action, use the Select Action dialog (which you open by pressing the ellipsis button to the right of the row you want to change), and select Assignment from under the Special section. Fill out the Make Assignment dialog that follows, as shown in Figure 9.7, and press OK to accept the action.
Figure 9.7
Configuring the Make Assignment dialog.
For the second action, select the orderStockItem operation (DataServices/orderStock/ orderStockItem), and type page1 for the third action. Save the builder call when you are finished. The last builder call to add is a Button builder call. This inserts a button at the submitOrder <span> tag on the HTML page, and it enables users to submit a stock order (by calling the Action List you created earlier, which calls the Web service and displays the results on the page). Add a Button builder call and configure it as shown in Figure 9.8. Save the builder call when you are finished. The service consumer is now ready to test.
262
Figure 9.8
Chapter 9
Using Web Services and Manipulating XML
Configuring the Submit Order button.
Testing the Order Stock Portlet
To test the orderStock model, run it from your IDE. The page shown in Figure 9.9 should display in your default Web browser. The functionality of the service consumer is the same as the service provider, so step through the same three tests for the service consumer.
Figure 9.9
Interface for the service consumer.
After you have tested your model, you should rebuild the application containing the portlet and test it on the portal server as a portlet (for instructions on how to do this, see the example in Chapter 1). After you have added the portlet to a page in the portal, your portlet should display as shown in Figure 9.10.
XML Transform Builders
Figure 9.10
263
The Order Stock portlet.
XML Transform Builders You have already seen how to create, access, and manipulate XML using the IXml interface, which gives you control over your XML processing. However, WPF also comes with a number of builders to perform several common XML transformations, which are discussed in the following sections. These builders don’t give you as much control as the IXml interface, but they can be implemented without writing any Java code (although it is also possible to extend them with custom Java transformation methods).
Transform This builder can be used to copy a source XML document into a target XML document. You use this builder if you want to modify the structure of an XML document from one schema to another. To use this builder, both the source and target XML documents must be defined by a schema. The example in the next chapter uses this builder to copy an XML element into a structure where it can be rendered as a chart via the Web Charts builder.
Transform - Filter This builder can be used to filter elements in and out of an XML document or Java bean. The filtered XML can be saved over the top of the old XML document or into a new XML document. Note that although some of the other XML builders also enable basic element level filtering, the Transform - Filter builder enables you to specify detailed filter expressions based on variable values. So, for example, you can filter all elements with a name that does not equal a certain variable value.
264
Chapter 9 Using Web Services and Manipulating XML
Transform - Rename This builder renames a particular element in an XML document, and then saves the results based on a particular schema (it doesn’t necessarily have to be the same as the original schema). The target XML of the builder does not have to be the same as the source XML.
Transform - Sort This builder enables you to sort an XML document or Java Bean collection, based on a particular element name. The XML document must be based on a schema to be used by this builder. The source and target XML do not necessarily have to be the same, but they need to be based on the same XML schema. An example using XML transformation is provided in Chapter 10, “Using Charts in Portlets.”
Summary In this chapter, you learned how to create a Web service and consume it from WPF. You also learned how to store, access, and manipulate XML documents using WPF features, such as the IXml interface, the Variable builder, Schema builder, and the Transform builders. You also created, deployed, and tested two applications: a Web service providing an operation to order stock items and a portlet application to enable users to order stock items via the Web service. The examples in this chapter can be extended not just to providing and consuming other Web services from within WPF, but to providing Web services to applications developed outside of WPF, and consuming publicly available Web services as well (which could have been developed in any number of different languages). A list of publicly available Web services can be found at www.xmethods.org. Chapter 10 discusses how to create charts in a portlet using WPF.
Important Points • Web services can be implemented in WPF using the service provider/consumer pattern. Web services built in WPF can be accessed both inside and outside of WPF applications. Applications built outside of WPF can access WPF Web services. • A model can be converted into a Web service by adding a Web Service Enable builder, or by enabling the Generate WSDL option on the Service Definition builder. • A model can call a Web service using the Web Service Call builder. Web services should be called from a service provider. • XML documents are often used in WPF for Web service communication and are usually sent via SOAP messages. Consumers send a request message to the Web service, which then returns an appropriate response.
Important Points
265
• The IXml interface is a powerful way to create, access, and manipulate XML documents in WPF. • The Transform builders can be used to transform one XML document into another, without writing any Java code. • The Schema builder can be used to define an XML schema for Web service requests and responses.
This page intentionally left blank
C
H A P T E R
1 0
Using Charts in Portlets
Graphical charts are an impressive, visually appealing way to present information, and WebSphere Portlet Factory (WPF) provides the capability to automatically render a variety of charts inside your portlets. This chapter focuses on how to use the charting capabilities of WPF, and by the end of the chapter, you will have a good knowledge of the different chart configuration options available. To demonstrate these techniques, you will build a portlet that displays sales information, which you can then use to experiment with the different chart styles available. This portlet also enables users to drill down in the chart to get more information on a particular chart item. The example application discussed in this chapter utilizes one model and one XML style definition, which are available for download from ibmpressbooks.com/title/9780137134465, under the Chapter 10 folder (instructions for copying these files into your project are included in a readme.txt file in the same folder). However, to increase your understanding of the topics discussed in this chapter, it is recommended that you create these files yourself by following the example. The following topics are covered in this chapter: • Charting data in WPF • Creating a service provider • Creating a sales chart portlet • Adding drill-down capabilities • Custom chart styles • Upgrading to a deployment license
267
268
Chapter 10 Using Charts in Portlets
Charting Data in WPF The Web Charts builder enables you to quickly and easily add charting capabilities to your portlets in WPF, based on the contents of an XML data set. You can create an XML data set in WPF by using one of the data access builders (such as Domino Data Access or SQL DataSource), which automatically create XML data sets based on data taken from a particular data source. You can also explicitly specify XML data sets in Variable builders or XML files; the example in this chapter demonstrates this approach. A number of different chart styles are available when using the Web Charts builder, which are: • Area
• Line
• Radar
• Bar
• Pyramid
• Step
• Dial
• Pie
You can also add custom styles to a chart by importing XML style definition files into your project, which you can create using a program called Web Charts 3D (available online from www.gpoint.com). Web Charts 3D gives you a graphical interface for selecting and customizing chart styles, and it lets you change chart colors, format chart text, and add effects to charts (such as shadows and translucency). An example of using a custom style is provided later in this chapter.
TIP The Web Charts builder included with WPF is an evaluation version only. There is no expiration date on the evaluation period; however, an evaluation version message is displayed at the bottom of each chart until you obtain a deployment license.You can obtain a deployment license by contacting GreenPoint at [email protected]. The “Upgrading to a Deployment License” section in this chapter describes how to upgrade the Web Charts builder after you obtain a license.
The Web Charts builder also provides drill-down capabilities; that is, the facility to activate sections of a chart so that users can click on them to bring up more information. Given that charts are easier to understand if they display only a small amount of data, this provides a convenient way to show detailed information on points of interest in your charts. For example, in this chapter, you will see how to add drill-down functionality to a chart displaying quarterly sales figures. When a user clicks on one of the bars in the chart (corresponding to a month in the last financial year), a pie chart is opened, displaying a breakdown of sales figures by sales area for that month. Developers can extend the capabilities of the Web Charts builder using the WebSphere Dashboard Framework. The Dashboard Framework is an extension to WPF, which provides (among other things) additional builders that offer new charting functionality. For example, the Bullet Graph builder provides a wizard-based interface for creating charts that measure performance against a goal, and the Map builder enables you to create color-coded maps of particular
Creating a Service Provider
269
geographical locations, which users can drill down into for more information. The Dashboard Framework also aggregates several common charting functions in WPF into higher-level builders; for example, the Summary and Drilldown builder can be used to quickly add page tabs (usually added by the Page Tabs builder) to navigate between a chart and its drill-down information (both of which are usually added by the Web Charts builder). The next section walks you through the process of creating a chart portlet in WPF. The chart displays monthly sales figures taken from a service provider model, which is built in the next section. For more information on the service provider/consumer pattern, see Chapter 2, “Providing and Consuming Services.”
Creating a Service Provider Before you can use the charting capabilities of WPF, you need a WPF project to hold the chart portlet. If you have a project from a previous chapter, you can use it, but you will need to add the GreenPoint Web Chart Builder feature set (to do this, open the Project Properties dialog and select GreenPoint Web Chart Builder from under the Charting category in the WebSphere Portlet Factory Project Properties/Feature Info section). If you don’t have an existing project, create a new WPF project with the GreenPoint Web Chart Builder feature set enabled (for more information on creating projects, see Chapter 1, “Introduction to WebSphere Portlet Factory”). The project will be published as a WAR file and deployed to a portal server, and you should also deploy the application to a local application server for testing (you can use the portal server if it runs on your local machine; otherwise, it is recommended you use the IBM WebSphere Application Server Community Edition server [WAS CE] that comes with WPF). In this section, you build a service provider model to make sales data available to the sales chart portlet. The following builders provide the model’s functionality: • Service Definition • Variable • Simple Schema Generator • Action List • Service Operation Note that the Service Definition builder call must come before the Service Operation builder call, and the Service Operation builder call must follow the Action List (otherwise, you will get errors that indicate that these resources are unavailable). Similarly, the Simple Schema Generator must follow the Variable builder call.
Creating a Model Create a new model called salesService in your project, under the folder WEB-INF/models/ chapter10. Because you will be storing your data in an XML variable rather than getting it from a database, you need to create the service provider manually. To do this, the model should be based
270
Chapter 10 Using Charts in Portlets
on the Empty template, which creates a model with no builder calls in it (for more information on creating models, see the example in Chapter 1).
Defining the Service Add a Service Definition builder call by selecting Service Definition from the Builder Palette (you can open the Builder Palette by clicking the icon in Outline view) and pressing OK. Name the Service Definition salesService, and then enable the Add Testing Support checkbox in the Advanced section. This lets you run the model and test whether your service operation is working correctly. Save the builder call when you are finished.
Adding Sales Data The next step is to add an XML document to the model, which defines the sales data displayed in the chart. Add a Variable builder call to the model and call it sales. Change the Type input to XML, and then type the following XML document into the Initial Value input: <Sales> <Month>January 113630134980253340182100684050630000 <Month>February 109990147330224320208650690290650000 <Month>March 142270155740
Creating a Service Provider
271
197630212740708380700000
WPF charts can graphically represent any XML document, provided that it is possible to meaningfully split up the document into rows and columns. In the previous XML, each Item element corresponds to a separate row, which contains a set of columns. In this section, the Month and TotalSales columns are used to display data on the chart for each row. Save the builder call when you are finished.
Adding a Sales Schema The next builder call to add is a Simple Schema Generator, which automatically creates an XML schema for you. This schema is used by the service to define the structure of the sales data it returns to service consumers. Add a Simple Schema Generator builder call to your model and name it salesSchema. For the Sample Data input, specify the Variable you created in the previous step (sales), and then specify the URI in the Target Namespace section as http://wpf.ibm.com/2002/10/sales. Save the builder call when you are finished.
Adding an Action to Retrieve Sales Data Now add an Action List builder call to the model and name it getSales. This action list returns the contents of the sales variable you created earlier and is called from the Service Operation builder call created next (you need to create the action list first; otherwise, you will get errors in your project, indicating missing resources). Change the Return Type of the builder call to IXml. IXml is a WPF interface used to manipulate XML and is used because there is no XML data type WPF as such. For more information on the IXml interface see Chapter 9, “Using Web Services and Manipulating XML.” Open the Select Action dialog for the first action in the Actions input (you can do this by pressing the ellipsis button to the right of the first row in the Actions input). From the Select Action dialog, select Special/Return to return a value to the caller of the Action List, then select the Sales element in the sales variable (Variables/sales/Sales). Press OK to accept the action, and save the builder call when you’ve finished.
Specifying the getSales Operation The final builder call to add to the service provider is a Service Operation. Add the Service Operation builder call now and name it getSales. Point the Data Service input to the Service Definition builder call created earlier (salesService) and specify the Action To Call as getSales (this is the action list you just created).
272
Chapter 10
Using Charts in Portlets
You now need to specify the inputs and outputs for the Service Operation. To do this, expand the Operation Inputs section and select ‘No inputs’ for the Input Structure Handling input (because no inputs are required, the operation just needs to return sales data). For the Operation Results section, select ‘Specify result schema’ for the Result Structure Handling, which lets you specify the schema of your results, and then select the Sales element of the salesSchema schema for the Result Schema input (salesSchema/Sales). Make sure the Result Field Mapping input is set to Automatic. This specifies the structure of the results returned from the operation. Make sure that the results from the getSales action are automatically mapped into this schema. Your service provider is now ready to test. Testing the Service Provider
Because you have opted to add testing support in the Service Definition builder call, WPF automatically generates test pages for your service provider (which you can disable from the Service Definition builder call). This enables you to test the service provider directly from the IDE. To test the service, run the salesService model from the WPF Designer by clicking the icon on the toolbar. This runs the currently active model (salesService) with the last run configuration you used. If you have not set up a run configuration before, you are prompted to do so— create a new configuration under the WebSphere Portlet Factory Model category (if you want more information on setting up a run configuration, see the “Testing the Application” section in Chapter 1). You should now see the index test page in your default Web browser, as shown in Figure 10.1. This screen displays a link to test the getSales operation on the salesService service.
Figure 10.1
Index test page from the salesService model.
To test the getSales operation, click the getSales link. You should see some sales data, as shown in Figure 10.2. Press Back to return to the index page. Your service provider is now complete.
Figure 10.2
Testing the getSales operation.
Creating a Sales Chart Portlet
273
Creating a Sales Chart Portlet In this section, you add a chart portlet to your WPF project to display quarterly sales figures for a fictional company. The total sales figures for each month display in bars along the x axis of the chart. The data used in the example is retrieved from an XML document stored inside the portlet, although you can use XML produced by any of the WPF builders (as well as imported XML files). A screenshot of the model output from this section is given in Figure 10.7 toward the end of this chapter. The “Adding Drill-Down Capabilities” section later in this chapter extends the portlet by enabling users to display a pie chart of sales figures by sales area for a particular month. In the “Custom Chart Styles” section of this chapter, you also add a horizontal line to the chart reflecting target sales for each period. The following builders are used in this section: • Portlet Adapter • Page • Action List • Service Consumer • Web Charts
Creating a Model To add a chart model to the project, create a new model called salesChart in your project, under the folder WEB-INF/models/chapter10. The model should be based on the Main and Page template, using a Page Type of Simple Page. For more information on creating models, see the example in Chapter 1. After it is created, your model should have two builders in it: an Action List called main and a Page called page1. The Action List runs automatically whenever the model is run and opens the page1 Page.
Modifying the Page Now open the Page builder and replace the contents of the Page Contents (HTML) input with the following HTML:
<span name=”salesChart”>
The chart you create in this chapter is placed at the salesChart span tag. Note that it is easier to share your HTML with other developers or between projects if use HTML files rather than the
274
Chapter 10 Using Charts in Portlets
Page builder; you can then reference these pages via the Imported Page builder. In this example, the Page builder is preferred because it is a fairly simple HTML page that is not intended to be reused in other portlets. Save the builder call when you are finished.
Modifying the Main Action List The main Action List defines the execution flow for the salesChart model. In this example, the execution logic is simple: You run the getSales operation, and then display the contents of page1. To do this, open the main action list. You can see that an action already exists to open page1, so create a new action by opening the Select Action dialog for the second action in the Actions input. Add an action to consume the getSales operation, as shown in Figure 10.3.
Figure 10.3
Consuming the getSales operation.
Press OK to accept this action. When control returns to the Action List builder, drag and drop the page1 action so that it occurs after the action you just created (you don’t want to display page1 until the getSales operation has executed). Save the builder call when you are finished.
Adding a Portlet Adapter You can now add a Portlet Adapter, which surfaces your model as a portlet when the application is deployed to a portal server. Select Portlet Adapter from the Builder Palette and press OK. Name the Portlet Adapter salesChart, and then change the Portlet Title to Sales Chart and the Portlet Description to This portlet displays a chart containing sales information. This surfaces the sales model as a portlet called Sales Chart. Save the builder call when you are finished.
Creating a Sales Chart Portlet
275
Consuming the Service Next, add a Service Consumer builder call to the model to consume the salesService service provider you created in the previous section. Name the Service Consumer sales, and point the Provider Model input to chapter10/salesService. Save the builder call.
Adding a Chart The final builder call to add to the salesChart model in this section is a Web Charts builder call, which displays the TotalSales values from the sales Variable in a bar chart. Add a Web Charts builder call to the model and name it salesChart, and then fill out the Page Location section of the builder call, as shown in Figure 10.4. This replaces the contents of the salesChart span tag on the page1 page with a chart defined by the Web Charts builder call.
Figure 10.4
Configuring the page location for the sales chart.
The next section of the builder, Chart Style and Data, defines the data set for the chart and how the chart is formatted. For the Chart Data input, open the Choose Reference dialog (by clicking the ellipsis button to the right of the input) and select the Sales element of the results from the getSales operation, as shown in Figure 10.5.
Figure 10.5
Specifying the Chart Data input.
276
Chapter 10 Using Charts in Portlets
This setting links the chart to the Sales element of the results returned from the getSales operation. Note that if you want to chart a sequence of values (as you normally do), then you need to point the Chart Data input to an element that contains a sequence of rows. For example, the Sales element contains a sequence of Item elements, so it is used as the target for the Chart Data input. If you try to link directly to the sequenced elements (Item, in this case), then only the first element in the sequence is charted. Make sure Bar is selected as the Chart Type, which outputs the results of the Web Charts builder as a bar chart. When you test your portlet in the next section, you can also experiment with some of the other available styles.
Configuring the Data Transformation The next section of the builder call, Data Transformation, defines how the data set is read to display it in the chart and whether any transformations need to be performed before the data is displayed (the original data set is not affected). Make sure Tabular Data (Rowset/Row) is set for the Data Source Format input; this transforms the sales data into a format readable by the Web Charts builder. When using this option, note that your data needs to be in a similar structure to that used in the example; that is, in a row set containing a sequence of rows, where each row contains one or more columns, as shown in the following (data returned from the data access builders is already in this format): Value1Value2Value3Value4
TIP You can also select the WebCharts Format option for the Data Source Format input, which does not perform any transformation on the data set. When you use the WebCharts Format option, the rows and columns in the data set must be defined in the following way: <XML>
ColumnName1
ColumnName2 Series1
Creating a Sales Chart Portlet
277
Series2 Each column name is defined in a COL element, and the column values are specified in ROW elements. The order of the column elements defines how they display in the chart. When specifying column values inside the ROW element, each COL reference must also have an index that relates the column value back to one of the columns defined by the COL elements. Note that the enclosing element must be called XML (case sensitive) and that row and column elements must be called ROW and COL, respectively (also case sensitive). Note also that the numeric values in each column are contained in quotes.
Because you are using the Tabular Data (Rowset/Row) format, you need to define which parts of the data set should be used in the chart (this is not required when using the WebCharts Format option). Type Month for the X Axis Tag input, which defines the values for the x axis of the chart in terms of the Month element (so each bar on the chart is labeled with a different Month value). Next, specify ‘Include columns’ for the Include or Exclude Columns input, so that you can choose the elements that display as bars in your bar chart (that is, the values for the y axis). A new input called Column List displays immediately below the Include or Exclude Columns input; select TotalSales for the first column (you will add other columns later in the chapter).
Specifying Chart Properties The Chart Properties section of the builder enables you to configure various aspects of the chart’s appearance. By default, these settings come from the underlying chart style. Because you are displaying only one column for each month (TotalSales), you don’t need a legend for the chart, so change the Show Chart Legend input to No Legend. When you add additional columns later in this chapter, you will revert this back to its default setting. You do not need to change any of the other default chart property settings at this point. Skip over the Chart Click Action section—you return to this section later in the chapter when you add drill-down capabilities to the chart.
Specifying Minimum Scale Values By default, the origin of the bar chart (that is, the point where the x and y axes meet) will be at coordinate {0,0}. This is fine in most circumstances, but because the y values in the current example (TotalSales) are so large, the difference between each month is not easy to discern. To make the chart easier to read, you should change the y axis so that it begins at point 600,000 rather than at point 0. This makes the differences between each month stand out more, without excluding any of the chart values (assuming, of course, that no value ever drops below 600,000). When you test the model in the next section, you can revert the y axis back to the default setting, which makes the effect of modifying the y axis setting clearer.
278
Chapter 10
Using Charts in Portlets
The Data Labels section specifies settings for placing data labels on the page. Leave the default settings in the Data Labels section, which uses the label settings specified by the chart style, and save the builder call. You now have a model displaying a chart of sales information, which you will test in the next section. Testing the Test Data Portlet
To test the chart portlet, first run the chart model from the WPF Designer. After you have run the salesChart model, you should see a chart displaying sales figures for a three-month period, as shown in Figure 10.6. Hover over each of the bars on the chart, and you should see the total sales figures for each month displayed.
Figure 10.6
Testing the salesChart model.
Experiment with some of the other chart styles available by opening the Web Charts builder call, modifying the value of the Chart Type input, and then retesting the model. The “Custom Chart Styles” section in this chapter discusses how you can modify the appearance of the default styles. You can also try to reverse the minimum value of the y axis back to the default setting, which demonstrates how you can modify the appearance of the chart by changing the axis settings. After you have tested the salesChart model, you should rebuild your application on the portal server (for instructions on how to do this, see the example in Chapter 1), after which you will be able to view the salesChart model as a portlet on the portal server. After you have added the salesChart portlet to a page in the portal, it should appear as shown in Figure 10.7. The next section of this chapter discusses how to add drill-down capabilities to the portlet, so that you can get more information about a particular chart item.
Adding Drill-Down Capabilities
Figure 10.7
279
The salesChart portlet.
Adding Drill-Down Capabilities In this section, you create a drill-down page for the salesChart model, which displays sales information by area in a pie chart. When a user clicks on a particular bar in the bar chart (corresponding to a particular month), he is taken to the sales area information for that month. Of course, you can use any page for the drill-down page, and you don’t necessarily need to display another chart (a table, for example, is a common way to display detailed information for a chart drilldown). The following builders are added in this section: • Comment (x2)
• Service Operation
• Variable (x2)
• Page
• Transform
• Web Charts
• Action List (x2)
• Button
TIP The WebSphere Portlet Factory Dashboard Framework comes with builders that automate much of the process involved in adding drill-down functionalities.
280
Chapter 10 Using Charts in Portlets
Adding Temporary Variables The drill-down chart displays the current row of the salesChart chart, which can be accessed from the rowData property of the Web Charts builder. Because the current row is a subset of the <Sales> element, it is in the following form: <Month>
As it is, this data cannot be properly rendered in a chart, as it is not enclosed inside a Rowset element (such as Sales). One way to get around this is to transform the rowData XML so that it is enclosed inside a rowset. To transform the rowData XML, you need two temporary variables: The first holds the rowData XML before the transformation, and the second holds a transformed version of the XML. Both variables need to be based on a schema to use them with the Transform builder, which is used to perform the actual transformation (see Chapter 9 for more information on XML transformation). The transformation should be made to the service provider rather than the service consumer (this makes testing easier and it makes it easier to reuse the functionality elsewhere), so open the salesService model. Add a Comment builder call, name the Comment Sales Area Chart, and then save the builder call. This comment will provide a clear divide in your model between the builder calls for each chart. Now, add a Variable builder call to the salesService model. Name the builder call currentRow, and change the Type of the variable to salesSchema/Item (the item element in the schema), which automatically fills in the namespace of the Variable for you. This builder call holds the value of the current row. Save the builder call when you are finished. Add another Variable builder call to the model and name it currentRowTransformed. Change the type of the variable to salesSchema/Sales. This builder call will hold the value of the transformed row. Notice that the root element of the variable is a rowset (Sales); this enables the variable to be rendered by the Web Charts builder in a chart. Save the builder call when you are finished.
Defining the Transform The next step is to define the XML transformation itself. Add a Transform builder call to the model and name it transformCurrentRow. The Transform builder requires two variables: a source variable, which contains the untransformed XML (currentRow in this case), and a target
Adding Drill-Down Capabilities
281
variable, containing the transformed XML (currentRowTransformed). Fill out the top section of the builder call, as shown in Figure 10.8. This defines the deepest element of the currentRow variable (Item) and the deepest element of the currentRowTransformed variable (Sales/Item).
Figure 10.8
Configuring parent elements for the Transform builder.
The Child Node Mappings section of the builder defines the mappings between the source and target XML documents. Both XML structures use the same element names, as they are based on the same schema, so there is no need to change any of the default mappings. Scroll down to the Parent Node mapping input, which defines the mapping of elements that are higher in the XML document structure than the elements specified in the Child Node Mappings section. In this case, you have to map only the Item element (you cannot map the Sales element, as it has no equivalent in the currentRow variable). Configure the Parent Node mapping section of the builder, as shown in Figure 10.9 (you can delete rows by right-clicking them and selecting ‘Delete row’. Note that you cannot change the value of the source node attribute, so you should delete the row that contains the blank source node value). Save the builder call when you are finished.
Figure 10.9
Configuring parent node mapping for the Transform builder.
282
Chapter 10 Using Charts in Portlets
Specifying a Method to Get the Current Sales Item The next step is to create a Method that can used to retrieve a particular sales item from the sales Variable, based on the user’s selection. To do this, add a Method builder call to the model and name it getRow. Add an argument of String called index in the Arguments section of the builder call, and then enter the following code in the Method Body input: { //set index int iteratorIndex = 0; //get sales Variable IXml sales = webAppAccess.getVariables().getXml(“sales”); List salesList = sales.getChildren(“Item”); //find current row for (Iterator it = salesList.iterator(); it.hasNext();) { //get sales item from sales list IXml item = (IXml)it.next(); //if current item matches index... if (index.equals(Integer.toString(iteratorIndex))) { try { //set currentRow variable webAppAccess.getVariables().setXml(“currentRow”, ¯XmlUtil.parseXml(item.toString())); } catch (java.io.IOException e) { //set currentRow variable to null webAppAccess.getVariables().setXml(“currentRow”, ¯null); } //exit for loop break; } //increment search index
Adding Drill-Down Capabilities
283
iteratorIndex ++; } }
This code iterates through the items in the sales variable and checks to see whether the index of the item matches the index passed in to the method. If there is a match, the currentRow Variable is set using a String representation of the current item (if an error occurs with the current Row Variable set to a null value). Save the builder call when you are finished.
Specifying an Action for the getSalesArea Operation Now, you need to specify the sequence of actions that will be run when the getSalesArea operation is run. Add an Action List builder call to the model and name it getSalesArea. Add an argument of type String called index, and then change the Return Type of the builder call to IXml (you will be returning an IXml object to the caller of the service operation). Finally, specify the actions for the Actions input that are shown in Figure 10.10.
Figure 10.10
Specifying getSalesArea actions.
The first action runs the getRow Method, passing in the index from the Action List as an argument. The getRow Method then sets the value of the currentRow Variable based on the index. The second action, transformCurrentRowCallCopy, runs the Transform builder’s Call Copy operation, which transforms the currentRow Variable and stores the result in the currentRowTransformed Variable. The final action then returns the Sales element of the transformed Variable. Save the builder call when you are finished.
Specifying the getSalesArea Operation The final configuration for the service provider is to specify an operation to run the getSalesArea Action List. To do this, add a Service Operation builder call to the model. Name the Service Operation getSalesArea, and specify the Data Service as salesService. For the Action to Call input, specify getSalesArea. You now need to specify inputs and outputs for the operation. To do this, expand the Operation Inputs section and select ‘Use structure from called action’ for the Input Structure Handling input and make sure the Result Field Mapping input is set to Automatic. This takes the structure of
284
Chapter 10 Using Charts in Portlets
the inputs from the inputs of the getSalesArea Action List and automatically maps inputs to the service operation into this structure. For the Operation Results section, select ‘Specify result schema’ for the Result Structure Handling, which lets you specify the schema of your results, and then select the Sales element of the salesSchema schema for the Result Schema input (salesSchema/ Sales). Make sure the Result Field Mapping input is set to Automatic. This specifies the structure of the results returned from the operation. Be sure the results from the getSalesArea action are automatically mapped into this schema. Save the builder call when you are finished. This completes the configuration of the service provider.
Testing the getSalesArea Operation You should run a quick test of the service provider model at this point. Run the salesService model from your IDE and click on the getSalesArea link from the index test page. When prompted, enter an index—0, 1, or 2. You should see a one row table showing sales information for a particular month. For example, the results for index 1 (February) are shown in Figure 10.11.
Figure 10.11
Testing the transform in the service provider.
Adding a Page for the Sales Area Chart You now need to configure the salesChart model to display the results of the transform. To do this, first open the salesChart model and add a Comment builder call to it. Name the Comment Sales Area Chart and save the builder call. This comment provides a clear divide in your model between the builder calls for each chart. Add a Page builder call to the model and name it salesAreaPage. This builder call defines the HTML that contains the drill-down chart. Type the following HTML into the Page Contents (HTML) input:
Note the two span tags on the page: The salesAreaChart tag displays the chart, and the backButton tag displays a Back button to enable users to return to the first chart. Save the builder call when you are finished.
Adding an Action to Display the salesAreaPage Next, add an Action List builder call to the model and name it displaySalesAreaPage. This action list defines the actions taken when a user clicks on a bar in the first chart. Add an argument called index of type String to the Arguments section, so that the action list can receive the index of the selected area of the salesChart chart, and then add three actions to the Actions input, as shown in Figure 10.12. The first action sets the index argument of the getSalesArea operation using the index passed in the Action List, and the second action then runs the operation. The final action displays the salesAreaPage, which shows the transformed XML in a pie chart.
Figure 10.12
Adding actions to the displaySalesAreaPage action list.
Save the builder call when you are finished.
Configuring the Click Action Open the Web Charts builder for the first chart, and then enable the Enable Click Action option in the Chart Click Action section. Several other inputs now display, enabling you to configure an action to be taken when a bar on the chart is clicked. Type the name of the Action List you just created into the Method to Call input (displaySalesAreaPage), and specify a single argument in the Inputs input. Call the argument index, and set the value as ${ChartValues/colIndex}, which you can type directly or select from under the ChartValues category in the Choose Reference dialog. This calls the displaySalesAreaPage action list, passing in the column index for the currently selected row (0 for January, 1 for February, and 2 for March). Save the builder call when you are finished. When you click on a bar in the bar chart, the salesAreaPage is loaded; however, the page is currently empty. You now need to add some builders to populate the salesAreaPage page.
Populating the salesAreaPage Page Add another Web Charts builder call to the model, and name it salesAreaChart. Set the Page Location section of the builder, as shown below in Figure 10.13.
286
Figure 10.13
Chapter 10
Using Charts in Portlets
Configuring the page location for the drill-down chart.
Select Pie for the Chart Type input in the Chart Style and Data section, and then select the data set for the Chart Data input, as shown in Figure 10.14. This creates a pie chart based on the Sales element of the results from the getSalesArea operation.
Figure 10.14
Selecting the data set for the Chart Data input.
Fill out the Data Transformation section of the builder call, as shown in Figure 10.15. Notice that no X Axis Tag has been defined; this causes each pie segment to simply display the relevant column values (which, in this case, are the sales figures for each sales area). Save the builder call when you are finished. Next, add a Button builder call to the model to create a Back button at the backButton tag on the salesAreaPage page. Name the builder call backButton, and then fill out the Page Location section of the builder, as shown in Figure 10.16. Specify Back for the Label input, and then type page1 in the Action input (this reloads the first page). Be sure to also change the Action Type input from ‘Submit form and invoke action’ to ‘Link to an action’, as the salesAreaPage has no form. Save the builder call when you are finished.
Adding Drill-Down Capabilities
Figure 10.15
Configuring the data transformation for the drill-down chart.
Figure 10.16
Configuring the page location for the Back button.
287
Testing the Drill-Down from the salesChart Model You have now added drill-down capabilities to your sales chart portlet. If you test the model from your IDE, you should be able to click on a chart bar and see sales area information for any given month. For example, if you click the total sales bar for January, you should see the drill-down chart shown in Figure 10.17. Hovering your cursor over a particular pie chart segment should display the total sales for that sales area in the given month. Note that you don’t need to rebuild the application to see the changes on the portal server; if you have enabled automatic synchronization for the portlet WAR (in the WebSphere Portlet Factory Project Properties section of the Project Properties dialog), you should automatically see your changes on the portal when you save your model (although if you are logged in to the portal when the synchronization occurs, you need to log out and log back in before you can see the changes).
288
Chapter 10 Using Charts in Portlets
Figure 10.17
Testing the sales chart drill-down functionality.
Custom Chart Styles When you specify a value for the Chart Type input in the Web Charts builder, you link your chart to an XML file in your project that contains various parameters that describe the style and type of the chart. For example, the bar and pie charts used in this chapter are defined by the Bar.xml and Pie.xml files, respectively, which can be found in the WEB-INF/factory/samples/WebCharts directory in your project. By default, the chart parameters in the file corresponding to your chart type are used by the Web Charts builder whenever the setting ‘Use chart style’ is selected for an input, but you can also override the default chart settings with different values. Alternatively, you can also create your own custom XML chart styles, which is the subject of this section.
TIP The WEB-INF/factory/samples/WebCharts directory also contains files with a .wcp extension, which are WebCharts Project files. These files can be opened in the WebCharts Designer tool, which is a graphical interface for modifying and creating the XML style files used by the Web Charts builder.You can also modify and create your own XML style files manually, although this requires that you are familiar with the syntax used by the WebCharts style files.
In this section, you create another series for the bar chart, to display the TargetSales for each month. You then add a custom style file to your project. The custom style changes the bar color to a pastel blue and specifies that the target sales should be drawn as a red horizontal line
Custom Chart Styles
289
across the chart. Note that you do not need the WebCharts Designer tool to complete this section, but it is recommended that you download the tool to avoid having to type chart styles manually in the future.
Displaying TargetSales To add the new series for TargetSales and create the new chart style, first open the salesChart Web Charts builder call and navigate to the Data Transformation section. Add the TargetSales column to the Column List input and save the builder call. This displays values for TargetSales and the TotalSales already displayed on the chart. Because you specified Bar as the chart style, the TargetSales is displayed as additional bars on the chart. To display the TargetSales as a line instead of a series of bars (while still displaying the TotalSales in bar form), you need to create a custom style. To do this, create a new XML file by selecting New, Other from the File menu, and then select File from under the General category. Select the WEB-INF/factory/samples/WebCharts directory, and then type the name of the file as BarAndLine.xml and press Finish. The new XML file opens in the Editor window. Type the following XML into the BarAndLine.xml file, and then save the file: <elements outline=”black” drawShadow=”true” showMarkers=”false”> <series index=”0” shape=”Bar”> <paint color=”#00688B”/> <series index=”1” shape=”Line”> <paint color=”red”/>
This file achieves a number of things. First, it turns off the 3D effect of the chart (is3D= “false”), which makes the chart easier to read with both lines and bars on the chart. Then, it defines charts for two data series: a blue bar chart and a red line chart. Note that the blue for the bar chart is specified in hexadecimal; you can get hexadecimal codes from the WebCharts 3D Designer. Each series has a number of effects applied to it: namely, a black outline, a shadow around each bar or line, and no markers to delineate each individual value. Also, the labels for each x axis element are now printed horizontally instead of on a slant (because there is no element as there is in the bar.xml file).
290
Chapter 10 Using Charts in Portlets
TIP It is recommended you use the Web Charts 3D Designer to create your chart styles, as this enables you to configure your styles visually and then lets the Designer automatically create the XML style files for you. However, there are a few commonly used style settings that can be easily and quickly inserted into the XML style files, which are listed in the following: This line can be used to modify the font and font size used on chart labels (size 14, Arial font is used in the previous example). <paint paint=”Plain” /> This line can be used to turn off the color gradient on chart segments. This line can be used to set a minimum and maximum for the y axis (0 and 100 are used in the previous example). The following attributes can be added to the elements element to modify chart attributes: shapeSize=”80” This attribute can be used to modify the width of bars on bar charts (80 is the default). lineWidth=”3” This attribute can be used to modify the width of the line on line charts (3 is the default). place=”Stacked” This attribute can be used to place two bars in a bar chart on top of each other, rather than side by side. shape=”Column” This attribute turns a bar chart on its side, displaying horizontal bars instead of vertical bars (note that if you use this setting, you need to define a separate ‘elements’ element to contain the line portion of your chart, as you cannot apply the shape=“Column” attribute to a line chart).
Using the Custom Style The next step is to link the salesChart chart to the new custom style. To do this, open the salesChart Web Charts builder call, and then enable the Provide Custom Style checkbox in the Chart Style and Data section. In the Style Data input that displays, type /WEB-INF/factory/ samples/WebCharts/BarAndLine.xml. Also, change the Show Chart Legend input in the Chart Properties section to Use Chart Style, which causes the chart legend settings to be taken from the chart style (you haven’t defined a chart legend in the chart style, so a legend is displayed by default). Save the builder call when you are finished. You have now added a custom style to the salesChart chart in the salesChart portlet. Test the model from your IDE now, and you should see the output shown in Figure 10.18. Note that
Custom Chart Styles
291
you do not need to rebuild the application to see the changes on the portal server (provided that you have enabled automatic synchronization for the portlet WAR).
Figure 10.18
Testing the sales chart drill-down functionality.
Upgrading to a Deployment License After you have obtained a deployment license for the Web Charts builder from GreenPoint, you need to configure your WPF projects to use the new license key. You can upgrade your WPF project template so that all future projects use the deployment license; however, if you have any existing WPF projects, they need to be upgraded separately. To upgrade a particular WPF project, switch to the Package Explorer view in the WPF designer. Directly under the project folder, you will see links to the JAR files contained in the WebContent/WEB-INF/lib directory of your project. Make a copy of the wc50.jar file and paste it to a temporary location (such as the Windows desktop).
TIP By default, the wc50.jar file does not display under the WebContent/WEB-INF/lib directory in the Project Explorer view, as the Project Explorer does not display JAR files that are on the build path. Use the Package Explorer when browsing archives on the build path. Note that you can view the contents of archives from the Package Explorer, but you need to open or copy the archives elsewhere to be able to edit them.
292
Chapter 10 Using Charts in Portlets
Open the wc50.jar file using a decompression utility and extract the webcharts3d.xml file to a temporary directory, and then open the webcharts3d.xml file and search for the text license. This should reveal the following attribute: license=”null”
Type the new license key in place of the text null, and then save the webcharts3d.xml file. Using a compression utility, add the modified webcharts3d.xml file back to the root folder of the wc50.jar archive, and then copy the modified archive back into your project. If you do not intend to upgrade any more projects, you should restart the application server or portal server that your WPF project(s) are deployed to. To upgrade the project template so that future WPF projects use the deployment license, copy a modified version of the wc50.jar file (that is, one with a valid license key) into the <WPFinstallDirectory>/FeatureSets/WEB-APP_/Templates/Project/wpf.war/ WEB-INF/lib directory. When you run your WPF chart portlets, they will no longer display the evaluation version message.
Summary In this chapter, you learned how to add charts to your portlets and how to customize the appearance of these charts using a combination of prepackaged and custom chart styles. You also learned how to add drill-down functionality to your charts to provide detailed information on particular chart segments, and you created a simple WPF portlet application to demonstrate these techniques. Chapter 11, “Field Validation, Formatting, and Translation,” discusses how to use a combination of out-of-the-box builders and Java methods to customize the way form fields are processed in WPF.
Important Points • Charting capabilities can be added to a portlet using the Web Charts builder. The Web Charts builder that comes with WPF is an evaluation version only; you need to obtain a deployment license from GreenPoint (www.gpoint.com) to use the full version. • The Web Charts builder offers drill-down functionality that enables users to drill down on sections of your chart to get more information. • You can create custom styles for your charts using a program called Web Charts 3D. This program creates XML style definition files that you can then import into your WPF projects and reference from the Web Charts builder. • The WebSphere Dashboard Framework is an extension to WPF, which offers (among other things) a number of builders that have new charting functionality and that aggregate the functionalities of other lower-level builders. • When upgrading the Web Charts builder to a deployment license, you need to copy the license key into the webcharts3d.xml file and save it into the root folder of the wc50.jar file in the WebContent/WEB-INF/lib directory.
C
H A P T E R
1 1
Field Validation, Formatting, and Translation
This chapter walks you through the process of adding field-level validation, formatting, and translation to portlets. Several different techniques are discussed, including the use of out-of-thebox builders, Java methods, regular expressions, resource bundles, and data definitions. By the end of this chapter, you will have a good understanding of each approach and be able to implement each one in the appropriate scenarios. The example application discussed in this chapter utilizes one model, a properties file and a Java source file, which are available for download from ibmpressbooks.com/title/ 9780137134465, under the Chapter 11 folder (instructions for copying these files into your project are included in a readme.txt file in the same folder). However, to increase your understanding of the topics discussed in this chapter, it is recommended that you create the files yourself by following the example. The following topics are covered in this chapter: • Validating, translating, and formatting fields • Creating a projects portlet • Writing a Formatter class • Further validation customizations
Validating, Translating, and Formatting Fields Most portlets that provide information in fields benefit from some validation, translation, and formatting before they are saved or displayed to users. Validation, for example, can help ensure data integrity in backend data sources, whereas formatting can present potentially confusing data in a way that end users can more easily understand. Translation, on the other hand, can be used as a kind of formatting in reverse—that is, it converts screen inputs into a format more readily used by
293
294
Chapter 11 Field Validation, Formatting, and Translation
applications or data sources. For example, you might use formatting to display a reference code as a particular product name, so that it can be understood by people using the system; you might then translate this sentence back to a code after it is submitted as part of a form, so that it can be economically stored in a database. WPF provides several different techniques for validating, formatting, and translating fields—the subjects of this chapter. Some of these techniques are briefly introduced in the following sections, and the example demonstrates their uses in the proper context.
Schema Typed and Non-Schema Typed Fields If you work with fields defined by a schema in WPF, you should look to the Rich Data Definition builder as your first point of reference. The Rich Data Definition enables you to define common validation, translation, and formatting settings for schema typed variables, and specifies more advanced settings through the use of various expressions. For example, specifying Optional Date(yyyy-MM-dd) for a field’s validate expression ensures that if that field has a value, it must be formatted as a four-digit year, two-digit month, and two-digit day (in that order). There are a number of preset expressions available, or you can create your own; you can also use regular expressions when you need more control over the specific patterns used in a given field. After you have entered your field validation, formatting, and translation settings using the Rich Data Definition builder, you can save your configuration in XML files called data definitions so that they can be easily reused. Rich Data Definitions also have an input to link to a properties file called a resource bundle, which modifies the messages displayed to a particular user or group after validation occurs. The example in this chapter utilizes a data definition and a resource bundle to customize settings for fields on a data entry form. If your fields are not schema typed, you should look at applying validation, translation, and formatting through the Data Field Modifier builder, which enables you to modify one or more fields on a form through the same sort of expressions used in the Rich Data Definition builder. The builder you use generally depends on whether your fields are defined by a schema; however, it is possible to use both builders for the same data set. A Data Field Modifier can be used to override settings in your Rich Data Definition, which is useful if you want to apply expressions to many fields at once (the Rich Data Definition requires that expressions are defined on a per-field basis).
TIP When using schema typed fields, you can also use the schema itself for limited validation. As a best practice, you should keep schema validation to a minimum and define as much as possible of your validation using Rich Data Definition or Data Field Modifier builders.
In addition to the Data Field Modifier and Rich Data Definition builders, a data display builder such as the Data Page builder or View & Form builder is usually employed to display your
Validating, Translating, and Formatting Fields
295
data fields to end users. The Data Page also provides a number of features for validating, translating, and formatting fields that aren’t available from the other builders, such as specifying the location of validation messages on a page.
Formatter Classes Both the Data Field Modifier and Rich Data Definition builder use something called a formatter class to provide functionality for the different formatting, validating, and translating of expressions, and a class called StandardFormatter is used to support the default expressions available. It is also possible to create your own formatter class if you want to further customize what you can do with the Data Field Modifier and Rich Data Definition builders, however the StandardFormatter should be preferred wherever possible because you can use it without writing any Java code. However, creating your own formatter class can be useful if you have requirements that either cannot be covered or are too complicated to cover with the default expressions in the StandardFormatter. The example in this chapter shows you how to create a custom formatter class to customize a phone number field on a project form.
Client-Side and Server-Side Validation When validating fields, you have another decision to make in addition to choosing which builder you will use for validation. That is, you have to decide whether you want to use client-side or server-side validation. Client-side validation reduces the load on the server and can speed up your applications; however, there is only so much that can be validated without having to check information via the server (in a backend data source, for example). The Rich Data Definition and Data Field Modifier builders are normally used for server-side validation, and JavaScript is used for client-side validation. You can use the Dynamic Validation builder to dynamically display validation messages for server-side validation.
TIP Although it is possible to add JavaScript validation to your applications (by inserting it directly into HTML pages or using the Client JavaScript builder), it is not advisable that you do this unless the other methods available do not fit your specific validation requirements. JavaScript validation routines are usually more difficult to maintain than the other validation methods in WPF and can be problematic in applications that are used with different Web browsers.
The remainder of this chapter focuses on demonstrating these techniques in a sample application. The sample application provides a portlet containing a form for storing information about projects.
296
Chapter 11 Field Validation, Formatting, and Translation
Creating a Projects Portlet In this section, you build a portlet to provide functionality for entering details for new projects (such as start dates and budgets). Project data entered by the user is stored in a schema typed variable inside the portlet. A screenshot of the interface is shown later in Figure 11.7. Before you proceed with this section, you need a WPF project to house the portlet. If you have a project from a previous chapter, you can use it; otherwise, you should create a new WPF project (for more information on creating projects, see Chapter 1, “Introduction to WebSphere Portlet Factory”). The project will be published as a WAR file and deployed to a portal server, and you should also deploy the application to a local application server for testing (you can use the portal server if it runs on your local machine; otherwise, it is recommended that you use the IBM WebSphere Application Server Community Edition server [WAS CE] that comes with WPF). After you have a WPF project set up, you can add the model for the project’s portlet. The following builders are added in this section: • Portlet Adapter • Action List • Comment (x3) • Page (x2) • Schema • Variable • Data Page (x2) • Button • Link • Text • Rich Data Definition • Dynamic Validation Note that the order of some of these builder calls is important: the Rich Data Definition should follow the Schema, the Data Page should follow the Page, and the Dynamic Validation builder call should follow the Data Page. If any of these elements are not in order, you will receive a message in the Problems view indicating the unavailability of resources.
Creating a Model Create a new model called projects in your project, under the folder WEB-INF/models/chapter11. The model should be based on the Main and Page template, using a Page Type of Simple Page. For more information on creating models, see the example in Chapter 1. After it is created, your model should have two builders in it: an Action List called main and a Page called page1. The Action List runs automatically whenever the model is run and opens the page1 Page.
Creating a Projects Portlet
297
Modifying the Page Open the Page builder, which is used to define the layout of the project form itself. Replace the contents of the Page Contents (HTML) input with the following HTML, and then save the builder call:
Notice that the Page contains a form with four main sections. The first section is the heading for the form, and the second section (fullError) defines a location for validation messages to appear (this will be written to through the Data Page builder later in the chapter). The third section is for the data entry form itself (also placed by the Data Page builder), and the final section (submitButton) is for a button control to submit the form.
Adding a Portlet Adapter The first builder call to add is a Portlet Adapter, which surfaces your model as a portlet when the application is deployed to a portal server. Select Portlet Adapter from the Builder Palette (you can open the Builder Palette by clicking the icon in Outline view) and pressing OK. Name the Portlet Adapter projects, and then change the Portlet Title to Project Form and the Portlet Description to This portlet displays a form for filling out project details. This surfaces the model as a portlet called Project Form. Save the builder call when you are finished.
Organizing Builders for page1 Add a Comment builder call to the model and name it page1. This comment makes it easier to distinguish the parts of your model that pertain to the first page in the model. Note that two pages are used in this model: The first is the project form, and the second is a results page displayed when the project form is submitted.
298
Chapter 11 Field Validation, Formatting, and Translation
Save the builder call so that it displays in Outline view, and then drag it so that it displays above the page1 builder call. Also, drag the Portlet Adapter builder to the top of the model, and save the model when you are finished. The next few builder calls contain functionality for the first page in the model.
Adding a Schema The next step is to add a schema to define the structure of the project form data. Specifying a schema for your data enables you to define validation, translation, and formatting using a Rich Data Definition builder (which you add to the model later). Add a Schema builder call, and then name it dataEntrySchema. Select ‘Specify Explicitly as Builder Input’ for the Schema Source Type input, and then enter the XML schema shown in the following code into the Schema Source input. Save the builder call when you are finished. <xsd:schema xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:tns=”http://wpf.ibm.com/2002/10/dataEntrySchema” targetNamespace=”http://wpf.ibm.com/2002/10/dataEntrySchema”> <xsd:element name=”Items”> <xsd:complexType> <xsd:sequence> <xsd:element ref=”tns:ProjectCode” minOccurs=”1” maxOccurs=”1” /> <xsd:element ref=”tns:ProjectManager” minOccurs=”1” maxOccurs=”1” /> <xsd:element ref=”tns:ProjectManagerContactNo” minOccurs=”1” maxOccurs=”1” /> <xsd:element ref=”tns:ProjectStartDate” minOccurs=”1” maxOccurs=”1” /> <xsd:element ref=”tns:ProjectEndDate” minOccurs=”1” maxOccurs=”1” /> <xsd:element ref=”tns:ProjectBudget” minOccurs=”1” maxOccurs=”1” /> <xsd:element name=”ProjectCode” type=”xsd:string” /> <xsd:element name=”ProjectManager” type=”xsd:string” /> <xsd:element name=”ProjectManagerContactNo” type=”xsd:string” /> <xsd:element name=”ProjectStartDate” type=”xsd:string” /> <xsd:element name=”ProjectEndDate” type=”xsd:string” /> <xsd:element name=”ProjectBudget” type=”xsd:string” />
Creating a Projects Portlet
299
Adding a Variable to Hold User Input Next, add a Variable builder call to the model. This builder call creates a variable to hold values entered by users and is based on the schema created in the previous step. Change the name of the variable to dataEntry, and then select dataEntrySchema/Items for the Type input. The Namespace input should be automatically populated with the namespace specified in the schema. Save the builder call.
Modifying the Main Action List Now that you have created both the page and variable that you need to access, you should modify the actions in the main Action List so that it performs two actions: First, it clears the contents of the variable used to store the values for each field, and then it opens the first page in the model. To do this, open the main Action List, and notice that there is already an action to open page1, the first page in the model. Press the ellipsis button next to the second action in the Action List to create a new action. In the dialog that follows, select Assignment from under the Special heading and press OK. In the Target field, select Variables/dataEntry; this is the name of the variable used to hold the current project data. Leave the Source field blank and press OK. When control returns to the Action List, click and drag the page1 action so that it occurs after the action you just created, and then save the builder call.
Creating a Form The next step is to create a form for the project fields so that users can edit them. To do this, add a Data Page builder call to the model and name it dataEntry. Change the Variable input to point to the variable you created earlier (Variables/dataEntry), and then specify page1 as the Page in Model input. Select Data Entry as the Page Type, and then specify dataEntry in the Location for New Tags input. This creates a form of editable fields at the dataEntry tag on page1, using the dataEntry variable. Save the builder call when you are finished.
Adding a Submit Button Add a Button builder call to the model and name it submitButton. This button is used to submit user values for the project form. Specify page1 and submitButton as the Page and Tag, respectively, in the Page Location section, and then change the Label input to Submit. Make sure the Action Type input is set to ‘Submit Form and Invoke Action.’ This places a button at the submitButton tag on page1 that submits the form with the user’s values. The Action input should open the confirmation page, but because the page hasn’t been created yet, just leave this input blank for now and save the builder call. You have finished adding all of the builder calls for page1. The next few steps walk you through the process of creating a second page to confirm to the user that the entries in the project form were submitted.
300
Chapter 11 Field Validation, Formatting, and Translation
Adding a Confirmation Page Add a Comment builder call to the model and change the name to page2. Save the builder call. This builder call separates the builder calls in your model so that it is easier to see which builder calls are related to page1 and which are related to page2. Now add another Page builder call to the model. Change the name of the builder call to page2, and then enter the following HTML into the Page Contents (HTML) input:
Notice that this page has a tag called results on it—this is where another Data Page will show the user’s inputs in the display format (as specified by any formatting formulas). The translatedResults tag displays the results in the translated form (that is, as specified by any translating formulas). A Back button will be placed at the back tag so that users can enter details for more than one project. Save the builder call.
Modifying the Submit Button Now that you have a page to use for your confirmation page, open the submitButton builder call again and change the Action input to page2. Save the builder call when you are finished. The Submit button now displays the confirmation page when clicked.
Creating a Projects Portlet
301
Displaying the User’s Inputs Add a Data Page builder call to the model to display the result of the user’s submitted inputs in the display format (that is, with any relevant formatting formulas applied). Change the name of the builder call to displayResults, and then select the Items element of the dataEntry variable for the Variable input (you can do this through the ellipsis button to the right of the Variable input). Specify page2 as the Page in Model, and then change the Page Type to View Only so that users are not able to change form values after they have been translated and submitted. Finally, specify results for the Location for New Tags input and save the builder call. Now add a Text builder call to the model, which you can use to display the translated versions of the user’s inputs to the screen. Name the builder call translatedDataEntry, and then specify the Page and Tag in the Page Location as page2 and translatedResults, respectively. For the Text input, open up the Choose Reference dialog and select the Java/ option, as shown in Figure 11.1.
Figure 11.1
Specifying text for the Text builder.
Press OK, and then specify the Java expression as follows: webAppAccess.getVariables().getXml(“dataEntry”).toString()
This gets the internal value of the dataEntry Variable (with any translating formulas applied) and displays it as a string. Press OK to accept the Text input value, and save the builder call when you are finished.
Adding a Link to Return to the Project Form The final builder call to add in this section is a Link builder call. This builder call provides a link that executes the main Action List when clicked; effectively, this resets all of the form values and displays the project form again to the user. Add the Link builder call and name the builder call
302
Chapter 11 Field Validation, Formatting, and Translation
backLink. Change the Page input to page2 and the Tag input to back. Type Return to the project form for the Link Text input, and then change the Action Type input to ‘Link to an
action’ (there is no need to submit a form because page2 doesn’t contain a form to submit). Change the Action to main and save the builder call.
Running a Preliminary Test At this point, you should test the model to see how it runs without any custom validation, formatting, or translation. To do this, first run the chart model from the WPF Designer by clicking the icon on the toolbar. This runs the currently active model (for instance, projects) with the last run configuration you used. If you have not set up a run configuration before, you are prompted to do so—create a new configuration under the WebSphere Portlet Factory Model category (if you want more information on setting up a run configuration, see the “Testing the Application” section in Chapter 1). After you have run the projects model, you should see a form with a series of data entry fields, as shown in Figure 11.2.
Figure 11.2
Testing the projects model.
Enter some test values and press the Submit button. Notice that there are no restrictions on what you can type into each field and that when you submit the form, none of the submitted values are translated—that is, they are submitted in exactly the same format as you typed them. After you’ve submitted the form, you should see a results page similar to that shown in Figure 11.3. Notice that all of the fields on the project form have a red asterisk next to them, which is the default character in WPF for denoting mandatory fields. These validation constraints are placed on each field by your XML schema, but they won’t actually prevent the confirmation page from displaying (however, if you leave a field blank, submit the form, and then press the Back button to return to the project form, you will see a validation message display for that field). In the next section, you add some validation, formatting, and translation formulas to the fields on this page, and
Creating a Projects Portlet
303
you also see how to prevent users from submitting the form when a field does not pass its validation formula.
Figure 11.3
Testing the results page.
Adding Formatting, Validation, and Translation The next step is to define some validation, translation, and formatting expressions for the fields on the project form. Before you continue, add another Comment builder call to the model and name it VTF. This comment makes it easier to distinguish the VTF-specific (validation, translation, and formatting) builders used in this section. Save the builder call when you are finished.
Setting Data Page Validation Before you add any VTF-specific builders, first open the dataEntry builder call, and then scroll down to the Required Field and Input Validation Settings section. This section contains inputs that specify how validation is performed on the form. By default, the Validate From Schema input specifies that validation information for required fields and data types should be taken from the schema (although this is overridden with any validation specified by the Rich Data Definition). This section also lets you specify what happens after the values in the Data Page are submitted. It is possible to run an action after the validation has finished, and actions can also be run in the event of a successful or failed validation process. At this point, you will not use a post-save action; however, you do need a success action to display a results page after the project form has been submitted. Change the Success Action input to page2 to open the page2 Page whenever the validation succeeds.
304
Chapter 11 Field Validation, Formatting, and Translation
By default, validation messages display next to the field that they pertain to, but it is also possible to add a block of text to the HTML that lists all of the errors together. To make this block display, enter fullError for the Full Error Location input. This inserts the error block at the fullError tag on page1. Save the builder call when you are finished.
TIP You can prevent individual validation messages from displaying next to each field by removing the following tag from your HTML template: <span name=”FieldValidationError” class=”ValidationErrorText” />
Modifying the Submit Button Now open the submitButton builder call, and for the Action input, select page1_NextAction instead of page2. This method is automatically created by the Data Page builder when you specify post-validation actions, and selecting this method for the button simply kicks off the method when the button is pushed (and after the form has been submitted). The method itself simply displays page1 if any errors are picked up when the form is submitted; otherwise, it displays page2. Save the builder call when you are finished configuring the Button builder call.
TIP You can view the contents of the page1_NextAction method by navigating to the WebApp/Method section of the WebApp Tree tab in the Model Editor and scrolling down to the entry for page1_NextAction. There is also an entry in this section for page1_SaveData, which performs the validation. Later in this chapter, you see how you can add post save logic to this method to gain a greater degree of control over the validation process.
Adding a Rich Data Definition Now add a Rich Data Definition builder call to the model, and point the Schema input to the dataEntrySchema schema. You should see a list of fields display in the Fields area of the Data Definition Editor section, as shown in Figure 11.4. Notice the value of the Base Data Definition File input. This input points to an XML file in your project that holds settings and expressions used by the Rich Data Definition.
Creating a Projects Portlet
Figure 11.4
305
Fields in the Rich Data Definition.
TIP
After you have finished modifying the Rich Data Definition, you can save your changes to a new data definition file by pressing the Create Data Definition button in the Save Sample Data Definition File section.You can then use these data definitions to populate settings in other Rich Data Definition builder calls by modifying the value of the Base Data Definition File input in the Rich Data Definition builder to point to the new data definition.You can preview a data definition file in the Advanced section of the Rich Data Definition builder, and you can also manually edit data definition files with an XML editor.
Select the ProjectCode field, and notice that WPF populates some of the settings on the right of the Fields box with validation, translation, and formatting settings. In the next few steps, you modify some of these settings for each of the fields on the project form. Modifying Field Settings
First, select each field and modify the Label input so that words in field labels are separated by spaces (so, for example, ‘ProjectCode’ should be changed to Project Code). This makes the labels that display next to each field a little easier to read. By default, each field has the Required checkbox enabled and a Data Type of string. The Required checkbox causes fields to be validated when a user attempts to save the project form, and if any of the fields are blank, a validation error is raised and an error message is displayed to the user. Note that the Data Type input doesn’t actually change how the data is stored (the data is still stored in XML), but it causes the Data Page to handle the field as a date field (it is equivalent to changing the type in the schema).
306
Chapter 11 Field Validation, Formatting, and Translation
Unselect the Required checkboxes for each field, with the exception of the ProjectCode field. This enables you to save a project without necessarily having to fill in every field on the form (you only have to enter a project code before you can save the form). The Base Data Definition input enables you to specify an entry in your data definition file, which defines the default values for a field’s inputs. For example, click the ProjectStartDate field, and then select base_Date as the Base Data Definition. If you don’t enter values for any of the ProjectStartDate inputs, the values will be taken from the base_Date values in the data definition (for a discussion of the base_Date entry itself, see the “Data Definition Entries” sidebar).
DATA DEFINITION ENTRIES Data definition files consist of a series of entries that define sets of default field values in the Rich Data Definition builder. For example, open the default data definition file (/WEBINF/factory/data_definitions/base_datadef.xml) and navigate to the entry for base_Date (just search for the text base_Date).You should see the following XML element: com.bowstreet.builders.webapp.CalendarPickerBuilder Image ... /factory/images/ calendar/calendar_picker.gif MM/dd/yyyy blue true en Format (yyyy-MM-dd$MM/dd/yyyy)Translate (yyyy-MM-dd$MM/dd/yyyy)Date(yyyy-MM-dd)
Creating a Projects Portlet
307
The entry for base_Date contains several subelements. The first element, DataEntryControl, defines a builder (in this case, a calendar picker) to place a UI data entry control on the page for the current field. The Inputs element then assigns values for the inputs in the builder specified in the DataEntryControl (the Format input is particularly important, as it assigns dates that come from the date picker in the format MM/dd/yyyy). Finally, the three elements at the end of the base_Date entry list default expressions for formatting, translating, and validating fields. A field assigned to the base_Date entry uses these expressions by default, unless they are overridden in the Rich Data Definition builder. For a complete list of the elements used in the data definition file, refer to the WPF Designer help.
Adding Date Expressions Now, select ‘date’ for the Data Type input, which saves the value for the ProjectStartDate field as a date rather than a string. Note the formatting, validating, and translating expression inputs for the ProjectStartDate field. They are currently blank, which causes the expressions to be taken from the base_Date entry in the data definition file. The default formatting expression for the base_Date entry is as follows: Format(yyyy-MM-dd$MM/dd/yyyy)
The value in brackets before the $ sign is the format in which you are expecting the date (that is, year, month, and day, in that order), and the part after the $ sign is the format in which you would like the date displayed (that is, month, day, and year, in that order). If the date format does not match the format that you expect, then no formatting is applied. The default expressions should be fine in most cases, but in the current example, you work with the requirement that dates should be displayed in MM/dd/yyyy format and stored in dd/MM/yyyy format. To do this, change the value of the Format Expression field to this expression: Format(dd/MM/yyyy$MM/dd/yyyy)
This takes dates formatted as dd/MM/yyyy and formats them as MM/dd/yyyy. Now change the value of the Translate Expression field to this expression: Translate(dd/MM/yyyy$MM/dd/yyyy)
The value in brackets before the $ sign is the format you would like to translate the date to, and the part after the $ sign is the format you would like the date translated from. The translation listed converts dates in the MM/dd/yyyy format to the dd/MM/yyyy format. Because you have now changed the format in which the ProjectStartDate is stored (via a translate expression), you need to also change the validate expression so that it expects dates in the new format. To do this, change the Validate Expression input to this expression: Optional Date(dd/MM/yyyy)
308
Chapter 11 Field Validation, Formatting, and Translation
This validates the translated field based on the correct format, but only if a value for the field is entered (that is, it is not mandatory to enter a value for the field). Make the same changes to the ProjectEndDate field as you just made to the ProjectStartDate field (setting the Base Data Definition, Data Type, Validate Expression, and Translate Expression).
Adding a Drop-Down List for the ProjectManager Field Select the ProjectManager field, and in the Enumeration Values input type, Bob,Sam,Sally with a leading space before the first comma. This causes the ProjectManager field to be a dropdown box with four values: Bob, Sam, Sally, and a blank value that can be used when the project manager is not yet known.
Modifying the ProjectBudget Select the ProjectBudget field, and change the Base Data Definition input to base_Currency. This applies the default settings for the base_Currency entry; in particular, it defaults the data type of the field to decimal and sets the following default formatting expression: NumberFormat(#,###.00)
This causes numeric values to be separated by a comma every three digits, and have two zeroes appended after the decimal place if no decimal value is explicitly specified. Change the value of the Translate expression to this expression: NumberFormat(####.00)
This removes the commas when you save the ProjectBudget field. Also, the default decimal data type does not have any validation associated with it, so change the Data Type input to double. Users are not required to enter a value for ProjectBudget, but if they do, the value is validated as a double.
TIP If you have entered a value for a drop-down input in the Rich Data Definition and you want to reset that value to use the data definition, you need to set the value to [Default] rather than simply change it to a blank value (otherwise, WPF puts the removed value back into the input after the builder call is saved).
Adding a Regular Expression The last field modification you will do in this section is the ProjectCode. Say, for example, that you want to enforce a particular syntax on project codes (in this case, it is a three-letter code followed by a single digit). To do this, you should specify a regular expression for the Validate Expression so that users do not enter invalid codes.
Creating a Projects Portlet
309
Select the ProjectCode field, and enter the following expression into the Validate Expression input: RegularExpression([A-Z][A-Z][A-Z][0-9]$)
This ensures that users enter a valid project code before the project details are saved (for more information on regular expressions, see, for example, Regular Expression Pocket Reference by Tony Stubblebine). Save the builder call when you are finished.
Adding Dynamic Validation Currently, all of the validation you are using in the model is performed on the server by submitting the form and reloading the page. To add some dynamic validation to the form, add a Dynamic Validation builder call to the model. Change the name of the builder call to dynamicValidation, and then press the ellipsis button next to the first row in the Fields input. Select the ProjectBudget field from the [page1]dataEntry\Items heading and press OK. Finally, change the Trigger Event field to onchange. This causes the validation for the ProjectBudget field to be run whenever the field is changed. Save the builder call when you are finished. The next section walks you through the process of testing the various validation, formatting, and translating settings used on the project form.
Testing the Projects Portlet Run the projects model again from your IDE. The first area to test is the validation of mandatory fields. Press the Submit button without entering any values, and you should see a validation error printed next to the ProjectCode field as well as at the top of the form in the fullError section of the page, as shown in Figure 11.5.
Figure 11.5
Testing mandatory fields.
310
Chapter 11 Field Validation, Formatting, and Translation
TIP Some of the default validation messages used on the project form are not user friendly. It is possible to change these messages by using a resource bundle, or adding to or overwriting Portlet Factory’s validation method. These techniques are discussed in the “Further Validation Customizations” section in this chapter.
Enter a project code containing a three-letter code followed by a single digit (such as ABC1). Press the Submit button again and you should see the results screen shown in Figure 11.6.
Press the Return to project form link to return control to the project form.
Figure 11.6
Testing validation on the ProjectCode field.
The next test is for the validation on the two date fields. Enter a string of letters for each date input and try to submit the form (don’t enter a project code). You should receive validation error messages for each field. Try to press the Calendar icon next to each date field, and select a date from the calendar picker for each field. This populates the date fields with dates specified by the format MM/dd/yyyy, which is the format specified in the Format input of the base_Date definition (this format defines the format of dates returned from the date picker, and is separate to the formatting formula you applied to the field, which is evaluated only when the form is submitted). Submit the form, and you should have no validation errors for the date fields (later in this chapter, you see how you can add validation to these dates to ensure that the end date occurs after the start date). Now, type the date 15/03/2008 for both date fields. Submit the form again, and you should see
Testing the Projects Portlet
311
that the dates have been converted to the format MM/dd/yyyy, courtesy of the respective formatting formula for each field. Test the validation for the ProjectBudget field. Enter a string of letters and submit the form—you should see validation error messages displayed next to the ProjectBudget field as well as at the top of the form. Enter a numeric value with more than three digits and submit the form. The value for the field should be modified to include commas after every three digits and to append a decimal place to the number followed by two zeroes if you don’t enter a decimal value. The final test is to test the translation expressions for the date fields and the ProjectBudget field. To do this, enter valid dates for both date fields using the date pickers, and then enter a number with more than four digits in the ProjectBudget field. You need to also enter a valid project code so that you can successfully submit the form. When you submit the form, check the ‘Results (translated values)’ section of the confirmation page and you should see that the dates have been translated to the format dd/MM/yyyy (rather than the default translation of yyyy-MM-dd). You should also see that the commas have been removed from the ProjectBudget field. After you have tested the projects model, you should rebuild your application on the portal server (for instructions on how to do this, see the example in Chapter 1), after which you can view the projects model as a portlet on the portal server. After you have added the Project Form portlet to a page in the portal, it should display as shown in Figure 11.7.
Figure 11.7
The Project Form portlet.
The next section of this chapter describes how to write a custom formatter class to customize the formatting, validation, and translation used in the ProjectManagerContactNo field.
312
Chapter 11 Field Validation, Formatting, and Translation
Writing a Formatter Class The functionality for the various translating, validating, and formatting expressions in WPF are taken from a Java class called a formatter class. By default, this functionality comes from a formatter class called StandardFormatter; however, if you need to modify this functionality, you can write your own formatter class using the IInputFieldFormatter interface. This section describes how you can add a formatter class to your WPF project, which adds the following functionality to the ProjectManagerContactNo field: • The field is formatted so that spaces are added every three characters from the end of the number. • The field is translated so that spaces are not stored as part of the number. • The field is validated so that only numeric values are allowed. The following builders are added in this section: • Comment • Linked Java Object • Data Field Modifier
Adding a Formatter Class To facilitate this functionality, the first step is to add the formatter class itself, after which you can add builders to utilize the formatter’s functionality. If you have access to ibmpressbooks. com/title/9780137134465, download the com folder from the Chapter 11 link on the site, then copy it into your WebContent/WEB-INF/work/source directory of your project. This adds the CustomFormatter class to your project. If you are able to do this successfully, skip to the “The CustomFormatter Class” section. Otherwise, you need to add the class manually. To do this, open the New Java Class dialog in the WPF Designer by clicking File, New, Class. Specify the package as com.ibm (substituting your own company name for ibm), and then change the name of the class to CustomerFormatter (note that the package com.ibm is used throughout this chapter, but in each case, you should substitute your own company name for ibm). Press the Finish button, which creates a skeleton source file in the appropriate package in the WebContent/WEBINF/work/source directory. Change the contents of the CustomFormatter.java file so that they read as shown in the following, and then save the file when you are finished: package com.ibm; import java.util.ArrayList; import java.util.List; import com.bowstreet.methods.IInputFieldFormatter; import com.bowstreet.webapp.WebAppAccess;
Writing a Formatter Class
313
public class CustomFormatter implements IInputFieldFormatter { private String errorMessage; private WebAppAccess webApp;
//These Lists define the available expressions protected static List formatExpressionList = null; protected static List translateExpressionList = null; protected static List validateExpressionList = null; //These Strings define the content of the Lists public final static String ADDSPACES = “Add spaces to phone number”; public final static String REMOVESPACES = “Remove spaces from phone number”; public final static String VALIDATEPHONENUMBER = “Validate phone number”; //Set the contents of each List private static final String[] formatExpressions = {ADDSPACES}; private static final String[] translateExpressions = {REMOVESPACES}; private static final String[] validateExpressions = {VALIDATEPHONENUMBER}; static { formatExpressionList = makeList(formatExpressions); translateExpressionList = makeList(translateExpressions); validateExpressionList = makeList(validateExpressions); } //Used for formatting public String format(String value, String expression) { String result = value; StringBuffer modifiedResult = new StringBuffer(); if(getErrorMessage() == null) { if (expression.equals(ADDSPACES)) { //Insert a space every three characters from the end for (int index=result.length(); index >= 0; index=index-3) { if (index-3 >= 0 ) modifiedResult.insert(0, “ “ + result.substring(index-3, index)); else
314
Chapter 11 Field Validation, Formatting, and Translation
modifiedResult.insert(0, result.substring(0, index)); } result = modifiedResult.toString(); } } return result; } //Used for translation public String translate(String value, String expression) { String result = value; if (expression.equals(REMOVESPACES)) { //Remove spaces result = result.replaceAll(“ “, “”); } return result; } //Used for validation public boolean validate(String value, String expression) { boolean result = true; //Reset error message setErrorMessage(null); if(expression.equals(VALIDATEPHONENUMBER)) { //Check to see if phone number contains letters for(int index = 0; index < value.length(); index++) { if (value.charAt(index)<48 || value.charAt(index)>57) { setErrorMessage(“Phone numbers cannot contain letters.”); result=false; } } } return result; }
Writing a Formatter Class
315
//These methods are required by the interface public void setErrorMessage(String msg)
{ errorMessage = msg; }
public String getErrorMessage()
{ return errorMessage; }
public void setWebAppAccess(WebAppAccess webApp)
{ this.webApp = webApp; }
public WebAppAccess getWebAppAccess()
{ return webApp; }
public void setTranslateSuccessFlag(boolean parm1)
{ }
public boolean getTranslateSuccessFlag()
{ return true; }
public List getFormatExpressionList() ¯formatExpressionList; }
{ return
public List getTranslateExpressionList() ¯translateExpressionList; }
{ return
public List getValidateExpressionList() ¯validateExpressionList; }
{ return
//This method creates a list from a String array protected static List makeList(String[] array) { List list = new ArrayList(array.length); for (int i = 0; i < array.length; i++) list.add(array[i]); return list; } }
The CustomFormatter Class After you have finished adding the CustomFormatter class to your project, there are several points of interest in the CustomFormatter class that you should be aware of. The three main methods in the class—format, translate, and validate—are used to provide functionality for the various expressions specified in the builders. Each method takes in a value and an expression; the value is the data to be modified, and the expression is a string denoting how the data should be modified. These expression strings are defined by the three List objects in the class (formatExpressionList, translateExpressionList, and validateExpressionList), and the contents of these List objects display in the expression dropdowns in the Data Field Modifier. For example, the formatExpression ADDSPACES declares that spaces be added to a phone number. If ADDSPACES is selected for the Format Expression input in the Data Field Modifier, then the format method will receive ADDSPACES for its expression argument whenever that field is formatted. The format method can then run the appropriate code to format the field value passed in.
Adding a Linked Java Object You now need to make some changes to the projects model to utilize the new formatting class. First, add a Comment builder call to the model and call it Custom Formatter. This comment
316
Chapter 11 Field Validation, Formatting, and Translation
separates builder calls in previous sections from the builder calls used for the custom formatter. Save the builder call. Add a Linked Java Object builder call to the model. This builder call provides a link to the CustomFormatter class from the projects model, so that it can be used as if it was part of the model. Change the name of the builder call to customFormatter, and then specify the Class Name as com.ibm.CustomFormatter (substituting your own company name for ibm). Save the builder call when you are finished. The CustomFormatter class is now available as a formatter for your form fields, which you can specify using a Data Field Modifier.
Adding a Data Field Modifier Add a Data Field Modifier builder call to the model and name it formatContactNo. In the Fields section, specify the ProjectManagerContactNo field for both page1 and page2 by pressing the ellipsis button at the end of the first row, and then selecting the appropriate field from under the [page1]dataEntry\Items and [page2]displayResults/Items headings. Scroll down to the Formatting section of the builder call, and select com.ibm.CustomFormatter for the Formatter Class input (again, substitute your own company name for ibm). The values for the various List objects in your CustomFormatter class (that is, formatExpressionList, translateExpressionList, and validateExpressionList) should now be available as expressions in the inputs in this section. Select ‘Add spaces to phone number’ for the Format Expression, ‘Remove spaces from phone number’ for the Translate Expression, and ‘Validate Expression’ for the Validate Expression inputs, and then save the builder call when you are finished. To test your custom formatter class, run your model and enter several letters for the ProjectManagerContactNo field. When you submit the form, you should receive a validation error. Change the phone number so that it is a numeric value, and after you submit the form, you should notice that spaces have been added every three characters from the right of the number. Finally, enter a valid project code so that the form submits without raising a validation error. On the results page, you should see that the translation method has removed the spaces from the number.
Further Validation Customizations In this section, two further customizations to the validation process are discussed. The first involves changing the validation messages displayed to the user via a properties file called a resource bundle; these files can then be easily transferred between projects. The second customization involves adding a method to the validation process, which gives you a better understanding of how WPF handles validation and translation, and also gives you more control over how the validation and translation in your model are performed.
Adding a Resource Bundle To add a resource bundle to your project, first open the dataEntry Data Page builder call in your projects model, and scroll down to the Label Translation Settings section. Press the Create Resource Bundle button, and then navigate to the WebContent/WEB-INF/work/source directory
Further Validation Customizations
317
of your project. Type the name of the properties file as projectForm.properties and press the Save button. Change the Resource Bundle Name input to projectForm, and then save the builder call. After the builder call is saved, the projectForm.properties file is created in your source directory; it is used to provide validation messages for the Data Page. Open the projectForm.properties file. It should display as shown in the following: dataEntry=dataEntry dataEntry=dataEntry Project_Code=Project Code Project_Manager=Project Manager Project_Manager_Contact_No=Project Manager Contact No Project_Start_Date=Project Start Date Project_End_Date=Project End Date Project_Budget=Project Budget ErrorMessage.Boolean=Not recognized as a Boolean value. ErrorMessage.DateTime=Not recognized as a DateTime value. ErrorMessage.FloatingPoint=Not recognized as a Floating Point ¯value. ErrorMessage.Integer=Not recognized as an Integer value. ErrorMessage.Required=This field is required.
The first section of the properties file lists field labels that are displayed for validation messages, and the second section lists the validation messages themselves. Note that WPF automatically inserts underscores into the field names, but this won’t affect any of the validation. Notice also that there is no entry to redefine messages for regular expressions; if you want to modify a regular expression message, you need to modify or add to the validation procedure itself (which you can do after the resource bundle has been configured and tested). Also, notice the two entries for dataEntry—these entries are for the page1 and page2 Data Pages themselves and are not used. Delete the two dataEntry lines from the properties file.
TIP If you have disabled automatic builds in the WPF Designer, you will need to manually rebuild your project before the properties file can be used (which you can do by pressing Ctrl+B).
Some of the error messages are not needed, and the ones that are needed should be modified so that they are user friendly. Modify the properties file so that it displays as shown below, and then save it when you are finished: Project_Code=Project Code Project_Manager=Project Manager
318
Chapter 11 Field Validation, Formatting, and Translation Project_Manager_Contact_No=Project Manager Contact No Project_Start_Date=Project Start Date Project_End_Date=Project End Date Project_Budget=Project Budget ErrorMessage.DateTime=Must be a valid date. ErrorMessage.FloatingPoint=Must be a valid dollar amount.
To test the resource bundle, run your projects model and enter invalid values for the ProjectStartDate, ProjectEndDate, and ProjectBudget fields. The validation messages displayed should now be taken from the resource bundle.
Changing Messages for Regular Expressions To change the message for a regular expression, you can either modify the validate method of a formatter class, create a post-save action, or modify Portlet Factory’s validation methods. The validation process is contained in a method called page1_SaveData, which WPF creates automatically whenever you have a Data Page builder call in your model containing data entry fields. Although you can change this method by overwriting it with a Method builder call, the process is error-prone, and it is recommended you create a post-save action instead. Post-save actions are particularly useful when you want to validate a field’s value in relation to another field’s value. In the example that follows, you can see how to change the validation message for the ProjectCode field and how to validate the date for the ProjectEndDate field to make sure it occurs after the ProjectStartDate. To complete this section, you need to add the following two builders to the projects model: • Comment • Method
Creating the Post-Save Action Add a Comment builder call to the model and call it Custom Save. This comment sections off the previous builder calls from the builder calls used for the custom formatter. Save the builder call. Select the WebApp Tree tab in the Model Editor, and then navigate to the WebApp/Method section. The page1_SaveData method is not listed because it is hidden by default, but you can display it by selecting Window, Preferences in the WPF Designer, and then enabling the ‘Show hidden objects’ checkbox from the WebSphere Portlet Factory Designer section. After you have done this, the page1_SaveData method should display in the WebApp Tree under the WebApp/ Method section. Select the page1_SaveData method, and then click the code listing on the right side of the screen. Copy the contents of the page1_ SaveData method (the curly braces and everything that is between them) to the Clipboard by highlighting it and pressing Ctrl+C. When you create a postsave method in the next step, you will use the contents of the Clipboard as a starting point.
Further Validation Customizations
319
Now add a Method builder call to the model. Name the builder call additionalValidation, and paste in the Java code from the Clipboard to the Method Body input. Notice the code steps through each field applying validation and translation formulas specified in your builders using the appropriate formatter classes. Take, for example, the following code fragment, which processes the ProjectBudget field: str = webAppAccess.getRequestInputs() ¯.getInputValue(“ProjectBudget”); formatter = (IInputFieldFormatter)variables ¯.getObject(“StandardFormatter”); str = formatter.translate(str, “NumberFormat(###0.0)”); if (!formatter.validate(str, “Optional Floating Point Number”)) errors.addMessage(“ProjectBudget”, formatter.getErrorMessage()); dataEntry.setText(“ProjectBudget”, str);
This code first gets the input value for the ProjectBudget field, and then it retrieves the appropriate formatter class—StandardFormatter in this case (all of your fields use StandardFormatter, except the ProjectManagerContactNo field). It then translates the field value using the translate expression specified in the Rich Data Definition builder call, and then validates it using the default validation expression for the double data type. If the validation fails, the appropriate error message for that validation method is taken from the formatter class. The final line inserts the translated value into the dataEntry variable.
WARNING Be careful if you are overwriting the page1_SaveData method, as any changes you make to your validation, formatting, or translating at the builder level can no longer be incorporated into the method. To incorporate such changes, you need to disable your page1_SaveData method, and then copy the new modifications from the page1_SaveData method created by WPF (which you can access from the WebApp Tree tab in the Model Editor). Because of the maintenance difficulties this approach can cause, a custom save method is generally not advisable unless it is not possible to perform your changes using a different technique.
Modifying the Post-Save Action Code Because this validation is already being run, you don’t actually need all of the code. Remove all of the code, except for the top section of the method and the part that validates the ProjectCode. When you are finished, your code should display as shown in the following: { String str; Variables variables = webAppAccess.getVariables();
320
Chapter 11 Field Validation, Formatting, and Translation
To change the validation message displayed for the ProjectCode field, locate the section of code that pertains to the ProjectCode field and modify the line containing the addMessage method so that it reads as follows: errors.addMessage(“ProjectCode”, “Must be a valid project code (three ¯uppercase letters followed by a single digit)”);
Now, just before the last curly bracket at the bottom of the code, add the Java code shown in the following code. This validates the ProjectEndDate to see if it occurs after the ProjectStartDate. //check to see if project start date is before project end date if ( ! webAppAccess.getRequestInputs().getInputValue(“ProjectEndDate”). ¯equals(“”) && ! ¯webAppAccess.getRequestInputs().getInputValue(“ProjectStartDate”). ¯equals(“”)) { try { java.text.SimpleDateFormat format = new ¯java.text.SimpleDateFormat(“dd/MM/yy”);
Summary
321
Date startDate = ¯format.parse(webAppAccess.getRequestInputs().getInputValue ¯(“ProjectStartDate”)); Date endDate = ¯format.parse(webAppAccess.getRequestInputs().getInputValue ¯(“ProjectEndDate”)); if ( endDate.before(startDate) ) errors.addMessage(“ProjectEndDate”, “Must be after the project ¯start date.”); } catch (java.text.ParseException pe) { errors.addMessage(“ProjectEndDate”, “Must be after the project ¯start date.”); } }
Save the builder call when you are finished.
Running the Post-Save Action To use the additionalValidation method as a post-save action, open the dataEntry Data Page and scroll down to the Required Field and Input Validation Settings section. Select additionalValidation for the Post-Save Method input and save the builder call. To test the new save method, run your model again from the IDE and check to see whether the message for ProjectCode has changed. Then, enter a ProjectEndDate that occurs before the ProjectStartDate, and see if you get a validation error when you submit the form. Finally, make sure that when you put in valid values for these fields, the form submits successfully!
Summary In this chapter, you learned how to add field level validation, formatting, and translating to your portlets. A number of different techniques were discussed, such as the use of the Rich Data Definition, Data Page, Data Field Modifier, and Dynamic Validation builders, as well as resource bundles, regular expressions, data definitions, and custom Formatter classes. You also built a portlet that provides the facility to enter details for new projects in a simple project application, which demonstrates the use of the techniques just mentioned. Chapter 12, “Profiling Portlets,” discusses how to use profiling to cater to the different contexts and states in which your WPF applications are used.
322
Chapter 11 Field Validation, Formatting, and Translation
Important Points • There are numerous ways in which formatting, validating, and translating can be added to fields in WPF applications. The best approach is to use a Rich Data Definition builder when dealing with schema typed fields, and use a Data Field Modifier for fields that aren’t defined by a schema. • The Data Page builder lets you present a variable as a set of fields on a form, but it also provides validation settings not available in other builders (such as setting post save methods and defining a tag name where validation messages will appear). • The Data Page builder automatically creates methods for your validation processes called page1_saveData and page1_nextAction, where page1 is the name of the HTML page where the Data Page resides. • The Dynamic Validation builder enables you to add client-side validation to your applications. • You can modify validation messages using a resource bundle. • A formatter class is a class that provides functionality for validation, formatting, and translation in WPF. A class called StandardFormatter is used by default, but you can write your own formatter class using the IInputFieldFormatter interface.
C
H A P T E R
1 2
Profiling Portlets
One of the three core architectural components of WebSphere Portlet Factory (WPF) applications (in addition to models and builders) is profiles. In this chapter, you use profiling to customize a company announcements portlet. By the end of the chapter, you will have a portlet that displays announcements tailored to the user’s language (in either English or Spanish) and department (IT or HR), and you will apply this knowledge to profile portlets in other WPF applications. The files used in this chapter are available for download from ibmpressbooks.com/title/ 9780137134465, under the Chapter 12 folder (instructions for copying these files into your project are included in a readme.txt file in the same folder); however, to increase your understanding of the topics discussed in this chapter, it is recommended that you create these files yourself by following the example in this chapter. The following topics are covered in this chapter: • Profiling builder inputs • Creating an announcements portlet • Creating a custom selection handler
Profiling Builder Inputs A profile in WPF is a set of values that can be assigned to builder inputs, enabling you to build multiple variations of a portlet from the same code base. These values are assigned to builder inputs based on certain contexts, roles, or settings. For example, the portlet you develop in this chapter will contain two XML documents: one will have company announcements in English and the other will have them in Spanish. The items displayed to the user will be determined by the user’s current locale, and profiles will be created for each country and language that the application supports: US English (US is the country code for the United States of America) and ES Spanish
323
324
Chapter 12 Profiling Portlets
(ES is the country code for Spain). Each profile will contain values used to print an appropriate heading for the announcements, and each will have values to enable or disable announcements based on the current department that the user belongs to (HR or IT). These profile values will be used to populate various builders in the model, so that whenever a request to the portlet is made, the appropriate values for the inputs will be taken from the correct profile (so English-speaking users can see the English announcement items for their department, and Spanish-speaking users can see the Spanish items for their department). See the “Profiles” section of Chapter 1, “Introduction to WebSphere Portlet Factory,” for a more in-depth discussion of how this process works in WPF. WPF decides how to map portlet requests to particular profiles using a mechanism known as a selection handler. WPF comes with a number of different selection handlers, which automatically provide the facility to map profiles in a host of different situations (for a description of these handlers, see the “Default Selection Handlers” sidebar). The functionality for the default selection handlers is provided by Java classes stored in the selectionhandlers.zip file under the WEB-INF/work directory, and each selection handler is configured using an XML configuration document (found under WEB-INF/config/selection_handlers). Although you don’t necessarily need to worry about these files when profiling your applications, you can use them as a basis for creating your own custom selection handlers (although the default selection handlers should be sufficient in the majority of cases).
DEFAULT SELECTION HANDLERS WPF provides a number of selection handlers out of the box, which can be used to map portlet requests to profiles. These selection handlers are listed here: Explicit handler—This handler enables you to explicitly specify a particular profile (a default profile is used if no profile is selected). File Segment handler—This handler maps requests to profiles through user-profile mapping defined in a simple XML file. J2EE Role handler—This handler maps requests to profiles using J2EE roles (for example, a particular profile might be mapped when users have the System Administrator role). LDAP Group-Based selection handler—This handler associates LDAP groups with particular profiles. You need to configure this handler’s XML configuration file (ldapselectionhandler.xml) to use it, specifying such things as the LDAP server and LDAP bind user. Locale selection handler—This handler maps requests by associating locales (for example, en or en-US) with profiles. Portal Execution Mode handler—This handler maps requests to profiles depending on the mode in which the application is executed. There are two possible modes: Standalone and Portal.
Creating an Announcements Portlet
325
User Attribute handler—This handler enables you to map requests by associating a user attribute (such as uid or cn) with a particular profile. You can change the attribute used by configuring the XML configuration file (userattributehandler.xml). WP Composite Application Role selection handler—This handler maps profiles to requests through WebSphere Portal Composite Application roles. WPS Group Segment handler—This handler maps requests to profiles based on their WebSphere group. This handler doesn’t require any configuration, so it is a simpler alternative to the LDAP Group selection handler if your WebSphere groups are the same as the groups defined in your LDAP directory. WPS Execution Mode handler—This is a deprecated version of the Portal Execution Mode handler, used only for backward compatibility. Use the Portal Execution Mode handler for new applications.
In this chapter, you use two of the default selection handlers to map requests to profiles based on user locales and the WebSphere Portal Server group that the current user belongs to: the Locale selection handler and the WPF Group Segment handler (this does not require you to modify the selection handlers in any way).
Creating an Announcements Portlet In this section, you build a portlet that displays company announcements in one of two possible country and language combinations (US English and ES Spanish), tailored to one of two company departments (HR and IT). A screenshot of the finished portlet is shown later in Figure 12.10. Before you proceed with this section, be sure you have a WPF project to contain the portlet. If you have a project from a previous chapter, you can use it; otherwise, you should create a new WPF project (for more information on creating projects, see Chapter 1). The project will be published as a WAR file and deployed to a portal server, and you should also deploy the application to a local application server for testing. After you have a project set up, you need to add a model to the project (which will be surfaced to the portal server as the company announcements portlet). This model uses the following builders to provide its functionality: • Action List • Page • Portlet Adapter • Localized Resource • Text (x2) • Import to XML • Data Page (x2)
326
Chapter 12 Profiling Portlets
Creating a Model Create a new model called companyAnnouncements in your project, under the folder WEBINF/models/chapter12. The model should be based on the Main and Page template, using a Page Type of Simple Page. For more information on creating models, see the example in Chapter 1. After it is created, your model should have two builders in it: an Action List called main and a Page called page1. The Action List runs automatically whenever the model is run, and it opens the page1 Page.
Modifying the Page Open the Page builder, and change the contents of the Page Contents (HTML) input to the HTML that follows:
Announcements
<span name=”hrHeader”>
<span name=”hrNews”>
<span name=”itHeader”>
<span name=”itNews”>
This page contains span tags for displaying two separate news sections. The first section is for the HR announcements and consists of hrHeader and hrNews. The heading for the HR announcements is placed at the hrHeader tag, and the HR announcements themselves are placed
Creating an Announcements Portlet
327
at hrNews. Similarly, the itHeader tag is replaced with the heading for the IT announcements, and the IT announcements are then displayed at the itNews tag. Save the builder call when you are finished.
Adding a Portlet Adapter You add a Portlet Adapter builder call to your model to surface your model as a portlet when the application is deployed to a portal server. To do this, select Portlet Adapter from the Builder Palette (you can open the Builder Palette by clicking the icon in Outline view) and press OK. Name the Portlet Adapter companyAnnouncements, and then change the Portlet Title to Company Announcements and the Portlet Description to This portlet displays company announcements. This surfaces the model as a portlet called Company Announcements. Save the builder call when you are finished.
Creating a Default Resource Bundle In this example, you localize the heading strings for the announcements so that they pertain to a specific country and language. There are a number of ways to do this, but perhaps the best approach is to create a resource bundle for each country and language combination (one for US English and one for ES Spanish) to hold the text for the announcement headings. After you have done this, you can then use profiling and the Localized Resource builder to access the heading text. Using resource bundles enables you to easily modify and transfer localized properties without needing to modify any code, and this is the approach adopted in this example. The first resource bundle you need to create is a default resource bundle that can be used if no other resource bundle can be matched against the current user’s country and language. To create the default resource bundle in the WebContent/WEB-INF/work/source/com/ibm folder of your project, create a plain text file called companyAnnouncements.properties, substituting ibm for the name of your company (note that the package com.ibm is used throughout this chapter, but in each case you should substitute your own company name for ibm). You can create a plain text file by selecting File, New, Other in your IDE to open up the Select a Wizard dialog, and then select File from under the General category. Open the companyAnnouncements.properties file and enter the following text: # localized resources for companyAnnouncements portlet announcements.hrHeader=English HR Announcements announcements.itHeader=English IT Announcements
This makes two string properties available (the HR and IT headings) in English, which will be the default language that your portlet supports. You access these properties from the Localized Resource builder later. Save the file when you are finished.
Creating a US English Resource Bundle Because the default resource bundle is already using US English, you don’t really need a separate resource bundle for US English users. However, it is a good idea to create one anyway, just in
328
Chapter 12 Profiling Portlets
case you decide to modify the default resource bundle later (and you want to leave the US English settings intact). To do this, in the WebContent/WEB-INF/work/source/com/ibm folder of your project create a plain text file called companyAnnouncements_en_US.properties (after again substituting ibm for the name of your own company). The first part of this filename, companyAnnouncements, is the name of the resource bundle. The next part (after the first underscore) is the language of the resource bundle, followed by the country code after the second underscore. Portlet Factory recognizes this naming convention, so that when you configure the Localized Resource builder later in the chapter, you can specify builder inputs for the name of the resource bundle (companyAnnouncements), the name of the country, and the name of the language. This then reads in the text from the appropriate resource bundle (the inputs are profiled so that they can be set based on the country and language settings for the current user). Open the companyAnnouncements_en_US.properties file and enter the same text as you did for the default resource bundle. Save the file when you are finished.
Creating an ES Spanish Resource Bundle Now create another blank text file in the same directory as the other resource bundles, and this time, call it companyAnnouncements_es_ES.properties. Enter the announcements as shown in the following: # localized resources for companyAnnouncements portlet announcements.hrHeader=Español Anuncios de Recursos Humanos announcements.itHeader=Español Anuncios de Informática
These are the ES Spanish equivalents to the US English announcements you just created. Save the file when you are done adding the announcements.
Localizing the Announcement Headings Now that you’ve created the resource bundles, you need to use a Localized Resource builder to access them from WPF. The Localized Resource builder can make some methods available in your model that will enable you to reference the text of specific properties in your resource bundles. Add a Localized Resource builder call to your model by selecting Localized Resource from the Builder Palette and then pressing OK (you can open the Builder Palette by clicking the icon in Outline view). Name the builder call localizedResources, and then select com.ibm.companyAnnouncements for the Resource Bundle Name input. This specifies the default resource bundle for the Localized Resource builder; the variants of this resource bundle (that is, the US English and ES Spanish resource bundles) can be accessible depending on how you set the remaining builder inputs. For example, if you type en for the Language input and US for the country input, this will cause your localized text to be read from the companyAnnouncements_en_US.properties file. Ideally, however, you need these inputs to be dynamic; that is, if the user’s country and language are ES and es, respectively, it would be useful if the values of the inputs could be retrieved based on these settings. To do this, you need to profile the Country and Language inputs.
Creating an Announcements Portlet
329
Profiling the Language Input Profile the Language input by pressing the icon to the left of the Language input. This opens the Profile Input dialog shown in Figure 12.1.
Figure 12.1
The Profile Input dialog.
The Profile Input dialog enables you to configure profiling for a particular builder input. There are three main points of configuration on this dialog: the Profiles, Profile Entry, and Profile Set, and their purposes will become clearer as you progress through this section. In the Profiles area, you define different scenarios that you want to cater for (in this case, US English and ES Spanish); the profile entry describes the value for the builder input (in this case, the language of the resource bundle that you want to display). The profile set is simply a container for the profiles and profile entries, which you can access later after your settings have been saved. For a summary of the profiling terms used in this chapter, please refer to the “Profiling Terminology” sidebar.
PROFILING TERMINOLOGY The following list summarizes and briefly describes the basic profiling terms used in this chapter: Profiled input—A profiled input is a builder input that takes its value from a value in a profile. Profile entry—Profile entries are points of variance in an application. Profile entry values are given to builder inputs when a builder input is profiled. Profile—Profiles are collections of values for profile entries to be used in a certain context or for a particular role. Profile set—Profile sets are containers of profile entries and profiles.
330
Chapter 12 Profiling Portlets
Segment—A segment is the context associated with a profile (for example, your segments might be J2EE roles if you use a J2EE role handler). Selection handler—Model requests are assigned to a profile using an algorithm specified by a selection handler. For example, this algorithm might use something like the user’s current language preferences or LDAP attributes to determine the profile to assign to the request.
The first thing you need is a profile set. Profile sets are stored as files with a .pset extension in the WEB-INF/profiles directory, and can be used to configure your profiles. After you press OK on the Profile Input dialog, the values you enter are saved in the profile set specified in the Profile Set Name field. By default, WPF creates a new profile set for you the first time you profile a builder input in the model, using the name of the model followed by the suffix ‘ps’ (companyAnnouncementsps). The name of the default profile set is not overly descriptive, so create a new profile set by clicking the Create Profile Set button. In the New Profile Set dialog that follows, enter localeValues for the Name, and Localization settings for the Description. Don’t base the profile set on an existing one (that is, leave the dropdown at the bottom of the dialog set to <none>). Press OK when you are finished, and you should see that the Profile Set Name field has been set to the name of the new profile. Next, you need a profile entry that defines the type of value you will insert into the Language input. The default suffices: a profile entry called LocalizedResource_Language. If you want to see the settings used for this profile entry, press the Create Entry button, which displays a dialog to enter a new profile entry but uses the settings of the LocalizedResource_Language profile entry as a starting point (press Cancel when you have finished looking at this dialog, as you don’t want to create a new profile entry in this instance). For a description of the fields used for each profile entry, see the “Profile Entries” sidebar.
PROFILE ENTRIES Profile entries describe the values assigned to builder inputs from profiles, and are an integral part of the profiling process. Each profile entry is made up of a number of fields, which are briefly listed and described in the following list: Name—This field denotes how the profile entry displays in the profile set editor and the Profile Input dialog. Prompt—This field denotes text that displays when you set the profile entry value in the WPF Designer. If you enable the setting of profiles through the portlet configure and portlet edit pages in the Portlet Adapter builder, then this field also denotes text that displays next to the profiles on these pages.
Creating an Announcements Portlet
331
UI Type—This field changes the type of UI controls that display when changing the profile entry value in the IDE, portlet configuration page, or portlet edit page. Additional profile entry fields (such as Width) may display in the profile set editor depending on the value given to the UI Type field. Default Value—This field provides a value for the profile entry in the Default profile (which is subsequently given to other profiles as well, unless you change them). Execution Time—This field indicates when the value of the profile entry is assigned. If the value is set to false (the default), the value is evaluated at generation time (when the WebApp is generated). A profile value that is evaluated at generation time can potentially result in a different WebApp being generated for each possible value. If the field is set to true, then the value is evaluated at execution time (that is, when a user executes the application). In this case, only one WebApp is generated, and shared across each potential profile/model combination. To help decide which setting to use, think about how the value is used. If the builder input affects generation, it should be set to false (for example, language and country settings in the Localized Resource builder are normally read in at generation time). If the input supports an indirect reference (that is, one that enables a value of ${…} ), then you should set the execution time field to true. Builder inputs such as Button labels and HTML attributes should almost always be evaluated at execution time.
The final section of this dialog is the Profiles section, which is where you define the different contexts that give the Language input different values. A profile called Default is already listed in this section—this profile is used if there are no other profiles, or if another profile cannot be assigned. The Profile Values column lists the value each profile gives to the profile entry (in this case, the value given to the Language input). For this example, you want the default Language to be ‘en’, so enter the text en into the Profile Values column next to the Default profile. To create a new profile, press the Create Profile button and type USEnglish as the profile name. Press OK when you are finished, and then press the Create Profile button again, this time naming the profile ESSpanish. Press OK when you are finished, and two new profiles should be listed under the Default heading in the profiles section. Place the cursor in the row for the USEnglish profile in the Profile Values column, and type the value en. This is done so that whenever the USEnglish profile is used, the profile entry LocalizedResource_Language has this value; it is then assigned to the Language input in the Localized Resource builder. Do the same for the ESSpanish profile, except specify the value as es. You have now finished configuring the Profile Input dialog. When you press OK, a profile set called localeValues.pset is created in the WEB-INF/profiles directory and it contains a profile entry called LocalizedResource_Language. The profile set also contains two profiles—ESSpanish and USEnglish—which will give a different value to the profile entry when each is used. If no profile is used, the value en is used. Press OK to accept the changes. The Language input now becomes blue to indicate it has been profiled, and the input itself is grayed out (as shown in Figure 12.2).
332
Chapter 12
Figure 12.2
Profiling Portlets
The profiled Language input.
TIP
You can remove profiling from a field at any time by pressing the Remove Profiling button on the Profile Input dialog.
Profiling the Country Input
You should now profile the Country input. To do this, open up the Profile Input dialog by pressing the icon next to the Country input in the Localized Resource builder call, and then make sure the localeValues profile set is selected for the Profile Set Name. The default settings for the profile entry (LocalizedResource_Country) are sufficient, so there is no need to modify them. You are using the same profile set as before, so the profiles you created earlier can be used for the Country input; all you need to do is set the appropriate values. To do this, type US for the USEnglish profile and Default profile, and type ES for the ESSpanish profile. One of these values is then used for the Country input, depending on the profile matched to a given request. Press OK to close the Profile Input dialog, and the Country input will become blue to indicate it has been profiled. Save your changes to the builder call. Setting a Selection Handler
By default, your profile set can use the Explicit Handler as its selection handler, which means that you need to explicitly assign profiles to use them. You can explicitly assign profiles programmatically, or by giving users a configuration page that they can use to pick a profile. In this example, you need to change the selection handler, as you want to be able to link your profiles to particular locales. To do this, double-click the localeValues.pset profile set in the WEB-INF/profiles directory, which opens the profile in the Profile Set Editor, as shown in Figure 12.3. The Profile Set Editor consists of three tabs: one for managing profiles, one for editing profile entries, and one for changing your selection handler. Switch to the Select Handler tab and change the value for Profile Selection Handler to Locale Selection Handler (press OK when prompted that your profiles may be affected).
Creating an Announcements Portlet
Figure 12.3
333
The Profile Set Editor.
TIP It is also possible to assign a value setter from the Select Handler tab. A value setter dynamically specifies profile entry values when the WebApp is generated (as opposed to whenever profile entries are edited). This is useful when you want to do a custom lookup or execute some code to dynamically determine profile entry values. For example, you might use a value setter to display a different customer service phone number depending on the time of day.
You are now using the Locale Selection Handler to resolve requests to your model with particular profiles; however, you have not specified which profiles should map to which locales. To do this, switch to the Manage Profiles tab. You will see a list of profiles in the profile set; select the profile for US English and press the icon to open the Edit Profile dialog. In the Edit Profile dialog, click the Advanced heading to open an interface for associating particular scenarios with this profile. These scenarios are called segments; in this case, each segment corresponds to a particular locale. The list of segments on the left is the list of suggestions for locales that you can assign with the current profile, and it is populated through a class that is specified in the configuration file for your current selection handler (Locale selection handler). The list on the right is the list of segments associated with the current profile.
334
Chapter 12
Profiling Portlets
Select the segment en_US from the list on the left, and then press the > button to assign it to the profile. Press OK when you are finished. The locale en_US is now associated with the US English profile. When users run your application, a selection handler class works behind the scenes to map the user’s preferred locale (as specified in a request attribute) with a segment in one of your profiles (to avoid ambiguity, you should never associate a particular segment with more than one profile). TIP
You can assign multiple segments to any given profile. A profile is used to provide a value for a builder input if the selection handler is able to match any of its segments to the appropriate value (a locale, for example). Pressing the Add External button in the Edit Profile dialog enables you to specify segments not provided in the Available for Association section of the dialog.
Edit the ES Spanish profile in the same way you did the English profile, only this time, add the associated segment es_ES instead of en_US. Save the profile set when you are finished. Placing Headings on the Page
The next step is to place the appropriate heading for each country and language combination at the hrHeader and itHeader tags. To do this, first add a Text builder call to the model and name it hrHeader. Change the Page and Tag inputs to page1 and hrHeader, respectively, and then open the Choose Reference dialog for the Text input. You should see an entry called LocaleData created by the Localized Resource builder. Through this, you can access the hrHeader property from the companyAnnouncements resource bundle. Select the hrHeader property as shown in Figure 12.4 and press OK. This builder places the contents of the hrHeader property at the hrHeader tag on page1. Save the builder call when you are finished.
Figure 12.4
Selecting the hrHeader property.
Creating an Announcements Portlet
335
Now add another Text builder call to the model, and this time name it itHeader. Specify page1 and itHeader for the Page and Tag, respectively, and then for the Text input, select the itHeader property from the LocaleData section (after you select it, it should display in the builder call as ${Variables/LocaleData/Data/announcements.itHeader}. Save the builder call when you are finished.
Adding English Announcements The next step is to add the actual U.S. English announcements to the project. To do this, create a new folder under the WebContent/WEB-INF folder called chapter12, and then create a new XML file in the Chapter 12 directory called USEnglishAnnouncements.xml (both of these actions can be performed from the Select a Wizard dialog, which you can open from the File, New, Other menu). Open the file and enter the following XML: Please hand in your timesheets by close of business tomorrow.This Friday we will be raising money for Peter, who would like some more money.The portal server will be down for maintenance between 5:00PM - 7:00PM Wednesday.wpsadmin is having a baby!
Notice that the announcements are split into two sections; the first section (HR) contains announcements concerning the HR department and the second section (IT) relates to the IT department. Save the file when you are finished.
Adding Spanish Announcements Now add another XML file in the same directory; this time call it ESSpanishAnnouncements.xml. Open the file and enter the following XML: Por favor entregen sus hojas the honorarios antes de cierre de negocio mañana.Este viernes acumularemos mas dinero para Peter, quien quisiera más dinero.
336
Chapter 12 Profiling Portlets El servidor del portal estara fuera de servicio desde 5:00PM a 7:00PM este miércoles para mantenimiento. wpsadmin tendrá un bebe.
This XML describes the ES Spanish version of the HR and IT announcements. Save the file when you are done editing it.
Importing the Announcements Now that you have added announcements for US English and ES Spanish, you need to import them into your model as an XML variable. To do this, add an Import to XML builder call to your model and name it announcements. For the File Path, you need to specify the filename of the announcements XML that you would like to use, which can change depending on the profile of the current user. To set this up, press the icon next to the File Path input to profile the File Path input. Make sure the Profile Set Name is set to localeValues, and leave the default Profile Entry Name (Config_FilePath). For the profile values at the bottom of the dialog, enter the following profile value for the USEnglish and Default profiles: /WEB-INF/chapter12/USEnglishAnnouncements.xml
For the ESSpanish profile, enter the value that follows: /WEB-INF/chapter12/ESSpanishAnnouncements.xml
Press OK when you are finished. This profiles the File Path input using the profiles in the localeValues profile set. The announcements specified in the USEnglishAnnouncements.xml file are used as the default (that is, if a match cannot be made against one of the other profiles). You have finished configuring the Import to XML builder, so save the builder call.
Displaying the Announcements The final builder calls to add are the Data Pages, which you use to display the contents of the XML announcements to the hrNews and itNews tags. Add the first Data Page builder call to the model and name it hrAnnouncements. Change the Page in Model input to page1, and then change the Page Type to View Only (you don’t need to enable users to edit the announcements). Expand the Created Element Settings section and change the Location for New Tags input to hrNews. The final change to make to the first Data Page builder call is to specify the appropriate variable in the Variable input. The variable you are after is the XML variable created by the announcements Import to XML builder, so open the Choose Variable or Schema dialog for the Variable input (by pressing the ellipsis button to the right of it) and select the HR announcements, as shown in Figure 12.5. Press OK, and then save the builder call.
Creating an Announcements Portlet
Figure 12.5
337
Selecting the HR announcements.
Now, add another Data Page to the model, but this time, name it itAnnouncements. As before, change the Page in Model input to page1 and the Page Type input to View Only. Change the Location for New Tags input in the Created Element Settings section to itNews. For the Variable input, select the IT announcements from the same place you found the HR announcements in the previous Data Page (after it is selected, it should display in the builder input as Variables/ announcements/Announcements/IT). Save the builder call when you are finished. You have now finished adding all of the builders you need for the companyAnnouncments portlet. In the next few steps, you will run a quick test of the model, and then implement some additional profiling so that only the announcements for the department that the user belongs to are displayed.
Running a Preliminary Test The easiest way to test a profiled portlet is to use the Applied Profiles view in the WPF Designer, which lets you preview the effect of a particular profile on your application. Select the Applied Profiles view in the WPF Designer (if a tab for the Applied Profiles view is not already visible, you can open it from the under the WebSphere Portlet Factory heading in the Show View dialog, which you can open by selecting Show View, Other from the Window menu in your IDE). In the Applied Profiles view, select USEnglish for the Profile dropdown and press Apply. Then run your model from the WPF Designer by clicking the icon on the toolbar. The company announcements should appear in English, as shown in Figure 12.6. To test the Spanish profile, select Spanish for the Profile dropdown in the WPF Designer, and then press Apply. Run the model again, and you should see the company announcements in Spanish, as shown in Figure 12.7. To test the Default profile, select Default for the Profile dropdown in the WPF Designer and press Apply. When you run the model again, you will see the US English announcements displayed (earlier you set these announcements to display by default).
338
Chapter 12
Figure 12.6
Company announcements in English.
Figure 12.7
Company announcements in Spanish.
Profiling Portlets
Testing the Selection Handler
Finally, you should test that the profiles work when the application is run without an explicit profile set in the Applied Profiles view. To do this, first turn off the explicit profile specified in the Applied Profiles view by closing the model and then reopening it. After this is done, you need to retest each profile. To test the US English profile, make sure your current locale is set to en-US in your Web browser (the browser settings are used by the selection handler and are required now because no explicit profile has been specified in the Applied Profiles View). To do this, first open the browser settings for your Web browser (for example, in Internet Explorer, select Tools, Internet Options from the menu bar). Then open the language settings (you can do this in Internet Explorer by pressing the Languages button on the General tab) and make sure the first entry is en-US (add it if this is not the case). Run the companyAnnouncements model again, and you should see the announcements in English.
Creating an Announcements Portlet
339
To test the Spanish profile, change the locale settings so that the first entry is es-ES. You need to close and reopen the browser again to see the changes. When the model loads, you should see the company announcements in ES Spanish. Finally, to test the Default profile, change the top locale entry to another region (such as af-ZA), and rerun the model. The default profile should cause the US English announcements to be displayed.
Profiling the hrHeader Based on Department The final feature to add to the companyAnnouncements model is to restrict which announcements are displayed based on the user’s department (note that this will not affect the previous profiling you set up in the localeValues profile set). The department of the user will be taken from the group that the user belongs to in WebSphere Portal, so you need to add two groups to test with (one called HR and the other called IT), with a test user in each that you can use to log in and see whether that user sees the appropriate announcements. The exact process you go through to set up these groups may differ depending on the version of WebSphere Portal you use and how your user directory is configured; for example if you’re using an LDAP directory, you can set up these groups through an LDAP administration utility. If you are unsure how to add and configure portal groups, consult your server documentation (or LDAP directory documentation if you’re using an LDAP directory) for instructions pertaining to your specific product version. After you have set up a HR group and an IT group, you need to configure the display builders in your model so that they are enabled or disabled depending on the department that the current user belongs to. To do this, first open the hrHeader builder call, and expand the Properties section. Press the icon next to the Enable Builder input to open the Profile Input dialog. You need to configure this dialog to assign a value of true when the current user belongs to the HR group and false if the user belongs to the IT group. The profiles you use can’t be resolved using the Locale selection handler, so you need to create a new profile set to use a different selection handler. Press the Create Profile Set button to open the New Profile Set dialog, and name the profile set userDepartment. Change the Description to Enable builders based on department and press OK to save the new profile set. The default profile entry (Text_BuilderCallEnabled) doesn’t need to be modified, but you need to add two profiles so that the hrHeader builder can be disabled if the user belongs to the IT group, and it can be enabled if the user belongs to the HR group (leave the Default profile with the value true, so that by default the builder is enabled). For the first profile, press the Create Profile button and enter HR for the Profile Name. Press OK to add to the profile (the profile automatically takes its value from the Default profile). Now, press the Create Profile button again to create a second profile, but name the profile IT. Press OK to add the profile, and then change the value of the profile to false. Press OK on the Profile Input dialog to accept your changes to the userDepartment profile set. You have now profiled the hrHeader builder, so that it will not display the hrHeader if the current user belongs to the IT group. Save your changes to the hrHeader builder call when you are finished.
340
Chapter 12 Profiling Portlets
Profiling the itHeader Based on Department Now open the itHeader builder call, and press the icon next to the Enable Builder input to open the Profile Input dialog for the Enable Builder input. You now need to configure this dialog to assign a value of true when the current user belongs to the IT group and false if the user belongs to the HR group. You can use the same profile set created in the previous step, so make sure the userDepartment profile set is selected. The default settings for the Profile Input dialog will be fine, except that you need to change the value for the HR profile to false. After you do this, press OK to accept the change to the profile set, and then save the builder call.
Profiling the hrAnnouncements Based on Department Now that you have profiled the announcement headings, you need to profile the announcements themselves. To do this, first open the hrAnnouncements builder call, and as before, press the icon next to the Enable Builder input. You need to configure this dialog to assign a value of true when the current user belongs to the HR group and false if the user belongs to the IT group (as before, the default value is true, meaning that the HR announcements are displayed by default). Make sure the userDepartment profile set is selected, and change the value for the IT profile to false. Press OK to accept the change to the profile set, and then save the builder call.
Profiling the itAnnouncements Based on Department The final input you need to profile is the Enable Builder input in the itAnnouncements builder call, so open this builder call now and press the icon next to the Enable Builder input. Make sure the userDepartment profile set is selected, and then change the value for the HR profile to false (the other profiles should have a value of true). Press OK to accept the changes, and save the builder call when you are finished.
Configuring the userDepartment Profile Set Now that you have configured all of your profiled inputs, you need to modify the userDepartment profile set so that it uses the WPS Group Segment selection handler. This selection handler maps requests to your model with the HR and IT profiles based on the user groups that you created earlier in the portal (namely, the HR and IT groups). Open the userDepartment.pset file in the WebContent/WEB-INF/profiles directory, and switch to the Select Handler tab. Change the Profile Selection Handler from Explicit Handler to WPS Group Segment Handler, and press OK when you are prompted that your profiles may be affected. Now switch back to the Manage Profiles tab so that you can see a list of the profiles contained in the profile set. You need to modify the HR and IT profiles so that they are associated with the HR and IT groups in the portal. To do this, first select the HR profile and then click the icon to edit the profile. Expand the Advanced section, and you will be given the option to associate a particular WPS group with the HR profile. No suggestions are listed for the WPS Group Segment handler; however, you can manually specify a WPS group by pressing the Add External button. On the dialog that follows, type HR and press OK. An entry for HR is added to
Creating an Announcements Portlet
341
the Associated box on the right of the Edit Profile dialog, which means the profile is now associated with the HR group. Press OK to accept the changes. The last change you need to make to the userDepartment profile set is to associate the IT group with the IT profile. Select the IT profile, and then press the icon to edit the profile. Press the Add External button under the Advanced section, enter IT, and press OK. An entry for IT is added to the Associated box on the right of the Edit Profile dialog, which means that the profile is now associated with the IT group. Press OK to accept the changes, and save the profile set when you are finished. The companyAnnouncements portlet is now complete. The next section describes how you can test this portlet from your IDE.
Testing the companyAnnouncements Portlet To test the new profiling changes to the companyAnnouncements portlet, first open the Applied Profiles view in your IDE and select HR for the userDepartment profile (you can leave the localeValues profile as Default). This simulates what happens when you try to access the application as a member of the HR group. Run the companyAnnouncements model from your IDE, and you should see that only the HR announcements are displayed, as shown in Figure 12.8.
Figure 12.8
Testing the HR profile.
Return to your IDE and change the userDepartment profile to IT. Rerun the companyAnnouncements model, and you should see the IT announcements displayed, as shown in Figure 12.9.
Figure 12.9
Testing the IT profile.
342
Chapter 12 Profiling Portlets
After you have tested your model, you should test it in the portal so that you know the selection handler is correctly finding the user group and associating it with the correct profile. To do this, first rebuild the application and add it to a page in the portal (for instructions on how to do this, see the example in Chapter 1). Notice that the localeValues profile set is still in effect, so changing your browser’s locale settings can still affect the language that the announcements are displayed in. For example, if you try to access the portlet as a user who is a member of the HR group with the en-US locale setting in your browser, you should see the portlet shown in Figure 12.10.
Figure 12.10
The companyAnnouncements portlet.
After the portlet has been added to a page, log in to the portal first as a user who is a member of the HR group, and then as a user who is a member of the IT group. In the first case, you should only see the HR announcements, and in the second case, you should see only the IT announcements.
Summary In this chapter, you learned how to customize a WPF application using profiling. The sample portlet developed in this chapter utilized the default Locale Selection Handler and WPS Group Segment Handler to customize company announcements depending on the locale and WPS group of the current user. The knowledge gained in this chapter should give you a good foundation from which to implement profiling in your other WPF applications, and to experiment with some of the other selection handlers available in WPF. Chapter 13, “Using Ajax and Dojo,” describes how to use the Ajax capabilities of WPF to increase the speed and interactivity of your portlets.
Important Points • WPF portlets can cater to different types of requests using the profiling mechanism. • Profiling settings are stored in profile sets. The profile sets in your application are kept in .pset files in the WEB-INF/profiles directory.
Important Points
343
• A profiled input is an input in a builder that gets its value from a profile in a profile set. Profiled inputs have a blue icon next to them in the Model Editor. • A profile entry is an individual point of variance in your application (such as a value for a particular builder input). A profile contains a series of profile entries, and the appropriate profile entry value is given to the appropriate builder input depending on certain conditions (for example, the locale or department of the current user). • A selection handler is used to determine the profile that should be used for any given request. • You can remove profiling from a builder input by pressing the Remove Profiling button on the Profile Input dialog.
This page intentionally left blank
C
H A P T E R
1 3
Using Ajax and Dojo
Ajax development techniques and the Dojo JavaScript toolkit provide rich client functionality to your Web applications, improving interactivity between end users and your portlets and reducing loading times. The subject of this chapter is how you can use these technologies in WebSphere Portlet Factory (WPF). By the end of this chapter, you will have a firm grasp on the Ajax and Dojo builders available, and you will also know how to use Dojo widgets that are not already part of the default Dojo builders. You will also have built a sample application to demonstrate these techniques, which displays key performance indicators for employees in a fictional organization. Each of the files in this chapter is available for download from ibmpressbooks.com/title/ 9780137134465 under the Chapter 13 folder (instructions for copying these files into your project are included in a readme.txt file in the same folder); however, to increase your understanding of the topics discussed, it is recommended that you create these files yourself by following the example in this chapter. The following topics are covered in this chapter: • Using Ajax • Creating a service provider • Creating a performance portlet • Using Dojo
Using Ajax Reloading entire Web pages every time you want to interact with the server can be a timeconsuming process, especially for your typical portal where there is a lot of code that needs to execute when the portal displays (you generally want to update only a small portion of the page for each request). Ajax is a series of techniques that can be used to mitigate this issue by
345
346
Chapter 13 Using Ajax and Dojo
asynchronously communicating with backend servers while partially refreshing your portal pages (so you don’t have to continually reload the entire page). If you completed the example in Chapter 11, “Field Validation, Formatting, and Translation,” you have already implicitly used one of these techniques in the form of the Dynamic Validation builder, which dynamically runs client/server validation routines.
CAUTION It is important to note that when using Ajax in a portal environment, you cannot make portal API calls because Ajax calls effectively run outside the context of the portal server. So, if your pages contain any code that references portal URLs (when you use Property Broker communication, for example), then these regions cannot be loaded using Ajax. However, you can add Ajax to specific parts of the page by using the Ajax Region builder, which enables partial page refreshing for all controls that fall within particular tags. Also, most of the WPF builders that can run actions from a UI component (for example, the Button and Link builders) contain a Post-Action Behavior section, which can be used to enable partial page refreshing for that specific component. In the example in this chapter, you can see how to use partial page refreshing to show and hide sections of the page without needing to reload it.
There are two other main ways you can use Ajax in WPF. The first is through one of the Ajax-enabled builders (such as the Ajax Type-Ahead builder, which gives you a series of server calculated prompts when typing text into a text input). The second is through the Dojo JavaScript toolkit, which you can access if you add the Dojo Extension feature set to your project. The example in this chapter demonstrates how to use both of these techniques. The first few sections of this chapter discuss how you can work Ajax capabilities into a simple key performance indicator (KPI) application. The final section of this chapter gives a brief introduction to using the Dojo JavaScript toolkit in WPF, and then expands on the KPI example by introducing some more Ajax UI improvements via the Dojo builders.
Creating a Service Provider In this section, you build a service provider model for retrieving a list of employee KPIs. Before you proceed with this section, you need a WPF project to house the service provider model (as well as the KPI model), so if you have a project from a previous chapter, you can use it. Otherwise, you should create a new WPF project (for more information on creating projects, see Chapter 1). In the “Using Dojo” section of this chapter, you add a feature set to the project so that you can use the Dojo capabilities of WPF. The project will be published as a WAR file and deployed to a portal server, and you should also deploy the application to a local application server for testing (you can use the portal server if it runs on your local machine; otherwise, it is recommended you use the IBM WebSphere Application Server Community Edition server [WAS CE] that comes with WPF).
Creating a Service Provider
347
After you have a project set up, you need to add the service provider model. This model uses the following builders to provide its functionalities: • Service Definition • Variable • Simple Schema Generator • Comment (x2) • Method (x2) • Service Operation (x2)
Creating a Model Create a new model called performanceService in your project, under the folder WEBINF/models/chapter13. Because you will be storing your data in an XML variable rather than getting it from a database, you need to create the service provider manually. To do this, the model should be based on the Empty template, which creates a model with no builder calls in it (for more information on creating models, see the example in Chapter 1).
Defining the Service Add a Service Definition builder call to the model by selecting Service Definition from the Builder Palette (you can open the Builder Palette by clicking the icon in Outline view) and pressing OK. Name the builder call performanceService, and expand the Testing Support section at the bottom of the builder call, and then enable the Add Testing Support option. WPF now automaticallys generate test pages for the operations in your service every time the model is regenerated. Save the builder call when you are finished.
Adding Performance Data The next step is to add your sample data for the service provider. Add a Variable builder call to the model and name it performance. Change the Type input to XML and enter the following XML into the Initial Value input: Dheeraj Glenmore 0$17,00070
348
Chapter 13 Using Ajax and Dojo Emilio Parkdale 3$25,00090Navin Carrum 5$15,00080
After you’ve entered the information, save the builder call and then add a Simple Schema Generator builder call to the model. This builder call is used to automatically generate a schema based on the Variable builder call, which is then used to define the structure of the result sets for the Service Operation builder calls. Name the builder call performanceSchema, and for the Sample Data input, select ‘performance’ from the drop-down list (this is the variable you created earlier). Save the Simple Schema Generator builder call when you are finished.
Adding Functionality for Retrieving Performance Data The next step is to specify functionality for the getPerformanceData operation, which retrieves a list of employee performance information based on a filter passed in to the operation (a filter consisting of an empty String (“”) returns all employee performance information). To do this, first add a Comment builder call to the model and name it getPerformanceData. This builder call makes it easier to see which builders are specifically related to the getPerformanceData operation. Next, you should add the method to run when the getPerformanceData operation is consumed. Add a Method builder call to the performance model, and name the Method getPerformanceData (there is no problem with this being the same name as your Service Operation). Add one argument for the Method in the Arguments input, called toGet of type String, and change the return type of the builder call to IXml. The argument for this Method is a filter matching the value of a specified Area in the performance data, and the result returned from the Method is an IXml object taken from the performance data (and filtered according to the value of the toGet argument).
Creating a Service Provider
349
In the Method Body input of the builder call, enter the following code (note that you can copy this code from the getPerformanceData Method.txt file, available for download from ibmpressbooks.com/title/9780137134465, under the Chapter 13 folder): { try { //get performance Variable IXml performance = webAppAccess.getVariables().getXml(“performance”); //if toGet is blank, return all rows if (toGet.equals(“”)) return performance; //otherwise, iterate through each row and assemble an XML object //consisting of the rows that match toGet IXml result = XmlUtil.parseXml(“”); List theList = performance.getChildren(“Row”); //for each entry in theList... for (Iterator it = theList.iterator(); it.hasNext();) { IXml thisEntry = (IXml)it.next(); //check to see if the Area of the current entry equals toGet if (thisEntry.findElement(“Area”).getText().equals(toGet)) { //create a new XML entry in the result XML based on the values in ¯thisEntry IXml newEntry = result.addChildElement(“Row”); newEntry.addChildWithText(“Name”, thisEntry.findElement(“Name”).getText()); newEntry.addChildWithText(“Area”, thisEntry.findElement(“Area”).getText()); newEntry.addChildWithText(“NewCustomers”, thisEntry.findElement(“NewCustomers”).getText()); newEntry.addChildWithText(“TotalSales”, thisEntry.findElement(“TotalSales”).getText()); newEntry.addChildWithText(“FeedbackRating”, thisEntry.findElement(“FeedbackRating”).getText());
350
Chapter 13 Using Ajax and Dojo
newEntry.addChildWithText(“Comments”, thisEntry.findElement(“Comments”).getText()); } } //return the result return result; } //if there is an IOException, return null catch (java.io.IOException e) { return null; } }
This code first gets a reference to the performance Variable, and then checks to see whether the toGet input is blank. If it is, then the performance Variable data is returned as is; otherwise, a result set is constructed based on the employee area specified in the toGet input. First, an empty XML document called result is created, and then a List of Row elements in the performance Variable is iterated through to find each employee with an area matching the area specified in the toGet input. For each matching entry, a new row is added to the result variable based on the current row in the performance Variable. After the performance Variable has been iterated through, the result is returned to the caller. Finally, a catch block at the end of the code catches any IOExceptions thrown by the parseXml command (for more information on error handling in WPF, see Chapter 14, “Error Handling, Logging, and Debugging”). Save the builder call when you are finished.
Specifying the getPerformanceData Operation Now you need to create the service operation to retrieve the performance data. Add a Service Operation builder call to the model, and make sure the Service Operation Properties section is expanded. Select performanceService for the Data Service input, which links the operation to the performanceService service created by the Service Definition builder call. After this is done, change the Operation Name input to getPerformanceData, which defines how the operation is referred to by consumers. The Action To Call input defines what happens when the operation is consumed; this input should be changed to point to your Method (getPerformanceData). Make sure ‘Use structure from called action’ is selected for the Input Structure Handling input in the Operation Inputs section of the builder call. This specifies that the operation will take its inputs from the getPerformanceData Method.
Creating a Service Provider
351
Finally, expand the Operation Results section of the builder call, and select Specify Result Schema for the Result Structure Handling input. This enables you to manually specify the structure of the results to be returned from the operation. For the Result Field Mapping input, select the top element in the performanceSchema schema (RowSet). The RowSet element contains a set of performance data and is returned from the getPerformanceData Action List. The input should read performanceSchema/RowSet when you are finished. Make sure the Result Field Mapping input is set to Automatic, which automatically maps results from the called action to the result returned from the service operation. Save the builder call when you are finished.
Adding Functionality for Updating Performance Data You now need to add functionality for updating the performance data, which is used to save comments entered through the UI. First, to organize the builders related to the update functionality in your model, add a Comment builder call to the model and name it updatePerformanceData. Save the builder call when you are finished. The builders that follow this Comment are specifically used to update performance data. Add a Method builder call to the performance model, and name it updatePerformanceData. Now add one argument for the Method called toUpdate of type String, and change the return type of the builder call to void. The argument for this Method is an IXml object representing the new performance data used to update the existing performance data. No result needs to be returned from the Method, as all that is required is that the backend XML data is updated. Enter the following code into the Method Body input (you can copy this code from the updatePerformanceData Method.txt file available for download from ibmpressbooks.com/title/ 9780137134465, under the Chapter 13 folder): { //get performance Variable IXml performance = webAppAccess.getVariables().getXml(“performance”); //get list Variables List performanceList = performance.getChildren(“Row”); List updateList = toUpdate.getChildren(“ins:Row”); //iterate through updateList for (Iterator uIt = updateList.iterator(); uIt.hasNext();) { IXml uEntry = (IXml)uIt.next(); //for each entry in updateList, iterate through performanceList for (Iterator pIt = performanceList.iterator(); pIt.hasNext();) {
352
Chapter 13 Using Ajax and Dojo
IXml pEntry = (IXml)pIt.next(); //check to see if the entries in updateList and performanceList have the ¯same name if (pEntry.findElement(“Name”).getText().equals(uEntry.findElement ¯(“Name”).getText())) { //update the comments in the current entry for performanceList pEntry.findElement(“Comments”).setText(uEntry.findElement(“Comments”).getText()); } } } //set performance variable webAppAccess.getVariables().setXml(“performance”, performance); }
As with the previous Method, this code begins by getting a reference to the performance Variable. Two lists are then created: the first (performanceList) iterates through the rows in the performance XML, and the second (updateList) iterates through the rows in the toUpdate argument passed to the Method. Note that the Row element for the toUpdate Method has the prefix ‘ins:’—this prefix is added to each element as it is passed to the service operation from the Data Page, so you need to use it in the previous code to locate elements in the toUpdate Variable. The updateList is then iterated through, and for each row in the updateList, a corresponding row (matching on the Name) is found in the performanceList. After it is found, the comments in the row in the performanceList are updated with the comments in the updateList. At the end of the Method, the performance XML data inside the Method is written to the WPF performance Variable, so that it is accessible via the getPerformanceData operation (note that the performance XML data inside the Method is updated whenever the setText method is run). Save the builder call when you’ve finished editing it.
Specifying the updatePerformanceData Operation As before, you now need to create a service operation to surface the functionality you just created. To do this, add a Service Operation builder call to the model, and then select performanceService for the Data Service input to link the operation to the performanceService service. Change the Operation Name input to updatePerformanceData, which defines how the operation is referred to by consumers. For the Action To Call input, select your update Method (updatePerformanceData). Because the input to the updatePerformanceData Method is an IXml object, you need to specify the schema of the object in the Operation Inputs section. First, select Specify input schema for the Input Structure Handling input; then in the Input Schema input, select the Rowset
Testing the Service Provider
353
of the performanceSchema schema (performanceSchema/RowSet). After you have done this, change the Result Structure Handling input in the Operation Results section to ‘No results’, as the operation does not return a value. Save the builder call when you are finished. Your performance service is now ready. The next section walks you through the process of testing this model from your IDE.
Testing the Service Provider Because you enabled testing support for the service provider, WPF automatically generates test pages for it. This enables you to test the service provider directly from the IDE. To test the service, run the performanceService model from the WPF Designer by clicking the icon on the toolbar. This runs the performanceService model with the last run configuration you used. If you have not set up a run configuration before, you are prompted to do so— create a new configuration under the WebSphere Portlet Factory Model category (if you want more information on setting up a run configuration, see the “Testing the Application” section in Chapter 1). After you run performanceService, you should see the index test page in your default Web browser, as shown in Figure 13.1. This screen lists all the operations available on the performanceService service (it should display only one for getPerformanceData).
Figure 13.1
Index test page from the performanceService model.
Testing the getPerformanceData Operation To test the getPerformanceData operation, click the getPerformanceData link. A new page loads, requesting the value of the toGet Variable. To test that all results can be retrieved, leave the toGet field blank and press the Submit button. You should see the full table of performance data, as shown in Figure 13.2. After you have done this, run the test again, but this time, try specifying the area for a particular employee (say, for example, Glenmore). After you press Submit, you should see only the results for that area (for Glenmore, you would see Dheeraj’s results). Note that you can’t actually test the updatePerformanceData operation from the service, as it requires an XML argument (which you can’t specify in the default testing interface). Instead, you will test the update feature through the performance portlet in the next section.
354
Chapter 13 Using Ajax and Dojo
Figure 13.2
Testing the getPerformanceData operation.
You now have a service provider called performanceService. The next section walks you through creating a service consumer model to display the performance data to the screen, and manipulate this display using Ajax.
Creating a Performance Portlet This section describes how to add a second model to your project, a service consumer that displays a user interface for accessing KPI information from the performanceService service. This model uses some of the Ajax capabilities of WPF, and is surfaced to a portal server as a portlet (for more information on the service consumer/provider pattern in WPF, see Chapter 2, “Providing and Consuming Services”). Later in this chapter, Figure 13.6 shows a screenshot of the portlet using the builders added in this section. In this section, you create a service consumer model using the following builders: • Action List (x3)
• Visibility Setter (x2)
• Page
• HTML Event Action
• Portlet Adapter
• Ajax Type Ahead
• Service Consumer
• Text Input
• Data Page
• Button
• Check Box
Creating a Model Create a new model called performance in your project, under the folder WEB-INF/models/ chapter13. Although you can use the List and Detail Service Consumer option in this example (even though you don’t have any detail data), it is easier to base the model on the Main and Page template, using a Page Type of Simple Page. For more information on creating models, see the example in Chapter 1. The reason for this is that you need a form element on the page to use the Check Box builder, which WPF does not automatically create if you don’t specify any detail data (because it thinks that you are just displaying data on the screen). Rather than trying to manually add a form element to the page, it is easier to create the list yourself using a Data Page.
Creating a Performance Portlet
355
After it is created, your model should have two builders in it: an Action List called main and a Page called page1. The Action List runs automatically whenever the model is run and opens the page1 Page.
Modifying the Page Open the Page builder, and change the contents of the Page Contents (HTML) input to the HTML listed in the following:
The HTML contains several span tags that will be replaced later in this section using builders in WPF: The first (performanceDisplay) is for the table of performance data, and the second (areaSelect) is for a text box where users can select a specific area to display in the performance table. The third span tag (submitButton) is for a button that will be used to apply the area specified in the areaSelect text box, and the final span tag (feedbackCheckbox) will be replaced with a checkbox used to toggle whether one of the columns in the performance table is visible. Save the builder call when you’ve finished entering the contents of the Page Contents (HTML) input.
Adding a Portlet Adapter The first builder call to add to the performance model is a Portlet Adapter builder call, which surfaces the model as a portlet on the portal server specified in your deployment configuration. Add a Portlet Adapter builder call to the model, and then change the Name to performance. Then,
356
Chapter 13 Using Ajax and Dojo
change the Portlet Title to Performance and the Portlet Description to This portlet displays information on KPIs. This surfaces your model to the portal server as a portlet called Performance. Save the builder call when you are finished.
Consuming the Service Add a Service Consumer builder call to the model. You use this model to access the getPerformanceData operation in the performanceService service. Change the name of the builder call to performance, and then specify the location of the provider in the Provider Model input (chapter13/performanceService). Save the builder call when you are finished. Because you want the getPerformanceData operation to run every time you open the model (this causes the performance data to be displayed on the screen without the user having to manually click on anything inside the portlet), you should add a line into the main Action List to consume the getPerformanceData operation. Because this operation takes an argument, you should also add a line to set the argument to an empty String so that all the performance data is displayed.
TIP You don’t necessarily need two separate actions to call an operation that requires arguments. One way to specify only a single action is to use the WithArgs method for your operation (for example, performanceGetPerformanceDataWithArgs). Specifying two separate actions can, however, make your Action Lists easier to read and maintain.
You should set the argument before you call the operation, so for the first action, open the main Action List, and then insert a line before the page1 action (you can do this by right-clicking the page1 action and selecting Insert). Then, open the Select Action dialog for the new action (by clicking the Ellipsis icon to the right of the new row) and select Special, Assignment. This enables you to assign a source value to a target Variable. You can leave the Source field blank (as you want to assign an empty String), and for the Target field, open the Variables dialog and then select the toGet argument for the getPerformanceData operation, as shown in Figure 13.3. Press OK when you are finished to add the action. For the next action, insert another line before the page1 action, and then change it to read as shown in the following: DataServices/performance/getPerformanceData
Note that you don’t have to type this action directly, as you can select it from the DataServices category in the Select Action dialog available from the ellipsis button to the right of the action. After you add the new action, your Action List will retrieve all employee performance data via the getPerformanceData operation. Save the Service Operation when you are finished.
Creating a Performance Portlet
Figure 13.3
357
Selecting the toGet argument.
Displaying the Interface Now add a Data Page builder call to the model (for more information on the use of the Data Page builder, see Chapter 3, “Using Data from a Relational Data Source”). The purpose of this Data Page is to show the results of the getPerformance operation on the screen. Name the Data Page displayPerformance and change the Variable input to the results retrieved from the getPerformance service (DataServices/performance/getPerformanceData/results). Then, change the Page in Model input to page1, and the Location for New Tags input to performanceDisplay, so that the results of the builder are displayed at the performanceDisplay tag on page1. Save the builder call when you are finished.
Hiding and Showing a Column Using Ajax In this example, you hide and show the feedback rating column in the performance data, based on whether the user has enabled a checkbox on the screen. To do this, first add a Variable builder call to the model and name it feedbackCheckboxValue. This variable will hold the value of the checkbox that will be used to toggle whether the feedback rating column is displayed (if you don’t use a variable, then the value of the checkbox will be lost whenever the page is submitted). Change the Type of the Variable to Boolean and the Initial Value to true (so that the feedback column is displayed by default), and then save the builder call. Now add a Check Box builder call to the model, and name it feedbackCheckbox. Specify the Page input as page1 and the Tag input as feedbackCheckbox, which will place a checkbox on the feedbackCheckbox tag on page1. Change the label input to Show feedback rating, which
358
Chapter 13 Using Ajax and Dojo
will be the text displayed next to the checkbox, and then change the Default value to the feedbackCheckboxValue Variable (you can do this by selecting feedbackCheckboxValue from the Variables section of the Choose Reference dialog). This causes the value of the checkbox (that is, whether it is enabled or not) to be taken from the feedbackCheckboxValue Variable. Save the builder call when you are finished. The next change is to actually hide the feedback rating data and the feedback column heading when the feedbackCheckbox checkbox is enabled. To do this, add a Visibility Setter builder call to the model. Call it hideFeedback and change the Page input to page1, and then change the Tag input to FeedbackRating. Change the Visibility Rule to ‘Hide when Value is “false” or “hide”’, and specify the Value as the value of the feedbackCheckboxValue Variable (after you have selected the Variable value from the Choose Reference dialog, it should display as ${Variables/feedbackCheckboxValue} in the Value input). This causes the contents of the FeedbackRating tag (that is, the column of feedback ratings) to be hidden when the checkbox is unchecked. Save the builder call, and make a copy of the builder call in the same model. Change the name of the builder call to hideFeedbackHeader and the Tag to FeedbackRatingHeader. This causes the heading for the feedback column to be hidden when the checkbox is set to false. Save the builder call when you are finished.
Responding to a Change in the Checkbox Value You now need to define what happens when the user toggles the checkbox on or off. In this case, you need to set the value of the feedbackCheckboxValue Variable to whatever the feedbackCheckbox value is, and then reload the page. To do this, add an Action List to the model and call it changeFeedbackCheckboxValue. Leave the Return Type as Object, and then open the Select Action dialog for the first action in the Actions input. Select Assignment from under the Special category, which enables you to assign a source value to a particular target. For the Target, select the feedbackCheckboxValue Variable (it then displays in the Target field as Variables/ feedbackCheckboxValue), and for the Source, select the value of the checkbox from under the Inputs section of the Choose Reference dialog (it then displays in the Source field as ${Inputs/ feedbackCheckbox}). Press OK when you are finished, and the action is added to the Action List. Add a second action underneath the first by selecting page1 for the second row in the Actions input, which refreshes the page after the feedbackCheckboxValue Variable has been set. Save the builder call when you are finished. Now add an HTML Event Action builder call to the model. The HTML Event Action can be used to intercept a particular HTML event (such as the onChange event of the checkbox) and execute a particular action when that event occurs (any original actions attached to that event are overwritten). Change the name of the builder call to feedbackCheckboxChange, and then specify the Page as page1 and the Tag as feedbackCheckbox. For the Event Name, select onChange, and for the Action input, select the Action you just created from the Select Action dialog (that is, changeFeedbackCheckboxValue). This causes the changeFeedbackCheckboxValue action to run every time the checkbox is checked or unchecked. Also, make sure the Action Type
Creating a Performance Portlet
359
is set to ‘Submit form and invoke action’, as this ensures that the form is submitted when the event is triggered, and that the Visibility Setters are therefore able to update the feedback column. One final change to make to this builder is to make sure that the entire page is not reloaded after the changeFeedbackCheckboxValue action is run. To do this, expand the Post-Action Behavior section, and change the Post-Action Behavior input to ‘Refresh specified page location after running action’. Some more inputs display underneath, which correspond to the section of the page that you need to refresh after the action is run. Change the Page input to page1 and the Tag input to performanceDisplay (this is where the performance data is displayed), and save the builder call. This causes the performanceDisplay section of page1 to be refreshed after the changeFeedbackCheckboxValue action executes.
Adding an Area Select Field The next builder adds a text input box to the areaSelect tag, enabling users to specify an area they would like to use for filtering the performance data. Add a Text Input builder call to the model and name it areaSelectField. In the Page Location section, change the Page to page1 and the Tag to areaSelect. Leave the other fields with their default values and save the builder call (note that leaving the Text input blank causes the text input to be blank when the page first loads).
Adding Submit Functionality Now, you need to add a button to the page so that users can apply the filter specified in the area select field. To do this, first add an Action List builder call to the model and name it submitAction. This Action List defines the sequence of actions to execute when the button is clicked. Make sure the Return Type is Object, as the final action in the list displays the contents of page1. You need three actions for this Action List. The first assigns the value of the areaSelect input to the toGet argument of the getPerformanceData operation; the second executes the getPerformanceData operation; and the final action displays page1. Open the Select Action dialog for the first row in the Actions input, and select Assignment from the Special category. As you did in the main Action List earlier, you should then select the toGet argument for the Target field from the Data Services section of the Variables dialog. For the Source input, select the areaSelect input from under the Inputs category in the Choose Reference dialog and press OK. This action takes the value of the areaSelect text input and assigns it to the toGet argument. For the second action, execute the getPerformanceData operation as you did in the main Action List (it should display in the list as DataServices/performance/getPerformanceData). Finally, the third action should load page1, so select page1 from the Pages section of the Select Action dialog on the third row. Save the builder call when you are finished. Now that you have an Action List handling the submit functionality, you need a button to enable users to execute this functionality. Add a Button builder call to the model and name it submitButton. In the Page Location section of the builder call, specify page1 and submitButton as the Page and Tag, respectively, which places the button just to the right of the area select text input. Type Submit for the Label, and for the Action, specify submitAction (make sure the
360
Chapter 13 Using Ajax and Dojo
Action Type input is set to Submit form and invoke action). The final change to make to this button is to ensure that the page is partially refreshed after the button is pushed (so that the entire portal page does not have to be reloaded). To do this, change the Post-Action Behavior input in the Post-Action Behavior section to ‘Refresh specified page location after running action’. Then, change the Page input to page1 and the Tag input to performanceDisplay (this is where the performance data is displayed). This ensures that only the performanceDisplay section of page1 needs to be refreshed. Save the builder call when you are finished.
Adding Ajax Type Ahead The next step is to add Ajax Type-Ahead capabilities to the area select field in the performance data. This enables users to select an area without necessarily having to type the entire name of the area. To do this, add an Ajax Type-Ahead builder call to the model, and change the Name to areaSelectTypeAhead. Specify the Page as page1 and the Tag as areaSelect, and then enter the following values into the Values input: Parkdale,Glenmore,Carrum
The default behavior for the Ajax Type-Ahead builder is that whenever a user enters in a value where the first letter matches one of the first letters specified in the Values input, the matching element from the Values input displays as a suggestion for the user (which they can select and use as the new value if they wish). Notice that you can also obtain these values from a lookup table if you wish (which you would create using a Lookup Table builder). An example using the Lookup Table builder is given in Chapter 16, “More Techniques for Domino.” Also, note that at the bottom of the builder call you have options to change the way the suggestion prompts are filtered, and you can also change how many suggestions display at one time. In the current example, it is more useful if all of the type-ahead values are displayed at once, so change the Filter mode input to ‘Show all values’. Save the builder call when you have finished configuring it. Your service consumer model is now ready to test and deploy to a portal server, which is covered in the next section.
Testing the Performance Portlet Make sure the performance model is the currently active model in your IDE, and then run the performance model from the WPF Designer. The performance data should display in your default Web browser, as shown in Figure 13.4. Note that this data displays by default because there is an action in the main Action List to consume the getPerformanceData operation (passing in an empty String for the toGet argument). Notice that by default, the Show feedback rating checkbox is enabled, which means that the feedback column is displayed. Disable the checkbox, and you should see that the feedback column is removed (the entire column should disappear). If you have configured the Post-Action Behavior section correctly, then this is done without reloading the page. You can check if the page
Testing the Performance Portlet
361
has reloaded by looking at the Back button on your browser. If there is no page to go back to, then the column was updated without actually reloading the page. Of course, the difference in response time is not as noticeable now because the model is being viewed by itself, but in the context of the portal, the increase in response time should be immediately obvious (particularly on pages with multiple portlets).
Figure 13.4
Testing the display of performance data.
To test the area select functionality, type a character into the area select field. You should notice that a box of suggestions is now displayed underneath the text input (as shown in Figure 13.5). Click one of these entries (say, for example, Carrum) and it will populate the area select text input with the appropriate value. After you press the Submit button, the performance data should refresh so that only employees working in the area specified are displayed (so only Navin’s details are displayed for Carrum, for example).
Figure 13.5
Ajax Type-Ahead for the area select field.
After you have done this, remove the contents of the areaSelect input and press the Submit button again. The performance data should reload, displaying performance data for every employee. Close the performance model when you’ve finished testing it. You should now rebuild your application on the portal server (for instructions on how to do this, see the example in Chapter 1), after which you will be able to view the performance model as a portlet. After you have added the portlet to a page in the portal, it should display as shown in Figure 13.6. Note that when you click on the checkbox, it updates the feedback column without updating the rest of the portal page.
362
Figure 13.6
Chapter 13 Using Ajax and Dojo
The Performance portlet.
Using Dojo Dojo is a JavaScript toolkit that can be used to add powerful UI capabilities to Web applications. When you add the Dojo Extension feature set to a WPF project, a library of JavaScript widgets is added to your project (in WPF 6.0.2, these widgets are added under the WebContent/factory/dojo folder), which you can then use in your portlets. WPF 6.0.2 comes with version 0.4 of the Dojo toolkit, which contains interface improvements from dialog boxes to drag-drop functions, and it is worthwhile having a browse through the Dojo libraries to see if there is anything that might be able to improve the UI of your portlets. You can find out more about the Dojo toolkit on the Dojo home page at http://dojotoolkit.org/). There are two ways to use Dojo in WPF: One is to use the default WPF builders, and the other is to access the Dojo library directly (which you can do from HTML, JavaScript, and even other builders). To use the Dojo builders, you simply need to add the builder to your project as you would any other builder—you don’t need to be concerned with how the functionality is implemented. If you access the Dojo libraries directly, however, you do need some knowledge of them (at minimum, you need to know what to call and how to call it). In this second scenario, you also need to add a Dojo Enable builder call to your model, which primes your application to use certain Dojo features. The example in this section uses both of these techniques. One caveat worth mentioning with regard to Dojo (and, indeed, with Ajax in general) is that your applications can become sluggish if you have an abundance of UI controls that constantly update the server. This is particularly true if you have users who communicate with your portal over a slow connection. As a general precaution, then, it is recommended that these components are used sparingly. The rest of this section walks you through the process of adding some useful Dojo functions to the performance portlet built earlier in the chapter. The following builders are used:
Using Dojo
363
• Comment • Action List (x2) • Dojo Inline Edit • Dojo Tooltip • Dojo Enable • Attribute Setter A screen shot of the final portlet is shown later in Figure 13.14.
Enabling Dojo Capabilities Before the Dojo libraries are even available in your WPF project, you need to make sure you’re using the Dojo Extension feature set. To do this, select Project, Properties from the menu bar in the WPF Designer, and then open the WebSphere Portlet Factory Project Properties, Feature Info section. On the Feature Info page, enable the box for the Dojo Extension (found under the Dojo Ajax category; alternatively, you can also just enable the Dojo Ajax category) and press OK. When prompted whether you would like to deploy, select ‘No’, as you can rebuild your project at the end of this section. You should now notice a new folder under the WebContent/factory directory called dojo, and if you open the Builder Palette, you can also see several new builders (all beginning with the prefix Dojo). Before you proceed with this section, add a Comment builder call to the model and name it Dojo builders. Save the builder call. This builder call will serve as an organizational divide between the builders in the previous section of the chapter and the builders in this section.
Adding an Action to Save Comments A number of actions need to be executed whenever users save a comment. First, the toUpdate argument of the updatePerformanceData operation needs to be set, after which the updatePerformanceData operation needs to be called. Finally, page1 needs to be redisplayed (the form will be submitted when a user tries to save a comment, so if you don’t redisplay page1, no output will be produced). Add an Action List builder call to the model. Change the name of the Action List to saveComment, and make sure the Return Type is set to Object. For the first action in the Actions input, open the Select Action dialog and select Assignment from the Special category. The Source variable in this case should be the RowSet of the argument to the updatePerformanceData operation, as shown in the Variable dialog in Figure 13.7. For the Target value, select the data used in your data page—that is, the results from the getPerformanceData operation (as shown in Figure 13.8). This data holds the values of the current data in the performance table, including any new comments added by the user. Press OK when you are finished. The toUpdate argument is now set to the performance data displayed on the screen, so that this data can be updated by the updatePerformanceData operation.
364
Chapter 13 Using Ajax and Dojo
Figure 13.7
Selecting a Source variable.
Figure 13.8
Selecting a Target value.
The next action to add will run the updatePerformanceData operation, so open the Select Action dialog and select the updatePerformanceData operation from the DataServices section (it should then display in your Action List as DataServices/performance/updatePerformanceData). For the third action, select page1. Save the builder call when you are finished. You will call this Action List in the next step, where you add inline edit capabilities to the comments column.
Using Dojo
365
Adding Inline Edit Capabilities To add inline edit capabilities to the Comments column in the performance table, add a Dojo Inline Edit builder call to the model and name it inlineEdit. This builder adds a small Pencil icon next to a set of tags on the page, which display text input boxes when clicked. This mechanism can be used to dynamically change the values of cells in a table created by a Data Page. The Dojo Inline Edit builder provides a number of customization features (for example to change the graphics used for the icons), but in most cases, the default settings are fine. You do, however, need to change the Fields input to tell the builder which fields should receive the Pencil icon. To do this, click the ellipsis button next to the Fields input to bring up the Data Page Field Chooser dialog, and then select the Comments column from the displayPerformance Data Page, as shown in Figure 13.9. Press OK when you are finished.
Figure 13.9
Selecting the Comments column.
To run the saveComment Action List every time a new comment is added, enable the Submit Form input, and then enter saveComment in the Action input that displays. Save the builder call when you are finished. If you run your application at this point, you should see the Pencil icon display next to each cell in the Comments column. When you click on the Pencil icon for a particular row, you can add comments for that person, as shown in Figure 13.10.
366
Chapter 13 Using Ajax and Dojo
Figure 13.10
Editing a comment.
Adding Tooltips In this section, you add a tooltip to the FeedbackRating column, so that users will know what the column values mean when they move the mouse cursor over the column. To do this, add a Dojo Tooltip builder call to the model and name it feedbackTooltip. For the Page Location, specify the Page as page1 and the Tag as FeedbackRating. By default, this places the tooltip hotspot at this location, but it may also collide with any other Dojo controls you decide to add to the FeedbackRating column later. To avoid this, you should change the Location Technique to ‘Relative to Named Tag’, and then specify Wrapper for the Placement input and feedbackTooltip for the New Tag Name. Although your tooltips will work with the default placement, the Wrapper placement is preferable in that it does not replace the underlying tag you are applying the tooltip to. The Tooltip Type input enables you to either show a line of text or run a particular action when the cursor is over the tooltip hotspot. In this example, you use a combination of both techniques to display the feedback rating and some surrounding text. To do this, first make sure Text is selected, then type Feedback score is out of 100 into the Tooltip Text input. Then place the cursor just before the word out in this text, and open up the Choose Reference dialog. From the Variables category, select the current row’s feedback rating as shown in Figure 13.11 and press OK. Add a space after the feedback rating, so that the Tooltip Text now appears as: Feedback score is ${Variables/RowLoopVar/Row/FeedbackRating} out of 100
Save the builder call when you are finished. If you test the model at this point and move the mouse cursor over the FeedbackRating column, you should see a tooltip pop up as shown in Figure 13.12.
Using Dojo
367
Figure 13.11
Selecting the current row’s feedback rating.
Figure 13.12
Feedback tooltip.
Adding a Feedback Bar The final Dojo modification in this chapter is the modification of the feedback rating column to display a graphical representation of the feedback rating. The bar is created using the dojo. widget.ProgressBar widget in the Dojo toolkit. Because there are no Dojo builders that provide this functionality by default, you need to first load the appropriate widget into your model (using
368
Chapter 13 Using Ajax and Dojo
the Dojo Enable builder), and then overwrite an element on the page for the result (which you can do using the Attribute Setter builder). Note that the other Dojo builders in WPF implicitly use a Dojo Enable builder behind the scenes to get access to the appropriate Dojo libraries. First, add a Dojo Enable builder call to the model and name it dojoEnable. The Dojo Enable builder loads any Dojo widget packages specified in the Requires Package List so that they are accessible in your model. In this case, you need only the ProgressBar widget, so type dojo.widget.ProgressBar in the Requires Package List input and save the builder call. Note that you can also specify JavaScript in the JavaScript input or inside a .js file in the JavaScript Files input; these scripts are then added to the target page. You might use specific scripts here if you want to do things such as add a Dojo dialog box to the page (for example, you can define the box and its properties). In this example, leave the inputs blank. The final change to make to the model is to add an Attribute Setter builder call. Do this now, and change the name of the builder call to addPerformanceBar. This builder call is used to replace the contents of the new Feedback Bar column with Dojo widgets, which you can configure inside the Attribute Setter (you can also add these widgets at any place in the model where you can specify HTML, but it is easier and more common to use an Attribute Setter for this purpose). Change the Page input of the builder call to page1, and change the Tag input to FeedbackRating. As with the Dojo Tooltip, you should create a wrapper for the tag rather than replace it, so change the Location Technique to ‘Relative to Named Tag’, the Placement input to Wrapper, and the New Tag Name to FeedbackBar. After this is done, fill in the Attribute List section of the builder call, as shown in Figure 13.13.
Figure 13.13
Configuring the Attribute Setter.
Notice that several different attributes have been added to each entry in the FeedbackRating column (at the FeedbackBar tag), and these attributes specify properties for the Dojo ProgressBar. The dojoType input specifies that the widget should be a ProgressBar (you don’t need to specify the full package name for this attribute), and the progressValue is used to set the current
Important Points
369
value of the bar. Note that in the example, this value is taken from the FeedbackRating on the current row, which is provided through the RowLoopVar variable. The maxProgressValue attribute defines the maximum value for the bar (in this case, it is 100) and the width and height define the dimensions of the bar in pixels. Save the builder call when you are finished. Test the model from your IDE, and you should see that a feedback bar is displayed for each person, as shown in Figure 13.14. Note that the tooltip still works with the feedback bar, and you can also hide and show the feedback bar by toggling the ‘Show feedback rating’ checkbox.
Figure 13.14
The performance model with feedback bar.
Summary In this chapter, you learned how to use Ajax to improve the usability and responsiveness of your WPF applications. You also learned about the strengths and limitations of Ajax in WPF, and how you can use the Dojo JavaScript toolkit to further enhance your WPF portlets. You also created an example portlet that demonstrates some of these techniques and that displayed a table of employee key performance indicators. Chapter 14, “Error Handling, Logging, and Debugging,” discusses how to enhance your WPF development with builders that provide error handling, logging, and debugging capabilities.
Important Points • Most builders that can run an action from a UI component contain a Post-Action Behavior section, which can be used to enable partial page refreshing for a UI control. • The Ajax Region builder can be used to turn on partial page refreshing between certain tags on the page. Sections of the page that contain portal URLs cannot be displayed using Ajax because Ajax code essentially runs outside the context of the portal.
370
Chapter 13 Using Ajax and Dojo
• The Ajax Type Ahead builder can be used to prompt users with several suggestions for a text input. • The Dojo JavaScript toolkit can be used in WPF by enabling the Dojo Extension feature set. Adding a Dojo Enable builder call to your model lets you access any of the Dojo widgets in the Dojo 0.4 library. • The Dojo Tooltip builder enables you to pop up context-sensitive text or actions at a particular page control. • The Dojo Inline Edit builder adds an edit control inside a data page field, which is useful for inline editing of table columns.
C
H A P T E R
1 4
Error Handling, Logging, and Debugging Portlets
Error handling, logging, and debugging are important aspects of the portlet development process, and can increase the reliability and overall quality of your applications. This chapter explains how to work these elements into your WebSphere Portlet Factory (WPF) applications, using a combination of Eclipse, log4j, and the default WPF builders. By the end of this chapter, you will have a good understanding of the different error handling, debugging, and logging techniques available in WPF. You will also create a sample application that applies some of these techniques to make the application more robust. The files discussed in this chapter are available for download from ibmpressbooks.com/ title/9780137134465 under the Chapter 14 folder (instructions for copying these files into your project are included in a readme.txt file in the same folder); however, to increase your understanding of the topics discussed, it is recommended that you create these files yourself by following the example in this chapter. Note that this chapter does not discuss non-WPF techniques of error handling, logging, or debugging (such as JUnit). The following topics are covered in this chapter: • Error handling • Debugging • Logging
Error Handling Typically, there are two types of errors that can occur in a WPF application: errors that occur at compile time and errors (or exceptions) that occur at runtime (note that the word ‘runtime’ is used broadly here to mean an error that occurs when the application is run, and does not specifically
371
372
Chapter 14
Error Handling, Logging, and Debugging Portlets
refer to the collection of Java exceptions that inherit from the RuntimeException class). Compile time errors show up in the Problems view (marked with an icon) and should be addressed before deploying your application, as models with compile-time errors may behave unpredictably when run. There are some (albeit rare) scenarios in which you may decide not to address compile-time errors (for example, when your model accesses a JAR file that doesn’t exist on the compile time classpath), but as a best practice, you should always eliminate errors before running your application (in the case of the example just mentioned, then, you might add the JAR to the classpath used in the WPF Designer, and then exclude it from being deployed so that it doesn’t conflict with the same JAR on the portal server). In trying to handle scenarios where errors occur after you have executed your application, the main technique used in Java development is the try/catch mechanism. A basic try/catch block looks similar to the following: try { //some code that might cause an exception to be raised } catch (SomeExceptionClass e) { //code to handle exception }
The code in the try block will run, and if an exception is raised for which there is a catch block (such as the SomeExceptionClass exception), then the code in the catch block runs. If there is no block to catch the exception, then it is thrown back to the calling method to handle (for more information on the try/catch mechanism, see the Java exception-handling tutorial at java.sun. com/docs/books/tutorial/essential/exceptions/handling.html). In WPF, you can use the try/catch mechanism in your own code, regardless of whether it is contained in a Linked Java Object (LJO) or Method builder. However, a few points are worth noting about how these exceptions can be thrown to calling methods in WPF: • You cannot use checked exceptions in a Method builder. Checked exceptions are Java exceptions that extend the java.lang.Exception class, and are contrasted with runtime exceptions, which extend the java.lang.RuntimeException class. Checked exceptions are also declared in the method header, and they document the exceptions that are thrown back to the calling class. If you want to use checked exceptions in your code (and there aren’t many people who do!), you should include your Java code in LJOs rather than Method builders. • You can throw exceptions from a Method builder (using the throw command), and then catch these exceptions using the Error Handler builder. The Error Handler builder automatically adds try/catch blocks to certain areas of your application depending on how you configure the builder.
Error Handling
373
• Exceptions can also be thrown from an LJO back to a WPF Method. That is, if you access an LJO from a Method and an exception is generated in that LJO, and then an attempt is made to throw the exception using the throw command, the exception is passed back to the Method builder. If the exception is not caught by the Method builder, you can still use an Error Handler builder to catch the exception, in the same way that you would use it with the Method builder. The example in this section first demonstrates how to use the try/catch block in a Method builder. Later in this section, another exception is thrown to demonstrate how to catch exceptions using an Error Handler builder, which displays a user-friendly error page to the end user. The example is fairly simple: its only purpose is to divide a number (the dividend) by another number (the divisor) and display the result to the screen, but it can be used to quickly and easily demonstrate all of the techniques in this chapter. If you prefer, you can use a model from a previous chapter, however, the steps in this chapter will refer to the division example just described. Before you proceed with this section, you need a WPF project to house the model that performs the division. If you have a project from a previous chapter, you can use it; otherwise, you should create a new WPF project (for more information on creating projects see Chapter 1, “Introduction to WebSphere Portlet Factory”). The project is deployed to a local application server for testing (you can use the portal server if it runs on your local machine; otherwise, it is recommended you use the IBM WebSphere Application Server Community Edition server [WAS CE] that comes with WPF). Note that there is no need to deploy this application as a portlet because what is important in this chapter is not so much the application, but the application of certain techniques to the development process. After you have a project set up, you need to add the model to perform the division, and implement the appropriate error-handling techniques. In this section, you use the following builders to provide its functionality: • Action List (x2) • Page (x2) • Variable (x4) • Method • Text • Error Handler
Creating a Model Create a new model called division in your project, under the folder WEB-INF/models/chapter14. The model should be based on the Main and Page template, using a Page Type of Simple Page. For more information on creating models, see the example in Chapter 1.
374
Chapter 14 Error Handling, Logging, and Debugging Portlets
After it is created, your model should have two builders in it: an Action List called main and a Page called page1. The Action List runs automatically whenever the model is run and opens the page1 Page.
Modifying the Results Page Open the Page builder, and change the contents of the Page Contents (HTML) input to the HTML listed in the following:
The dividend divided by the divisor is: <span name=”result”>
This page contains a single span tag called result. A Text builder is used later to display the result of the division at this tag. Save the builder call when you are finished.
Adding Division Variables The next step is to add variables for the dividend, divisor, and result. You can do this with three Variable builders. Add three Variable builder calls to your model (you can add a Variable builder call from the Builder Palette, which you open by clicking the icon in Outline view) and specifying each variable as type Integer. The first builder call should be called dividend with an Initial Value of 10, the second should be called divisor with an Initial Value of 5, and the third should be called result (you can leave the Initial Value for the result Variable blank, as it will be overwritten later). Save the model when you’ve added all three builder calls.
Defining the Division Process Next, you need a method to perform the actual division. Add a Method builder call to the model and name it division, and then change the Return Type to void as there is no need to return any values from the method. Change the Method Body so that it displays as shown in the following: { try { //divide dividend by divisor, and store result in ‘result’ ¯variable webAppAccess.getVariables().setInt(“result”, webAppAccess.getVariables().getInt(“dividend”) / ¯webAppAccess.getVariables().getInt(“divisor”));
Error Handling
375 } catch (java.lang.ArithmeticException e) }
{
}
Note that the division itself is enclosed in a try block. The JVM attempts to divide the dividend Variable by the divisor Variable, and place the result in the result Variable. If an ArithmeticException is raised (as happens, for example, when the divisor is zero), the exception is caught by the catch block. The catch block doesn’t do any further processing, but it prevents a nonuser-friendly error message from displaying to the screen. Later in this chapter, you modify the catch block to throw an additional exception, which you then catch using the Error Handler builder. Save the builder call when you’ve finished editing it. To run the division Method, open the main Action List and insert an action before the page1 action to run the division Method. You can do this by right-clicking on the page1 action and selecting Insert to insert an action, and then selecting Methods\division for the first action using the Select Action dialog (which you can open by clicking the ellipsis icon next to the first action). Save the builder call when you are finished.
Displaying the Result Add a Text builder call to the model called displayResult. This builder call places the value of the result Variable onto the page1 page. Set the Page input to page1 and the Tag input to result to configure the correct location for the result, and then select the result variable for the Text input (it displays in the input as ${Variables/result}). Save the builder when you are finished.
Running a Preliminary Test Run a quick test of the division model from the WPF Designer, which you can do by clicking the icon on the toolbar. This runs the currently active model (for instance, division) with the last run configuration you used. If you have not set up a run configuration before, you are prompted to do so—create a new configuration under the WebSphere Portlet Factory Model category (if you want more information on setting up a run configuration, see the “Testing the Application” section in Chapter 1). Your default Web browser should open, and the following message should be displayed: The dividend divided by the divisor is: 2
This result is expected, as 10 divided by 5 is 2. Now change the Initial Value input for the divisor to 0 so that the division Method attempts to divide 10 by 0. Run the model again, and you should see the following message displayed in the browser: The dividend divided by the divisor is:
Because the result of dividing any number by zero is undefined, an ArithmeticException was raised when you tried to run the division Method. This exception was then caught by the
376
Chapter 14 Error Handling, Logging, and Debugging Portlets
catch block in your code, so as far as the JVM is concerned, the ArithmeticException was handled (however, no result was assigned to the result Variable). Notice that if you comment out the try and catch blocks in the division method so that only the lines that perform the actual division remain (as well as the curly braces that surround the entire method), the exception is not handled and the error shown in Figure 14.1 is displayed to the screen. You can click the ‘Click here for a detailed error message’ link on this screen to see the exception message and the full stack trace.
Figure 14.1
An unhandled exception displayed on the screen.
Creating a Custom Exception To demonstrate the use of a custom exception and show how to catch an exception using the Error Handler builder, you need to create a custom Java exception to throw from the division Method. You don’t need to throw custom exceptions to use the Error Handler builder (you can use any type of exception), but custom exceptions are useful in that they enable you to cater to an exception that is thrown only when you explicitly throw it, which enables you to describe and define the nature of the exception itself. In this example, you create a custom exception called MyException, which is a catch-all exception for WPF processes that can’t be handled. A MyException is thrown only when you explicitly call the ‘throw new MyException();’ command in a Java Method. Create a Java source file for the MyException class by selecting File, New, Class in the WPF Designer. Specify com.ibm as the package (substituting your own company name for ibm) and MyException as the name (note that the package com.ibm is used throughout this chapter, but in each case, you should substitute your own company name for ibm). Also, change the Superclass of the class to java.lang.RuntimeException. Press Finish to create the custom exception class. After the class is created, it will automatically open in the WPF Designer. Edit the file so that it contains a constructor method, as shown in the following: package com.ibm; public class MyException extends java.lang.RuntimeException { public MyException(java.lang.Exception e) { } }
Error Handling
377
The constructor MyException executes whenever the MyException is thrown, and receives an Exception object as an argument. In the “Debugging” section of this chapter, you add some code to the constructor to display a debugging message whenever the MyException is thrown. Save the file when you are finished.
TIP Depending on how you have configured the WPF Designer, you may get a warning when the MyException class is open. This indicates that you haven’t declared a serialVersionUID field. In the current example, this warning can be disregarded. For more information on this warning, see the example in Chapter 8, “Using Java in Portlets.”
Throwing the Custom Exception To throw the MyException, edit the catch block in your division Method so that it displays as follows: catch (java.lang.ArithmeticException e){ //throw MyException back to Error Handler builder throw new MyException(e); }
This throws a MyException, passing the ArithmeticException e as an argument. The argument is passed so that information on the ArithmeticException is accessible in the MyException class, which will enable you to process the exception further if desired. Before you save the method, you also need to expand the Import List section of the builder call, and modify the Input List input so that it contains an entry for com.ibm.MyException (substituting your own company name for ibm). If you do not take this step, the MyException class is not recognized. Alternatively, you can specify the full package name when you throw the MyException, but this can make your code more difficult to read. Save the method when you are finished. If you test your application now, an ArithmeticException is raised in the try block (because you are trying to divide by zero), which is then caught and a MyException is thrown. Because you aren’t handling the MyException, a fairly nonuser-friendly message is displayed on the screen, so the next step is to display a different error page.
Adding an Error Page To add a more user-friendly message whenever the MyException occurs, add a Page builder call to the model and name it errorPage. Change the Page Contents (HTML) input so that it reads as follows:
378
Chapter 14 Error Handling, Logging, and Debugging Portlets
Error Page
An error occurred while loading this page. Please ¯contact your system administrator.
Save the builder call when you are finished.
Adding an Error Flag The next step is to add a variable that flags whether the MyException has occurred. This is useful because you can then add an if-then statement to the main Action List, which checks the value of the flag, and displays only the page1 page when no MyException has occurred. If you don’t do this, page1 is displayed regardless of whether there is an exception (in the event that there is no exception, page1 executes as per normal; in the event that there is an exception, the JVM considers it handled by the catch block created by your error handler, and passes control back to the main Action List to display page1). Add a Variable builder call to the model, and name it MyExceptionFlag. Change the Type of the Variable to Integer, and then set the Initial Value to 0, and save the builder call. After you have done this, open the main Action List and encapsulate the page1 action in an if-then block, which results in the page1 action only being run when the value of MyExceptionFlag is 0. To do this, insert an action above page1 and open the Select Action dialog for the new action by pressing the ellipsis icon to the far right of the new row. From the Select Action dialog, select ‘if’ from under the Special, Conditional category and press OK. You will see a dialog asking you to enter details for the if statement—specify the details as shown in Figure 14.2, and then press OK. The statement should then display in the Actions input as ‘!IF (${Variables/MyExceptionFlag} == 0) THEN’. To close the if block, add another action at the end of the Actions list, selecting endif from under the Special, Conditional category, and then press OK. The action displays in the Actions input as !ENDIF. Save the builder call after you have added both of these actions.
Error Handling
Figure 14.2
379
Configuring the if-then statement.
Adding an Error Action The next change to make is to define the sequence of actions to execute when the MyException is caught. The sequence of actions is to first assign a value of 1 to the MyExceptionFlag (to indicate that an exception has occurred and to prevent page1 from displaying), and then to display the errorPage you created earlier. To do this, add an Action List builder call and name it catchMyException. For the first action in the Actions input, select Assignment from under the Special category in the Select Action dialog, and then specify the MyException variable as the Target (it displays as Variables/MyExceptionFlag) and specify 1 as the source. Press OK to add the action, which should display in the Actions list as ‘Assignment!Variables/MyExceptionFlag=1’. For the second action, select the name of the error page (errorPage) and save the builder call.
Adding an Error Handler The final step in the error-handling process is to tie everything together; try to run the division Method, catch the MyException if it occurs, and then run the catchMyException Action List when the exception is caught. To do this, add an Error Handler builder call and configure it as shown in Figure 14.3. An important point to be aware of with this builder is that it is possible to use it to either catch exceptions thrown by a specific action (as you have done with the division Method in the example) or catch all exceptions thrown by a model. You can toggle this setting by changing the value of the Exceptions input. If you want to have a generic error page displaying whenever an exception is thrown and not recovered from, then you might opt for catching all unhandled exceptions (however, when doing this, be aware of the exceptions that you’re catching, and make sure you are handling them appropriately). Save the builder call when you are finished.
380
Figure 14.3
Chapter 14
Error Handling, Logging, and Debugging Portlets
Configuring the Error Handler builder.
Testing the Division Model
If you test the division model now, you can see that the exception is caught and the errorPage displays accordingly, as shown in Figure 14.4. If you change the value of the divisor Variable to an integer other than zero, you will see that the original result message displays instead.
Figure 14.4
The error page.
At this point, you are throwing, catching, and handling the ArithmeticExceptions in the division model, but there is little in the way of feedback to help you assess exactly what your application is doing when it runs. The next two sections discuss how to obtain such feedback by extending the current application using WPF debugging and logging techniques. Debugging
There are numerous ways to debug your applications in WPF. The first and probably quickest way is to look for errors and warnings showing up in the Problems view of the WPF Designer (warnings appear with an icon next to them). Although warnings don’t necessarily prevent your application from running, they can help to explain runtime errors, and you should at least be aware of why each warning is displayed. After you have addressed the warnings in your application, it is useful to add debugging statements into your code so that you can get feedback on what your application is doing as it runs. The example that follows adds a few debugging statements to the division model and explains how to view the results.
Debugging
381
TIP If you use Rational Application Developer or Rational Software Architect for your IDE or you use an extended version of Eclipse with the Web Tools Project tools, there may be a set of validators turned on that can generate numerous warnings. The JSP, HTML, and WSDL validators in those tools are meant for developers hand-coding those artifacts, and the validators get confused by the WPF-generated pages and Property Broker-based WSDL files. It is best to turn these validators off in WPF projects, so that you can see the actual warnings that pertain to your application models (you can do this by filtering the Problems view or by configuring the WPF Designer preferences).
Debugging Statements Open the main Action List and insert an action at the top of the Actions input, and then open the Select Action dialog for this action. Select Special, SystemOut in the Select Action dialog. In the prompt that follows, specify debugging information such as Division application starting..., and then press OK. Add a similar Action at the end of the Actions input, but specify the debug text as Application finished successfully. When you test this application in a moment, you will see how to access this debugging information. Open the division Method, and insert the following lines immediately below the comment in the try block: System.out.println(“Attempting to divide dividend by ¯divisor”); System.out.println(“Value of divisor: “ + ¯webAppAccess.getVariables().getInt(“divisor”));
As before, this also causes particular debugging messages to be printed when your application runs. Save the builder call when you are finished.
TIP You can print the contents of variables (including XML variables) simply by specifying them as the argument to a SystemOut or System.out function.
Before you debug the model, change the value of the divisor Variable to a nonzero integer, and save the model. Run the application from the WPF Designer, and then open the System.out file on the application server specified in your application server deployment configuration (this file is normally located under the logs directory of your application server). Note that if you are using WAS CE, the file is actually called server-log4j.properties and is found under the var/log directory of WAS CE (it also displays in a console window if you use one).
382
Chapter 14
Error Handling, Logging, and Debugging Portlets
Scroll to the most recent messages in the file, and you should see all of the messages specified in the main Action List display in sequence. The exact format of these messages differs depending on the application server version you use, and also on how your application server is configured. As an example, in a default WebSphere Portal Server 6.0 installation, the message displaying the value of the divisor displays similarly to the following: [10/03/08 21:56:29:688 EST] 00000060 SystemOut
O Value of divisor: 1
TIP
If you use SystemOut or System.Out statements in your code, be sure to remove them when you are done with them. This improves the performance of your applications and it also helps to un-clutter your log files.
Next, open the MyException Java class and add the following line into the MyException constructor: System.out.println(“A MyException occurred: “ + e.getMessage());
This prints a debugging message to the console whenever the exception is thrown. The message incorporates the error message taken from the ArithmeticException e. Save the file when you are finished, and then change the value of the divisor Variable to 0 so that the MyException is thrown. Save the model and run the model again. Among your other debugging messages, you should now also see a debugging message from the MyException class, similar to that shown in the following: [8/04/08 20:31:52:922 EST] 0000004d SystemOut occurred: / by zero
O A MyException
Using Eclipse Debugging
Another common way to debug WPF applications is to use the debugging capabilities of the IDE. These capabilities enable you to step through your code (including the automatically generated WPF code) one line at a time. To set this up, you need to configure debugging on both the application server and in the WPF Designer.
TIP
When debugging, it is often helpful to look at the generated code for your application in the WebApp Tree tab of the Model Editor. This can tell you exactly what WPF generates as a result of the inputs in your builders.
Debugging
383
Configuring the Server Before you can debug your application, you need to start the debugging service on the application server. The exact way that you do this differs depending on the application server and version you use, so you should consult your server’s documentation for specific instructions. As a starting point, if you use a WebSphere Portal 6.0 server, you can find the setting on the Admin console under the Servers, Application Servers, WebSphere_Portal, Debugging Service category (there is a checkbox called Startup that you need to enable). For WAS CE, you can specify jpda start as the first argument to the geronimo script (this process is discussed under the section Reference, Commands, Geronimo of the version 1 WAS CE user documentation).
Configuring the WPF Designer After you enable debugging on the server, you need to configure the WPF Designer for debugging as well. To do this, first open the Project Properties dialog by selecting Properties from the Project menu in your IDE. Then, open the Java Build Path section and switch to the Source tab. Click the Add Folder button, and add the WebContent/WEB-INF/factory/generated folder as a Source directory for your project. This directory contains the automatically generated Java code in your application, and after you’ve added it, you can debug WPF-generated code and your own code (stored in the WebContent/work/source directory). Accept your changes and close the Project Properties dialog. Next, you need to create a Debug configuration. To do this, open the Debug Configuration dialog by clicking Run, Debug. The dialog shown in Figure 14.5 should display. After the dialog is open, select Remote Java Application from the tree list and press the New button to create a new configuration. Give the configuration a name (such as WPF debug configuration). Some settings will display in the right pane; confirm that these settings are correct (in particular, check that the hostname and port are correct). Switch to the Source tab of the dialog, and press the Add button to add a source lookup path. Click Workspace Folder, and then select the generated source folder in your project (WebContent/WEB-INF/factory/generated) and press OK. The generated folder should display in the dialog as shown in Figure 14.6. Press Close to save the configuration when you are finished.
TIP Temporarily disabling builders in a model can help you isolate problems in your application.
384
Chapter 14
Error Handling, Logging, and Debugging Portlets
Figure 14.5
The Debug Configuration dialog.
Figure 14.6
Adding the generated folder to the debug configuration.
Debugging Your Model
To debug your model, switch to the Debugging perspective in the WPF Designer (which you do by selecting Window, Open Perspective, Debugging), and then click the icon to run the last debug configuration you used (you can choose the debug configuration you want by selecting Run from the Debug menu). Your model runs in debug mode, which means that you can step
Logging
385
through it as you would any other Java application (this includes setting breakpoints in the generated code). The next section of this chapter discusses some additional debugging techniques that use the logging capabilities of WPF.
Logging Another way to debug your WPF applications is to log points of interest in them while they run. In WPF, this is achieved with log4j logging, which is a commonly used Java logging framework. Before you proceed with the example in this section, there are a few points that you should understand about how logging works in WPF. The first thing you need to know is the location of the logs themselves. WPF log files are stored under the WEB-INF/logs directory in your deployed applications (don’t use the WebContent/WEB-INF/logs directory in the WPF Designer because the log files there are not necessarily updated when you run the application). Different log files are used to log different aspects of your application, and several of these files are introduced in the example later in this section. The other important point is that you can configure your logging settings in the WebContent/WEB-INF/config/log4j.properties file. This file contains settings for how, where, and when WPF logs information. It also contains a set of logging properties that you can change to determine what WPF is actually logging. Each of these logging properties can be configured to log different levels of information. The default logging levels used in WPF are as follows: 1. DEBUG 2. INFO 3. WARN 4. ERROR 5. FATAL The lowest of these levels (DEBUG) provides the most amount of debugging information, and subsequent levels provide progressively less information until the highest level (FATAL), which logs messages only when an error occurs that can’t be recovered from. These levels prevent you from cluttering up your logs unnecessarily, and in some cases, higher levels can also help to improve system performance. Later in this chapter, you will see how to use these levels to log the division model. For information on some of the more useful logging properties in WPF, see Appendix B “Portlet Factory Properties.”
Debug Tracing The first logging technique you use for the division model is debug tracing, which you can add via the Debug Tracing builder. The Debug Tracing builder enables you to trace actions in your application, and log various types of information about those actions, such as variable values and HTTP request parameters. By default, this information is written to the debugTacing.txt log file in your application’s logs directory.
386
Chapter 14
Error Handling, Logging, and Debugging Portlets
TIP
Before you use the Debug Tracing builder, set the level in the bowstreet.system.debugTracing property to DEBUG. When this property is set to DEBUG, it provides more information, such as session ID and profile statistics. To improve performance, set the property to a higher level after you have finished debugging.
Add a Debug Tracing builder call to the division model and name it traceExceptionFlag. You use this builder to log the value of the MyExceptionFlag Variable. Enable the ‘Trace all actions’ checkbox so that all actions in your model are logged. To define the specific categories that you want to trace, you need to edit the log4j.properties file, but before you do this, change the value of the ‘Additional debug input’ input to the MyExceptionFlag Variable, so that you can track the value of this variable as it changes (after you’ve selected it, it displays in the input as ${Variables/MyExceptionFlag}). Notice that there is an input on the Debug Tracing builder to Log parameters, which logs HTTP request parameters for your application. This setting is useful if you want to see the fields that are sent when forms are submitted. Leave this box unchecked, as you are not submitting anything in the current example, and save the builder call when you are finished. Run the division model from your IDE, and after it has run, open the debugTracing.txt file in the logs directory of your deployed application (on the server specified in your application server deployment configuration). You should see output similar to the following: *-- TIME: [2008-04-10 10:46:24,438] --* Category: bowstreet.system.debugTracing Priority: INFO Thread: WebContainer : 0 Msg: Debug Tracing Session: uoiZFaqhpsH29-i1dysbsSY Model: chapter14/division Stack Trace: 234 672 Method: main ${Variables/WPFExceptionFlag} = 0 47 47 .Method: division ${Variables/WPFExceptionFlag} = 0 203 203 .Page: page1 ${Variables/WPFExceptionFlag} = 0
From this output, you can see the sequence of actions in your model as they run and how long each action takes in milliseconds (the first column is the amount of time spent in each action, and the second column is the amount of time in that action and all subactions called by that action). In the previous example, the main action took less than 672 milliseconds in total to run (but only 234 miliseconds were spent inside the main action), and the division method took less
Logging
387
than 47 milliseconds. Notice also that the value of the MyExceptionFlag is logged as each action is executed. Next, open the log4j.properties file in the WPF Designer. The logging properties in this file have a particular level assigned (in most cases, this is WARN, by default, which means that virtually no debugging information will be logged) and an appender that defines additional output locations (such as the console). Most properties have a default appender with a similar name to the property itself, which defines default settings for that property. See Appendix B for a list of some of the more useful properties in the log4j.properties file. Change the log4j.logger.bowstreet.system.modelActions property to DEBUG,ModelActions. This property notifies you of the actions that are running in your model and how long each action takes to execute. Save the log4j.properties file when you are finished. Run the model again, and then reopen the debugTracing.txt file. At the end of the file, you should see output similar to the following: *-- TIME: [2008-04-10 10:47:00,156] --* Category: bowstreet.system.debugTracing Priority: INFO Thread: WebContainer : 0 Msg: Debug Tracing Session: GRrAVkOj6jztWbzkbTgBVvX Model: chapter14/division Stack Trace: Start Request: WebAppRunner.doRequest 0 0 [chapter14/division] 0 47 Instantiate: chapter14/division 31 31 .Regen: No profile 16 16 .Compile: Methods Class 0 0 Method: main ${Variables/MyExceptionFlag} = 0 0 0 .Method: division ${Variables/MyExceptionFlag} = 0
Several new actions have now been added to the log, including actions to regenerate your model and compile your code. Note that the value of the MyExceptionFlag is not logged until the main method executes. This is because it doesn’t exist before then and there are some actions that have zeroes next to them (these actions took less than 1 millisecond to run). Additionally, note that if you look in the logs directory of your deployed application, the logging messages for model actions are written to the modelActions.txt file. Spend some time experimenting with some of the different log4j properties, checking the logs directory as you go. For a list of some of the more useful properties, see Appendix B. Note that before you start experimenting with properties files, it is a good idea to back them up first, so that it is easier to revert to the original versions later if required (although you can always copy the properties files you need from a new WPF project).
388
Chapter 14 Error Handling, Logging, and Debugging Portlets
Using a Custom Logging Category Custom logging categories can be used to log events that don’t properly fit under any of the other log4j logging categories. For example, to log MyExceptions in the division model, add a Logging Category builder call to the model and name it MyExceptionLogger. Specify the Log4j Category name as bowstreet.application.MyException, which defines how the message is labeled in the logs (the bowstreet.application prefix is usually employed to name logging categories). Change the Associated Appenders input to UserLog, as the default setting will not write the output to a log file. Save the builder call to complete the configuration of your custom logging category. To use the logging category, you need to log a message against the new logging category at the desired log4j level. Open the catchMyException Action List and then open the Select Action dialog for a new action at the bottom of the Actions input. Select the warn method for the new category from the bottom of the Methods\MyExceptionLogger category (just above the Pages category, as shown in Figure 14.7), and then specify the message on the resulting dialog as MyException occurred. Check value for divisor. Save the builder call when you are finished.
Figure 14.7
Logging a new debug message.
To test the changes, make sure the divisor Variable is 0 (so that a MyException is thrown), and run the model. Open the userLog.txt log file in your logs directory, and you should see an entry similar to the following:
Logging
389
*-- TIME: Category: Priority: Thread: Msg:
[2008-03-12 11:45:03,062] --* bowstreet.application.MyException WARN WebContainer : 1 MyException occurred. Check value for divisor.
Note that you can toggle custom logging messages on or off by setting the appropriate levels for them in your log4j.properties file. For example, if you create a MyException category property in your properties file as follows, the previous logging message would not be written to the logs because only messages with a level of ERROR or higher are logged: log4j.logger.bowstreet.application.MyException=ERROR,UserLog
Note that you need to add the prefix ‘log4j.logger’ before the property name. Also, be aware that the appender specified in the log4j.properties file overrides any appender specified in your Logging Category builder call.
TIP Whenever you upgrade a WPF project, you are prompted about whether you want to overwrite the log4j.properties file with the default properties file. If you have made changes to your properties file that you want to keep, you should back up these properties and select Yes when you receive the upgrade prompt (so that you receive WPF product updates).You can add your changes back in after WPF has finished the update.
Server Stats Server stats give you various metrics about your application, which you can configure via the log4j.properties file. You should always leave server stats set to at least INFO because this setting returns useful information about your application, and it does not affect performance. See Appendix B for a list of useful properties you can use to configure server stats. By default, server stats are enabled and run at five-minute intervals, so after you run your application, the appropriate metrics are logged to the serverStats.txt file every five minutes. Open this file now and you should see output similar to the following (for the default level of INFO): *-- TIME: [2008-04-10 10:57:14,734] --* Category: bowstreet.system.server.logging.serverStats.default Priority: INFO Thread: ServerStatsThread Msg: Sessions: 1 RestoredSessions: 0 ModelCacheRegenEntries: 1
Notice that this log enables you to monitor how effectively your application is utilizing system resources, and it includes various metrics for monitoring memory use and cache hits/misses. The bold section shown in the output shows the number of WebApp requests in the last log period. Notice that there is one point in the application where some latency has been recorded: 3,211 milliseconds in the division Method. These statistics can help you pinpoint problem areas and determine which methods take longer than they should. In this case, the latency is low and may have been caused by a temporary spike in activity on the server; it is likely that the next time the model is run, there will be no latency at all.
TIP
Some builders (such as the SQL Call builder) have built-in logging capabilities. To find out which builders in your model have these capabilities (and to toggle them on or off), use the Outline view in the WPF Designer. From the Outline view, press the Icon, and then select Debug Inputs. The resulting popup window lists all of the builders in your model that use built-in logging and enables you to turn them on or off.
Important Points
391
Summary In this chapter, you learned how to use the error handling, debugging, and logging capabilities of WPF to improve the quality of your WPF applications. You learned about the use of the Error Handler, Debug Tracing, and Logging Category builders, and you built a sample application using these builders. You also learned about the use of log4j in WPF. Chapter 15, “Performance and Process Optimization,” discusses the different ways in which you can optimize the speed and architecture of your applications, and it includes a section on creating your own builders. You can find more information about the techniques discussed in this chapter in the WPF product help, as well as in the WPF Best Practices Wiki page on the Web.
Important Points • Error handling can be implemented in WPF with the use of try/catch blocks. This can be done either in Java or in the Error Handler builder. • You can debug Java code using System.Out statements. Similarly, you can use the SystemOut command to debug Action Lists. • You can step through the execution of your code line by line using the Eclipse debugger. To use this feature, you need to start the debugging service on the application server and then configure Eclipse for debugging. • WPF uses log4j logging to enhance its debugging capabilities. These logs are stored in the WebContent/WEB-INF/logs directory of your deployed application, and they are configured in the log4j.properties file. • The Debug Tracing builder enables you to debug tracing in your model, which logs various metrics to the debugTracing.txt file. You can configure various categories for debug tracing in the log4j.properties file. • The Logging Category builder enables you to define custom logging categories, which are useful for when you want to log messages that don’t easily come under any of the existing logging categories. • Server stats give you useful metrics on your application’s performance. The INFO setting for server stats enables you to log useful performance information without affecting system performance.
This page intentionally left blank
C
H A P T E R
1 5
Performance and Process Optimization
This chapter outlines some of the ways in which you can improve the speed, efficiency, and architecture of your portlet applications. A number of techniques are discussed: caching, data set size, builder calls, session size, profiling, Ajax, and Dojo. Also, an example at the end of the chapter demonstrates how to optimize the portlet development process by creating your own builder and new model wizard. By the end of the chapter, you will be aware of a family of techniques that should help you improve your portlets in a variety of different environments and conditions. Each of the files in this chapter is available for download from ibmpressbooks.com/title/ 9780137134465 under the Chapter 15 folder (instructions for copying these files into your project are included in a readme.txt file in the same folder); however, to increase your understanding of the topics discussed, it is recommended that you create these files yourself by following the example in this chapter. The following topics are covered in this chapter: • Performance • Custom builders • Creating a new model wizard
Performance This section discusses some common ways to improve performance in your WebSphere Portlet Factory (WPF) applications. Many of these suggestions do not apply in every scenario, so if you have a particular WPF application in mind—or a particular development scenario—feel free to skip to the sections more relevant to your requirements.
393
394
Chapter 15 Performance and Process Optimization
Caching You can cache the output of specific actions in WPF by using the Cache Control builder. This builder is useful in scenarios where you have many users performing the same actions that produce the same results. Where the results are different for each user, you need to rerun the action each time. If the results change over time but are the same for every user, then you should still consider using a Cache Control builder. In this scenario, you need to set the builder’s ‘Refresh Interval (secs)’ input so that the results from the builder are suitably up to date (this will, of course, be different in every scenario). In some scenarios, even a slight inaccuracy in the method’s output is unacceptable, so in these cases you should not use the Cache Control builder. Several of the other WPF builders also have built-in caching features. For example, the Service Operation builder has a section called Result Caching that caches the results of the operation (this actually does the same thing as a Cache Control builder that points to the service operation call). Similarly, the Lookup Table builder has a ‘Refresh interval (secs)’ input that lets you configure when the contents of the Lookup Table should be updated. There is no difference in the caching used between these builders; however, in some cases, you may find it more convenient to use the built-in caching of certain builders instead of a Cache Control builder (for example, if you already have a Service Operation builder, you can cache its results directly from the builder call without having to add a Cache Control builder). Having said this, if you are making extensive use of caching, you might want to use Cache Control builders so that you can easily see where all of your model’s caching settings are.
TIP The log4j.logger.bowstreet.system.server.logging.serverStats property is particularly good for analyzing the use of caching in your application. By default, the output of this property is written to the serverStats.txt file in the logs directory of your deployed application every five minutes. This output also provides various types of information about the performance of your application, such as performance by model action. For more information on server stats, see Chapter 14, “Error Handling, Logging, and Debugging.”
A caveat worth noting is that cached data is profile-specific, which means that every different profile-model combination has its own cache. This probably won’t be a problem in most scenarios, but you should at least be aware that as the number of different profile-model combinations in your application increases, so do the corresponding memory requirements. Because of this, you should try to cache non-profiled models instead of profiled models wherever possible (although the degree to which you can do this depends on the requirements of your application).
Performance
395
Data Set Size Retrieving large data sets can be slow and memory-intensive, particularly if the data isn’t cached and you have a lot of concurrent requests. Of course, if the performance of data retrieval is an issue, you can (and should) use the Cache Control builder on your data retrieval methods, but there are some other steps you can take to make your data source calls smarter. For example, you can increase the performance of data retrieval operations by reducing the size of the data sets returned (such as by only returning customers relevant to the current user, rather than returning an entire database of customers). In other words, any queries to your data should be as restrictive as possible, and you should design your interfaces in such a way that users don’t have to sift through unwanted data. After you have done this, if you are still returning large data sets that have an impact on system performance, you should look at paging your data (pagination is covered in Chapter 5, “Customizing Portlet Appearance”). With pagination, you can opt to fetch your data in small chunks rather than all at once, which can greatly speed up your applications. For example, the SQL Statement builder has a Fetch Size input for this purpose, and the Domino Data Access builder has a Fetch Subsets of Data input for partial retrieval of Domino data. Alternatively, you can also write a custom data retriever in Java by extending the com.bowstreet.builders.webapp.methods.CustomDataRetriever class (an example using this approach is available on the Portlet Factory wiki page).
Builder Calls As a general rule, the more builders in your model, the more strain you put on system resources, and large models (containing 50 builders or more) can definitely slow down your applications and make them harder to maintain. To prevent this from happening, try to separate your builder calls into different models wherever possible, and use backend processes to replace builder functionality where appropriate. For example, it is better to run a backend Java method or Domino agent to finalize changes to a new record rather than run a WPF Method directly from the front end (which the user has to wait for). Keep in mind, also, that whenever WebApps regenerate in your project the builders need to run their generation routines, and the more of these that you have, the more of an effect it will have on performance. If you have problems with the execution or regeneration times of your models, you can use WPF’s debug tracing features to pinpoint problem areas. To do this, set the log4j.logger.bowstreet.system.modelActions property in the log4j.properties file to DEBUG level, and when you run the application, look at the corresponding metrics in the debugTracing.txt file (for more information on logging, see Chapter 14).
Session Size User session size can be a contributor to poor performance, but there are a number of things you can do to prevent session sizes from spiraling out of control. The first is to reduce the use of variables that store large values that differ for each user. If your variables actually refer to constants,
396
Chapter 15 Performance and Process Optimization
for example, you can mark your variables as ‘Shared (read-only)’, which prevents every user from having their own copy of the variable (of course, this is not always desirable). Also, consider marking variables as request scoped rather than session scoped, which prevents the variable from being stored in the session. This is useful when your variable data is not needed across more than request; however, if the data is the same across all users, you should use the ‘Shared (read-only)’ option instead. The effect of this is most noticeable when working with XML variables. Note that you should not use request scope for XML variables that are used in multiple requests if the XML is hard coded into the builder, as this can result in the creation of separate copies of the XML data, and these copies are initialized every time the variable is used. If the XML is not hard coded into the builder, it is useful to use request scope if the data is to be retrieved dynamically and discarded after each request. When designing WPF applications to reduce session size, you should also consider profiling. If you set user variables based on profiles, then you can share variables with all users in a given profile, rather than as session variables that differ for each user. For example, if admin users always have a particular set of parameters on a certain screen in your UI, you can set those parameters in variables marked as ‘Shared (read-only)’ with a value assigned by an admin profile. This still allows different users to have different settings, but the settings are based on profiles and shared across all users within each profile (for more information on profiling, read the “Profiling” section later in this chapter).
Session Tracing If you are concerned about session size in your application, you can enable session tracing to find out how big your sessions actually are. To do this, open the bowstreet.properties file and change the bowstreet.diagnostic.trace.enabled property to true. Then uncomment and set the bowstreet.diagnostic.trace.sessionSize.interval to 1. This causes session information to be logged to the sessionsize.csv file in the logs directory of your deployed application every second, so every request will log session information (provided that at least one of the session variables changes). You can leave the bowstreet.diagnostic.trace.sessionSize.userName property blank. Restart the application server and then run your application to kick off the trace, recreating any potentially high-load scenarios that you would like to test. A few caveats about session tracing should be noted: • Running session tracing for long periods of time can affect performance (particularly if your sessionSize.interval is set to log information every second), so you should disable session tracing after you have gathered the desired information. To disable session tracing, set the bowstreet.diagnostic.trace.enabled property in the bowstreet.properties file to false, and then restart the application server. • To ensure accurate metrics when running session tracing, it is important that only a single user accesses the traced application while the trace occurs.
Performance
397
After you have run your session tracing, check the contents of the sessionsize.csv file. The output is split into several columns, which are described in Table 15.1.
Table 15.1 Columns Used in the sessionsize.csv File Column Name Column Description Model
Model in which object occurs
Name
Name of object
Type
Data type of object (for example, java.lang.Integer)
Scope
Scope of object
Size
In memory size of object
StringSize
Size (in characters) of object when represented as a string
The Size metric is computed by counting the actual size of the object in memory, whereas the StringSize metric is computed by counting the number of characters returned by the toString method. Although these sizes are similar in many cases, there may be discrepancies for certain types of variables. In particular, in memory XML data is often much larger than the XML strings returned from the toString method (XML variables take up more space than String variables, so you shouldn’t use XML for simple single value strings). The Scope metric provides a code that corresponds to a particular variable scope. The possible variable scopes are included in Table 15.2. Note that only variables with a scope of 0 or 1 are stored in the user session, so these are the only variables you need to be concerned about when analyzing session size.
Table 15.2 Variable Scopes Listed in the sessionsize.csv File Scope Code Scope Description 0
Persisted for failover
1
In session, but not persisted for failover
2
Shared, read-only
3
Request scoped
398
Chapter 15 Performance and Process Optimization
TIP When caching a variable using the Cache Control builder or the built-in caching of a WPF builder, note that if you store the cached results in a variable, then the reference to the Java object is stored, not the object itself—so even though the variable will show up in traces as session scoped, the actual data will not be duplicated for each session (each session will hold a reference to the same shared Java object).
Profiling The “Caching” section of this chapter has already outlined some potential performance issues with regard to using caching inside profiled models, but when using profiling in your WPF applications, you should also keep in mind how the profiling mechanism works behind the scenes—it creates separate WebApps depending on the values in your profiles. Because some WebApps can be quite memory-intensive, a large number of profiles can put strain on system resources, so you should try to keep the number of profiles to a minimum. In particular, you should avoid creating separate WebApps for every different user setting, which you might inadvertently do if you have an Edit mode in your portlet and you have allowed users to edit their own profiles (you can add an Edit mode to your portlet from the Portlet Adapter builder). If you are allowing users to edit their profiles, you should set the Execution Time field on your Profile Entries to true, which differentiates runtime values from values that require a separate WebApp. For more information on profiling in general, see Chapter 1, “Introduction to WebSphere Portlet Factory,” and for an example using profiling, see Chapter 12, “Profiling Portlets.”
Using Ajax and Dojo Adding Ajax and Dojo to your portlets can improve performance and increase the usability and interactivity of your applications. For example, partially refreshing a page saves the entire portal page from reloading, when all you might want to do is kick off a server process or update a list on the screen. The main reason Ajax controls are used, however, is to increase interactivity, which they do by dynamically reacting to certain events (for example, a user clicking a button to open a Dojo dialog without reloading the page), which might otherwise be difficult or clumsy to implement without Ajax. Keep in mind, however, that in some cases Ajax controls can actually decrease performance. For example, if you add Dojo dialog boxes to a form, which in turn, open large and complex forms that take a while to load, you may find that the time taken to initially load the page noticeably increases. If you have several different dialog boxes on a single page, then you might find that the performance hit increases to an unacceptable level, and you may have to look at providing the same functionality by other means (for example, by using page tabs). When using Ajax and Dojo, then, it is a good idea to performance test your applications under the slowest conditions in which they will be used and determine whether the applications are still usable at that speed.
Custom Builders
399
Custom Builders Custom builders enable you to extend the core WPF builder set with builders that are more suited to the unique demands of your development environment. Custom builders can be used to do anything from add a UI control to a page, to add entire models or Java classes to your WPF projects. If you find yourself using the same set of builders over and over again, you may decide to collate these builders into a single, high-level builder that will automate the process of adding that set of builders (custom builders can be used to call and create other builders). You may also decide to create builders to introduce new functionality, such as a builder to automatically implement a security mechanism used in your organization. Custom builders benefit the development process in a number of ways: • They help to automate the development process. Of course, it is likely that you will not be able to automate every aspect of the development process, but there is certainly room for a custom builder whenever you are faced with tasks that need to be performed more than once. • They enable you to enforce architectural standards across a development team. For example, a custom builder can be used to create Java class skeletons or add UI controls in a particular way. • Custom builders have a wizard-based interface so they are easy to use, which reduces the skill level required to use them. This is the case when custom builders are used to write Java code. • Although there is an initial time commitment required to create a custom builder, after the builder is created, it is generally faster to use the custom builder than the methods that the custom builder replaces. As a result, custom builders can save developers a significant amount of time. • Development with custom builders is less error prone than traditional development methods, as the process is mostly automated (of course, this also depends on the quality and power of the builder). • Custom builders encourage reusability. The rest of this chapter walks you through the process of creating a custom builder and a custom new model wizard. Both of these are used to create an information portlet, which displays information for a portal belonging to a fictional company. The builder automatically adds a link to an HTML page, which opens a terms and conditions page. The terms and conditions page contains some default text, but developers are also able to configure this text from the builder. The page also contains a Back button to navigate back to the original page. After the portlet is complete, a new model wizard is created so that the information portlet is added as one of the possible options when users create a new model, showing you just how easy it is to automatically add large amounts of functionality with a new model wizard. A screenshot of the custom builder is shown later in this chapter in Figure 15.2, and a screenshot of the new model wizard is shown in Figure 15.6. The finished portlet is shown in Figure 15.5.
400
Chapter 15 Performance and Process Optimization
Before you proceed with this section, you need a WPF project to house the model for the information portlet. If you have a project from a previous chapter, you can use it; otherwise, you should create a new WPF project (for more information on creating projects, see Chapter 1). The project will be published as a WAR file and deployed to a portal server, and you should also deploy the application to a local application server for testing (you can use the portal server if it runs on your local machine; otherwise, it is recommended that you use the IBM WebSphere Application Server Community Edition server [WAS CE] that comes with WPF). After you have a project set up, you need to add the information model. This model uses the following builders to provide its functionality: • Action List • Page • Portlet Adapter • Terms & Conditions The Terms & Conditions builder doesn’t exist yet, but you will create it as you progress through this section.
Creating a Model Create a new model called information in your project, under the folder WEB-INF/models/ chapter15. The model should be based on the Main and Page template, using a Page Type of Simple Page. For more information on creating models, see the example in Chapter 1. After it is created, your model should have two builders in it: an Action List called main and a Page called page1. The Action List runs automatically whenever the model is run and opens the page1 Page.
Modifying the Page Open the Page builder, and change the contents of the Page Contents (HTML) input to the HTML listed here:
Welcome!
Welcome to the Acme portal. Use this portal to manage your account, get the latest information on our products, or browse our online store.
<span name=”terms”>
Custom Builders
401
This page displays a simple information message to the screen. The terms span tag is replaced with a link created by the Terms & Conditions builder. Save the builder call when you are finished.
Adding a Portlet Adapter Add a Portlet Adapter builder call to the model by selecting Portlet Adapter from the Builder Palette (you can open the Builder Palette by clicking the icon in Outline view) and pressing OK. This will surface the information model as a portlet on the portal server specified in your deployment configuration. Change the name of the Portlet Adapter to information, and then change the Portlet Title to Information. Also, change the Portlet Description to This portlet displays information on the portal. This builder surfaces your model to the portal server as a portlet called Information. Save the builder call when you are finished.
Creating a Terms & Conditions Builder The next step is to create the Terms & Conditions builder. Builders are actually comprised of XML definition files and Java code on the backend, but you don’t need to create these artifacts manually—in fact, you can use another builder (the Builder Skeleton) to do the work for you! Before you do this, you should create a new model to house the Builder Skeleton. You may want to recreate or reconfigure the builder later, and it is clearer if you do this from a dedicated builder creation model rather than from inside the information model.
TIP Builder definition files have a .bdef extension, and are stored in the WebContent/WEBINF/builders directory. Although you can manually edit these files, you can also create them automatically using the Builder Skeleton builder.
Add a new, empty model to your project under the WEB-INF/models/chapter15 folder, and call it builderTermsAndConditions. Next, add a Builder Skeleton builder to the model. After you configure this builder call and save it, WPF creates the necessary builder definition and class files based on your settings (or, it updates them if they already exist). Open the dropdown for the Builder Type input, and notice that there are four types of builders you can create (these types are briefly described in Table 15.3). In this section, you create a Page Control Builder because you want to be able to place content onto an HTML page. Select Page Control Builder for the Builder Type input, and notice that some of the other inputs in the builder (such as the Description) are automatically populated.
402
Table 15.3 Builder Types Builder Type
Chapter 15 Performance and Process Optimization
Builder Description
Basic Builder
This option creates a simple, ordinary builder.
Page Control Builder
Use this option for builders that insert elements onto a page, as the page location aspects of the builder will be automated.
Model-Based Builder
Use this option to create a custom builder based on profiled inputs in a model.
Model-Based Wizard Builder
This option creates an entry in the new model wizard that enables developers to automatically create models with a particular configuration.
Specify the ID for the builder as com.ibm.builders.TermsAndConditions. This ID uniquely identifies the builder and also determines the value used for the Builder Class input, which holds the name of the Java class that provides the builder’s functionality. Now, enter a Readable Name of Terms & Conditions, which is the actual name of the builder as it appears in the Model Editor and Outline view. For the mandatory Description input (this is different to the Description input at the top of the builder, which explains the builder type), enter This builder adds a link which opens a terms and conditions page. This description displays under the builder title when developers are using the custom builder in the Model Editor. Underneath the Builder Class input is a checkbox for generating a coordinator. A coordinator in WPF is a Java class that lets you dynamically update a builder’s interface. In this example, you use a coordinator to show and hide an input that accepts custom terms and conditions. Enable the Generate Coordinator checkbox, and you should see the Coordinator Class input filled in with the value com.ibm.builders.TermsAndConditionsCoordinator. The next checkbox enables you to generate a helper, which is essentially a utility class that can be called from your custom builder class. Helpers are useful when you are dealing with complex functionality that you don’t want to store in the builder class itself. In this case, you don’t need a helper, so leave the Generate Helper box unchecked. The Category input defines the category under which your builder will display in the Builder Palette. Select Custom Builders. Leave the Help File input blank, as you will not provide a help file for the builder in this example (for future builders, you can create HTML pages based on the existing WPF help pages, copy them into your project, and link to them from this input). Also, leave the Required Version input with the default value of 5.6.0, which is the minimum version required to use your custom builder. If your custom builder uses other builders that are available only in certain versions of WPF (such as the Dojo builders, which are available only in versions of WPF later than 6.0.1), then you need to change the value of this input to the appropriate version.
Custom Builders
403
The final input in the main section of the Skeleton Builder is for generating a builder API. This checkbox creates a Java class with the required get and set methods for each input in the custom builder, essentially enabling you to override the way WPF stores and retrieves these values. By default, each builder gets and sets its data based on the type of input used (String, XML, Date), which, in most cases, is all you need. However, you might want to modify this mechanism if you want your builder to be callable from other custom builders. For example, in the code later in this section, you will see how to access the builder API of the Button builder; if you generate a builder API for your custom builder, then you can access the builder from other builders in the same way. You can leave the checkbox disabled for the current example. Don’t save the builder call yet, as you still have to define the builder inputs themselves.
Defining Builder Inputs for the Custom Builder The final area of customization for the Builder Skeleton is the Builder Inputs section at the bottom of the builder, which is, of course, where you need to specify all your builder inputs. In the Name section of the builder call, you can already see an input for PageLocation as a result of selecting the Page Control Builder for the Builder Type earlier in this section. For this builder, you need to define several other inputs as well, so add some extra inputs as shown in Figure 15.1 (make sure they are in the right order, as this order defines the order in which they appear in the interface). You can define new inputs simply by clicking on blank rows and typing in the input name, and you can move inputs by dragging and dropping them.
Figure 15.1
Defining builder input names.
The first input, Name, defines the model-specific name of the builder as it appears in each model. Select the Name input, and then change the Input Type to ‘Builder name (required)’, which automatically gives the input the behavior of a standard name input in WPF. Also, enable the ‘Is Required’ box to make sure developers enter a name for the builder, and enter a Prompt of Name (this is the label that displays next to the input). Select the Page Location input, and notice that the Input Type is set to Page location. This setting automatically adds the standard Page and Tag inputs for placing an element onto a page.
404
Chapter 15 Performance and Process Optimization
After you have finished looking at the Page Location settings, select the BackAction input and change the Input Type to WebApp action picker, which enables users to open the Select Action dialog to fill in a value for the input. This input is used to specify an action to run when the user clicks a Back button on the terms and conditions page. Type Back Action for the Prompt, and enter Action to run when returning from the terms and conditions page for the Help Text. The Help Text displays when the cursor hovers over the input label. Now, select the OverrideMessage input. This input is a checkbox that enables developers to specify whether they want to override the default terms and conditions text. When the checkbox is not enabled, the NewMessage input is hidden (the NewMessage input enables developers to specify the custom text). The mechanism to handle this functionality is added into the Java class created by the Skeleton Builder, which you do later in this chapter. Change the Input Type for the OverrideMessage input to Checkbox, and for the Prompt, type Override Terms and Conditions. For the Help Text, specify Toggles whether you would like to override the default terms and conditions. Finally, select the NewMessage input and change the Input Type to Text area. Change the Prompt to New Terms and Conditions, and change the Help Text to Text to override the default terms and conditions.
Generating the Custom Builder Artifacts When you are finished entering the custom builder settings, save the builder call. This automatically creates an XML definition file called TermsAndConditions.bdef in the WebContent/ WEB-INF/builders/com/ibm/builders directory, it adds a Java source file for your builder (TermsAndConditions.java), and it adds a source file for your coordinator (TermsAndConditionsCoordinator.java) in the WebContent/WEB-INF/work/source directory. The builder definition file contains the settings from your Builder Skeleton. You don’t need to change the builder definition file, but if you want to share the builder with others, you need to provide them with this file. You also need to provide them with the Java classes just mentioned (you don’t need to share the model with the Builder Skeleton). When sharing builders, it is a good idea to export these files into a WebSphere Portlet Factory Archive, which other developers can then easily import into their own projects (you can do this by right-clicking a project, and then selecting Export, Other, WebSphere Portlet Factory Archive).
TIP You can add a zip file containing your custom builder to a feature set by copying the zip file into the <wpf_install_location>\WPFDesigner\FeatureSets\<Web-App_version_number>\ Packages folder. After you restart the WPF Designer, the package will be listed among the prepackaged WPF feature sets.
Custom Builders
405
Modifying the Custom Builder’s Functionality Open the TermsAndConditions.java file and have a look at the default code added by WPF. There is one method in the TermsAndConditions class called doBuilderCall, which is executed whenever the WebApp is regenerated. If you scroll through the code, you can see that by default, the builder creates a Text builder and uses it to place some text at the page location specified by the PageLocation input. Notice also that several points in the code are marked in the following way: /*##GENERATED_BEGIN*/ /*##GENERATED_BODY_END*/
Code that falls between these lines is overwritten by WPF whenever you update the Builder Skeleton builder call, so any modifications you make should be made outside these areas. In the current example, you need to overwrite the code that creates the Text builder, so that the custom builder’s functionality is as follows: 1. Defines HTML for the terms and conditions page 2. Creates a terms & conditions page 3. Places a Back button onto the terms and conditions page 4. Inserts a link at the PageLocation specified To do this, replace the contents of the doBuilderCall method so that it appears as shown in the following code snippet (note that you can also download this method fromibmpressbooks. com/title/9780137134465 under the Chapter 15 folder): public void doBuilderCall(GenContext genContext, WebApp webApp, BuilderCall ¯builderCall, BuilderInputs builderInputs, PageLocation pageLocation) { // System.out.println(“builderInputs: “ + builderInputs); /*##GENERATED_BODY_BEGIN#InputAccessorCode#*/ // Generated code to get all the builder inputs String name = builderInputs.getString(Constants.Name, null); String backAction = builderInputs.getString(Constants.BackAction, null); boolean overrideMessage = builderInputs.getBoolean(Constants.OverrideMessage, ¯false); String newMessage = builderInputs.getString(Constants.NewMessage, null); /*##GENERATED_BODY_END*/ //Define HTML for terms and conditions page StringBuffer html = new StringBuffer(“
¯
” + ¯“Terms & Conditions
”); if (overrideMessage) html.append(newMessage);
406
Chapter 15 Performance and Process Optimization
else html.append(“Here are the default terms and conditions.”); html.append(“
<span name=’backButton’>
”); //Create terms & conditions page com.bowstreet.builders.webapp.api.Page pageBuilder = new ¯com.bowstreet.builders.webapp.api.Page(builderCall, genContext); pageBuilder.setName(“termsAndConditionsPage”); pageBuilder.setPageData(html.toString()); pageBuilder.invokeBuilder(); //Place back button onto terms and conditions page Button buttonBuilder = new Button(builderCall, genContext); buttonBuilder.setActionType(“link”); buttonBuilder.setAction(backAction); buttonBuilder.setLabel(“Back”); buttonBuilder.setPageLocation(“Page termsAndConditionsPage NameSearch ¯backButton”); buttonBuilder.invokeBuilder(); //Insert link at pageLocation Link linkBuilder = new Link(builderCall, genContext); linkBuilder.setPageLocation(pageLocation); linkBuilder.setText(“Terms & Conditions”); linkBuilder.setActionType(“link”); linkBuilder.setAction(“termsAndConditionsPage”); linkBuilder.invokeBuilder(); }
The modifications to this method have been made under the generated text at the top of the class (which ends with ‘/*##GENERATED_BODY_END*/’), and replace the previous code to add a Text builder to the page. The first block of code defines a StringBuffer with the HTML to use for the terms and conditions page. If the OverrideMessage input is set to true, the NewMessage is inserted into the HTML; otherwise, a default message is used (notice that the first letter of each input in the code has been converted to lowercase, so that the names are consistent with Java naming standards). The second block uses a Page builder to create the terms and conditions page itself, and the third block uses a Button builder to place a Back button onto the page. The final block uses the Link builder to place a hyperlink onto page1, which is used to open the terms and conditions page. Notice that the invokeBuilder method for each builder kicks off that builder’s doBuilderCall method. For the javadoc on these classes, see the WebSphere Portlet Factory help file under the Reference, API documentation heading. Save the class when you are done modifying it.
Custom Builders
407
Modifying the Coordinator The next step is to modify the coordinator so that the Terms & Conditions builder dynamically hides and shows the NewMessage input depending on whether the OverrideMessage input is enabled. To do this, open the TermsAndConditionsCoordinator class. There are three main methods in the TermsAndConditionsCoordinator class: initializeInputs, which runs when the builder call is created or opened from the Outline view; processInputChange, which runs whenever an input value changes; and terminate, which runs whenever the OK or Apply buttons are pressed. Notice that there are large blocks of commented code telling you how to manipulate the initializeInputs and processInputChange methods, which you can modify to suit your needs. You need to make two changes to this class, and both concern setting the visibility of the NewMessage input based on the visibility of the OverrideMessage input. The first change will be made to the initializeInputs method and will set the visibility of overrideMessage when the builder call is first opened. To make this change, enter the following code just after the ‘/*##GENERATED_BODY_END*/’ line in the initializeInputs method: //toggle newMessage input if overrideMessage has changed defs.newMessage.setVisible(defs.overrideMessage.getBoolean());
For the next change, add the following code just after the first curly bracket under the definition for the processInputChange method: //toggle newMessage input if overrideMessage has changed if (changed == defs.overrideMessage) { defs.newMessage.setVisible(defs.overrideMessage.getBoolean()); return true; }
This checks to see if the OverrideMessage input has changed, and, if so, it sets the visibility of the NewMessage input accordingly. Save the class when you are finished editing it. You can come back and modify this class at any time, and it will update any instances of the Terms & Conditions builder the next time the builders are used.
Using the Terms & Conditions Builder Now that you have finished configuring the Terms & Conditions builder, you need to put it to use. Open the information model again, and open up the Builder Palette to add a new builder. If you scroll through the ‘All’ category, you should see the Terms & Conditions builder, and it should also display under the Custom Builders heading. Select the Terms & Conditions builder and press OK. The builder should display in the Model Editor, as shown in Figure 15.2. Notice that the NewMessage input is hidden; if this is not the case, make sure you configured the coordinator class correctly.
408
Figure 15.2
Chapter 15 Performance and Process Optimization
The Terms & Conditions Builder.
Change the name of the builder call to termsAndConditions, and then specify the Page and Tag as page1 and terms, respectively. For the Back Action, select page1 (notice that all of these inputs have the appropriate pickers available as a result of setting the Input Type input on the Builder Skeleton). Leave the Override Terms and Conditions input disabled for now, and save the builder call.
Testing the Information Model To test the information model from your IDE, run your model from the WPF Designer by clicking the icon on the toolbar. This runs the information model with the last run configuration you used. If you have not set up a run configuration before, you are prompted to do so—create a new configuration under the WebSphere Portlet Factory Model category (if you want more information on setting up a run configuration, see the “Testing the Application” section in Chapter 1). An information page with a link to open the terms and conditions page should display, as shown in Figure 15.3.
Figure 15.3
The information page.
Click the terms and conditions page, and you should be taken to a new page, as shown in Figure 15.4. Clicking the Back button should return you to page1.
Creating a New Model Wizard
Figure 15.4
409
The default terms and conditions page.
Now open up the Terms & Conditions builder call again, and enable the box to override the terms and conditions. If you have set up the coordinator class correctly, the NewMessage input should now display. Enter some new terms and conditions text and save the builder. When you rerun the model, you should see that the contents of the NewMessage input have been added to the terms and conditions page. After you have tested your model, you should rebuild the application and test it on the portal server as a portlet (for instructions on how to do this, see the example in Chapter 1). After you have added the portlet to a page in the portal, your portlet should appear as shown in Figure 15.5.
Figure 15.5
The Information portlet.
Creating a New Model Wizard
Now that you have successfully created a Page Control Builder, you will create a new model wizard that adds the option to create information portlets in the New Model Wizard. This is a useful feature because it enables you to automatically build a model with certain builders in it, based on a template you create yourself. Any time you would like to create a new model based on the template, you have only to select it from the New Model Wizard. Change the Builder Type of the builder call to Model-based wizard builder, which enables you to create new models based on your settings in the Builder Skeleton. For the Model Name input, specify chapter15/information, which links the custom builder to the information model you created earlier. Change the Builder ID so that it reads com.ibm.builders.Information, which also populates the Builder Class input. It is a good idea to make this change, as the default Builder Class will be taken from your model name, and the first letter of your model name is a lowercase letter (standard Java naming practice is to use an uppercase letter for the first letter of a Java class). Change the Readable Name input to Information, the Category to Custom Builders, and the Description to Creates an information portlet with a terms and conditions
410
Chapter 15 Performance and Process Optimization
page. All of these inputs define how the new Information option displays in the New Model wiz-
ard. Notice that there is a new input at the bottom of the main section called Builder Pages, which you can use to define a series of pages for your model wizard (similar to the pages you go through when adding a service provider or consumer). In this example, you don’t need any extra pages, so leave this checkbox unchecked. Finally, remove the Name input from the Builder Inputs section at the bottom of the builder, as there is no need for developers to name the builder itself (they will be asked to name only the model by virtue of the fact that you are using the Model-based wizard builder option). You can remove an input by right-clicking the input name and selecting Delete Row. Save the builder call when you are finished.
Testing the New Model Wizard Builder To test the new builder, create a new WebSphere Portlet Factory Model. Under the Custom Builders heading, there should be a new option for Information, as shown in Figure 15.6. Select this option and press Next. Name the model on the next screen and click Finish. If you have configured the builder correctly, you will have a new model in your project based on the information model template.
Figure 15.6
The Information option in the New Model Wizard.
Important Points
411
Summary In this chapter, you learned about the different ways in which you can optimize the performance of your WPF portlets. You learned about caching concerns, the proper use of Ajax and Dojo, data set and profiling considerations, session size testing, and performance logging. You also learned how to optimize the portlet development process using custom builders, and you built a simple information portlet using a custom page control and a custom model-based wizard. Chapter 16, “More Techniques for Domino,” expands on the Domino example in Chapter 4 and introduces some ways in which you can interact with Domino databases from a portlet.
Important Points • If used sparingly, Ajax and Dojo can improve the performance and usability of your applications. • The Cache Control builder can be used to cache the output of any action in your model. Many WPF builders have built-in caching capabilities as well. • Extensive use of profiling can drain system resources, so limit profile use to what you absolutely need. Cached information is profile-specific, so cache non-profiled models if possible. • Speed up data set retrieval operations by fetching only the records that you need. Also, use WPF’s paging capabilities so that only a snapshot of the total data set is ever needed at one time. • You can monitor your applications for performance using debug tracing (particularly the modelActions property) and by monitoring the serverStats.txt log. • You can monitor session sizes in your application by setting the appropriate properties in the bowstreet.properties file, and then restarting your application server. Session information is written to the sessionSize.csv file. • Custom builders enable you to extend WPF’s existing builder library with your own reusable, wizard-based components. These builders consist of an XML definition file and a set of Java classes. • A builder coordinator class can be used to dynamically change a builder interface depending on developer input.
This page intentionally left blank
C
H A P T E R
1 6
More Techniques for Domino
This chapter outlines some of the ways in which common Notes functionality can be surfaced in a portlet using WebSphere Portlet Factory (WPF). These functions are an extension of the techniques discussed in Chapter 4, “Using Domino Data.” By the end of the chapter, you will understand several common approaches to implementing Notes functionality in WPF. You will also create a sample application to demonstrate these approaches, and become aware of the various methods that WPF makes available to you through the Domino Data Access builder. Both the models used in this chapter are available for download from ibmpressbooks.com/ title/9780137134465 under the Chapter 16 folder (instructions for copying these files into your project are included in a readme.txt file in the same folder), although it is recommended that you build them yourself using the application developed in Chapter 4 as a starting point. If you haven’t already set up your environment to link to the Suppliers.nsf database, you should follow the instructions in the beginning of Chapter 4. The following topics are covered in this chapter: • Adding common Notes functionality • Domino data access methods
Adding Common Notes Functionality Most Notes application developers are acquainted with the different techniques used to build Notes applications, such as coding, scripting, GUI manipulation, and formula writing. When faced with the prospect of porting Notes applications to a portal, then, the question of how to
413
414
Chapter 16 More Techniques for Domino
implement this functionality in a portlet inevitably arises. How do you run a Notes agent from a portlet? Can you use formulas in portlets? This section discusses several useful techniques for surfacing some of the more common Notes functionality in a portlet. The suppliers application built in Chapter 4 is used as a starting point for these modifications, so if you have not already completed the example in that chapter, it is recommended you set up a WPF project with these artifacts (you don’t need to walk through the whole example; just download the artifacts for Chapter 4 and configure your project according to the instructions in the “Configuring Your Environment” section of Chapter 4). Note that the modifications in this chapter are independent of each other, so you don’t need to walk through each section in order (you can just skip the sections you aren’t interested in). Also, before you begin the examples in this section, ensure that you are using the suppliersService model as your service provider, rather than the suppliersStub model discussed at the end of Chapter 4.
Notes Formulas, Validation, and Translation Notes formulas are an integral part of Notes development, and depending on the type of formula used, there are a number of different ways to bring them across to a portlet. Column formulas in Notes views, for example, are automatically brought across into WPF when you use a Domino Data Access builder call or Domino View & Form builder call. Form formulas, however, are a little more nuanced. As you saw in the example in Chapter 4, computed for display fields are not automatically brought across into WPF, so you need to evaluate their formulas and display the results from within the WPF application. Similarly, even though computed fields are brought across into WPF, they are not evaluated according to the field’s formula in Notes. There are a number of ways to approach these situations. First, WPF comes with a Domino Formula builder, which is a quick and easy way to evaluate a Notes formula and display the results in a portlet. The downside of this builder is that it can’t send its output (the evaluated Notes formula) through a service, which means that it has to exist in the service consumer rather than the service provider. This is usually undesirable, as it mixes the presentation tier (what is displayed to the screen) with the model tier (the data itself); ideally, the field on the form should get its value from a service and should not have to worry about where the service is getting its data from. To implement a service-based approach to form formulas, then, you can use the evaluateFormula method of the Domino Data Access builder. The formula is evaluated by a service provider, and the results are then passed back to the service consumer, which displays the results to the screen. The example at the end of this section uses this approach. Although formulas in computed and computed for display fields do not automatically transfer across into WPF, validation formulas do come across in the sense that the Notes form is computed when the form on the portlet is submitted. For example, when you submit the supplier form in WPF, the validation formula for the supplierName field on the Notes form is evaluated, and if the validation fails, an error is generated. You can then cater for these errors by writing validation routines in WPF to output appropriate information to the user (validating fields is covered in Chapter 11, “Field Validation, Formatting, and Translation”).
Adding Common Notes Functionality
415
Field translation formulas, on the other hand, are not automatically transferred to WPF, and neither are the formulas specified in keyword lookups for form fields. In both of these cases, you need to implement the Notes formulas in WPF. Translating field data is covered in Chapter 11, and keyword lookups are covered in the “Keyword Lookups” section at the end of this chapter. Finally, Notes formula agents cannot be run directly from a WPF portlet, and you will need to recode these agents in either LotusScript or Java. LotusScript and Java agents can be run from WPF portlets, and you can also recode them directly in WPF using the Java Domino API (using Java in WPF is discussed in Chapter 8, “Using Java in Portlets”). The rest of this section describes how to transfer the supplier rating functionality of the Suppliers.nsf database into your WPF application. The formula is evaluated in the service provider and the results are published through the readSupplierRating operation, which is consumed by the service consumer and displayed when viewing or editing a supplier. The following builders are added in this section: • Comment (x2) • Variable • Service Operation • Action List • Text
Providing the readSupplierRating Operation To add Notes formula functionality to your service provider, first make sure the suppliersService model is the currently active model in your IDE, and then add a Comment builder call to the suppliersService model and name it Formula. Save the builder call when you are finished, and add a variable to the suppliersService model by adding a Variable builder call. Name the variable supplierRatingFormula. This variable will hold the formula that you want to evaluate (you can enter the formula into the service operation directly, although using a variable makes your model clearer and easier to maintain). Set the type of the variable to String and enter the formula shown below into the Initial Value input. Save the builder call when you are finished. currentYear := @Text(@Year(@Now)); returnsCount := @If(@IsError(@DbLookup(“”; “”; “LookupReturns”;supplierName+currentYear; 1)); 0; @Count(@DbLookup(“”; “”; “LookupReturns”;supplierName+currentYear; 1))); @If(@IsNewDoc; “”;returnsCount < 1; “This supplier has an excellent rating.”; returnsCount < 2; “This supplier has a good rating.”; returnsCount < 3; “This supplier has an average rating.”; returnsCount < 4; “This supplier has a poor rating.”; “This supplier has a very poor rating.”)
416
Chapter 16 More Techniques for Domino
Add a service operation to the suppliersService model, and fill out the inputs as shown in Figure 16.1. This creates a new service operation called readSupplierRating, which runs the evaluateFormula method of the suppliersServiceView Domino Data Access builder. The operation takes two arguments: The first is the formula you specified in the previous step, and the second is the UNID of the document you would like to retrieve the supplier rating for. Only the second argument needs to be specified by the service consumer. Note that the Specify Input Values setting enables you to manually map operation inputs to inputs in the called action.
Figure 16.1
Configuring the readSupplierRating service operation.
For the Operation Results section of the builder call, specify ‘Use structure from called action’ for the Result Structure Handling input and make sure the Result Field Mapping input is set to Automatic. This causes the service operation to take the structure of its results from the evaluateFormula method (which leads to a String value being returned). Save the builder call when you are finished. The formula is returned from the readSupplierRating operation on the service provider. You can test the operation by running the model from your IDE, and then selecting readSupplierView from the index test page. Copy one of the supplier’s universal identifiers (UNIDs), and then press the Back button to return to the index page. From the index page, select readSupplierRating and then paste in the UNID into the arg2 field (you can leave the arg1 field blank, as it is automatically set in the Service Operation builder). Note that you do not need to copy the UNID first if you specify a UNID in the testing defaults for the readSupplierRating operation in the Service Definition builder. Press the Submit Query button and you should see the results of the formula evaluation returned. The results for Fred Jackson, for example, are shown in Figure 16.2.
Adding Common Notes Functionality
Figure 16.2
417
Supplier rating for Fred Jackson.
Consuming the readSupplierRating Operation The next step is to configure the service consumer so that it consumes the readSupplierRating operation. To do this, make sure the suppliers model is the currently active model in your IDE, and then add a Comment builder call to the suppliers model and name it Formula. Save the builder call. Add an action list to the suppliers model and name it getSupplierRating. This action list calls the readSupplierRating service operation, passing in the UNID that was used to read the supplier document, and returns the supplier’s rating. The action list will be run from builder calls you are about to create. To add the appropriate action to the action list, open the Select Action dialog and then select the suppliersReadSupplierRatingsWithArgs method under the Methods heading. In the Define Method Call Arguments dialog that follows, leave the first argument blank but select the UNID used to read the supplier document from the Select Action dialog, as shown in Figure 16.3. Click OK to accept the argument, and then click OK again to accept the action. Save the builder call when you are finished.
Figure 16.3
Selecting the UNID of the current document.
418
Chapter 16 More Techniques for Domino
The next step is to call this action list and display the results in the portlet. You can do this with a Text builder call, and you will need to add one for each page you would like the formula results displayed on. In this example, you add only the formula results to the detail page. To add the formula results to the detail page, add a Text builder call to the suppliers model and fill out the inputs as shown in Figure 16.4. This calls the getSupplierRating action list and displays the results after the data table on the detail page for each supplier (the formula is evaluated whenever the detail page is opened for a supplier). Save the builder call when you are finished.
Figure 16.4
Adding formula results to the detail page.
The Notes formula for evaluating a supplier rating is retrieved from your service consumer model via the service provider model, and displayed to the screen. The detail page for Frank Jackson, for example, should display as shown in Figure 16.5 when the suppliers model is run from the IDE.
Adding Common Notes Functionality
Figure 16.5
419
A detail page displaying the supplierRating.
Notes Agents LotusScript and Java Notes agents (but not formula Notes agents) can both be used from WPF portlets. Using Notes agents from WPF has a number of benefits: • Notes agents provide an easy way to implement business logic from WPF, especially for Domino developers and even more so when working with Domino data. • You can reuse existing Notes agents with minimal rewriting. • Domino developers who don’t know how to code in Java can use LotusScript agents instead of Java methods in WPF. • Business logic in Notes agents is separated from the presentation layer of your application (your portlet in WPF). • Notes agents can take advantage of Domino security. However, there are some disadvantages as well: • Notes agents create a reliance on a Domino server, which is usually undesirable, unless the Domino server is also used as a data source. • Notes agents are fairly useless unless they’re employed to manipulate Domino data, as they have no access to the context in which the portlet runs (the portal API, the WPF WebApp, and so on).
420
Chapter 16 More Techniques for Domino • If you use any Java methods in your applications (as often happens when implementing business logic for non-Domino data sources), then your applications can become confusing if you use Notes agents as well. • Notes agents cannot return values to the functions that call them. • Notes agents run outside the jurisdiction of the portal server, so the portal server has no control over them (apart from deciding whether they are run in the first place).
When faced with the task of implementing business logic in WPF, these points need to be taken into consideration as part of your development strategy. Writing business logic in Java methods is the generally preferred approach when there are non-Domino data sources or Java frameworks in place, as it provides the most flexibility for future development. However, Notes agents provide an effective alternative when working with Domino data, particularly if the agents have already been written in Notes or if the developers writing the code are Notes developers. There is no hard and fast rule as to which approach is best, so the decision probably needs to be made on a case-by-case basis for each environment. The example that follows describes how to call the AddSupplierToMMDataSourceFromWPF Notes agent from a button on the supplier’s portlet. This agent is based on the code of the Add to Mail Merge Data Source button on the Supplier form, with only one or two minor changes required to make the code run from WPF. The code will add the current contact’s details to a file called supplierMailMerge.dat on the users C drive, which can then be used as a data source for mail merges. Because the changes to the code are very minor, using a Notes agent is a quick and easy solution for surfacing the Add to Mail Merge Data Source functionality. The agent functionality is provided by the service provider model and consumed by the service consumer. The following builders are added in this section: • Comment (x2) • Service Operation • Action List • Button
Providing the addSupplierToMMDataSource Operation To add Notes agent functionality to the service provider model, first sign the AddSupplierToMMDataSourceFromWPF agent with a user who has access to run restricted agents on the server. You can do this by opening and saving the agent in the Domino Designer®, provided your user ID has access to run restricted agents on the server. Otherwise, you can sign the agent with the Domino server’s ID by navigating to the Files tab in a Domino Administrator client and clicking the Suppliers.nsf database in the file list. Then, select Sign from the Database menu, as shown in Figure 16.6. On the Sign Database dialog that follows, select the Active server’s ID radio button option and press OK.
Adding Common Notes Functionality
Figure 16.6
421
Signing the supplier database.
Make sure the suppliersService model is the currently active model in your IDE. Add a Comment builder call to the suppliersService model and name it Agent. Save the builder call, and then add a service operation builder call to the suppliersService model and fill out the Service Operation Properties and Operation Inputs sections of the builder call as shown in Figure 16.7. This calls the runAgent method of the suppliersServiceView Domino Data Access builder, passing in the two arguments specified. The first argument is the name of the formula to call (AddSupplierToMMDataSourceFromWPF), and the second argument is a document UNID passed in from the service consumer. The formula is evaluated for the document UNID specified in the second argument. Select No Results for the Result Structure Handling input in the Operation Results section, as no results are returned from running the agent. Make sure Automatic is specified for the Result Field Mapping input, and save the builder call when you are finished. The agent can now be run from the addSupplierToMMDataSource operation on the service provider. You can test the operation by running the model from your IDE, and then selecting readSupplierView from the index test page. Copy one of the supplier’s UNIDs, and then press the Back button to return to the index page. From the index page, select addSupplierToMMDataSource, and then paste the UNID into the arg2 field (you can leave the arg1 field blank, as it is hard coded into the Service Operation builder). Press the Submit Query button and the agent runs. No results are returned, so when the screen reloads, it simply displays the Back button. However, if you check in the root of your C drive, you should have a file called supplierMailMerge.dat. If you open this file, you should see various details for the document specified in the second argument to the service operation. The results for Kate Tredeau, for example, are as follows: “Tile Kingdom”,”Kate Tredeau”,”1 Park Place”
422
Figure 16.7
Chapter 16 More Techniques for Domino
Configuring the service operation properties and inputs.
If you run the operation again, it appends information to the end of the file (you can reset the results by deleting the file from the file system).
Consuming the addSupplierToMMDataSource Operation The next step is to configure the service consumer so that it consumes the addSupplierToMMDataSource operation. To do this, make sure the suppliers model is the currently active model in your IDE. Add a Comment builder call to the suppliers model, and name the builder call Agent. Save the builder call when you are finished. Add an action list to the suppliers model, name the action list addSupplierToMMDataSource, and then add two actions to the list as shown in Figure 16.8. This action list calls the addSupplierToMMDataSource service operation, and then reloads the suppliers view page. Save the builder call when you are finished. Next, add a button to the supplier detail page, which will run the action list you just created. To do this, add a Button builder call and fill out the inputs shown in Figure 16.9. Note that the action is only linked to; it does not generate a submit form event (which would raise an exception as there is no form to submit on the supplier detail page). Save the builder call when you are finished configuring it.
Adding Common Notes Functionality
Figure 16.8
Configuring the addSupplierToMMDataSource action list.
Figure 16.9
Configuring the addSupplierToMMDataSourceButton builder call.
423
424
Chapter 16 More Techniques for Domino
It is now possible to run the addSupplierToMMDataSource Notes agent from the suppliers model. Test the model now from your IDE, and open up the details page for World of Tiles. Press the Add to Mail Merge Data Source button, and you will be returned to the suppliers view. This automatically creates a .dat file on your local machine (or updates the file if it already exists) with information on World of Tiles, which you can use to run mail merges in a word processor. If you check in the C drive on your local machine, you should see a file called supplierMailMerge.dat. Open this file. The file contains details for World of Tiles as follows (in addition to any previous details you added to the file): “World of Tiles”,”Jason Shearer”,”90 Centre Rd”
TIP When creating Notes agents that are accessed from WPF portlets, there are a number of important points to be aware of: • The Runtime target in the agent properties dialog in the Domino Designer should be set to None. • You can get a reference to the current document in WPF from the Notes agent by using the following script: Dim Dim Set Dim Set
session As New NotesSession db As NotesDatabase db = session.CurrentDatabase doc As NotesDocument doc = db.GetDocumentByID(session.CurrentAgent.ParameterDocID)
• The Run as Web User option in the agent security settings does not have any meaning when the agent is invoked via Domino Internet Inter-ORB Protocol (DIIOP), and is therefore irrelevant in these scenarios (by default, WPF communicates with Domino by DIIOP). The AddSupplierToMMDataSourceFromWPF agent provided with the Suppliers.nsf database should serve as a good template for creating future Notes agents in WPF.
Keyword Lookups Keyword lookups are often used on forms in Notes to provide users with a prepopulated list of options for field values. In the suppliers application, for example, the stockSupplied field has a lookup to the Stock view, and because the Allow Values Not in List checkbox on the stockSupplied field properties is not enabled, users can specify stock for a supplier only when that stock is listed in the database. This functionality does not come across into WPF when using the View & Form builder, but there are two main options available for recreating the same functionality in your WPF portlets. The first is to use the Domino Keyword Lookup builder, which provides a list of values
Adding Common Notes Functionality
425
from a Notes view, profile document, or Notes formula. You can then add a Select builder call to your model, which takes a list of values that users can select from and inserts it at a particular tag on a page. The list used by the Select builder comes from the results of the keyword lookup. This approach is a quick and easy solution, but unfortunately, the Domino Keyword Lookup builder call requires a builder with Domino access in the same model. Because of this, the presentation and model tiers of your application are mixed together. As an alternative, you can use a servicebased approach, where the presentation tier (your service consumer) makes calls to the model tier (the service provider) to provide the keyword lookup via a Lookup Table builder call. It is a little more work, but this approach allows a clearer separation of components, and is easier to maintain in the long run. The rest of this section provides an example of how to implement a service-based keyword lookup. The Stock view in the suppliers database will be used as the basis for the lookup, which is then shown on the update and create pages of the supplier form. To simplify the example, the stockSupplied field referenced in the Domino database takes only a single value; if you change this field to accept multiple values, you also need to write a Java routine that separates out each value in the field (separated by the pipe (|) symbol) and then selects the appropriate values in the dropdown box. Working with Java is covered in Chapter 8. The following builders are added in this section: • Comment (x2) • Domino Data Access • Service Operation • Lookup Table • Data Field Modifier
Providing the readStockView Operation To implement keyword lookup functionality in the service provider model, first make sure the suppliersService model is the currently active model in your IDE. Add a Comment builder call to the suppliersService model and name it Keyword lookup. Save the builder call. Add a new Domino Data Access builder call to the suppliersService model by selecting Domino Data Access from the Builder Palette, and then pressing OK. Configure the builder call as shown in Figure 16.10. This connects to the Stock view rather than the Suppliers view. Don’t enable the Enable Document and Form Support checkbox under the Document Form Support section, as there is no need to enable document support; you just want to return the list of stock items for a keyword lookup. Save the builder call when you are finished. Next, add a Service Operation builder call to the suppliersService model. This service operation returns the list of stock items provided by the stockView Domino Data Access builder call. Fill out the Service Operation Properties and Operation Inputs sections of the builder call as shown in Figure 16.11.
426
Chapter 16 More Techniques for Domino
Figure 16.10
Configuring the stockView builder call.
Figure 16.11
Configuring the service operation properties and inputs.
Fill out the Operation Results section of the builder by selecting Use Structure from Called Action for the Result Structure Handling input, and Automatic for the Result Field Mapping input. This specifies that the structure of the results should come from the DataServices/ stockView/readTable action, which causes an XML list of stock items to be returned. Save the builder call when you are finished.
Adding Common Notes Functionality
427
The keyword lookup is now available from the readStockView operation on the service provider. You can test the operation by running the model from your IDE, and then selecting readStockView from the index test page. You should see a list of stock items, as shown in Figure 16.12.
Figure 16.12
Testing the readStockView service operation.
Consuming the readStockView Operation You now need to configure the service consumer so that it consumes the readStockView operation. To do this, make sure the suppliers model is the currently active model in your IDE. Add a Comment builder call to the suppliers model and name it Keyword lookup. Save the builder call. Create a lookup table by adding a Lookup Table builder call to the model. Name the lookup keywordLookup, and specify Data Service as the Data Source. In the Data Service input, select DataServices/suppliers/readStockView from the Select Action dialog, and then type the text Item_Code into the Value Tag and Label Tag inputs. This retrieves a list of stock items from the readStockView operation, and then creates a keyword lookup based on the Item_Code column. The Label Tag for each stock item—that is, the text displayed to users—is the Item_Code (for instance, AB232, CT234), and the Value Tag for each stock item—the value that is stored for each selection—is also the Item_Code. Although these tags are often the same, you might want to use a different field for the label if the value isn’t user friendly (for example, the value might just be a numeric index, which won’t make much sense to end users). Save the builder call when you are finished. The final step involves creating form inputs on the update and create pages so that users can specify the stock supplied based on the keyword lookup (you need to add two separate form inputs, although you can add both at one time with the same builder). To do this, add a Data Field Modifier builder call to the model and name it stockSuppliedLookup. Then, in the Fields input, specify the two form inputs as shown in Figure 16.13. Scroll down to the Field Settings section and change the Lookup Table Used input to keywordLookup. Save the builder call when you are finished.
428
Chapter 16 More Techniques for Domino
Figure 16.13
Adding lookups for the stockSupplied fields.
It is now possible to look up the stock view from the suppliers model. Test the model from your IDE, and press the Create Supplier button. You should see the output shown in Figure 16.14. Notice that the stockSupplied field displays as a drop-down list box with pre-populated values from the lookup view.
Figure 16.14
Testing the lookup view.
Categorized Views Categorized views are data lists where one or more fields are used to group information together, and they can make your data easier to navigate. These views can be implemented in WPF using the Category View builder. In the example that follows, you add a screen for the Returns view in the suppliers database, which is a view of returned stock categorized on the return date. The following builders are added in this section: • Comment (x2) • Domino Data Access • Service Operation • View & Form • Button (x2) • Category View
Providing the readReturnsView Operation To add categorized view functionality to the suppliers application, the first step is to configure the service provider so that it provides an operation to access data from the Returns view. To start this
Adding Common Notes Functionality
429
configuration, open the suppliersService model from the Project Explorer view. Add a Comment builder call to the model and call it Category view. Save the builder call when you are finished. Now add another Domino Data Access builder call to the model and call it returnsView. This builder call is used to access the Returns view in the Suppliers.nsf database. Type Returns in the View Name input, and then change the Rows to Include input to All Rows. By default, WPF does not display categorized rows, so if you do not change this input, you need to specify the categorized fields manually when you enable categorized view support in the service consumer. Save the builder call when you are finished. Add a Service Operation builder call to the model, which is the public interface for service consumers to retrieve information from the Returns view. Change the Data Service input to suppliersService to associate the operation with the suppliersService service, and then type readReturnsView as the Operation Name. Specify DataServices/returnsView/readTable for the Action To Call, which accesses the Returns list from the Domino Data Access builder call. Make sure the No Inputs option is selected for the Operation Inputs section. The Operation Results section can be left with the default settings, as you will let the structure of the results be defined by the Domino Data Access builder call. Save the builder call when you are finished. If you test the service at this point and run the readReturnsView operation, you should see a list of returned documents, as shown in Figure 16.15.
Figure 16.15
Testing the lookup view.
Consuming the readReturnsView Operation The next step is to configure the service consumer to display the results of the readReturnsView operation. To do this, open the suppliers model from the Project Explorer view and add a Comment builder call to the model. Name the builder call Category view, and save it when you are finished. The suppliers model already has a Service Consumer builder call pointing to the suppliersService model, so it already has access to the new service operation for accessing returns
430
Chapter 16 More Techniques for Domino
information. However, you need to define an interface for using this information. Add a View & Form builder call to the model and name it returnsView. Specify DataServices/suppliers/readReturnsView as the View Data Operation, and disable the checkbox for Generate Main in the Advanced section (if you don’t do this last step, you will get errors when you try to run the model, as you already have a main method defined in your other View & Form builder call, and you can’t have more than one in the same model). Save the builder call when you are finished.
TIP Categorized views are intended as an alternative to pagination, and as a result, you cannot use pagination on a view that is also categorized. When using categorized views, make sure the Paged Data Display input on the View & Form builder is not enabled. For more information on pagination, see Chapter 5.
Next, you need to add some buttons to enable users to navigate between the Suppliers view and the Returns view. Add a Button builder call to the model, and fill out the inputs as shown in Figure 16.16. This button displays only when the suppliers list is visible, and it runs the returnsView_ShowResults method when pressed (this method first reloads the returns view by calling the suppliersReadReturnsView operation, and then it displays the results on the screen).
Figure 16.16
Configuring the switchToReturns builder call.
Now add a second Button builder call and fill out the inputs as shown in Figure 16.17. This second button displays only when the returns list is visible, and it shows the suppliersView_ ViewPage page. Notice that the second button does not load the supplier information from the
Adding Common Notes Functionality
431
service when it is pressed; this is because the supplier information is already loaded by the main Action List, and there is no need to load it twice.
Figure 16.17
Configuring the switchToSuppliers builder call.
Save the model when you are finished. If you run the suppliers model at this stage, the returns view displays as an ordinary, noncategorized view. To add the collapsible twisties (small, clickable arrows) familiar to Notes users, you need to add a Category View builder call to your model. Do this now, and name the builder call categorizeReturns. Specify the Container Field input as [returnsView_ViewPage]returnsView_ViewPage/ViewData/Row, which applies the builder call to each row of the returns view. Also, make sure the Category Selection input is set to Use Domino Category Rows, which automatically sets the categories in the returns view as they are specified in the returns view in Notes. Note that this setting does not work if the Domino Data Access builder call that defines the view has the Document Rows Only option set for the Rows to Include input. Also, make sure the Span Columns input is disabled to prevent category fields from becoming merged together. Save the builder call when you are finished.
TIP Even if you do not use a view that is categorized in Notes, you can still categorize the view in WPF by selecting the Specify One or More Columns by which to Group the Data option for the Category Selection input. This brings up a list of each column in the view and enables you to set which column(s) should be categorizable.
432
Chapter 16 More Techniques for Domino
Removing Unwanted Fields from the Returns View By default, the suppliersService service provides two additional fields (UNID and isDocumentRow) that you don’t need in this example, so you can remove them using your Data Field Modifier. In Chapter 4, you created a Data Field Modifier called hideUnwantedFields, which you can use to hide the rows in the Returns view. Open this Data Modifier builder call and add the two rows shown in Figure 16.18.
Figure 16.18
Adding fields to the Data Field Modifier builder call.
Before you save the builder call, you need to move the Data Field Modifier so that it can access the results of the returnsView View & Form builder call (Data Field Modifiers must follow the builder calls that create the fields that they act upon). Select both the Modifiers Comment builder call and the hideUnwantedFields builder call, and drag them into the white space at the bottom of the model. Save the model when you are finished. It is now possible to access the Returns view as a categorized view from the suppliers model. If you test the model now, you should see the output shown in Figure 16.19.
Figure 16.19
Testing the suppliers model.
Notice that there is a button on the page to switch to the Returns view. If you press this button, the Returns view should display. You should be able to expand and collapse category sections
Adding Common Notes Functionality
433
in this view by clicking on the appropriate twisty icons (the small green arrow next to each category), as shown in Figure 16.20.
Figure 16.20
Testing the categorized view.
Hide-When Fields In Domino development, hide-when fields are often used to hide fields (or entire sections) of a form based on the values of certain fields. In WPF, the best way to do this is to use the Visibility Setter, which lets you hide sections of a form based on another field value. The hide condition is evaluated only when the relevant section of the page is refreshed, so you don’t necessarily have to refresh the entire page (many builders contain a Post-Action Behavior section that lets you perform partial page refreshes. Partial page refreshing is covered in Chapter 13). To refresh all or part of the page, you may also need to use an HTML Event builder to act as the trigger for the page refresh. For example, you can link an HTML Event builder call to the onChange event of a particular text field, and then tell the builder to refresh part of the page every time the event is fired. So, every time the text field changes, the fields in the refreshed part of the page may disappear or reappear, depending on the conditions specified in the Visibility Setter. An example of hiding portions of a page without refreshing the entire page is provided in Chapter 13.
Rich Text Rich text is a popular format for storing formatted text in Notes and can also be used to store attachments. You can display Notes Rich Text in WPF by enabling the Retrieve Rich Text Items as HTML input in the Domino Data Access builder, although note that this is specifically for Notes Rich Text fields, and not for Notes fields that contain HTML text (when displaying HTML text, you can use an ordinary Text builder in WPF, but make sure the Text Format input is set to Allow HTML Formatting of Text). There is no native support in WPF for editing Notes Rich Text, however you can emulate Notes Rich Text edit functionality to some degree. For example, if you store your fields as HTML
434
Chapter 16 More Techniques for Domino
in Notes, then you can use a combination of HTML tags and the Dojo Rich Text Editor widget to display and edit them. This is not perfect Rich Text functionality (it does not, for example, enable you to attach files), but it may be suitable if you just want to use formatted text, as well as familiar HTML elements such as tables, images, and links. Attaching files is discussed in the next section. The use of the Dojo Rich Text Editor is beyond the scope of this book, but an example is available for download from the WebSphere Portlet Factory wiki at www.ibm.com/ developerworks/wikis/display/PortletFactoryID/IBM+-+Dojo+Rich+Text+Editor+sample. For more information on the Dojo toolkit and its use in WPF, see Chapter 13.
Attachments You can use the Domino Attachment builder call in WPF to convert all of the attachments in a document into clickable links on the page, which gives users an interface to launch attachments from your portlets. You can then place a Browse button on the page to upload files using the File Upload builder, and copy the uploaded file into Domino via a Java method. For example, you can write an Action List that gets executed when you submit a form in WPF. This would run a Java method that would get a reference to the uploaded file (using the java.io.File class), and then it would copy the file into a Rich Text item in the appropriate document (using the Domino Java API). There are a few caveats with this process, however. The File Upload builder may not be supported by certain portal server versions if they are part of a JSR-168 portlet application (however, version 6.x and later of WebSphere Portal fully support the use of the builder). Also, in order to use the Domino Attachment builder, it is necessary to link to a Domino View or Domino View & Form builder, and both of these builders mix your presentation layer with your data layer. This might be undesirable if you are trying to maintain a clean break between these layers in your application. Attaching Domino documents is beyond the scope of this book, but for more information on using Java in WPF, see Chapter 8 of this book.
Domino Data Access Methods The Domino Data Access builder provides a number of useful methods for accessing and manipulating Domino data. You can access these methods from a Select Action dialog of any model that contains a Domino Data Access builder call. For example, in the suppliersService model, you can press the ellipsis button next to the Action To Call input on any Service Operation builder call; this opens the Select Action dialog. Then, expand the Methods category and scroll down to the name of the Domino Data Access builder call (suppliersServiceView in the example). Each of these methods can be made available in a service provider, and the results can then be consumed from a service consumer. These method calls can also be inserted into other methods, which are useful for methods such as getDominoSession (this gives you access to the Domino Session object for the current user).
Domino Data Access Methods
435
TIP The Domino Data Access methods are also available from the Domino View & Form builder, which is an amalgamation of the Domino Data Access builder and View & Form builders. It is recommended that you use the Domino Data Access builder rather than the Domino View & Form builder, as it encourages separation of your presentation and model tiers.
The runAgent and evaluateFormula methods have already been discussed in this chapter, as have the numerous methods used for basic interactivity with the supplier database (createDocument, deleteDocument, and so on). However, there are a number of other useful methods that you should be aware of, as discussed in the following sections.
getDominoSession() and getDominoDatabase() The getDominoSession() and getDominoDatabase() methods provide you with access to the Session and Database objects, respectively (the Java versions of the NotesSession and NotesDatabase objects). These methods are useful in Java methods when you want to access and manipulate Domino data; as any Domino developer will know, after you have access to these two objects, you can get references to just about every object in Domino (documents, views, and so on).
setComputeWithFormEnabled(boolean) This method enables you to toggle whether Notes forms are computed when the equivalent WPF form is submitted, which is more useful than it may at first seem. By default, the validation formulas on a Notes form are calculated when the form is submitted in WPF, but being able to turn this off saves you from having to recreate custom forms in Notes when you don’t want to use the Notes validation in WPF. This also enables you to use alternative methods of validation, such as that provided by some of the default validation builders. Validation in WPF is discussed in more detail in Chapter 11.
getUserName() The getUserName() method can be useful when you want a string that names the current Notes user being used to authenticate with the Domino server from WPF. If you leave the default value of the Runtime Credentials input on the Domino Data Access builder (that is, ‘Use regen credentials specified above’), then the user that is specified in the Domino Server properties file is returned from this function. Otherwise, the returned value differs depending on the value of the Runtime Credentials input.
sendDocument(IXml, boolean, boolean) and getDocumentData(String) The sendDocument method enables you to send a Notes document via an email message, and it accepts three arguments. The first is an IXml representation of the Notes document that you want to send, the second argument specifies whether you want to save the document before it is sent,
436
Chapter 16 More Techniques for Domino
and the third specifies whether you want to attach the Notes form to the email message (note that if you set both Boolean arguments to true, the form will be saved as part of the document). The IXml used for the first argument is a WPF interface used to manipulate XML documents (for more information on the IXml interface, see Chapter 9, “Using Web Services and Manipulating XML”). You can create the IXml argument for the sendDocument function by using the getDocumentData function, which returns an IXml object. The getDocumentData method takes one String argument, which is the UNID of the document that you would like an IXml representation of.
Summary In this chapter, you learned about implementing common Notes functionality in WPF, including formulas, keyword lookups, agents, categorized views, hide-when fields, rich text, and attachments. The application built in this chapter provided this functionality by expanding on the suppliers application built in Chapter 4. You also learned about some of the different methods available to you via the Domino Data Access builder.
Important Points • The Domino Data Access builder and Domino View & Form builder contain a number of Java methods for interacting with Domino data. It is usually preferable to use Domino Data Access builder calls rather than Domino View & Form builder calls, as they enable you to separate your presentation tier from your model tier. • Calling the evaluateFormula method of the Domino Data Access builder in a Service Operation is the preferred method for using Notes formulas in WPF. The Domino Formula builder also enables you to perform this function, but it does not enable you to separate the presentation tier from the model tier. • Notes procedures can be run in WPF by either recoding them in Java (using the Java Domino API) or including them in Notes agents, which are then run using the runAgent method of the Domino Data Access builder. When running Notes agents in WPF, the Runtime target setting in the agent properties should be set to None. The CurrentAgent.ParameterDocID method of the NotesSession class can be used to get a reference to the current document in the Notes agent. • Using a Lookup Table builder call in a service provider model is the preferable method for emulating keyword lookup functionality in WPF. The Domino Keyword Lookup builder also enables you to perform this function, but it does not enable you to separate the presentation tier from the model tier. • Categorized views can be implemented in WPF using the Category View builder. Note that the categorization is intended as an alternative to pagination, so you should not apply both to the same view.
Important Points
437
• The best way to implement Notes hide-when functionality in WPF is to use the Visibility Setter builder. This builder can be used in conjunction with Ajax to perform partial page refreshes that simulate the use of hide-when fields in a Notes client. • There is no native support in WPF for editing Notes Rich Text fields, but you can emulate this functionality using the Dojo Rich Text Editor widget and HTML fields. You can enable rich text fields to be displayed in WPF by enabling the Retrieve Rich Text Items as HTML input in the Domino Data Access builder. • The Domino Attachment builder can be used to convert all of the attachments in a document into clickable links on a page. The File Upload builder provides a file upload button on a form.
This page intentionally left blank
A
P P E N D I X
A
Setting Up Your Environment
This appendix provides instructions for setting up your environment to use WebSphere® Portlet Factory. You should step through this appendix like a checklist, making sure you have completed all the relevant steps for your own environment. You can skip sections if they are irrelevant to the applications you’re building (for example, you can skip the section on Lotus Domino if you aren’t planning on completing any of the examples that use it) and come back to them later if you change your mind. The following points are covered in this appendix: • Installing WebSphere Portlet Factory • Configuring WebSphere Portal • Configuring Lotus Domino • Creating a test database in DB2 • Creating a test database in SQL Server • Configuring a JDBC resource
Installing WebSphere Portlet Factory If you have already installed WPF, you can skip this section. After you have obtained a copy of WPF (WPF 6.0.2 is available for purchase and download from the IBM site at www-306.ibm.com/software/genservers/portletfactory/), you can install WebSphere Portlet Factory by following these steps: 1. Unzip the WPF install files into a temporary directory on your machine. 2. Double-click the install.bat file to start the install wizard.
439
440
Appendix A Setting Up Your Environment 3. Select your language from the first screen in the wizard, and then click OK. 4. Depending on the install file version you have, you may get some introductory information on the next screen. Click Next. 5. The next screen asks you to accept the license agreement. Click ‘I accept the terms of the License Agreement’ and then click Next. 6. Type in a new pathname if you don’t want to install WPF into the default install path. Press Next when you are finished. 7. On the next screen, the install wizard asks you to specify which software you would like to install. WPF installs into an Eclipse-based IDE, so you can either install WPF into an existing Eclipse installation, or you can install a new copy of Eclipse when you install WPF (version 3.2.2 of Eclipse is included with WPF 6.0.2). Press Next when you are finished. 8. On the next screen, you need to verify the IDE you would like to install WPF into. If you choose to install WPF into an existing copy of Eclipse, you will be prompted to verify its location in the file system (it must be the same version of Eclipse). If you choose to install WPF into Rational Application Developer, Rational Software Architect, or Rational Web Developer, you will be prompted to select the appropriate installation from a list box. Otherwise, you need to verify the install path for your new installation of Eclipse. Press Next when you are finished. 9. The next screen asks whether you would like to install WebSphere Application Server Community Edition (WAS CE) as a development test server. WAS CE is a lightweight version of the WebSphere Application Server (WAS) and is ideal for testing because it is much faster than a full-blown install of WAS. Unless you prefer an alternative setup, it is recommended you click the checkbox to install WAS CE. 10. If you opted to install WAS CE on the previous screen, this next screen prompts you for the install location. Change the path if desired, and then press Next. 11. The next screen displays a summary of your settings. Verify that your settings are correct, pressing the Previous button to navigate back through the wizard if required. If all of your settings are correct, press Install to begin installing WPF. 12. After WPF has finished installing, you should see a success message similar to the one shown in Figure A.1. Click Done to close the install wizard, which then opens a page in your default Web browser that contains a brief introduction to WPF.
You have now successfully installed WPF. You can start the WPF Designer (that is, the Eclipse IDE with WPF installed into it) by pressing the Start button in Windows, and then selecting IBM WebSphere, Portlet Factory, Designer from the Programs menu.
Configuring WebSphere Portal
Figure A.1
441
Success message in the install wizard.
Configuring WebSphere Portal If you already have a target page on the portal for your portlets and you have access to deploy portlets to the portal server, you can skip this section. To follow the examples in this book, you need to set up a target page (or several pages) to add your portlets to. It is assumed that you already have a portal server installed and available for you to use; if this is not the case, you need to set this up first (WebSphere Portal 6.0 is used throughout this book). There are no restrictions on the page(s) you use, although if you’re deploying to a test portal server, it makes testing faster if you use the Welcome page or a page that is easily accessible from the Welcome page. If you’re using an existing portal page as a target page, no additional configuration should be necessary, unless you don’t have access to add portlets to that page. If this is the case, skip to the “Setting Up Access” section in this chapter. Note that the steps in this section may differ slightly depending on the version of WebSphere Portal you use (the steps in this section cover WebSphere Portal 6.0).
Setting Up a New Target Page To set up a new target page for your portlets, log in to the portal with a user who has access to add a page to the portal. If you do not have sufficient access, skip to the “Setting Up Access” section. After you are logged into the portal, navigate to the administration page from the Launch menu.
442
Appendix A Setting Up Your Environment On the administration page, press the Manage Pages link, as shown in Figure A.2.
Figure A.2
Selecting the Manage Pages link.
When the Manage Pages portlet displays, you need to navigate to where you want to add the new page. Select Content Root, and then select Home. The Manage Pages portlet now lists the pages contained in the topmost menu in the portal. You can add a page to this menu by pressing the New Page button. Alternatively, you can add a page to a different part of the portal (such as the Welcome page) by navigating to that page in the Manage Pages portlet, and then by pressing the New Page button. After you press the New Page button, the Page Properties portlet displays. Give your new page a title (for example, WPF Test Page). Press the OK button to save the new page and return to the Manage Pages portlet. The new page should display as shown in Figure A.3.
Figure A.3
The new page displayed in the Manage Pages portlet.
Configuring Lotus Domino
443
Leave the administration page by selecting Home from the Launch menu. You have now successfully added a page to the portal. Additional pages can be added through the same process. You can navigate to the page you just created by selecting it from the page navigation menu (if it is not immediately visible, you may need to navigate to its parent page first).
Setting Up Access Other than setting up a target page, you should make sure that you have access to add portlets to the target page and that you have access to deploy applications to the server. If you don’t have this access, you have to give your Web Application Archive (WAR) files to an administrator so that he can deploy them and add them to the portal for you—a highly undesirable arrangement in a test environment, but a common occurrence in production environments (where it is assumed that the updates are less frequent). The best way to get all the required permissions is to use an administrator account—that is, a user (such as the wpsadmin user, which is normally set up when installing the portal) who is a member of the administrators group in the portal (usually called wpsadmins). If you are unable to get an administrator account, you may be able to request an administrator to assign you the appropriate access in an ordinary user account. In WebSphere Portal, the access levels you need are as follows: at least Privileged User access to the Pages resource if you’re going to add your page to a private page, or at least Editor access to the Pages resource if you’re going to add your page to a shared page. To deploy and update your applications, you need to have Manager access to the Web Modules resource. If you can’t get the appropriate access, you need to get an administrator to deploy and add your portlets to the portal. In this case, it is worthwhile to set up a local portal server for testing. If this is not possible, then you should set up a local application server, which you can use to test your portlets as ordinary Web applications (although note that you won’t be able to test any features offered by the portal itself—such as inter-portlet communication—until you’ve deployed the application to a portal server). The WAS CE install that comes with WPF is a perfect candidate for this.
Configuring Lotus Domino If you do not intend on building any applications that require a Lotus Domino server (such as the example discussed in Chapter 4, “Using Domino Data”), you can skip this section. Several steps are required to set up communication between WPF and a Domino server: 1. Start the Domino Internet Inter-Orb Protocol (DIIOP) and HTTP tasks. 2. Enable HTTP clients to browse databases. These steps are covered in the following sections. (It is assumed that you already have a Domino server installed and the Domino Designer and Domino Administrator clients. Release 6 or later is recommended for both the Domino server and the clients.)
444
Appendix A Setting Up Your Environment
Start the DIIOP and HTTP Tasks WPF uses DIIOP to communicate with Domino by default, although you can specify to use HTTP instead. To use them, both of these protocols require a corresponding task to run on the Domino server. Domino servers earlier than version 6.x require you to have both tasks running; otherwise, you need to enable only one or the other. It is useful, however, to have both tasks running regardless of the version you use, so that you can use the connectivity test page provided by WPF (this test page is discussed in Chapter 4). This section describes how to start both tasks.
TIP During development, you can configure which port you want to use for Domino connectivity in WPF via the Use HTTP Access field of the Domino Data Access builder.
To start the DIIOP and HTTP tasks on the Domino server, complete these steps: Using a text editor such as Notepad, open the notes.ini file for the server you want to use to communicate with WPF. The notes.ini file is located in the root directory of your Domino server installation (for example, C:\Program Files\Domino). Do a search for the text, “servertasks”. The result of the search should show a line similar to the following: ServerTasks=Update,Replica,Router,AMgr,AdminP,CalConn,Sched
Change this line to include entries for HTTP and DIIOP, as shown in the following example (note that if there are already entries for HTTP and DIIOP, there is no need to add them again): ServerTasks=Update,Replica,Router,AMgr,AdminP,CalConn,Sched, ¯DIIOP,HTTP
Save and close the notes.ini file. Doing so loads the HTTP and DIIOP tasks the next time Domino starts. To load the HTTP and DIIOP tasks, you can either restart the Domino server (by typing restart server at the Domino console) or type the following commands into the Domino console: load HTTP and then load DIIOP. You can stop these tasks by using the commands tell HTTP quit and tell DIIOP quit (if you want to stop them from being loaded when the server restarts, take the corresponding entries out of the ServerTasks line from the notes.ini file).
Enable HTTP Clients to Browse Databases Enabling HTTP clients to browse databases enables you to browse the databases on the server from within the WPF Designer (you need to complete this step even if you use DIIOP instead of HTTP).
Configuring Lotus Domino
445
To enable HTTP clients to browse databases, first open up the Domino Administrator client and make sure it points to the server that will communicate with WPF. You can change this server by selecting Open server from the File menu. If you don’t have the Administrator client installed, open a Lotus Notes client, and then open the names.nsf database on the server that will communicate with WPF. Open the server document for the server that will communicate with WPF. If you use the Administrator client, this should be done by clicking Current Server Document on the Configuration tab, as shown in Figure A.4.
Figure A.4
Opening the server document from the Administrator client.
If you use the Administrator client, open the server document by double-clicking the server document from the All Server Documents view, as shown in Figure A.5.
Figure A.5
Opening the server document from the Notes client.
Press the Edit Server button at the top of the document to edit the server document, and then navigate to the Internet Protocols tab. Scroll down to the R5 Basics section, and make sure the setting ‘Allow HTTP clients to browse databases’ is set to Yes (note that this should still be done, even if you use a release of Domino later then R5). Save and close the server document. Restart the server by typing restart server at the Domino console. Your Domino environment is now configured and should be accessible from WPF. Chapter 4 discusses how to set up a WPF project to connect to a Domino server.
446
Appendix A Setting Up Your Environment
Creating a Test Database in DB2 The rest of this appendix concerns the application discussed in Chapter 3, “Using Data from a Relational Data Source.” If you do not intend to build the Chapter 3 example, you do not need to read the rest of this appendix. Complete the steps in this section if you intend to build the application discussed in Chapter 3 and if you want to connect to a DB2 database. If you intend to build this application but want to use an SQL Server database instead, skip to the next section, “Creating a Test Database in SQL Server.” This section covers how to create a DB2 database for the example in Chapter 3. This database will contain a list of basic contact information. There is no strict requirement that you use this particular database, so feel free to use another database if desired (but note, of course, that certain steps in the example will need to be adjusted accordingly). There is no restriction on whether the DB2 server is installed locally or remotely to your application servers and portal servers. To create the contacts database in DB2, first start the DB2 Command Editor from the Start menu in Windows, and then copy and paste the contents of DB2TESTDB.sql file into the topmost edit pane in the Command Editor (you can obtain this file from the Appendix A folder at ibmpressbooks.com/title/9780137134465). If you do not have access to the DB2TESTDB.sql file, copy the following text into the Command Editor: — Create and connect to database CREATE DATABASE TESTDB; CONNECT TO TESTDB; — Create contacts table CREATE TABLE “ADMINISTRATOR”.”CONTACTS” ( “ID” INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), “FIRSTNAME” VARCHAR(15) NOT NULL, “LASTNAME” VARCHAR(15) NOT NULL, “COMPANY” VARCHAR(20), “ADDRESS” VARCHAR(30), “PHONE” VARCHAR(15), “EMAIL” VARCHAR(30), “FAX” VARCHAR(15) ) IN “USERSPACE1”; COMMENT ON TABLE “ADMINISTRATOR”.”CONTACTS” IS ‘A list of ¯contacts used to test use of DB2 in WPF’; ALTER TABLE “ADMINISTRATOR”.”CONTACTS” ADD CONSTRAINT “CONTACT_ID” PRIMARY KEY (“ID”); COMMIT;
Before you execute the script, substitute the four occurrences of the word ADMINISTRATOR with a user who has access to create databases, add tables, and insert records on the DB2 server. Press the button to execute the SQL script. Running the script creates a DB2 database called TESTDB. It contains a table called CONTACTS. The columns in the CONTACTS table contain various items of information about each contact, including an automatically generated primary key (that is, a value that uniquely identifies each contact). The script also adds five contacts to the table. The results of running the SQL script display in the bottom pane of the Command Editor. Scroll through the results and ensure they indicate that each command was performed successfully—if this is not the case, make sure you entered the SQL script correctly and that you have enough access to create new databases and tables. You can test whether the script worked by selecting TESTDB from the Target dropdown and by typing the following SQL into the Command Editor (substituting the name ADMINISTRATOR with the name you used earlier): SELECT * from “ADMINISTRATOR”.”CONTACTS”
Press the button and you should receive the output shown in Figure A.6. You now have a database you can use to complete the example in Chapter 3, but you need to add a JDBC resource for it before you can use it in WPF (unless you are using WAS CE, in which case, you need to set up a database pool instead. Setting up a database pool is covered in the Reference, WAS CE Reference and Troubleshooting section in the WPF Designer help). If you are not using WAS CE, skip to the “Configuring a JDBC Resource” section at the end of this appendix.
448
Figure A.6
Appendix A Setting Up Your Environment
Testing that the SQL script worked correctly.
Creating a Test Database in SQL Server Complete the steps in this section if you intend to build the application discussed in Chapter 3 and you want to connect to an SQL Server database. If you intend to build this application but want to use a DB2 database, complete the previous section instead. This section covers how to create an SQL Server database for the example in Chapter 3. This database contains a list of basic contact information. There is no strict requirement that you use this particular database, so feel free to use another database if desired (but note, of course, the steps that refer to the database in the example will need to be adjusted accordingly). There is no restriction on whether the SQL Server is installed locally or remotely to your application servers and portal servers. To create the contacts database in SQL Server, run the SSTESTDB.sql script on a machine with the SQL Server Management Studio installed by double-clicking the SSTESTDB.sql file (you can obtain this file from the Appendix A folder at ibmpressbooks.com/title/ 9780137134465). If you do not have access to the SSTESTDB.sql script, you should create an empty file with the same name and copy in the contents shown below (save the file and run it when finished): — Create database USE [master] GO CREATE DATABASE TESTDB GO USE [TESTDB] GO
Creating a Test Database in SQL Server
449
BEGIN TRANSACTION GO CREATE TABLE [ADMINISTRATOR].CONTACTS ( ID int NOT NULL IDENTITY (1, 1), FIRSTNAME varchar(15) NOT NULL, LASTNAME varchar(15) NOT NULL, COMPANY varchar(20) NULL, ADDRESS varchar(30) NULL, PHONE varchar(15) NULL, EMAIL varchar(30) NULL, FAX varchar(15) NULL ) ON [PRIMARY] GO ALTER TABLE [ADMINISTRATOR].CONTACTS ADD CONSTRAINT CONTACT_ID PRIMARY KEY CLUSTERED ( ID ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] GO —Add contact records INSERT INTO [ADMINISTRATOR].CONTACTS ([FIRSTNAME],[LASTNAME], [COMPANY],[ADDRESS],[PHONE],[EMAIL],[FAX]) SELECT ‘Bob’, ‘Jones’, ‘12 Elm St’, ‘Bobs Plumbing’, ‘555-2323’, ‘bob@bobs_plumbing.com’, ‘555-2333’ UNION ALL SELECT ‘Sally’, ‘Williams’, ‘23 Millewa Ave’, ‘Acme Co.’, ‘747-7767’, ‘swilliams@acme.com’, ‘747-7769’ UNION ALL SELECT ‘James’, ‘Cummings’, ‘46 Church Rd’, ‘Ties r Us’, ‘555-1187’, ‘jcummings@tiesrus.com’, ‘555-1180’ UNION ALL SELECT ‘Haley’, ‘Smith’, ‘46 Church Rd’, ‘Ties r Us’, ‘555-1188’, ‘hsmith@tiesrus.com’, ‘555-1180’ UNION ALL SELECT ‘Patrick’, ‘Johnson’, ‘23 Millewa Ave’, ‘Acme Co.’, ‘747-7768’, ‘pjohnson@acme.com’, ‘747-7769’ GO COMMIT
450
Appendix A Setting Up Your Environment
After you run the SSTESTDB.sql script, you are prompted to authenticate with SQL Server—make sure your server settings are correct and press the Connect button to connect to the server and open the SQL Server Management Studio. Before you execute the script, you need to change the three occurrences of the word ADMINISTRATOR to the name of a user who has access to create databases, add tables, and insert records on the SQL Server. When you have finished making these changes, press F5 to execute the SQL script. Executing the script creates a SQL Server database called TESTDB; it contains a table called CONTACTS. The columns in the CONTACTS table contain various items of information about each contact, including an automatically generated primary key (that is, a value to uniquely identify each contact). The script also adds five contacts to the table. The results of running the SQL script display in the Messages tab in the bottom pane of the Management Studio. You should get a message saying that five rows were affected (these are the contacts you added to the CONTACTS table). Make sure the results don’t include any error messages (text printed in red). If you do get errors, check that you correctly changed the ADMINISTRATOR name to the name of a user that has sufficient access to complete all of the operations in the SQL script. You can test whether the script worked by pressing Ctrl+N to create a new query, selecting TESTDB from the database drop-down box on the toolbar, and then typing the following SQL into the SQL editing pane (substituting the name ADMINISTRATOR with the name you used earlier): SELECT * from ADMINISTRATOR.CONTACTS
Press F5 to execute the script, and you should see the table structure in the Results tab of the Management Studio, as shown in Figure A.7.
Figure A.7
Testing that the SQL script worked correctly.
You now have a database that you can use to complete the example in Chapter 3, but you need to add a JDBC resource for it before you can use it in WPF (unless you are using WAS CE, in which case you will need to set up a database pool instead. Setting up a database pool is covered in the Reference, WAS CE Reference and Troubleshooting section in the WPF Designer help). The next section explains how to set up a JDBC resource.
Configuring a JDBC Resource
451
Configuring a JDBC Resource Complete the steps in this section if you intend to build the application discussed in Chapter 3 and you have already created a contacts database. Note that if you use WAS CE, you need to set up a database pool instead, which is covered in the Reference, WAS CE Reference and Troubleshooting section in the WPF Designer help. To connect to a relational data source from WPF, you need to set up a JDBC resource for it on an application server (the JDBC resource is what you connect to from inside WPF). You can use the server specified in either of your deployment configurations, but the JDBC settings on the server specified in your application server deployment configuration are used to automatically populate suggested options for some of the builder inputs, so it is recommended that you use this server. To set up a JDBC resource on an application server, first open the administration console for the server on which you would like to set up the JDBC connection. For example, for a default configuration on a local portal server, you might use https://localhost:10039/ibm/ console/.
TIP In a WebSphere Portal installation, both the server1 and WebSphere_Portal instance have corresponding administration consoles. Although the exact URLs might differ depending on your configuration, the administration consoles can usually be accessed by adding one to the port number and changing the protocol to use https. For example, this address: http://localhost:10038/wps/portal would become: https://localhost:10039/ibm/console/
After the administration console has opened, navigate to the JDBC Providers link under the Resources heading, as shown in Figure A.8.
Figure A.8
Navigating to the JDBC Providers link.
452
Appendix A Setting Up Your Environment
Make sure the application server you want to use is listed in the Server box (you can specify a server by clicking the Browse Servers button, selecting the server you want, and then pressing OK). The driver providers available on that server will be listed below the server box. If you already have a driver provider for the type of database you are connecting to (DB2 or SQL Server), click on the provider from the list and skip to the “Configuring the Driver” section. Otherwise, you will need to create a driver provider for the appropriate database type. Press the New button as shown in Figure A.9. If you are setting up a driver provider for an SQL Server database, skip to the “Setting Up a SQL Server Driver Provider” section. If you are setting up a driver provider for a DB2 database, continue to “Setting Up a DB2 Database Driver Provider.”
Figure A.9
Creating a new driver provider.
Setting Up a DB2 Database Driver Provider To set up a driver provider for a DB2 database, select DB2 as the database type. Then, select DB2 Universal JDBC Driver Provider for the provider type, and Connection pool data source for the implementation type. Click Next when you are finished. The next screen enables you to configure details for the provider. Accept the defaults and press OK. On the confirmation screen that follows, press the Save link, and then press the Save button on the screen that follows to save the new provider.
Configuring a JDBC Resource
453
The driver provider you just created will display in the driver providers list. Unless you change the default name for the provider, the DB2 provider is called DB2 Universal JDBC Driver Provider. Click on the driver provider you just created, which opens the configuration page for the provider. Skip to the section “Configuring the Driver.”
Setting Up a SQL Server Driver Provider To set up a driver provider for an SQL Server database, select SQL Server as the database type. Then, select WebSphere embedded ConnectJDBC driver for MS SQL Server for the provider type, and Connection pool data source for the implementation type. Click Next when you are finished. The next screen enables you to configure details for the provider. Accept the defaults and press OK. On the confirmation screen that follows, press Save, and then press the Save button on the screen that follows to save the new provider. The driver provider you just created now displays in the driver providers list. Unless you change the default name for the provider, the SQL Server provider is called ‘WebSphere embedded ConnectJDBC driver for MS SQL Server’. Click the driver provider you just created, which opens the configuration page for the provider.
Configuring the Driver On the provider configuration page, click the Data source link (under the Additional Properties heading), as shown in Figure A.10. This displays the data sources currently configured for the provider.
Figure A.10
Clicking the Data sources link.
The next step is to add a data source for the TESTDB database. To do this, first press the New button as shown in Figure A.11. Change the name, JNDI name, and description of the data source, as shown in Figure A.12. Note that the JNDI name has a jdbc/ prefix, which is required by WPF (WPF picks up JDBC data sources with the jdbc/ prefix only). If your provider is for a SQL Server database, skip to the “Configuring a SQL Server Data Source” section. If your provider is for a DB2 database, continue to the next section, “Configuring a DB2 Data Source.”
454
Appendix A Setting Up Your Environment
Figure A.11
Creating a new data source.
Figure A.12
Setting the name, JNDI name, and description for the TESTDB data source.
Configuring a DB2 Data Source Change the DB2 Universal data source properties, as shown in Figure A.13 (specifying a different hostname if your DB2 server is not on localhost). These settings define where to obtain the TESTDB database from. Press OK when you are finished. The new data source for the TESTDB database should be listed as shown in Figure A.14. Press the Save link on the confirmation screen, then press the Save link on the screen that follows (as highlighted in Figure A.14) to save the data source settings. Skip to the “Configuring an Authentication Mechanism” section when finished.
Configuring a JDBC Resource
Figure A.13
Setting the DB2 Universal data source properties.
Figure A.14
Saving changes to the data source.
455
Configuring a SQL Server Data Source To configure the SQL data source, change the Connect JDBC data source properties, as shown in Figure A.15 (specifying a different hostname if SQL Server is not running on localhost). These settings define where to obtain the TESTDB database from. Press OK when finished.
456
Figure A.15
Appendix A Setting Up Your Environment
Setting SQL Server data source properties.
The new data source for the TESTDB database should be listed as shown in Figure A.16. Press the Save link on the confirmation screen, then press the Save button on the screen that follows (as highlighted in Figure A.16) to save the data source settings.
Figure A.16
Saving changes to the data source.
Configuring an Authentication Mechanism The next step is to configure an authentication mechanism for the data source. Click the TESTDB data source in the list, and then click the J2EE Connector Architecture (J2C) authentication data entries link under the Related Items heading, as shown in Figure A.17.
Configuring a JDBC Resource
Figure A.17
457
Specifying an authentication mechanism.
Click the New button to create a new authentication entry, then fill out the general properties as shown in Figure A.18 (the username and password fields should correspond to a user that has access to either the DB2 or SQL Server database). Press OK when you are finished.
Figure A.18
Specifying credentials for the authentication mechanism.
Don’t save the authentication entry just yet—first open the TESTDB data source again by clicking the TESTDB link, and then scroll down to the ‘Component managed authentication alias’ area. Select the authentication alias you just created from the dropdown, and then press OK. Press the Save link on the confirmation screen that follows, and then press the Save button to save the data source. You have now successfully created and configured a data source for the TESTDB database. If you are using a SQL Server database, you can skip to the “Testing the Data Source” section to
458
Appendix A Setting Up Your Environment
test the data source. Otherwise, one final step that is required before you can test the data source is to make sure your DB2 environment variables are set correctly, as these will be used to locate your DB2 drivers (DB2 environment variables are not set by default in WebSphere Portal 6). To do this, navigate to the WebSphere Variables page, as shown in Figure A.19. On the next screen, if there is a server specified in the Server box, remove it and press the Apply button to make sure you are looking at all the environment variables for the current node.
Figure A.19
Navigating to the WebSphere Variables page.
Scroll down through the list to find the DB2UNIVERSAL_JDBC_DRIVER_PATH environment variable, then make sure it points to a directory that holds a file called db2jcc.jar. You can find this file by searching for it on the machine running DB2 (by default, it is located in the \Program Files\ibm\SQLLIB\java directory). If you need to change an environment variable, you can click on it in the list, change the Value field, and then press OK. Your data source is now ready to test.
Testing the Data Source To test your connection to the TESTDB data source, press the Test connection button, as shown in Figure A.20. You should now see a success message, as shown in Figure A.21. If error messages are returned, check that the credentials you used to connect to the TESTDB database are correct, and that you have set the appropriate environment variables correctly.
Configuring a JDBC Resource
Figure A.20
Testing the connection to the TESTDB data source.
Figure A.21
Successful test of the TESTDB data source.
459
NOTE Before the TESTDB becomes available in WPF, you need to restart the application server.
You have now configured a JDBC data source for the contacts database, and you are ready to start building the example in Chapter 3.
This page intentionally left blank
A
P P E N D I X
B
Portlet Factory Properties
This appendix lists and describes the properties files included with WebSphere Portlet Factory (WPF) and it highlights some of the most important and commonly used settings contained in them. The following areas are covered in this appendix: • Properties files • Using properties
Properties Files WPF uses a number of text-based properties files, which contain settings that alter the way your application behaves. In most cases, you don’t need to change these files, as the default settings are sufficient. However, changing these files can help you fine tune and tweak your applications, so it is a good idea to spend time familiarizing yourself with some of the more important settings. You can find these files in the WebContent/WEB-INF/config directory of any WPF project (stored with the extension .properties).
TIP Although you can change any of the properties files directly, you should always make your changes in an override.properties file and leave the original files intact. Any settings in an override.properties file will override those in other properties files, and keeping all of your changes in one place will enable you to easily see what you have changed (and revert back to the default settings if necessary). Also, using an override.properties file will prevent your settings from being overwritten by new installs or upgrades of WPF.
461
462
Appendix B Portlet Factory Properties
Although there are other files in the config directory used to configure your project (such as the selection handler configuration files that are discussed in Chapter 12, “Profiling Portlets”), only files with a .properties extension are considered properties files. Properties contained in properties files are the only settings that can be overwritten using an override.properties file. The following sections list the properties files used in WPF and provide brief descriptions of how each file is used.
bowstreet.properties, cluster.properties, and server.properties These files store general settings for your WPF application and contain the majority of the properties used in WPF. These are the properties files you are most likely to change when configuring your WPF application. For example, the bowstreet.properties file contains project directory and file locations, and the cluster.properties file stores settings for file uploads and Simple Object Access Protocol (SOAP). The server.properties file contains properties that specify hostnames and port numbers (this is particularly useful if you want to configure WPF to run through a proxy).
jdbcDrivers.properties, pageprocessors.properties, persistentstore.properties These files store settings for the Java Database Connectivity (JDBC) drivers used in WPF. You should leave these files with their default values, as they do not contain any documented settings that can be modified. Note that the data source settings in these files are independent from the data source settings found in builder calls (you should use the builder settings wherever possible).
log4j.properties This file stores advanced settings pertaining specifically to the log4j logging capabilities of WPF. You do not normally need to change this file, but doing so enables you to set such things as log file truncation thresholds and logging categories.
logging.properties This file contains several high-level settings for logging in WPF, mainly for logging server statistics and enabling or disabling particular logging filters.
migrate-profilesets.properties This file contains settings for migrating your properties files into a relational data store, such as Oracle, SQL Server, or DB2. Normally you migrate only your profile sets if you want to enable users to edit profile values at runtime, but do not want to make the Edit or Configure modes available. In addition to these files, projects with the Lotus Collaboration Extension feature set also have a domino_config directory under the config directory, which you should use to store your Domino server connection properties files (similar directories are also created when you use any
Properties Files
463
of the other Integration Extension feature sets, such as the SAP Extension). By default, WPF creates a file called default_domino_server.properties in this directory, which you need to configure to point to your Domino server for any of the Domino builders to work correctly. You can make copies of this file to store settings for different servers, and a single project can contain as many configuration files as there are Domino servers in your environment. Setting up WPF to connect to Domino is discussed in the “Configuring Lotus Domino” section of Appendix A. Changes to properties files normally take effect instantly; however, certain settings require the server where the application is deployed to be restarted. These settings are listed in Table B.1. Table B.1
Using Properties This section lists some of the more useful functions you can perform by modifying properties in the WPF properties files. Remember that you should not modify any of the default properties files directly, but use an override.properties file instead (stored in the WebContent/WEB-INF/config directory or in the plugins/com.bowstreet.designer.core[version]/config directory if you want the properties to be overridden only when your application is run from WPF Designer). The following functions are covered in this section: • Domino server settings • File upload settings
Using Properties
465
• Specification of an alternate compiler • Dynamic class loading • SOAP and proxy access for Web services • Errors caused by file length limitations • The WPF cache • General logging • Debug tracing • Event logging • Page automation
TIP An override file doesn’t exist by default, so if you want to change any properties, you should create a file called override.properties in the WebContent/WEB-INF/config directory. Another copy of the override file is used in WPF; it is stored by default in the plugins/com.bowstreet.designer.core[version]/config directory where WPF was installed (where [version] is the version of your WPF installation). This file overrides properties only when your application is run from the WPF Designer.You can specify an alternative to this second override file for all the projects in your current workspace by selecting Window, Preferences, and then by changing the Override Properties File Location setting under the WebSphere Portlet Factory Designer heading.
Domino Server Settings If you want to connect to a Domino server from WPF, you need to modify the properties shown in Table B.2, which can be found in the default_domino_server.properties file. By default, this file is linked to from builder calls that require Domino connectivity; alternatively, you can use a different file, as long as you change the Properties file input in the relevant builder calls. The directory that contains the default_domino_server.properties file, WebContent/WEB-INF/config/domino_ config, exists only when your project has the Lotus Collaboration Extension feature set added. The ServerName property specifies the hostname for the Domino server you want to connect to. Note that the port number must be included if you are not connecting to Domino via port 80 (WPF uses DIIOP on port 63148 to connect to Domino by default). The Username and Password properties specify the credentials of a Domino user with access to browse databases on the Domino server.
466
Appendix B Portlet Factory Properties
Table B.2 Configuring Domino Server Settings Properties File Property default_domino_server.properties
Example Setting
ServerName
domino.local.com:63148
Username
dominoAdmin
Password
password
File Upload Settings Before you can use the File Upload builder in WPF, you need to enable file uploading by setting the bowstreet.upload.enabled property to true. The bowstreet.upload.destinationPath property defines the path used to store uploaded files, and the bowstreet.upload.maxFileSizeK property defines the maximum file upload size in Kb. Finally, the bowstreet.session.semaphore.timeOut property defines a time limit (in seconds) that is used for file upload requests. The settings used to configure file uploading are shown in Table B.3.
Specifying an Alternate Compiler If you have multiple JDKs available to you, you can specify an alternate compiler for your Java code and JSPs by setting the property shown in Table B.4. This can be useful when you want to use features from a particular JDK version.
Table B.4 Specifying an Alternate Compiler Properties File Property cluster.properties
bowstreet.methods.compiler
Example Setting C:/jdk131/bin/javac.exe
Dynamic Class Loading In WPF, it is possible to dynamically load classes that aren’t on your classpath. Dynamically loaded classes can be reloaded without needing to restart your application, which can help speed
Using Properties
467
up the development process. Note that dynamic class loading with a large number of classes can affect the performance of the application itself, but this effect is usually negligible. As a best practice, however, it is recommended that you only enable dynamic class reloading in development environments, and disable it when deploying into production. The settings used to configure dynamic class loading are shown in Table B.5. The bowstreet.dynamic.class.load.checkTimestamp property specifies whether dynamic class loading is enabled. A value of false disables dynamic class loading; a value of true causes a timestamp check to be performed whenever a Java class or JSP is accessed. This check then determines whether the resource needs to be reloaded. The bowstreet.dynamic.class.load.path property determines the directories (separated by commas) from which Java classes are dynamically loaded.
Table B.5 Configuring Dynamic Class Loading Properties File Property bowstreet.properties
SOAP and Proxy Access for Web Services The properties in the bowstreet.properties file shown in Table B.6 enable you to specify settings (hostname, port, username, and password) for a proxy server to use when retrieving WSDLs or requesting access to Web services. Proxy settings can also be configured under the Advanced section of the Web Service Call builder. The cluster.properties settings shown in Table B.6 are for configuring the use of Simple Object Access Protocol (SOAP) in WPF, which is used to communicate with Web services. The bowstreet.soap.enabled property specifies whether SOAP messages are used when communicating with Web services (disabling this property renders the values of the other properties redundant). The bowstreet.soap.wsdl.fetchTimeout property specifies the default timeout (in seconds) on the fetch WSDL operation performed from the Web Service Call builder. The bowstreet.soap. targetModelURIPrefix property specifies a default targetNamespace for WSDLs generated from your model, and the bowstreet.soap.httpErrors property specifies whether to send HTTP Errors as part of the SOAP fault envelope (SOAP fault envelopes are part of the response sent back from a called Web service).
468
Appendix B Portlet Factory Properties
Table B.6 Configuring SOAP and Proxy Access for Web Services Properties File PropertyExample Setting server.properties
cluster.properties
bowstreet.soap.proxyHost=
melbourne.local.com
bowstreet.soap.proxyPort=
6789
bowstreet.soap.proxyUser=
proxyUser
bowstreet.soap.proxyPassword=
password
bowstreet.soap.enabled
true
bowstreet.soap.wsdl.fetchTimeout
60
bowstreet.soap.targetModelURIPrefix
http://wpf.ibm.com/2002/03/ models/
bowstreet.soap.httpErrors
true
Avoiding Errors Caused by File Length Limitations Some application servers have a limit of 255 characters on the length of application path names. If you experience problems caused by the path name of your application being too long, you can change the property in Table B.7 so that WPF generates the profile name portion of your path name (not the entire path name) as a relatively compact and uniquely identifying string (provided it exceeds the value of the bowstreet.profiles.maxKeyLength property). Note that using shorter model and action names can also help to circumvent errors caused by file length limitations.
The WPF Cache WebApps, models and the output of certain actions can all be cached in WPF, and this cache can be configured using the properties in Table B.8. The bowstreet.cache.output.disable property enables or disables the caching of output from your application (you can cache actions in your application using the Cache Control builder). The bowstreet.cache.model.size property sets the total number of models and WebApps you can have in the cache at any one time. When changing this setting for an application that uses profiling, keep in mind that you may have multiple WebApps for a single model. Finally, the bowstreet.cache.model.timeOutMinutes property specifies how many minutes the unused models will stay in the cache before they are discarded.
Using Properties
469
Table B.8 Configuring the WPF Cache Properties File Property bowstreet.properties
Example Setting
bowstreet.cache.output.disable
false
bowstreet.cache.model.size
30
bowstreet.cache.model.timeOutMinutes
20
log4j Logging Many of the logging properties in WPF define log4j logging categories and utilize the log4j format. The first value of a logging category property is the priority level, which defines the level of information to be returned for that category. In order of restrictiveness, these priority levels are DEBUG, INFO, WARN, ERROR, and FATAL, with DEBUG being the most inclusive logging level and FATAL being the most restrictive. The other values in a logging category property define appenders and layouts. An appender declares an output destination for logging information (such as Console), and a layout determines how the information should be presented (such as ModelActions). Note that custom logging categories can also be specified using the Logging Category builder. Table B.9 shows some of the properties used for configuring log4j logging in WPF. The log4j.rootCategory property is a log4j category property that defines the default priority level, appenders, and layouts for WPF logging. The log4j.logger.bowstreet.system.out property defines the log4j category for System.out calls made from the Action List builder.
TIP You can make System.out calls from the Action List builder by selecting Special/SystemOut from the Select Action dialog, which you open by pressing the Ellipsis button next to an action in the Action List builder.
The remaining properties in Table B.9 define logging levels for several useful areas of WPF. Many of these categories have a default value of WARN, which means that no output is generated, so you need to increase these levels to get logging output. For more information on these properties, see the WPF Help under Reference, Property Files, Log4j properties.
470
Appendix B Portlet Factory Properties
Table B.9 Configuring General Logging Properties File Property log4j.properties
Debug Tracing Debug tracing enables you to automatically log information about different parts of your application, and it is enabled by adding the Debug Tracing builder to a model in your project. Debug tracing information is written to the debugTracing.txt file in the WebContent/WEB-INF/logs directory, the application server’s console window and also to the System.out file on the application server. See Chapter 14, “Error Handling, Logging, and Debugging,” for more information on logging and debug tracing in WPF. It is recommended that you set the default debug tracing information level to DEBUG to receive more detailed information. You can do this by setting the property shown in TableB.10 to DEBUG,Console,DebugTracing.
Event Logging An event in WPF logging refers to when a logging message is generated. Table B.11 shows some of the properties you can use to configure event logging. The logging.driver.eventSink.directory property declares the location of your event log files (relative to the WPF installation directory). Changes to this property do not take effect until you restart the application. The logging.driver.eventSink.prefix property defines the prefix for event log files, and the logging.driver.eventSink.suffix property defines the suffix (event log files have the format: prefix + timestamp + suffix). The logging.driver.eventSink.maxSize property defines the maximum size (in Kb) of your event log files—if a file exceeds this limit, it no longer has to be written to and a new file is created. The logging.event.flush.interval property defines the time interval (in seconds) that WPF waits before writing messages to the log file. You can also use the logging.event.criterion.abnormal.flushImmediately property to define when logging messages are written to the log file, which overrides the logging.event.flush.interval property.
Server Statistics Logging A number of server statistics can be logged in WPF, which are configured via the logging.properties file. Table B.12 shows some of the properties you can use to configure server statistics logging. The logging.serverStats.enabled property determines whether server statistic logging is enabled, and the logging.serverStats.snapshotInterval property specifies an interval (in seconds) to poll a server for statistics. The log4j.additivity.bowstreet.system.server.logging.serverStats property denotes whether server stats are written only to the server stats file and not to the System.out log. The log4j.logger.bowstreet.system.server.logging.serverStats property specifies a logging level for server stats (INFO is the recommended level). Server statistics are discussed in more detail in Chapter 14 of this book.
472
Appendix B Portlet Factory Properties
Table B.12 Configuring Server Statistics Logging Properties File Property logging.properties
Page Automation The automation of constructing Web page interfaces (and how information in these interfaces is formatted and stored) is a major feature of WPF, and a number of builders are available (such as the Data Page builder and Data Field Modifier) to configure this process. There are also a number of properties that can be used to configure page automation, which are shown in Table B.13. The first six properties in Table B.13 define the way the Date, Time, and DateTime data types are stored and displayed. The bowstreet.pageautomation.date.data property defines the way Date datatypes are stored, and the bowstreet.pageautomation.date.display property defines the way they are displayed. The bowstreet.pageautomation.dateTime.data property defines the way DateTime data types are stored, and the bowstreet.pageautomation.time.data property defines the way they are displayed. Finally, the bowstreet.pageautomation.time.data property defines the way Time datatypes are stored, and the bowstreet.pageautomation.time.display property defines the way they are displayed. The bowstreet.pageautomation.date.yearAdjustCutoff property is used to parse dates for different centuries, primarily when two-digit year values have been specified. Any year values less than the value of this property have 2000 added to them (it is assumed that the date is in the twenty-first century); year values that are greater than the value of this property have 1900 added to them (it is assumed that the date is in the twentieth century). This property should be commented out when working with dates prior to the twentieth century. Finally, the bowstreet.pageautomation.force_separate_leaf_when_repeated property is used to specify whether repeated elements in XML variables should be displayed separately (a value of true) or separated with commas (a value of false).
This glossary provides brief definitions of the main technical terms used in this book.
Action A command that can be run from a builder call. There are a number of actions available by default (such as Assignment and System Out) but actions can also be defined using builder calls (such as Method, Web Service Call, Action List, or Page builder calls). Actions are called from builder inputs—for example, the Button builder has an Action builder input that lets you specify the action that is run when the button is clicked. Ajax See Asynchronous JavaScript and XML. Ant script An XML file often used to automate the process of building Java projects. Requires the Ant software library from Apache, which is an open source library that is freely available from the Apache website (http://ant.apache. org).
API See Application Programming Interface. Appender An entry in a log4j property that defines an output destination for logging information. Applet A small program that is run on a client (such as a Web browser), as opposed to running on a server (as a servlet does), that can be run from WPF applications via the Applet builder. Application In the context of WPF, an application is a set of deployable files (stored in a WAR file) that can be run on an application server or portal server. There are two types of WPF applications: a portal application, which can contain one or more portlets that are hosted on a portal server and accessed through a portal, or a Web application, which is hosted on an application server and is accessed without having to go through a portal.
475
476
Application Programming Interface (API) A code-based interface for accessing a computer program’s functionality. For example, portlets built to the JSR-168 standard utilize a particular collection of methods and classes (the JSR-168 portlet API) on the backend, which provides access to certain functionality (such as accessing user configuration options for the portlet). Application server A server (such as WebSphere Application Server) that runs applications (such as those stored in EAR or WAR files) and serves up the results to a client (such as a Web browser). Application servers often have features for managing the various parts of an application, such as security and data access. Application server deployment configuration A collection of settings for deploying one or more applications to a target application server. Note that either an ordinary application server or a portal server can be specified as a target in an application server deployment configuration. See also portal server deployment configuration and deployment configuration. Applied Profiles view In the WebSphere Portlet Factory perspective, this view enables you to test the effects of profiles on your application from within the WPF Designer. Asynchronous JavaScript and XML (Ajax) A technology that lets you update a Web page without having to reload the entire page. Ajax works by making JavaScript calls that communicate via XML data. The Ajax Region and
Glossary
Ajax Type-Ahead builder both let you work Ajax functionality into your WPF applications. Attribute Provides information about a tag or element in a markup language, such as HTML or XML. For example, the name attribute in this HTML fragment provides the name of the <span> tag: <span name=”navigation”>
See also element. BAT file See batch file. Batch file A file with a .bat extension that runs a sequence of commands when executed. Several useful batch files are provided with WPF in the WebContent/WEB-INF/bin directory in your project. BDEF The file extension for a builder definition. See builder definition. Breadcrumbs In the context of a portal, breadcrumbs are a style of navigation that gives users an indication of their current page location in the portal hierarchy. Browser See Web browser. Build Building a project can refer to one of two things. First, it can refer to the process of compiling project code (performed automatically when you save changes to a Java source file or
Glossary
when you use the Build commands under the Project menu in the WPF Designer). The product of this process is sometimes referred to as a build. Second, it can refer to the process of creating deployable WAR files (performed via the WPF menu options in the project context menu). These WAR files can then be deployed to an application server or portal server. Build path See Java build path. Builder A WPF artifact that has a wizard-like interface and that helps automate the development process. They can do anything from put a button on a form, to load a Web page, or to access a Web service. WPF comes with over 160 builders, and you can also create your own builders to automate common development tasks. Builder call An entry in a model that invokes a particular builder. Double-clicking a builder call in your IDE opens the builder’s interface in the Builder Call Editor. Builder definition A .bdef file that defines how to invoke a builder and how to display it when a developer wants to use it. You don’t normally need to worry about .bdef files unless you create your own builders. Builder input A field on a builder that enables developers to configure the builder’s settings, such as the number of rows to place on a page or the label for a button. Builder inputs can be profiled;
477
that is, the value of the input can be determined by a profile. Builder palette A dialog box used to add builder calls to a model; it can be accessed via the Cog icon in the Outline view. Business logic The rules governing an application that reflect the way the business is run. In WPF, business logic is usually implemented in action lists or in Java code. Code that handles workflow is an example of business logic. Cascading Style Sheet (CSS) A file with a .css extension that defines presentation styles for a document written in a markup language (such as HTML). Cascading style sheets can be referenced from the Style Sheet builder, through builder inputs, or directly from an HTML page. Category See logging category. Classpath Tells the JVM where to look for user-defined classes and packages. The classpath is normally defined in an environment variable called CLASSPATH. Configuration file See properties file. Construct An abstract type that WPF matches to certain HTML fragments. It contains one or more elements (or even other constructs) that define how HTML should be laid out and formatted. Constructs are specified in HTML templates.
478
Consumer See service consumer. Controller Refers to the part of an application that handles execution flow and responds to events. A controller is usually employed as part of a Model View Controller pattern. In WPF, the Factory controller servlet acts as a controller, routing off requests to your application. Criterion See logging criterion. CSS The file extension of a cascading style sheet. See Cascading Style Sheet. Data set A collection of data. Data sets are usually presented to users in WPF applications using the Data Page builder. Data source A repository for storing data, which WPF connects to and then processes in some way (usually by displaying the data on the screen in an easy to understand format). A data source could be anything from a complex database to a simple text file. You would normally connect to a data source in WPF using a service provider, or from an application server via a JDBC data source. Deploy In the context of WPF, refers to the process of updating a WAR file on an application server or portal server. In WPF, you can automatically deploy your applications (unless they are located on a remote machine and are not accessible through a mapped drive).
Glossary
Deployment configuration A collection of settings for deploying one or more applications to a target application server or portal server. See also application server deployment configuration and portal server deployment configuration. Deployment WAR file See Factory deployment WAR. Designer See WebSphere Portlet Factory Designer. Detail In WPF, an individual record in a result set returned from a data source. A collection of records is referred to as a list. Development WAR file See Factory development WAR. DIIOP See Domino Internet Inter-Orb Protocol. Document (Lotus Notes) Databases in Lotus Notes are made up of documents. A document is the rough equivalent of a record in a relational database and consists of a series of fields, which hold values that describe different attributes of the document. Documents are usually created using a form. Document (XML) See XML document. Dojo An open source JavaScript toolkit that facilitate dynamic functionality in Web applications.
Glossary
Domino Internet Inter-Orb Protocol (DIIOP) By default, WPF uses DIIOP to connect to Domino. In order to establish this communication link, the DIIOP task must be started on the Domino server. Alternatively, you can also configure WPF to communicate with Domino via HTTP. Domino server The main functions of a Domino server (often just referred to as Domino) are to serve up Lotus Notes databases and to run the various processes required to access and manage these databases. EAR See Enterprise Archive. Eclipse A Java-based IDE that can be used to build J2EE applications. A number of other IDEs are based on Eclipse, such as Rational Software Developer and Rational Software Architect. WPF installs into a variety of Eclipse-based IDEs. For a full list of IDEs supported by the version of WPF you are using, consult the WPF Release Notes. Editor A screen in an Eclipse-based IDE that lets you input information. You can save information in an editor, something you would not normally do from a view. The Model Editor and Profile Set Editor are the main editors used in WPF, although editors for plain text and XML documents are also often used. EJB See Enterprise Java Bean.
479
Element In the context of a markup language, a particular section of structured content denoted by a start tag and an end tag. For example, this HTML fragment defines a span element, bounded by a start tag and an end tag: <span name=”navigation”>
Elements are organized hierarchically; for example, an HTML element often encompasses a body element, which will usually, in turn, encompass other elements. See also attribute. Enterprise Archive (EAR) A file used to package several modules (such as WAR files) together. It is deployed and executed on an application server. When a deployment WAR is created, it is stored inside an EAR file. EAR files can be viewed using most compression utilities. Enterprise Java Bean (EJB) A server-side component used in many J2EE applications, usually for the purpose of handling business logic. Event (logging) See logging event. Event (portal) A trigger method in a portlet used to pass properties and notifications between portlets. In WebSphere Portal, an entity called the WebSphere Property Broker is often used to handle these events. Execution time Refers to when the JSPs in an application are executed on a server.
480
Extensible Markup Language (XML) A markup language used to hierarchically structure data. XML data is stored in an XML document, and these documents are used extensively in WPF (especially for communication between services). The structure of XML documents is normally defined by an XML schema. Factory Although the word factory is sometimes used to describe a method or class whose purpose is to create other objects, it is also used as shorthand in the context of WPF development to refer to WebSphere Portlet Factory itself. Factory controller servlet The Factory controller servlet (WebEngineServlet) handles requests to your application at runtime. All model requests must pass through the Factory controller before they reach any given model. Factory Deployment WAR An application created in WPF, normally deployed to a WebSphere Application Server. Deployment WARs are generally used to deploy non-portlet applications into production environments. Deployment options for the Factory Deployment WAR are specified in the application-server deployment configuration. Factory Development WAR A development version of a WPF application that contains useful design artifacts in development that you normally wouldn’t want to deploy to a production environment. Factory Development WARs must be deployed to an application server. Deployment options for the Factory Development WAR are specified in the application server deployment configuration.
Glossary
Factory generation engine The WPF mechanism used to generate WebApps from builder calls in a model. See also Generation time. Field A space on a form for a piece of information that can be modified by the user or by code on the form. Form Used to enter data into a computer system and consists of one or more fields. In HTML, forms are specified with the