65
Portlet Development Workbook Using the Standard API Tim Hanis , WebSphere Software Engineer, IBM August, 2006 © Copyright International Business Machines Corporation 2006. All rights reserved.

Portlet Development Workbookpublic.dhe.ibm.com/software/dw/wes/pdf/0608_hanis-PortletWorkbo… · Portlet Development Workbook Table of contents Introduction.....3

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

  • Portlet Development Workbook Using the Standard API

    Tim Hanis, WebSphere Software Engineer, IBM August, 2006 © Copyright International Business Machines Corporation 2006. All rights reserved.

    mailto:[email protected]

  • Portlet Development Workbook Table of contents Introduction ......................................................................................................................... 3

    About the portlet application you create ......................................................................... 4 Chapter 1 - The development environment......................................................................... 4 Chapter 2 - Creating the Portlet Project .............................................................................. 5

    Overview ......................................................................................................................... 5 Detail ............................................................................................................................... 6

    Chapter 3 - Database access from our portlet ..................................................................... 8 Creating the database and table....................................................................................... 9 Define the data source ................................................................................................... 10 Create a Contact bean in Application Developer .......................................................... 11 Create a database access class....................................................................................... 11 Modify portlet doView method..................................................................................... 14 Modify the view JSP ..................................................................................................... 14 Start the portal server and execute ................................................................................ 15

    Chapter 4 - Portlet configuration....................................................................................... 16 Modify portlet deployment descriptor........................................................................... 16 Modify portlet doCustomConfigure method................................................................. 17 Modify Configure mode JSP......................................................................................... 18 Modify portlet processAction method........................................................................... 19 Modify code to use the data source name from portlet preferences.............................. 19

    Chapter 5 - Portlet code clean up ..................................................................................... 20 Constants ....................................................................................................................... 21 User id of the logged-in user ......................................................................................... 22

    Chapter 6 - Portlet Edit mode: Part 1 ................................................................................ 24 Chapter 7 - Portlet Edit mode: Part 2 ................................................................................ 29 Chapter 8 - Portlet Edit mode: Part 3 ................................................................................ 34 Chapter 9 - Enhanced View mode..................................................................................... 39 Chapter 10 - Required reading .......................................................................................... 42 Chapter 11 – Design considerations. Now? ..................................................................... 43

    Portlet state.................................................................................................................... 44 Changing portlet state.................................................................................................... 44 Session state .................................................................................................................. 45 Caching.......................................................................................................................... 46

    Chapter 12 – Internationalization...................................................................................... 47 Additional considerations.............................................................................................. 52

    Chapter 13 – Exception handling ...................................................................................... 53 Process Exception method ............................................................................................ 54 Error JSP ....................................................................................................................... 56 Portlet rendering methods ............................................................................................. 56 Testing with a runtime exception in the view JSP ........................................................ 57 Re-testing with a runtime exception in the view JSP.................................................... 59 Rendering phase exception handling............................................................................. 59 Database access class .................................................................................................... 60 Portlet class ................................................................................................................... 61

    Page 2 of 65

  • Portlet Development Workbook

    Re-testing with a configuration error ............................................................................ 62 Event phase exception handling.................................................................................... 62 Final re-testing with an event phase error ..................................................................... 63 Proper java method comments ...................................................................................... 64

    Download .......................................................................................................................... 64 Resources .......................................................................................................................... 64 About the author................................................................................................................ 65

    Introduction This workbook is a hands-on guide intended to help you learn how to develop portlets using the Java™ Standard Portlet API (JSR 168). I think it is critical to understand the basic API and the issues and challenges associated with portlet development. With this foundation, you will be able to see how the frameworks can help you. This is a workbook. I try to describe the important concepts of portlet development through the development of a working example. I also try to explain what we are doing and why. The hope is that it is not a simple typing exercise where you fall into a trap of following well-defined steps without truly understanding what is being accomplished and why certain approaches and decisions were chosen. As you progress though the workbook, I assume you will need less and less detailed instruction to complete the portlet, and that the discussion on the approach will be more valuable. Of course, the portlet code is provided for each chapter just in case you do get stuck and need to refer to the solution for that topic area. So, to get a good immersion into portlet development, you start with a very typical requirement and then you build a fully functional portlet. We also cover a few key items to make sure you have a good foundation in portlet development when you are done. The learning requriements this workbook addresses are: The development environment •

    • •

    Show portlet development using the IBM® Rational® Application Developer tooling (hereafter called Application Developer) Exercise the interactive test environment in Application Developer for portlet development and debugging Export the portlet so it can be deployed in a stand-alone portal environment

    Portlet application

    Create a portlet that uses a combination of modes (view, edit, configure). The portlet should have multiple page "views" per mode to demonstrate portlet page navigation The portlet should provide standard CRUD (create, read, update, delete) on some data -- this is a pretty typical requirement... the data may come from a variety of sources, and represent various things but the basic requirement is to show the user some data let them review it, update and delete it. The data should be persisted someplace (like a database)

    Page 3 of 65

  • Portlet Development Workbook

    Other considerations We should not have dependencies for the portlet to execute outside what we run locally on our machine (not connectivity to external servers, news feeds, etc)... we want to ensure that we can work on this in the comfort of running disconnect at a hotel room, on the casino floor, on the beach in Waikiki, in an X5 while stuck in traffic etc. We want the portlet application requirements to be reasonably meaningful without the details of the portlet business function getting in the way of the broad goal which is to understand portlet development -- that is we do not want to write a portlet to allow you to manage your stock portfolio. The details of what it means to manage a stock portfolio would get in the way of understanding portlet development.

    About the portlet application you create So, what I would like us to do is create a portlet application (or specifically three portlet applications) that manage a list of contacts. Kind of like an address book. The contact list itself will of course be specific to each individual portal user. There will be a main view that shows just the list of contacts that you have shown by name. You can select a name and then the view will show the details for that person - phone number, email address, etc. The edit mode for the portlet will allow the portal user to add, update and delete entries in the contact list. The configure mode will set up the appropriate DB information where the contacts lists themselves will be stored. Now, I know that a contact list itself is not the most interesting collection of things imaginable but it fits our purposes here and I couldn't think of anything better. I'd like to do this project in small increments. The idea being something like I will send frequent tasks to keep us moving along with the development. But, they will be of the size that it should take around 45 minutes to an hour to complete. I know you guys are busy having fun lives. But, I think if you know you can make progress given an hour or less that could work. Of course, you can always choose to do more than one increment at a time.... assuming you are bored and traffic is really backed up or once you've seen one view of Diamond Head you've pretty much seen them all, so it's time to get to work on Portal. The complete code is available for these tasks. In the portletwb-samples.zip file (which you can download from the cover page) is a directory structure that maps to the steps here. Within each directory is a portlet WAR file that can be deployed to Portal or imported into RAD. Source code is available in the WAR file. The version of the WAR file (or other resources) will be consistent with the state of the project for the given chapter.

    Chapter 1 - The development environment Insure that you have the following running on your system: • •

    IBM Rational Application Developer 6.0 (or with later fixpacks) IBM WebSphere Portal V5.1.0.1 (or later) installed in Application Developer as a Unit Test Environment DB2 (pretty much any current version; you can get the one from the Portal install CD's if you want; keep in mind I'm not asking for you to use DB2 here for the Portal configuration... you do not need to migrate the portal configuration to DB2... we are just using DB2 for application data.) Supported versions for WebSphere® Portal are Universal Database Enterprise Server

    Page 4 of 65

    http://www.ibm.com/developerworks/websphere/library/techarticles/0608_hanis/0608_hanis.html

  • Portlet Development Workbook

    Edition 8.2; DB2 Universal Database Enterprise Server Edition 8.1 FP7a or FP6a; or the Workgroup Server Editions. Edition 8.2; DB2 Universal Database Enterprise Server Edition 8.1 FP7a or FP6a; or the Workgroup Server Editions.

    Chapter 2 - Creating the Portlet Project Chapter 2 - Creating the Portlet Project In this step we will create the Portlet Application project in Application Developer for our Contacts List portlet. We will use the New Portlet Project (JSR 168) wizard to do a few things for us. In this step we will create the Portlet Application project in Application Developer for our Contacts List portlet. We will use the New Portlet Project (JSR 168) wizard to do a few things for us. 1. First, it will create the project in RAD. 1. First, it will create the project in RAD. 2. It will define the classpath for our project so builds will resolve the Portlet API properly. 2. It will define the classpath for our project so builds will resolve the Portlet API properly. 3. It will create the web and portlet deployment descriptors. 3. It will create the web and portlet deployment descriptors. 4. Finally it will generate some portlet code (java and jsp files) that provides that base

    implementation for things like sample rendering in various modes, storing portlet preferences, invoking JSP’s, putting data on the portlet session object, etc (all based on options selected in the New Portlet Project wizard.

    4. Finally it will generate some portlet code (java and jsp files) that provides that base implementation for things like sample rendering in various modes, storing portlet preferences, invoking JSP’s, putting data on the portlet session object, etc (all based on options selected in the New Portlet Project wizard.

    In my opinion the value of the project creation is in items 1 through 3 above. I generally dislike the code that is generated for us. One of the issues is in the code itself that is generated and a larger issue is that it isolates us from having more direct experience with the API for basic portlet processing.

    In my opinion the value of the project creation is in items 1 through 3 above. I generally dislike the code that is generated for us. One of the issues is in the code itself that is generated and a larger issue is that it isolates us from having more direct experience with the API for basic portlet processing. Given that we will still define our portlet to the wizard to allow it to create a functional portlet for us and we will use it to verify that our test environment is executing properly. We will then modify the generated code to ensure we have a good understanding of the portlet processing.

    Given that we will still define our portlet to the wizard to allow it to create a functional portlet for us and we will use it to verify that our test environment is executing properly. We will then modify the generated code to ensure we have a good understanding of the portlet processing. In general, I will try and give you exact names of projects, packages, classes, fields, etc, that I use. You can choose to use the same names or not. If you want to import some code from the completed project that I provide you will have fewer problems if the names are the same.

    In general, I will try and give you exact names of projects, packages, classes, fields, etc, that I use. You can choose to use the same names or not. If you want to import some code from the completed project that I provide you will have fewer problems if the names are the same.

    Overview Overview

    1. Create a JSR 168 portlet project called Contacts List – Standard API using the Portlet Project (JSR 168) wizard. In the wizard select a Basic portlet (JSR 168). In the Portlet Settings page change the package prefix to com.ibm.sample.portlet and the Class prefix to ContactsListPortlet. Select options to add Portlet action handling (with a form sample) and Portlet preferences handling. Also, select the options to add edit mode and configure mode.

    1. Create a JSR 168 portlet project called Contacts List – Standard API using the Portlet Project (JSR 168) wizard. In the wizard select a Basic portlet (JSR 168). In the Portlet Settings page change the package prefix to com.ibm.sample.portlet and the Class prefix to ContactsListPortlet. Select options to add Portlet action handling (with a form sample) and Portlet preferences handling. Also, select the options to add edit mode and configure mode.

    2. Create a Portal Server if needed. Add the Contacts List – Standard API enterprise application to the list of configured projects for the server. Start the server.

    2. Create a Portal Server if needed. Add the Contacts List – Standard API enterprise application to the list of configured projects for the server. Start the server.

    3. Point your browser to http://localhost:9081/wps/portal3. Point your browser to http://localhost:9081/wps/portal to execute the portlet as generated in the test environment.

    Page 5 of 65

    http://localhost:9081/wps/portalhttp://localhost:9081/wps/portalportalhttp://portalto

  • Portlet Development Workbook Detail From the File menu in Application Developer select New… Portlet Project (JSR 168). Enter the name of the project as Contacts – Standard. The Portal version should be 5.1. The EAR project and Context Root are automatically defined based on the project name entered. Those value are fine for our project. In the next page of the wizard select Basic portlet (JSR 168), on the next page deselect Web Diagram. The next page is the Portlet Settings page. In the Portlet Settings page change the package prefix to com.ibm.sample.standard and the class prefix to ContactsPortlet.

    Page 6 of 65

    The next page is the Actions and Preferences page. Select options to add Portlet action handling (with a form sample) and Portlet preferences handling. Do not add credential vault handling on the next page; nor support for additional markups on the next page. On that Miscellaneous page do add support for edit mode and configure mode. Select finish completing the wizard and creating the project. When the wizard completes you should be in the Web perspective with an editor opened on the newly created ContactsPortletView.jsp. Close the editor on that file, we will look at it later. The next step is to attempt to run this portlet in the test environment. If you do not have a portal test environment already set up you should do that now. Select the servers tab from the web perspective (bottom pane by default) and then right-mouse in that pane and select New… Server. A wizard open and you can select WebSphere Portal v5.1 Test Environment as the server type (hostname can be the name of your machine or localhost). Take the default for the port number on the next page (9081). You can change this if you have a port conflict. That could be the case if you intend to run a standalone portal server locally along with the portal test environment, since both default to 9081. Unless your Thinkpad/desktop is a lot bigger than mine that is not all that practical so we don’t need to change the port. We will just have one instance of a portal server running at any given time. On the last page of that wizard, Add and Remove Projects, add the EAR file for our portlet project, Contacts – StandardEAR. The EAR files selected as Configured Projects define the enterprise applications that will automatically deploy and get configured to test pages when we start the portal test server. After the server is configured you can change the portal applications by right-mouse clicking on the server definition and selecting the menu option to Add and Remove Projects. Once the portal server is defined and the applications are selected for deployment you can start the server. The server can optionally be started in debug mode. Debug mode allows you to set breakpoints in your code, inspect variables, and make live

  • Portlet Development Workbook

    code changes. This interactive debug mode is extremely valuable and we will look at it more closely later. For now, start the portal server by right-mouse clicking on the server configuration and selecting Start. The configuration will be published to the server and the server started.

    code changes. This interactive debug mode is extremely valuable and we will look at it more closely later. For now, start the portal server by right-mouse clicking on the server configuration and selecting Start. The configuration will be published to the server and the server started. Once the server status changes to Started, you can open a web browser with the URL to address the default page in the Portal. Point your web browser to http://localhost:9081/wps/myportalOnce the server status changes to Started, you can open a web browser with the URL to address the default page in the Portal. Point your web browser to http://localhost:9081/wps/myportal. After you log in you should see a portal page with our generated portlet displayed. Of course we have no idea on what this portlet is doing or how it has been implemented. Let’s take a look at the source code and make changes to simplify the code and ensure that completely understand the implementation details. One of the values of the portlet creation wizard we talked about earlier was the generation of the web and portlet deployment descriptors. Open the Portlet Deployment Descriptor and change to the Portlets tab. Notice that the Portlet class is defined as com.ibm.sample.standard.ContactsPortlet. This is our base portlet class and has been generated for us (the naming is based on the package prefix and the class prefix that we specified in the portlet creation wizard). This is the starting point for portlet execution and we will make changes to the code that was generated for us in that class. Also, notice on that same page that a Resource bundle is also defined (com.ibm.sample.standard.nl.ContactsPortletResource). Again, the class name is based on the prefix values that we defined along with locale dependant naming conventions. Resource bundles are used to hold translatable strings that our application may use. Language translations of those strings will exist in language specific resource bundle files. In the case of this file the name of our portlet is generated in a default and in an English language bundle. We will see those files in the generated source code as well. Change to the Source tab to see the underlying xml for this deployment descriptor. Notice the portlet class definition here along with the resource bundle. Also, notice the definition of the supported modes that we selected in the creation wizard… for view mode (default), with edit and configure modes. Also, notice the definition of the Portlet Preferences. In the creation wizard we selected an option that generated code for preferences handling. These sample entries were created to demonstrate portlet preferences. The JSR 168 API (Standard API) defines portlet preferences as name-value pairs with an optional read-only qualifier that, when set to true, defines configuration preferences. Portlet preferences that are set in configure mode and are preferences that are shared between portal users. Preferences that are not set to read-only are user-specific preferences and are set while the portlet is in edit mode. I tend to like to use the Source view of the XML files to make sure I understand exactly what is defined. The other views tend to over-complicate the definition in my view. So, we’ll start with the portlet class. In the JavaSource directory open the com.ibm.sample.standard.ContactsPortlet file. The generated code attempts to determine the

    Page 7 of 65

    http://localhost:9081/wps/myportalhttp://localhost:9081/wps/myportal

  • Portlet Development Workbook

    • •

    • •

    • •

    markup from the content type and look for the appropriate JSP file accordingly. For the purpose of our portlet we are going to assume that we are rendering HTML for a browser request. So, we will clean up that code a bit.

    1. Rename the three JSP files to remove the string “Portlet” from the file names; so for example ContactsPortletView.jsp will become ContactsView.jsp.

    2. Move the three JSP files directly into the jsp folder and delete the html folder 3. Move the jsp folder to the WebContent folder and delete the

    com_ibm_sample_standard folder. 4. Edit ContactsPortlet to now reference the renamed JSP files with the correct path:

    a. Change public static final String VIEW_JSP = "/jsp/ContactsView.jsp”; b. Change public static final String EDIT_JSP = "/jsp/ContactsEdit.jsp"; c. Change public static final String CONFIG_JSP = "/jsp/ContactsConfig.jsp";

    5. Delete the field definition for JSP_FOLDER 6. Delete the getJspExtension() method 7. Delete the getJspFilePath() method 8. Delete the getMarkup() method 9. Modify the doView() method to reference the VIEW_JSP directly as in

    PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(VIEW_JSP); 10. Make a similar change for the doEdit() method and EDIT_JSP 11. Make a similar change for the doCustomConfigure() method and CONFIG_JSP.

    Retest the portlet to ensure the structural changes we just made had no effect on the results that are rendered. The portlet should look exactly the same as before. That completes this task. Next we will start making changes to our portlet to begin the implementation of our Contacts function.

    Chapter 3 - Database access from our portlet Ok, this step may be a bit longer but the last step should have been on the short side so we’ll plan on making up for that. In this step we will set up the database and add the code to our portlet to access the database. The database access that we will first want is the ability to read from the database and get the collection of contacts for the user who is currently logged in. The main view of our portlet should show a list of our contacts so this work is necessary to do up front. We will have to do a few things here to complete this step:

    Create the database and the database table in db2 Put some sample data in the table for our the id that we will test with (this is just to get us started) In the app server, add a data source for our new database In RAD, create a new class that we will use to access the database (referencing that data source). Create a bean that will represent a contact entry Create a method in a new database access class that will create and return a collection of contact beans that collectively represent all our contacts. Invoke the db access method to get the list of beans from the doView method of our portlet class Pass those beans to the view JSP and modify the wizard generated view JSP to render the list of contacts.

    Page 8 of 65

  • Portlet Development Workbook Creating the database and table There are several approaches we could use to create the database and table. We could start the DB2 Control Center and use the wizards to create the database and table. I could provide a DDL from my database so you can load it into your system. Or we could use a DB2 command line to execute the appropriate SQL commands individually. I like this option since we have a small number of tasks to execute and we will have better control over the processing if we want to re-execute any step. 1. First, open a Command Line Processor window and create a database called pdwdb (which

    is going to stand for something like Portlet Development Workbook Database). To do this invoke the following command from the command line window: create database pdwdb

    2. Next we will need to connect to the database we just created. We will need to connect using

    an id (and password) that has appropriate access rights. By default, when we installed DB2 an id of db2admin was created. Let’s use that id and associated password for this. If you installed with a different id then you can use that instead and remember to use it instead of “db2admin” when I referenced in this document. In a real world scenario the database will either be created for you by the DBA or will already exist and the access ID will be provided. connect to pdwdb user db2admin using password

    3. Now, create the table in the pdwdb database that will hold our contact list information. The

    name of the table should be contacts with the following column definitions. Keep in mind that you can change the name of the database, table, and columns to anything you choose you will just need to keep those differences in mind as we go through the example code here. create table contacts (oid varchar(64) not null, userid varchar(64) not null, first_name varchar (64) not null, last_name varchar(64) not null , email varchar(64), company varchar(64), mobile_phone varchar(32), business_phone varchar(32), constraint cc1132115636631 primary key( oid)) Notice that we have defined a contact entry to contain the following information: first name, last name, email address, company name, mobile phone number, and business phone number. The table also defines a userid which is the id of the user for which this data entry is owned (since all contacts are stored in a single database table we need to be able to distinguish one user’s contacts from another’s. Also there is an object id that we will use as a primary key to uniquely identify this entry.

    4. Finally, add some sample entries into the table so when we execute our portlet to show our contacts we will see some data. Of course, before we are done we will develop an edit mode for our portlet that will allow us to add contacts. But, before we do that we want some feedback that our view mode is working. You can just copy and paste the following three commands (individually) into the command line window. Notice that for these entries the userid value is “wpsadmin”. If you will be testing your portlet using a different id than you should make the appropriate change here. insert into contacts (oid , userid, first_name, last_name, email, company, mobile_phone, business_phone) values ('0001', 'wpsadmin', 'Leslie', 'Weng', '[email protected]', 'IBM','617-693-1819', '617-693-1819')

    Page 9 of 65

  • Portlet Development Workbook

    insert into contacts (oid , userid, first_name, last_name, email, company, mobile_phone, business_phone) values ('0002', 'wpsadmin', 'Shu Sia', 'Lukito', '[email protected]', 'IBM','877-370-1026', '877-370-1026') insert into contacts (oid , userid, first_name, last_name, email, company, mobile_phone, business_phone) values ('0003', 'wpsadmin', 'Tim', 'Hanis', '[email protected]', 'IBM','919-247-4098', '919-247-4098')

    Define the data source Next we will want to define a data source in application server so access to the database can be managed as a resource. Since we will run our Portal Server out of the Application Developer environment we will define the data source in that server configuration. When we want to execute this portlet outside the Application Developer environment then we will need to create the data source using the admin console for the WebSphere App Server associated with that Portal server. For now, let’s just create the data source in the server configuration under RAD. In a previous step we created a WebSphere Portal v5.1 Test Environment. Double click on that server name to open the server configuration editor. In the editor that opens click on the tab on the bottom that is labeled Data source. When we are done with this configuration we will want this page to look something like this shown on the right. To define the data source first select Add… in the JDBC provider list section. In the dialog that opens select IBM DB2 as the database type. For the JDBC provider type select DB2 Legacy CLI-based Type 2 JDBC Driver. On the next page enter a name for the DB2 driver (Default DB2 JDBC Provider) and Finish to create the JDBC provider. Next we will want to create the data source definition for our database and we will do that on this same page. But first let’s create a JAAS Authentication alias to define our authentication credentials. When we connected to the database in the previous step we used an id/pw to authenticate. Similarly when we access the database through the data source definition the data source must be able to pass those same credentials to the database to allow authentication. A JAAS alias will hold those credentials and then we can reference this alias from the data source that we will create next. Switch to the Security tab in the same editor (next tab to the left of Data source). Select Add… to create a new JAAS Authentication Entry. Enter the id and password that you used in the previous step to connect to the database. Enter an alias name of your choosing. We will need to identify this alias by that name when we create the data source next. For the alias name you might use something like pdwDBAuth. Click on Ok to create the alias.

    Page 10 of 65

  • Portlet Development Workbook

    Return back to the Data source page by selecting that tab. With the JDBC provider that we just created being selected, click on Add… to create a Data source. On the first page of that wizard select DB2 Legacy CLI-based Type 2 JDBC Driver and a Version 5.0 data source. Then on the next page specify a name of your choice, I used Portlet Development Workshop Data Source. For the JNDI name you can also specify a name of your choice. This value will be used in our portlet code to look up this data source using the JNDI naming service. I used jdbc/pdwDS. For the Description enter Portlet Development Workshop Data Source. Next, associate the authentication credentials for this data source to connect to the database successfully. For Component-managed authentication alias select the security alias that we just created. Deselect the checkbox identified as “Use this data source in container managed persistence (CMP)”. Select Finish completing the data source creation.

    Return back to the Data source page by selecting that tab. With the JDBC provider that we just created being selected, click on Add… to create a Data source. On the first page of that wizard select DB2 Legacy CLI-based Type 2 JDBC Driver and a Version 5.0 data source. Then on the next page specify a name of your choice, I used Portlet Development Workshop Data Source. For the JNDI name you can also specify a name of your choice. This value will be used in our portlet code to look up this data source using the JNDI naming service. I used jdbc/pdwDS. For the Description enter Portlet Development Workshop Data Source. Next, associate the authentication credentials for this data source to connect to the database successfully. For Component-managed authentication alias select the security alias that we just created. Deselect the checkbox identified as “Use this data source in container managed persistence (CMP)”. Select Finish completing the data source creation. A final step is to actually associate our database, by name, to this data source. With the data source we just created selected edit the databaseName Resource property. Change the value of this property to the name of our database (the database, not the table). The name we used was pdwdb. With this done, we have completed the creation of the data source in our application server configuration. Unlike the application server console we cannot test this connection. So, hopefully you have followed these instructions carefully so we don’t have to debug data source definition errors!

    A final step is to actually associate our database, by name, to this data source. With the data source we just created selected edit the databaseName Resource property. Change the value of this property to the name of our database (the database, not the table). The name we used was pdwdb. With this done, we have completed the creation of the data source in our application server configuration. Unlike the application server console we cannot test this connection. So, hopefully you have followed these instructions carefully so we don’t have to debug data source definition errors!

    Create a Contact bean in Application Developer Create a Contact bean in Application Developer Next we will create a bean (a class that follows standard naming conventions and is serializable) that will represent a entry in our contact list. Select the JavaSource folder in our Contacts – Standard portlet project and add a new package called com.ibm.sample.standard.beans. In that package create a class called Contact that implements Serializable as defines the following fields

    Next we will create a bean (a class that follows standard naming conventions and is serializable) that will represent a entry in our contact list. Select the JavaSource folder in our Contacts – Standard portlet project and add a new package called com.ibm.sample.standard.beans. In that package create a class called Contact that implements Serializable as defines the following fields

    protected String oid = "";protected String userid = "";protected String firstName = "";protected String lastName = "";protected String email = "";protected String company = "";protected String businessPhone = "";protected String mobilePhone = "";

    Generate the getter and setter methods for these fields (in Application Developer select Contact from the outline or explorer view and from the context menu and select Source…. Generate Getters and Setters. Save the file when the access methods are defined.

    Create a database access class Next we will create a database access class with a public method that will return a list of Contact instances that represent the contacts of the logged in user. Select the JavaSource folder in our

    Page 11 of 65

  • Portlet Development Workbook Contacts – Standard portlet project and add a new package called com.ibm.sample.standard.persistence. In that package create a class called DbAccess. We don’t necessarily need to create new packages for this class or the Contact bean that we just created but for a larger application that could easily have many bean classes or supporting classes for persisting objects it is a good practice to organize those classes in appropriate packages for easier reference or if we determine the need to apply java package level access control to classes or methods. The database access class we implement here is really not different than the class we might develop for a servlet application. The fact that we are invoking this from a portlet really has no impact to the its implementation. With that in mind search for the topic titled Accessing data from application clients in the WebSphere Application Server Info Center (this information is not platform specific) and read that chapter. Our DbAccess implementation will follow closely the code sample shown there. Reading Reference: Accessing data from application clients

    Search on “Accessing data from application clients” Following that example, create a getDataSource method in DbAccess that returns a DataSource. The method should create an InitialContext and lookup our datasource by name. The datasource name that we defined in our server configuration can be hardcoded into this method at this point. In a future update we will get that value from portlet preferences and allow it to be set or reset by an administrator using the portlet config mode. Next, create a public method named getContactList(String userid) that will return a List. We will create and execute an SQL statement similar to the way this sample code shows. The difference will be in the way the parameters are substituted into the SQL query string. The example shows code that gets a Connection from the DataSource object. It builds a query string base by simply creating a String object appending the parameter values into the appropriate SQL statement. For us that would be something like: String sql = "select userid, oid, first_name, last_name, email, business_phonefrom contacts where userid = “+userid+”order by first_name"; and then the Statement is executed passing the sql string as a parameter. The potential issue with this is that if we substitute in a parameter that has special characters (like a quote) in it we could have runtime problems with the query successfully executing. Instead, we would like to use parameter markers to do the substitution. In this case build a similar SQL query string but using a “?” to mark the location of the parameter to be substituted later. String sql = "select userid, oid, first_name, last_name, email, business_phonefrom contacts where userid = ? order by first_name";

    Reading Reference: Providing Variable Input to Dynamic SQL Using Parameter Markers

    Also, search on “JDBC Interface for executing SQL Then we can create a PreparedStatement and after the statement is prepared, substitute the parameter as shown in the completed method below. public List getContactList(String userid) {

    Page 12 of 65

    http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsphttp://publib.boulder.ibm.com/infocenter/db2help/index.jsp?topic=/com.ibm.db2.udb.doc/ad/t0005862.htm

  • Portlet Development Workbook

    // Procedure specific fieldsString sql = "select userid, oid, first_name, last_name, email,business_phone from contacts where userid = ? order by first_name";

    Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;List contactList = new ArrayList();

    try {connection = getDataSource().getConnection();statement = connection.prepareStatement(sql);statement.setString(1, userid);resultSet = statement.executeQuery();contactList = createContactList(resultSet);

    } catch (Exception e) {e.printStackTrace();

    } finally {close(connection, statement, resultSet);

    }return contactList;

    }

    You see that our list of contacts is actually created from the result set using a createContactList method. We will need to add that method to our DbAccess class. That method will just iterate through the result set, instantiate a new Contact object for each returned row and add that instance to a List. The list will be returned when we have finished the result set. That method is shown here for reference. public List createContactList(ResultSet resultSet)

    throws SQLException {

    if (resultSet == null)return null;

    List contactList = new ArrayList();while (resultSet.next()) {

    Contact contact = new Contact();contact.setUserid(resultSet.getString("USERID"));contact.setOid(resultSet.getString("OID"));contact.setFirstName(resultSet.getString("FIRST_NAME"));contact.setLastName(resultSet.getString("LAST_NAME"));contact.setEmail(resultSet.getString("EMAIL"));contact.setBusinessPhone(resultSet.getString("BUSINESS_PHONE"));contactList.add(contact);

    }return contactList;

    }

    Finally, we need to add a method to close the connection, statement, and result set to ensure that we do not leave unused resources. Note: If you have trouble with the methods in this class refer to the completed code that is available for this task.

    Page 13 of 65

  • Portlet Development Workbook Modify portlet doView method Next we will create a database access class with a public method that will return a list of contacts. The doView method of the portlet will get executed every time the portlet is asked to render itself while in view mode. Accordingly, the doEdit, doCustomConfigure, and doHelp methods are executed when in edit, configure, help modes. For now we are only interested in the main view mode. Go to the doView method and add code to call the dbaccess method to create the list of contacts and add that list to the request object as an attribute so it can be passed to our view JSP to be rendered. In that method add the following lines.

    // Get the id of the logged in userString userid = “wpsadmin”;

    // Get the list of contactsDbAccess dbAccess = new DbAccess();List contactList = dbAccess.getContactList(userid);request.setAttribute("contactList", contactList);

    Obviously, at this stage we are not dynamically getting the id of the logged in user but are using a hard-coded value. We will change that later. Notice that we are adding the contactList object to the request as an attribute with the name “contactList”. We will need to reference that attribute by name in our JSP (in a userBean tag). We will do that next. But first, to complete the changes to the portlet code delete the section of code that checks if portlet session exists. It was generated for us and we will not use it. Delete the comment and the 4 or 5 lines of code that deals with the sessionBean that follow.

    Modify the view JSP Next we will modify the view JSP to display our contact list. Edit the ContactsView.jsp file. This file was also generated for us during the creation of our portlet project. Really there is very little in it that we will want to reuse so erase all the file contents. The completed JSP is shown below.

  • Portlet Development Workbook %>

    The first thing we did was to add two page directives. The first will ensure that we do not create a session object by default for the JSP. Typically a session will be created for a portlet running in an authenticated page. A session may not be created for anonymous page access and there is no reason for the JSP to create one if the portlet did not. The second directive will provide the import reference for our Contact bean class. Reading Reference: JavaServer Pages Syntax Reference Card

    JavaServer Pages Syntax Reference Doc We added a useBean element to reference the list of Contacts that we put on the request object in our doView method in the portlet. Again the name we used there is the same name (id) that we will use on the useBean element. The type was set to java.util.List which is of course the type of the object we put on the request. We also added a taglib directive to reference the portlet tag library. This is the set of JSP custom tags that are defined by Portal and which provide the portal function that we can use in our portlet JSP files. The defineObjects tag will make the portal variables renderRequest, renderResponse, and portletConfig available for use in scriptlets in our JSP. Although we don’t use them here in this JSP (at least not at this point) we include the tag invocation for general consistency with other JSPs and with the expectation that we may use those objects in the future. It is not uncommon to have a need to reference those objects in a JSP. Next, we added an HTML form tag. You will notice that we qualified the form name using the portlet namespace tag. When writing portlets we always have to keep in mind that our portlets are rendered as only part of an aggregated HTML markup response. The portal page that is rendered at the users browser is HTML that was generated by Portal (the theme and skins) and the markup fragments generated by the portlets on the page. When referencing elements of the document object model (DOM) we want to ensure uniqueness. We have no way of knowing that whoever wrote portlet A that is on the same page as our portlet did not already have a form named “viewform”. However, by using the namespace encoding a unique portlet identifier will be appended to “viewform” to ensure it’s uniqueness. We add that encoding to the form so then we can be sure that we can uniquely identify all the elements within that form by uniquely identifying the form. Finally, we add scriptlet code to iterate over the List object to get the Contact objects from the List and display the first and last name of each contact in a table. See the JSP Syntax reference for additional information on the JSP directives, elements, and expressions used here.

    Start the portal server and execute You should be done with changes for this task. Start (or restart) the portal server, log in as “wpsadmin” (or the id that matches the data we created in the database

    Page 15 of 65

    http://java.sun.com/products/jsp/syntax/2.0/card20.pdfhttp://java.sun.com/products/jsp/docs.html

  • Portlet Development Workbook table) and execute our portlet. You should now see a portlet with three contacts. You are finished with this task. If you have errors executing this portlet successfully, look at the console window in Application Developer for any error indications and also check the wps(timestamp).log in the /log directory where the Portal UTE is installed.

    Chapter 4 - Portlet configuration In this step, which should be considerably shorter than the previous step, we will modify the configure mode of the portlet to allow an administrator to specify the name of the data source for our database. The data source name will be configured as a “read-only” portlet preference. A portlet preference that has been identified as “read-only” in the portlet deployment descriptor can be customized by an administrator in the portlet config mode. Alternatively, preferences that are not identified as read-only can be modified by individual users in the portlet edit mode. This is described in the WebSphere Portal Info Center under the topic Comparing JSR 168 to the IBM portlet API. So, here are the steps needed to complete this task • •

    Modify the deployment descriptor to add a portlet preference for the data source name Modify the doCustomConfigure method in the portlet class to get the data source name from the portlet preferences and pass it to the JSP Modify the JSP to show the current data source name and provide a user interface to allow the name to be changed and provide a “save” request. Modify the processAction method in the portlet class to process the save request to update the portlet preference with the new data source name Modify the dbAccess class to use the portlet preference data source name instead of the hard-coded value that it is currently using. Test the portlet, including changing the data source name to an incorrect value to see the error behavior.

    Modify portlet deployment descriptor Let’s first edit our portlet deployment descriptor to add this preference and delete other preferences that have been defined there as part of the portlet generation wizard. Open the Portlet Deployment Descriptor in our project. In the editor that opens find the portlet preferences sections (). In that section you will find one preference already defined that is marked as read-only. It is named “.ContactsPortletConfigKey” and was created by the wizard to demonstrate config mode preferences function. Rename it to “datasource” (notice we will not use the preceding period in our naming here). You can choose any name you’d like; we will just need to reference this parameter by name when we retrieve it in our config mode portlet code. Change its value to the name of the data source that we previously defined in the server configuration (and is currently hardcoded in our DbAccess class). The value we are using for this is “jdbc/pdwDS”. It is case sensitive. Since the intent here is that we can modify this value using our portlet config mode you can choose to leave this value blank or even put in an incorrect value. Then when we execute the portlet we can go to config mode and make the correct update there.

    Page 16 of 65

  • Portlet Development Workbook There is a somewhat subtle difference here in testing our portlet in the Application Developer Unit Test Environment versus executing it to a stand-alone Portal server. There are two things to understand. First, when a portlet is installed into Portal the preferences are read from the portlet deployment descriptor file and loaded into a portal configuration database for reference and processing. Second, runtime updates to the preferences (like from edit or config mode) are stored in the portal configuration database. If the portlet is re-installed then the preferences from the portlet deployment descriptor are reloaded. Whenever the Application Developer Unit Test Environment is restarted the configured portlets are essentially redeployed. This is different then running Portal outside the test environment. What that means to us at this point is that when we modify these preferences at runtime (using the config mode) that change will persist as long as the UTE Portal server is running. When it is stopped and restarted, any runtime preference changes we made will be lost and the values that we entered into the deployment descriptor will be used again. This is not a big deal, just something to understand so you are not surprised (or think you have a coding error) when you see values that you set at runtime getting “lost” when you restart the server. Notice that there are many other (non read-only) preferences defined here as well. Again they were generated when the portlet was created and are used, as you probably have guessed, in the portlets edit mode. Let’s delete them all now while we are here. The edit mode, as it is currently implemented, in our portlet will not work. But, we will change that function in a future step anyway. This section of the portlet deployment descriptor should look like:

    datasourcejdbc/pdwDStrue

    Modify portlet doCustomConfigure method

    Next, we’ll change the portlet doCustomConfigure method. This method is responsible to process requests to render the portlet while in config mode. That is, when a use clicks on the little wrench icon on the portlet the doCustomConfigure method of the portlet will get called to process the request. Our method will simply get the value of the datasource preference, pass that string to the config JSP, and then invoke the JSP. If you look at the generated JSP for config mode (ContactsConfig.jsp) you will see code in the JSP that looks like it does something similar. It gets a preferences object from the render request object and then gets a value for a preference (the one that we deleted – although the name is obscured somewhat here since it is getting the name from a static field in the portlet). We could reuse this scriptlet code in our JSP but, in general, we should try to minimize scriptlets in JSPs. So, we will put this code in our portlet and simply pass the data source name to the JSP for rendering. Open ContactsPortlet and find the doCustomConfigure method. Modify it to add the following code to get the datasource preference. This code is very similar to what you saw in the generated JSP file. You can add this code to the beginning of the method.

    // Get the portlet preference for the datasource nameString dsName = "";PortletPreferences preferences = request.getPreferences();if (preferences != null) {

    dsName = (String)preferences.getValue("datasource", "");

    Page 17 of 65

  • Portlet Development Workbook

    }

    // Put list of contacts on request for renderingrequest.setAttribute("dsName", dsName);

    You can see that we get the PortletPreferences object from the render request. From the preferences object we get the value of the preference named “datasource”, and then put that value on the request object for use by the JSP.

    Modify Configure mode JSP Next, we will modify the JSP to display this value and add the form to allow the value to be modified and a request submitted to save it. Edit the ContactsConfig.jsp to make these changes. The final JSP should look something like this.

    Set the DATASOURCE NAME

    The first line is our standard directive to ensure that a session is not created by the JSP. The useBean directive will reference the data source name that we retrieved and put on the request object in our portlet code. Notice that the type of this object is a simple String. Again, we reference the portlet tag library to make those portlet JSP tags available. We use the actionURL tag from that library (in the form tag). Again we invoke the defineObjects tag from the library to make the request, response, and portlet context objects available to us but don’t use them here. On the form tag we specify the action attribute, which is the URL that gets invoked when the form is submitted. Within this form we have added a submit button that allows the user to save an updated data source name value. When the user hits the “save” button we want the form action to refer back to this portlet and also to invoke the “action” phase processing of the portlet. If you are unfamiliar with the two-phase processing of portlets (action and render), refer the main points to understand is that when a page is rendered, all portlets on that page are requested to render themselves in order to build the HTML markup for the entire page. When a user is interacting with a specific portlet on a page, as in this case where a user will click on the save button, that portlet can have action event processing prior to the portlet being rendered. So, we create a URL reference to our portlet that will invoke the portlets processAction method. We use the portlet tag actionURL to generate this URL The JSP uses the expression to display the data source name that we retrieved from portlet preferences. The remainder of the JSP simply does some formatting and labeling. Notice that we are using a style class named “wpsPortletHead”. This class and many others are provided in the CSS style sheet provided with Portal. It is good practice to use these styles to provide consistent look and feel between portlets.

    Page 18 of 65

    http://java.sun.com/portlet"prefix="portlet"%

  • Portlet Development Workbook

    Modify portlet processAction method We will need to modify the processAction method of our portlet to handle the “save” action event we just defined. Notice that in our JSP we named the “submit” element “actionEvent” and gave it a value of “save”. During portlet action event processing we can look for the actionEvent parameter on the request object and if it has a value of “save” we will know that the intended user action is. We can therefore use the same name, “actionEvent” on other submit elements in our portlet and if we use different values we can uniquely determine what action is being requested. Delete the entire contents of the existing processAction method and replace it with the following. String actionEventName = request.getParameter("actionEvent");

    // Save the configuration updateif ("save".equalsIgnoreCase(actionEventName)) {

    PortletPreferences prefs = request.getPreferences();try {

    prefs.setValue("datasource", request.getParameter("datasource"));prefs.store();

    }catch( ReadOnlyException roe ) {

    roe.printStackTrace();}catch( ValidatorException ve ) {

    ve.printStackTrace();}

    } This method is also straightforward. It simply gets the request parameter named “actionEvent” and if it is equal to “save”, gets the PortletPreferences object, sets the value of the “datasource” element, and stores the preference values.

    Modify code to use the data source name from portlet preferences Finally, we need to modify our code to use the value for the data source name from the portlet preferences. Since we had previously hard-coded that value in the DbAccess class let’s start there to make the appropriate modification. If you don’t already have one, add a field named dsName to the class definition to hold the data source name. If you already had one with the data source name hard coded, then initialize it to null instead. If you had hard coded the data source name in the getDataSource() method itself then change that method to use the dsName variable instead. protected String dsName = null;

    Add a class constructor that takes a String as a parameter. That string will represent the data source name and we will use its value to set the value of the field. The constructor will then be

    public DbAccess(String dsName) {

    Page 19 of 65

  • Portlet Development Workbook

    this.dsName = dsName;}

    Next we will need to modify the portlet code that creates an instance of this class. We will need to call this constructor instead. Modify the doView method to get the data source name from the portlet preferences and use that value to construct a new DbAccess instance. That section of the doView method will look like:

    String datasource = null;PortletPreferences preferences = request.getPreferences();if (preferences != null)

    datasource = (String)preferences.getValue("datasource", "");DbAccess dbAccess = new DbAccess(datasource);

    Restart the server to test these changes you should see a configure mode page like the following when you click on the config icon (wrench).

    You should be able to update the data source name and save the new value in portlet preferences. To test this, set the value of the data source name to something other than the correct value and hit save. Return to view mode by clicking on the return icon. You should get an error generated in the console (javax.naming.NameNotFoundException). The result to the web page will be an empty portlet. That is something we will change in the next step, to show a meaningful but not overly complicated message on the page.

    Chapter 5 - Portlet code clean up In this step we will clean up code that was generated for us that we will not use. In a previous step we cleaned up the portlet deployment descriptor and made complete changes to the view and config mode JSPs. The web application deployment descriptor should not need changes. That should leave us with the portlet class file, any supporting beans, and edit mode JSP to clean up. First, edit the ContactsEdit.jsp file and delete all of its contents. Let’s replace it with just some boilerplate JSP code for now. We are not trying to get anything functional here. Just get rid of the generated code that we will not use. The resulting JSP should just contain the following. We talked about these tags before.

    Next, a session bean class was generated that we will not use. Find the ContactsPortletSessionBean file and delete it.

    Page 20 of 65

    http://java.sun.com/portlet"prefix="portlet"%

  • Portlet Development Workbook This should generate errors in our ContactsPortlet. Edit it so we can resolve those references. Delete the method named getSessionBean. That method references the session bean and we will not use it either. When the changes to ContactsPortlet is saved and rebuilt we should have no other errors.

    Constants Still in the ContactsPortlet, find the static field String definitions. Generally, it is good practice to put static string references in fields so that any updates to those strings can be made in one place and also reduces that chance that we will have miss-matched strings by mistake. But, it is also a good practice to collect them in a single, separate file. So, create a new class in the same package called Constants and add only the three static field references for the JSP file names to it. That file should look something like this: package com.ibm.sample.standard;

    public class Constants {

    // JSP file namespublic static final String VIEW_JSP = "/jsp/ContactsView.jsp";public static final String EDIT_JSP = "/jsp/ContactsEdit.jsp";public static final String CONFIG_JSP = "/jsp/ContactsConfig.jsp";

    }

    Now, delete all the String static field references from ContactsPortlet. This should leave only the PortletMode static field definition in that class. When saved we should see three error messages indicating that our JSP field references cannot be resolved. Since we moved those field definitions to a new java class we will need to reference them properly. Change the existing reference to VIEW_JSP to Constants.VIEW_JSP. Make similar changes for EDIT_JSP and CONFIG_JSP to resolve these reference issues. We have other strings that are used as key or name values that should be added to our Constants class. Our config mode JSP sets a submit button name and value to some defined strings to indicate that a request is being made to save the updated data source name. In the JSP currently those values are coded in as “actionEvent” and “save”. Then in our ContactsPortlet processAction method we look for a request parameter by the name “actionEvent” and check it’s value. If we find it is “save” then we know the request was made to save the data source name. In that case, the updated data source name is retrieved as a request parameter with the name “datasource” which was, again, defined in our config JSP as the name of the text input for that value. You can see that we may inadvertently introduce an error here if we make a change to one of these names (“datasource”, “actionEvent”, or “save”) in either the JSP or the portlet and forget to make an equivalent change in the other location. Also, you can imagine that as the size if the application increases it becomes more difficult to keep track of the right references between JSP and portlet code. To help here we will add these strings to the constants file and make appropriate changes to the JSP and portlet to reference those strings. That way, we eliminate the possibility of changing the value in only one place and also make it more reasonable to search for cross references. In the ContactsConfig.JSP find the following lines:

    Page 21 of 65

  • Portlet Development Workbook

    And change them to:

    You will also need to add an import statement to the JSP to resolve references to the Constants class. Towards the top of the file with the other page directive add the following line.

    Your updates to the JSP are complete. We next need to update the Constants file to include these strings. So add statements to define a static string named DATASOURCE as “datasource”, ACTION_EVENT as “actionEvent”, and SAVE as “save”. Finally, make appropriate changes in ContactsPortlet to reference these fields instead of the strings. You should make changes in the processAction method to replace the strings listed there to use these field references instead. Also, recall that when we added a preference to the portlet deployment descriptor in a previous step we chose the name “datasource”. We should also change our reference to that preference in the portlet code to use a static field from our constants class. We now have a variable defined with that value, since we used that same string in the JSP as the name of the text input field for the data source name. We could simply use that variable. However, we do not necessarily want to tie the name value of that portlet preference to the string name of that text input field. One reason to avoid doing that is to allow us to more easily determine by looking at our code which we are referencing and also to isolate any value changes. So, let us create an additional field in Constants for our data source preference name, call it PREF_DS with the value of “datasource”. Now we can make appropriate changes in the doView, doCustomConfigure, and processAction methods to reference these fields. Here is the relevant portion of the processAction method that shows the changes. String actionEventName = request.getParameter(Constants.ACTION_EVENT);

    // Save the configuration updateif (Constants.SAVE.equalsIgnoreCase(actionEventName)) {

    PortletPreferences prefs = request.getPreferences();try {

    prefs.setValue(Constants.PREF_DS, request.getParameter(Constants.DATASOURCE));prefs.store();

    }…

    Start your portal server and retest this portlet. We should have the same function as before including the behavior when going into config mode and setting the data source name. Retest the portlet thoroughly to make sure we haven’t missed anything while we cleaned-up the code.

    User id of the logged-in user Once that has been verified we will add code to actually get the user id of the logged-in user. Previously, we hardcoded an id in the doView method in the ContactsPortlet. Let’s change the

    Page 22 of 65

  • Portlet Development Workbook doView method to set the user id dynamically using a getUserid(request) method that we will write. Before we start writing the getUserid method let’s take a look at a related topic in the InfoCenter. We will follow code sample discussed there for this method. First, read the short section in the WebSphere Portal InfoCenter on the “PUMA SPI overview” topic. This describes the SPI (Service Provider Interface) that is new with Portal 5.1.0.1 that provides the capability for portlets to access user profile and group information. Reading Reference: WebSphere Portal InfoCenter

    Search on “PUMA SPI overview” Create the getUserid(request) method following the code sample in the InfoCenter. Additionally, we will add some code to extract just the short user id. The provided code will get an id from the PumaProfile object. But, that ID will be in the form of the fully qualified distinguished name. For example, we might get an id returned of the form uid=”wpsadmin,o=default organization”. We could use the id in that form in our portlet since we just using this as a key to store and retrieve the user’s contacts in the database. But, since we already put some values in the database table using the short name (just “wpsadmin”) and it is a bit more readable in that format we will add some code to extract it. Here is the completed method implementation. The first part was described (to some extent) in the InfoCenter. The piece that we added does simple string manipulation so we won’t discuss this any further. protected String getUserid(PortletRequest request) {

    String userid = "";try {

    PortletServiceHome psh;javax.naming.Context ctx = new javax.naming.InitialContext();psh = (PortletServiceHome)ctx.lookup("portletservice/com.ibm.portal.um.portletservice.PumaHome");if (psh != null){

    PumaHome service = (PumaHome) psh.getPortletService(PumaHome.class);PumaProfile pp = service.getProfile(request);User user = pp.getCurrentUser();userid = pp.getIdentifier(user);

    }// Get just the id if name is returned in form "uid=xxxxx,o=yyyyyy"if (userid.substring(0,4).equalsIgnoreCase("uid=")) {

    int index = userid.indexOf(",");userid = userid.substring(4,index);

    } catch (Exception e) {e.printStackTrace();

    }return userid;

    }

    Start your portal server and retest this portlet. Again the portlet should function as before, as long as you log in with the same user id (“wpsadmin”).

    Page 23 of 65

    http://publib.boulder.ibm.com/pvc/wp/510/ent/en/InfoCenter/index.html

  • Portlet Development Workbook One last point, you would probably think that we would do something with exception and error condition handling here. In general, when an error or exception condition is encountered we will want to put sufficient detail in the logs so the problem can be debugged and resolved but also put a short but meaningful message out to the portlet to inform the user that a problem was encountered. Our code now prints the stack trace out to the logs, which is good, but does nothing to inform the user of the condition (or explicitly handle the error condition). This is an important topic and will defer it until later not because it should be done last. In fact we should have error condition handling be designed into our code from the start. But, for our first portlet project I want to let some other parts develop that will ultimately influence error handling. For now, let’s get back to implementing the full portlet functionality. We are missing the edit mode capability that will let us add contacts, edit contacts, or delete contacts. We are also missing the capability in view mode to show a detailed view of a selected contact. Let’s start with the edit mode function.

    Chapter 6 - Portlet Edit mode: Part 1 As we just said we want to add edit capability to allow the user to add a new contact, delete an existing contact or make updates to an existing contact. So, let’s make a main edit page that looks like the following screen shot.

    As you can see here we have a representation that looks like our main view but has radio buttons to select a contact and has submit buttons to add, delete or edit. As you would expect the add button will take us to a screen to add a new contract entry. The delete button will delete the selected entry and the edit button will take us to another screen that shows the selected contact information with the ability to make modifications. Let’s start by creating this edit view. Recall that ContactsPortlet already has a doEdit method that simply invokes the ContactsEdit.jsp. Also, notice that what we want to show for the edit mode page is similar to the view page with the addition of the radio buttons and select buttons. But, the list of contacts is the same. So, we will be able to reuse some code from our doView method. Copy the code from the doView method that gets the current user, gets the list of contacts for the user, and puts that list of beans on the request object for use by the JSP. Next, we will modify the JSP. Edit both the ContactsEdit.jsp and the ContactsView.jsp. We will copy the code from the view JSP. Let’s start by taking all of the code from the ContactsView.jsp and completely replace the contents of ContactEdit.jsp. We will make only one change at this point. In the ContactsEdit.jsp change the encoded name of the form tag from “viewform” to “editform”. This is not strictly necessary but is more readable. Make sure you save the changes to both the ContactsEdit.jsp and the ContactsPortlet, start the Portal test server if necessary and run our portlet. When you click on the edit icon you should see the same list of three contacts in edit mode and we saw in view mode. Next let’s add the radio button to the contacts.

    Page 24 of 65

  • Portlet Development Workbook Adding a radio button is pretty easy. We just need to make sure that when a radio button is selected we want its value to be an identifier for the contact so that we will be able to tell which contact was selected in our portlet code. So, we want to add something very similar to this after the tag. Notice that in that code in the JSP we are iterating over the list of Contacts and each Contact knows its object identifier. So, we use that value to uniquely identify the selected radio button.

    Add this line and retest the portlet. In edit mode you should now see essentially the same contents as view mode but with the radio buttons added.

    But, before we go on to the next step remember that we are adding name constants to the constants file and using those values instead. So, change the JSP to reference the radio button name by a constant.

    Don’t forget to add the page import directive toward the top of the JSP (same as we have in the ContactsConfig.jsp) and add the static field to the Constants file. public static final String SELECTED = "selected"; Retest the portlet. You should not see any changes in edit mode. Next we will add the submit buttons to this JSP. Edit the ContactsEdit.jsp. After the close of the table tag but before the close of the form tag add three input fields of type submit like the following.

    Notice that we introduced more strings here. We make string contants for names and values that we will interrogate later and want to make sure we don’t get tripped up inadvertently with mismatched strings. Therefore, we don’t need to add string constants like “submit” to our constants file. Likewise, we don’t add a constant for something that we are not using as a key value that we would otherwise have to hardcode into our portlet code. In the case above the value attributes will not be interrogated later. Instead the “add”, “delete”, and “edit” strings there are just used to label the submit buttons when they are rendered. Later when we talk about internationalization we can use a ResourceBundle and additional tags so that those labels could be changed dynamically to other languages. Even so, we will use those values as keys in upcoming changes so let’s update the Constants file to be prepared. Add constants for ADD, DELETE, and EDIT; such as: public static final String ADD = "add";public static final String EDIT = "edit";

    Page 25 of 65

  • Portlet Development Workbook public static final String DELETE = "delete"; Save everything and retest. The edit mode should now display exactly what we wanted. The only issue is that the submit buttons on the bottom of the page do not do anything. We will address that next.

    We will start with “add”. Again, let’s take a look at what we want for a final result. When we click on the add button we would like a new view to be rendered allowing us to add a new contact. As an aside, I use the term “view” here to reference the rendered representation of the portlet. I think I have used the terms screen and page also. All are somewhat overloaded terms. I will use “view” to represent what you see in the browser for the portlet. When used like that I do not mean to imply the portlet “view mode” necessarily. So, we are now talking about another view in edit mode. When I want to reference the “view mode” specifically I will make sure to state “mode” in the discussion. Ok, so we want another edit view and this one will let us add a contact. Here is what we want to see as a result.

    First we will create this JSP. Notice that this is essentially static HTML markup. That is there are no beans that we use to populate the view and no changes to the view based on any business logic. So, when we click on the “add” submit button all we need to do is invoke this JSP. In RAD, add a new JSP file to the jsp folder in our project. The new JSP should be named ContactsAdd.jsp. To get started with the JSP add the following contents:

    First Name

    Page 26 of 65

    http://java.sun.com/portlet"prefix="portlet"%

  • Portlet Development Workbook


    The first three lines are standard for our JSPs. When have seen the form tag before using the namespace tag to ensure we can uniquely identify our form data when this portlet is placed on a page with other portlets. Then we define a table with two columns. Notice that we use CSS styles defined in the style sheets provided with Portal and available to help ensure a common look-and-feel among portlets. So far this view will only allow the user to enter a first name for the contact. We will extend it to include all fields. There are also two submit buttons defined, one to save the new contact and one to cancel. At this point they don’t do anything. We will hook them up and extend the view for all contact attributes but first, let’s get this view to render. To do that, we will start back in the ContactsEdit.jsp. Edit that jsp and go to the submit button tag for “add”. In general, when you click on a submit button the URL specified as the action attribute on the form tag will be invoked. But, looking at this JSP we see that the form tag does not have an action attribute. We could add an action there like we did in the ContactsConfig.jsp where we specified action=" on the form tag. The issue with doing that here is that we have three submit buttons that we want to include within this form tag and the three have different meaning and are intended to be handled by Portal somewhat differently. So, to handle this we will use a small amount of javascript to dynamically set the action attribute when a submit button is clicked. Another consideration is what we specify in the action attribute. In the config JSP we used a tag provided by portal to create a URL pointing back to our portlet and ensuring that the portlet action event handling was invoked. We didn’t specify a specific portlet mode there so we remained in configure mode. In this case we will create a URL to our portlet and return to edit mode. That means that our doEdit method will be called. However, we already have code in that method to display the main edit view. In order for that method properly display the appropriate view we will need to add a parameter to this generated URL that we can interrogate in that method to determine if the main edit view should be generated or the “add contact” view should be generated. So, here is what that input submit tag should look like. It does get a bit complicated, but if you look at it in component pieces you will see that it does what we just described. It uses the renderURL tag that will create a URL reference to our portlet, but not with an action event, just with a call to render the portlet. We also use the param tag to add a parameter to the URL and this parameter is what we will use in the doEdit method to indicate that we want to display the add contact view. Let’s look at that tag now.

    Page 27 of 65

  • Portlet Development Workbook I split this across multiple lines to help make it more readable but you want to be careful that the browser doesn’t generate an error for an unterminated string if you span lines in the JSP. Again we have an update to our Constants file: public static final String EDIT_REQUEST = "editRequest"; Next we need to modify the doEdit method to check this parameter and act accordingly. The parameter will just be retrieved from the request object with the name EDIT_REQUEST and if its value is ADD then we know that we are looking for the add contact view. Otherwise, show the main edit view. So, the doEdit method could now have a check that looks like this. // Check for the edit request parameterString editRequest = request.getParameter(Constants.EDIT_REQUEST);if (Constants.ADD.equalsIgnoreCase(editRequest))

    doAddEdit(request, response);else

    doMainEdit(request, response); Then we can put the code that was in the doEdit method into a new method called doMainEdit. We can then add another method call doAddEdit that simply invokes the ContactsAdd JSP. Here is that method: // Set the MIME type for the render responseresponse.setContentType(request.getResponseContentType());

    // Invoke the JSP to renderPortletRequestDispatcher rd =getPortletContext().getRequestDispatcher(Constants.ADD_JSP);rd.include(request,response); Notice that we have another Constants reference that we need to resolve; for the JSP file name. Add an entry for ADD_JSP. public static final String ADD_JSP = "/jsp/ContactsAdd.jsp"; Retest the portlet, you should see the following view when you hit the add button from the edit mode of the portlet.

    Next complete the JSP changes for the remaining fields. Edit the ContactsAdd.jsp and find the table row definition for “First Name”.

    First Name

    Duplicate that set of lines 5 times and make changes to specify the following additional attributes. Last Name name=”lastName” Email name=”email”

    Page 28 of 65

  • Portlet Development Workbook Company name=”company” Mobile phone name=”mobilePhone” Business phone name=”businessPhone” And, of course, we shouldn’t use those names directly in our JSP so update the Constants file again with the following changes and use these references in the JSP instead.

    public static final String FIRST_NAME = "firstName";public static final String LAST_NAME = "lastName";public static final String EMAIL = "email";public static final String COMPANY = "company";public static final String MOBILE = "mobilePhone";public static final String BUSINESS = "businessPhone";

    Retest the “add” function in the portlet. You should now see an edit view exactly as we showed in the beginning of this section. That wraps up this task. The add function clearly doesn’t actually add a new contact but we will finish that as the first effort in the next step.

    Chapter 7 - Portlet Edit mode: Part 2 Let’s continue with the “add” function in our portlet edit mode. From the previous step you have the ability to click on an “add” button from the main edit mode view that takes you to another view, still within edit mode, that allows us to enter values for a new contract entry. There are two buttons on that page that allow you to save the entry and one to cancel. They have not been implemented yet. Let’s start with an easy one. We can implement the function to do the cancel. So, one thing we need to consider is where we want to return to when that cancel button is pressed. Do we want to return to the main edit view at that point or return to view mode (leaving edit altogether). Typically, this kind of decision would be made for the developer by the business users, user interface team, or application designer. But, since this is our portlet we will arbitrarily say that we want to return to the main edit view. Edit the ContactsAdd.jsp and change the input tag for the cancel button to have an onClick attribute similar to what we did in the previous step. The onClick attribute defines what will get executed when the submit button is clicked. In this case, like the one previously, we want to use that function to dynamically set the action attribute of the form object before the submit occurs. Again we have to reference the form object by its encoded name and then use javascript to set its action attribute. We will use the renderURL tag from the portlet tag library to create the URL pointing back to our portlet and use portletMode attribute to set the desired mode to “edit”. We will also add a parameter to this render URL to indicate to our doEdit code that we want to display the main edit page. Add a variable to our Constants class for this: public static final String MAIN = "main" Change that line in the JSP to this:

    You can retest the portlet and see now that the cancel button on the add contact view works as expected. Why did that work? Well, the difference between the URL created here to reference

    Page 29 of 65

  • Portlet Development Workbook return to the main edit mode view and the one we created in the previous step to display the “add contact” view is the render parameter. Our doEdit() method already checks this parameter and if the value is “add” shows the add contact view. Otherwise, it shows the main edit view. Since that is our default then setting the parameter to “main” will result in the main edit view getting rendered. Next we will complete the function to actually save the contact information that is entered on this page. To save the new contact we have to do a few things here. First we need to change the submit tag in the ContactsAdd JSP to invoke our portlet again. In this case, we are performing an action so we want our portlet’s processAction method to get invoked. Then we need to change that processAction method to recognize this particular request (since we already put code there in a previous step to save our config mode changes). Then we need to modify our database access code to let us actually do the database update. So, let’s edit the ContactsAdd.jsp again. This time we will change the submit tag