| < Day Day Up > |
|
In this section we show two examples of deploying portlets on z/OS as follows:
Using WPACONF job via TSO to deploy a portlet from the Portlet Catalog. Section 6.4.3, "Installing portlets obtained from the Portlet Catalog" on page 215 provides more information about portlets from the Portlet Catalog.
Using the WPAConfig script via USS to deploy an out of the box portlet installed during portal server installation but not deployed.
As our first example of deploying a portlet, we used the Health Care click-to-action (C2A) sample portlet available for download from the Portlet Catalog (NAVCODE 1WP10003L).
There are a number of different paths that an administrator can take to deploy portlets and make them available for users on a page. In fact, the only steps an administrator has to do is deploy the portlets and assign user rights so that users can add them to a page for themselves. In this example, we performed the following steps as an administrator to install the portlet:
Download the sample from the Portlet Catalog Web site to a Windows system and unzip the file. Follow the instructions in the supplied documentation in the form of file readme.html.
Tip | Because of the installation differences between distributed platforms and z/OS we used the following directories on z/OS instead of the ones referred to in the portlet installation instructions:
|
Tip | We recommend that changes are made, if required, to properties and xml files before deploying portlets on z/OS. This is because some of these files are in ASCII format, and some are stored inside the runtime of the expanded WAR file on the application server, making it difficult to change them after deployment. |
Stop the Portal Server from a TSO session and then restart it.
Note | The following steps will fail for the C2A portlets, if the Portal server is not stopped and restarted after following the portlet installation Steps 3-8 in the Readme. |
Ensure that WebSphere Portal server on z/OS is running, and from a Web browser go to the following URL: http://<HOST_NAME>/wps/portal.
Login as the portal administrator with the default userid and password of wpsadmin as shown in Figure 6-17 on page 208.
Go to the Portal Administration place and select Install Portlets.
Browse the directory on the distributed system that corresponds to <WPS_PATH>/PortalServer/app/DeployablePortlets directory and select healthcarec2a.war. Click Next.
On the Install Portlet Application screen, the portlet(s) contained in the WAR and to be deployed are shown as in Figure 6-13 on page 203.
Figure 6-13: Installing C2A portlets from the Portal Administration place
Click Install to install all the C2A Health Care sample portlets and ensure you receive the message Portlets successfully installed.
Logon to TSO and using the TSO ISH environment you can optionally verify that the portlet deployment job(s) have been added to directory <WPS_PATH>/temp/jobs as shown in Figure 6-14 on page 204.
Figure 6-14: C2A deploy work ready to run job WPACONF
Submit job WPACONF, after reviewing the JCL job card. Note that if the Portal Server is running it will be stopped and re-started for the deployment of each portlet it deploys from the WAR files in the work directory. Therefore, we recommend stopping Portal Server before submitting the job and re-starting it manually on completion. Check the job completes without errors.
Start Portal Server and login using the administrative user ID via http://<yourhost>/wps/portal. Navigate to the page you want to add the newly deployed portlets via Work with pages -> Edit layout and content -> Place+Page -> Get Portlets. We chose a filtered search on C2A and selected the Health Care sample portlets to be added to our page as shown in Figure 6-15 on page 205.
Figure 6-15: Adding the C2A portlets to a page
The next portal administration screen allows you to select the row and column containers to show the portlets on the selected page. Finally, ensure that page is activated.
Navigate to the page and the deployed portlets should be available and working, as shown for example in Figure 6-16 on page 206.
Figure 6-16: C2A example portlets
In this section we take you through the steps of installing a portlet supplied with WebSphere Portal but not installed as part of the post installation process. All portlets that come with WebSphere Portal can be found as WAR files in <WPS_PATH>/PortalServer/app/DeployablePortlets directory. We will install the Document Viewer portlet and place it on a page we created called Redbook that exists in the ITSO place that we also created. The following steps are enumerated:
Tip | Since the HFS on z/OS cannot be accessed directly from a browser or as a mapped drive, copy the portlet WAR file(s) to a distributed system where you are running the Web browser. |
Assuming that WebSphere Portal server on z/OS is running, bring up a Web browser and go to the default URL: <HOST_NAME>/wps/myportal.
Login as the portal administrator with the default userid and password of wpsadmin as shown in Figure 6-17 on page 208.
Figure 6-17: WebSphere Portal login window
Go to Portal Administration Pages and make sure the Install Portlets page is selected.
Browse to the directory on the workstation where you transferred docviewer.war from the z/OS HFS directory <WPS_PATH>/PortalServer/app/DeployablePortlets and select docviewer.war. See Figure Figure 6-18 on page 209. Click Next.
Figure 6-18: Initial window on portlet installation
On the Install Portlet Application page, you will the portlet(s) contained in the WAR file. Notice that the document viewer portlet is a portlet application that contains 5 separate portlets.
Click Install to install all the Document Viewer Portlets. This will take a few moments.
Wait until you receive the message - Portlets successfully installed.
If you go to the Manage Portlet Applications tab, and look for the docviewer.war in the list of Web modules, you will see that the Portlet application belonging to it are Active as seen in Figure Figure 6-19 on page 210.
Figure 6-19: Manage Portlet Applications window
Select Work with Pages place and click on Edit Layout and Content.
Select ITSO in the Place drop down and select Redbook in the Page drop down list.
Click Get portlets.
On the following screen entitled Get portlet for: ITSO, click the radio button to Show all portlets. Click Go.
You should see a list of all the available portlets. We will install 2 of the 5 portlets that were contained in the docviewer WAR file. Click the plus icon on the left of Name field to add PDF Document Viewer and Word Document Viewer. Those portlets will be added to the Portlet list. Click OK in the portlet menu bar.
You will be back at the Edit layout and Content window for the Redbook page. Highlight PDF Document Viewer Portlet, move your cursor to the left side of one of the columns on the page and click the Add icon.
Then click to select the WORD Document Viewer Portlet and click the Add portlet icon in the right-side column. The resulting screen is shown Figure 6-20 on page 211.
Figure 6-20: Laying out the portlets in portlet containers on a page
Remember to click the Activate label so that the page is activated and can be viewed.
From the place drop down list, select ITSO, navigate to the Redbook page and you should see the portlet outlines with a Error 404: File not found message as seen in Figure 6-21 on page 212.
Figure 6-21: Screen showing the newly installed portlets with the error message
Go to a z/OS TSO session screen and stop WebSphere Portal, in our case we issued command: /p WSPORTA.
Log on to the UNIX System Services on the target system as a user with administrator authority for WebSphere Application Server. (On our system we logged in as CBADMIN.)
Change directory to
/usr/lpp/PortalServer/PortalServer/zosinst/tools/deploy.
If need be run the batch file client_env, as follows:. client_env
Change directory to /usr/lpp/PortalServer/PortalServer/zosinst/scripts. Invoke the WPAConfig.sh command. This will run the job, start a conversation, and install the portlets on the WebSphere Application Server for z/OS and OS/390.
Wait for the committed message (Figure 6-22 on page 213):
Conversation WPSConversation_xxxxxxxx_hh:mm:ss was committed.
Figure 6-22: Command window output of running the WPAConfig script
Make sure there are no error messages. At this stage the portlets are loaded and portal server needs to be restarted.
From the z/OS console, start WebSphere Portal, in our case we issued command: /s WSPORT.WSPORTA.
Refresh the browser that was displaying the Redbook page in the ITSO place or bring up a new browser and go to the ITSO place and down to the Redbook page. The two portlets should be displayed without any errors as shown in Figure 6-23 on page 213.
Figure 6-23: Screen showing the docviewer portlets before configuration
Click the Edit icon (one that looks like a pencil) in the menu bar at the top of one of the portlets. Enter the name of the PDF document you want to view. You have the choice of displaying the document in a new window or within the portlet as shown in Figure 6-24 on page 214. After all the parameters are entered, click Save.
Figure 6-24: Configuring the PDF document viewer portlet with document details
You will taken back to the Redbook page and the contents of the PDF will be displayed.
You can similarly deploy and use the other portlets in the docview Web module.
Figure 6-25: PDF file viewed in the portlet
There are hundreds of portlets in the IBM Portlet Catalog to be found at
http://www-3.ibm.com/services/cwi/portal/_pagr/105/
Many of them are available for download for free although some provided by third parties require registration and a fee.
Note | Portlets are categorized in the IBM Portlet Catalog for different versions of WebSphere Portal and different editions. We recommend ensuring you download portlets for WebSphere Portal Enable V4.1 or V4.2. |
In the following examples the installation steps are the same as we have seen in the preceding sections on portlet deployment. For each of the examples we describe any unique configuration steps that are performed.
In this example we will download the WebSphere Host On Demand (HOD) portlet, install and deploy it. There are 4 HOD portlets in the catalog - HOD 3270 cached and non-cached and HOD 5250 Cached and Non-cached. We will usethe HOD 3270 Cached portlet for WebSphere Portal V4.1.4 (NavCode 1WP10006L).
Once the portlet WAR file has been downloaded the steps are same as listed in 6.4.2, "Example of installing a portlet using WPAConfig" on page 206 except for the fact that you browse the directory where the portlet WAR file was downloaded. We created a page called HOD in our portal ITSO place to place the 3270 Cached HOD portlet.
Assuming you are logged in as the portal administrator (or anyone else who has the authority to install portlets) do the following:
Navigate to Work with Pages->Edit Layout and Content.
Choose ITSO place and HOD page.
Get and select the HOD 3270 Download portlet.
Add it to a portlet container and Activate the page.
The portlet addition screen is shown in Figure 6-26 on page 217.
Figure 6-26: Window showing the placement of HOD portlet on the HOD page
The HOD portlet has to be configured like this, before you can actually work with it:
Navigate to Portal Administration -> Portlets -> Manage Portlets.
In the list of Portlets, highlight HOD 3270 Download and select Modify parameters.
In the hodCodeBase parameter enter the name of the server that is running the HOD server and point to the hod directory. In our example we used http://wtsc48.itso.ibm.com:86/hod.
Click Save. Then click Close.
Figure 6-27: Screen to modify the parameters for the HOD portlet
Navigate to the ITSO place and bring up the page that has the HOD portlet, you should see the HOD portlet displaying a login screen as seen in Figure 6-28 on page 218. Enter the User ID and password and click Log On.
Figure 6-28: HOD portlet login window
If there are any sessions configured, you should see icons reflecting them on the next screen. If you double-click on any icon then that session will be activated. A new floating screen will be displayed and an icon will appear in the Active Sessions pane of the HOD portlet. See Figure 6-29 on page 219. You can use this session like a normal 3270 session.
Figure 6-29: HOD portlet showing an active 3270 session
This ends the deployment and verification of the HOD portlet.
The Portlet Catalog provides several portlets for accessing Lotus Notes® functions available on a Lotus Domino server, for example there are portlets called iNotes, Notes and MyNotes. The iNotes portlet can be used with the WebSphere Portal Enable edition giving users a browser view of the following Lotus Notes databases: mail, calendar, to-do, contacts and notebook.
We downloaded the Lotus iNotes Mail portlet from the Portlet Catalog NavCode: 1WP10007P which is saved as file inotes.war. The WAR file contains separate portlets for portlets mail, calendar, to-do, contacts and notebook and has a single configuration parameter authMethod for user sign on kept in the portlet.xml file in the WEB-INF directory inside the WAR file.
Tip | We recommend that changes are made if required to web.xml and portlet.xml before deploying portlets on z/OS. This is because these files are in ASCII format and stored inside the runtime of the expanded WAR file on the application server, making it difficult to change them after deployment. |
After deploying the portlets, we clicked on the portlet edit icon, and then added the Notes server and user configuration properties to the iNotes portlets as shown in Notebook portlet example in Figure 6-30.
Figure 6-30: Edit the iNotes portlet
Click Save and the portlets will try to access the server and if successful you should see the Notes database as shown in the Mail portlet example in Figure 6-31 on page 221.
Figure 6-31: iNotes Mail portlet
WebSphere Portal on z/OS can also be used to integrate access to Lotus Quickplace and there are portlets for this available in the Portlet Catalog. We cheated by using the iFrame portlet that was already installed on the portal server to access our Quickplace server with quite good results as shown in "Accessing Lotus Quickplace from Portal Server" on page 222.
Figure 6-32: Accessing Lotus Quickplace from Portal Server
In 6.1.1, "Using Application Developer to develop a portlet" on page 184 we developed a portlet called HelloWorld.
The steps for installing and deploying a custom portlet developed using Application Developer are the same as listed in 6.3.1, "Portlet deployment jobs" on page 195, except for the fact that you browse the directory where the portlet WAR file was exported and copied to.
We followed the two-step process for deploying our Hello World portlet, placed it on a page on the portal and accessed the page with the result as seen in Figure 6-33.
Figure 6-33: Our Hello World portlet deployed in the portal
In this section we take a J2EE application called Trader that accesses an application running on CICS. The solution was developed for redpaper From code to deployment: Connecting to CICS from WebSphere V4.01 for z/OS, REDP0206, and run it under WebSphere Portal Server using the following two methods:
Use the iFrame portlet to access the Trader application from its browser based GUI.
Convert the Web application part of the J2EE application into a portlet.
Another option would be to use the ServletInvokerPortlet, that is installed but not deployed, to access and display the Web application from a portlet. The ServletInvokerPortlet can be configured with a URL to point to the Web page of a Web application. We did explore this option but at the time of writing we were not successful in getting this portlet to render correctly on z/OS.
The Trader application is available for download from:
http://publib-b.boulder.ibm.com/Redbooks.nsf/RedpaperAbstracts/redp0206.html?Open
It comes as packaged in a single file named Connector_code.zip. We downloaded the file and unzipped it to a directory and examined its contents. The unpacked file contains several directories: Completed Trader, Trader-Cobol, VisualAge® for Java and WebSphere Studio. Redpaper From code to deployment: Connecting to CICS from WebSphere V4.01 for z/OS, REDP0206, explains that the directory contents are as follows:
Completed Trader: the J2EE application EAR file
Trader-Cobol: code and JCL for the CICS application
VisualAge for Java: commarea data for the J2EE EJB
WebSphere Studio: WAR file and classes for the Web application
Figure 6-34 shows an overview of the complete Trader application.
Figure 6-34: Trader application
Deployment of the complete application required the following prerequisites which we will not cover further in this book:
WebSphere Application Server V4
CTG V4.0.2 or CTG V5
CICS TS V1.3
Once installed and deployed in a separate J2EE server region on our WebSphere Application Server and CICS the GUI for the Trader application looks like the example shown in Figure 6-35.
Figure 6-35: J2EE and CIC's Trader application GUI
We obtained the JNDI name parameter for locating the EJB from the systems management SMEUI GUI as shown in Figure 6-36 on page 226.
Figure 6-36: SMEUI view of TraderEJB home JNDI name
The iFrame portlet is one of the portlets that are installed and deployed during installation of the portal on z/OS. As its name implies it renders a target Web page by showing it inside an iFrame. The iFrame, which stands for inline frame, is still a fairly recent introduction to HTML and may not yet be supported by all browsers. We configured the iFrame portlet to point to URL http://wtsc58oe.itso.ibm.com:8081/TraderWeb/ and used the logon screen shown in Figure 6-37 on page 227 to logon to the Trader application.
Figure 6-37: Logging onto Trader using the iFrame portlet
Since the Trader Web application uses quite straightforward HTML to render its simple user interface pages, the iFrame portlet handles the application quite well. Figure 6-38 on page 228 shows another example screen from one of the application's pages.
Figure 6-38: Using the Trader application with the iFrame portlet
As we have seen in Figure 6-34 on page 224, the Trader J2EE application consists of an EJB part and a Web application part. To transform this into a portlet we decided to leave untouched the EJB and concentrate on converting the Web application, consisting of servlets and JSPs, into a portlet. Some would call this portalizing.
For this we left the Web application and EJB installation untouched, running on another J2EE server on the same application server as portal. Figure 6-39 on page 229 shows an example of what we wanted to achieve by portalizing the Web application into a portlet called TraderPortlet, in comparison to using the iFrame portlet.
Figure 6-39: TraderPortlet and iFrame portlet compared
A view from the systems management GUI SMEUI of our J2EE servers TAMAS and WSPORT is shown in Figure 6-40 on page 230. Note that for this project we did not enable any J2EE EJBROLES based security, so that by default our portal server ID was running with read access to EJBROLE. For a further discussion on this refer to Chapter 7, "Custom User Registry" on page 285.
Figure 6-40: SMEUI view of J2EE servers
Our example does not represent a complete set of instructions on how to portalize a Web application, but just shows the steps we performed to get the example working with WebSphere Portal.
We started by importing the Completed_Trader.ear file into Application Developer using the import wizard as seen in Figure 6-41 on page 231.
Figure 6-41: Import J2EE EAR wizard.
After completing this, then as shown from the J2EE perspective in Figure 6-42 on page 232 we were left with 275 project problems which Application Developer calls tasks.
Figure 6-42: Tasks outstanding after importing Completed_Trader.ear
Not to worry, at this stage there were undoubtedly missing JAR files and since we would work with only half of the project, the Web application WAR, we decided to filter them out by selecting Filter Tasks->On selected resource only as seen in Figure 6-43.
Figure 6-43: Filter tasks option
Our next step was to create the Portlet Application project in Application Developer by switching to a Portlet perspective, then right-clicking in the Navigator frame and selecting New -> Project -> Portlet application project as seen in Figure 6-44 on page 234.
Figure 6-44: Creating a Portlet application project
Figure 6-45 on page 235 shows the settings we provided to define our Portlet project which we decided to call TraderPortlet. After entering the project's settings we clicked on Finish since we did not want Application Developer to add portlets for us, because we will use the TraderWeb.war Web application as our base for a portlet.
Figure 6-45: Define the Portlet project
After clicking on Finish, Application Developer creates and populates the project for us as shown in Figure 6-46 on page 236.
Figure 6-46: TraderPortlet project
Next we added TraderWeb.war to the project by clicking on the TraderPortlet project name and selecting File -> Import -> WAR file, as shown in Figure 6-47 on page 237.
Figure 6-47: Import WAR
Figure 6-48 on page 238 shows the location of the WAR file and the options we set for the import.
Figure 6-48: Import options for the WAR file
Finally we set the Module dependencies, which will update Application Developer's build and runtime classpaths, by clicking the checkbox for TraderEJB.jar as seen in Figure 6-49 on page 239.
Figure 6-49: Set the module dependencies
We then clicked on Finish and also clicked on Yes to the dialog window shown in Figure 6-50.
Figure 6-50: Resource dialog
Application Developer completes the import of the TraderWeb Web application WAR file into our portlet project as shown in Figure 6-51.
Figure 6-51: Portlet application project with imported code from TraderWeb.war
We now need to make changes to the imported servlet files to convert them into a portlet. The main items we needed to consider, examine and possibly change are the following.
portlet.xml: Assign a servlet to be used for the portlet.
HTML pages: Convert to JSPs.
JSP source: Ensure only page fragments are in the JSP, use the Portal tag library as desired, parameterize the ACTION field of any FORMS, and ensure the INPUT field of any forms is unique in the portal namespace.
Java source: Use Portlet API instead of Servlet API.
web.xml: Update references.
Miscellaneous considerations for changing a servlet to a portlet.
Note that as we have already said, this list is not meant to represent all the items that need to be considered when portalizing a Web application, but it was enough for our project, and we explain each of the things we did in more detail in the following steps.
Servlets provide descriptor file web.xml in directory WEB-INF. In addition to this file portlets need to provide their own descriptor file portlet.xml. Parameters in portlet.xml typically include the servlet ID, concrete portlet ID, mark-up languages supported, title, and configuration options.
Using Application Developer we clicked on file portlet.xml in the WEB-INF directory, and noted that there were two tasks listed, complaining about the lack of a servlet reference. We expanded the Portlet application in the right hand Portlets view frame, and selected Portlet_1 as shown in Figure 6-52 on page 242.
Figure 6-52: Select Portlet_1 to assign a servlet to the portlet
Next we clicked on the Browse button next to the grayed out Servlet list box, and in the pop-up dialog window selected TraderServlet which was the only servlet listed, as shown in Figure 6-52 on page 242.
Figure 6-53: Select servlet to assign to the portlet
Click on OK, then File->Save portlet.xml and Application Developer updates the project and the Tasks listed for portlet.xml should have cleared.
Because WebSphere Portal aggregates multiple portlet JSPs into a single Web page, any existing HTML pages need to be converted to JSPs containing only page fragments that are rendered from the Portlet Java code. The Trader Web application servlet had a single HTML page Logon.html that we converted, bit in general this would need to be done for all HTML pages making up a Web application. Figure 6-54 on page 244 shows the top page level tags in Logon.html ready for deletion.
Figure 6-54: Deleting page level tags from the HTML
Figure 6-55 on page 245 shows the bottom page level tags in Logon.html ready for deletion.
Figure 6-55: Deleting page level tags from the HTML
The next to last step was to rename the HTML file from Logon.html to Logon.jsp as seen in Figure 6-56 on page 246.
Figure 6-56: The newly renamed Logon.jsp
Next we need to modify the JSPs that were imported from the servlet. WebSphere Portal aggregates multiple portlet JSPs into a single Web page, so the portlet JSPs must consist only of page fragments. This means we need to remove all page level HTML tags from the JSPs, for example:
<!DOCTYPE HTML - ....>
<HTML>, </HTML>
<HEAD>, </HEAD>
<BODY>, </BODY>, including <META> and <LINK>
<TITLE>, </TITLE>
Application Developer will automatically generate the DOCTYPE mark up for Web pages and JSPs and we needed to turn this off. Select Window->Preferences, expand Web Tools and select Files, as shown in Figure 6-57 on page 247.
Figure 6-57: Remove insert DOCTYPE preference
Uncheck Insert this DOCTYPE, also Include GENERATOR in HTML source, and click Apply followed by OK.
Next we modified JSPs Buy.jsp, CompanySelection.jsp, Quotes.jsp, Sell.jsp and TraderError.jsp to remove the page level tags. Using Buy.jsp as an example of how to do this we opened the source frame for the JSP and highlighted for deletion the tags above the <jsp:useBean...> tags as shown in Figure 6-58 on page 248.
Figure 6-58: deleting page level tags from the JSP, part 1
We left alone the <jsp:useBean...> tag and also other tags that form the data of the body of the JSP, and highlighted the remaining page level tag for deletion at the bottom of the JSP as seen in Figure 6-59 on page 249.
Figure 6-59: deleting page level tags from the JSP, part 2
We next need to change the ACTION field in any FORM's in the JSPs. The action field in an HTML form is used to tell the browser which service, for example a Web page, servlet, CGI, and so on, needs to be used to perform the action associated with the form. The JSPs from the Trader Web application servlet had a hard coded relative URI value of TraderServlet that is used to by the browser to call the servlet. In the case of a portlet running under Portal Server we do not know until runtime the value of the service that will be allocated by portlet container, so the JSP needs to obtain the ACTION field value when it executes.
There are different ways that the ACTION field value can be obtained at runtime. We chose to set a parameter for this value inside the portlet code; see "5) Changing the servlet Java code to portlet code" on page 255, and extract it from the PortletRequest object using the following code in the JSP: <FORM action='<%=request.getAttribute("actionVal")%>' method="POST">. We made this change as appropriate to all of our JSPs:
The next recommended step uses the Portal Server API from the portal tag library. This is done by adding statement <%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %> to the top of the JSPs, as shown in Figure 6-60 on page 250.
Figure 6-60: Portal Tag library reference in Buy.jsp
Next add file portlet.tld to directory /WEB-INF in the project as shown in Figure 6-61 on page 251.
Figure 6-61: Adding portlet.tld to the project directory
The portal tag library contains many useful functions that can be used by JSPs and we recommend that JSP and portlet developers become familiar with these functions. In our case we considered using the portal tag library to ensure that the INPUT fields on the FORMS inside are JSPs were assigned a unique value in the portal container namespace. This is necessary because the Portal Server reserves keyword values for its own use. An example of encoding an ACTION field in a form is to use a value such as: '<%= portletResponse.encodeNamespace("DisplayMode_attr")%>'.
In the end we did not bother to do this as we were working with a small number of portlets, and the existing values from the servlet JSPs did not clash with any of those reserved by the Portal Server, but it is the recommended practice for developing portlet JSPs.
The final change we made to the JSPs with our TraderPortlet project was to move the JSPs into project directory /WEB-INF, and additionally create another directory named /WEB-INF/html and copy the JSPs there as well as shown in Chapter 6, "JSPs in WEB-INF directory" on page 252.
Figure 6-62: JSPs in WEB-INF directory
Although not strictly necessary for our small project, you should be aware that to support different mark-up languages and NLS language translations, the portlet container assumes there is a specific directory structure for them in directory WEB-INF, and searches them starting from the bottom of the directory tree.
Example 6-2 shows the original CompanySelection.jsp taken from the servlet where we have highlighted lines that are subject to modification or change based on the preceding comments.
Example 6-2: Original CompanySelection.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"><!-- Sample JSP file --> <HTML> <HEAD> <META name="GENERATOR" content="IBM WebSphere Page Designer V3.0.2 for Windows"> <META http-equiv="Content-Style-Type" content="text/css"> <TITLE> Company Selection </TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF"> <jsp:useBean scope="request" /> <H1 align="center">Company Selection</H1> <CENTER> <TABLE width="500" border="1"> <TBODY> <TR> <TD width="59" align="center" valign="middle"><B>Company</B></TD> <TD width="70" align="center" valign="middle"><B>Quotes</B></TD> <TD width="70" align="center" valign="middle"><B>Buy</B></TD> <TD width="68" align="center" valign="middle"><B>Sell</B></TD> </TR> <% for(int i=0; i<companiesBean.numOfCompanies();i++) { String compName = companiesBean.getCompany(i); %> <TR valign="top" align="center"> <TD width="300" align="center" valign="top" height="27"><%= compName %></TD> <TD width="60" align="center" valign="middle" height="25"> <FORM action="TraderServlet" method="POST"><INPUT type="submit" name="doShowQuotes" value="Quotes"> <INPUT type="hidden" name="company" value="<%= compName%>"> </FORM> </TD> <TD width="60" align="center" valign="middle"> <FORM action="TraderServlet" method="POST"><INPUT type="submit" name="doShowBuy" value="Buy"> <INPUT type="hidden" name="company" value="<%= compName%>"> </FORM> </TD> <TD width="60" align="center" valign="middle"> <FORM action="TraderServlet" method="POST"><INPUT type="submit" name="doShowSell" value="Sell"> <INPUT type="hidden" name="company" value="<%= compName%>"> </FORM> </TD> </TR> <% } %> </TBODY> </TABLE> </CENTER> <FORM action="TraderServlet" method="POST"> <CENTER><INPUT type="submit" name="doShowLogoff" value="Logoff"></CENTER> </FORM> </BODY> </HTML>
Example 6-3 shows CompanySelection.jsp from our new TraderPortlet portlet where we have highlighted the lines that we actually added or modified. Note that we added a System.out.println statement at one stage to help us with debug as noted in Chapter 4, "Log Files" on page 136.
Example 6-3: Modified CompanySelection.jsp
<%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %> <jsp:useBean scope="request" /> <H1 align="center">Company Selection</H1> <CENTER> <TABLE width="500" border="1"> <TBODY> <TR> <TD width="59" align="center" valign="middle"><B>Company</B></TD> <TD width="70" align="center" valign="middle"><B>Quotes</B></TD> <TD width="70" align="center" valign="middle"><B>Buy</B></TD> <TD width="68" align="center" valign="middle"><B>Sell</B></TD> </TR> <% System.out.println("C= Henry Company JSP getAttribute = " + (String)request.getAttribute("actionVal")); %> <% for(int i=0; i<companiesBean.numOfCompanies();i++) { String compName = companiesBean.getCompany(i); %> <TR valign="top" align="center"> <TD width="300" align="center" valign="top" height="27"><%= compName %></TD> <TD width="60" align="center" valign="middle" height="25"> <% // Form action parameter obtained from portlet %> <FORM action='<%=request.getAttribute("actionVal")%>' method="POST"><INPUT type="submit" name="doShowQuotes" value="Quotes"> <INPUT type="hidden" name="company" value="<%= compName%>"> </FORM> </TD> <TD width="60" align="center" valign="middle"> <% // Form action parameter obtained from portlet %> <FORM action='<%=request.getAttribute("actionVal")%>' method="POST"><INPUT type="submit" name="doShowBuy" value="Buy"> <INPUT type="hidden" name="company" value="<%= compName%>"> </FORM> </TD> <TD width="60" align="center" valign="middle"> <% // Form action parameter obtained from portlet %> <FORM action='<%=request.getAttribute("actionVal")%>' method="POST"><INPUT type="submit" name="doShowSell" value="Sell"> <INPUT type="hidden" name="company" value="<%= compName%>"> </FORM> </TD> </TR> <% } %> </TBODY> </TABLE> </CENTER> <% // Form action parameter obtained from portlet %> <FORM action='<%=request.getAttribute("actionVal")%>' method="POST"> <CENTER><INPUT type="submit" name="doShowLogoff" value="Logoff"></CENTER> </FORM>
File web.xml from the servlet contained references to the welcome HTML page including Logon.html which we deleted. We also changed the url-pattern tag to /TraderServlet/*. Web.xml controls many of the execution and configuration parameters of the portlet -servlet, including for example security user ID's, that we did not explore here. For this project we could have left web.xml as it was, but for general conversion of a servlet to a portlet, examination and modification of this file will most likely be needed.
The major effort in changing the Trader Web application from a servlet to a portlet required changes to the servlet's Java code. The servlet consisted of two Java beans, ErrorInfoBean.java and UserInfoBean.java, that after examination we left unchanged.
The main servlet file TraderServlet.java needed quite significant modifications to convert it to a portlet. Some general items to be considered for this work are as follows.
Add the portlet API imports to the source, for example:
import org.apache.jetspeed.portlet.*; import org.apache.jetspeed.portlets.*
Decide which portlet class to use for the implementation. In the simple Hello World example in 6.2, "Portlet development example" on page 185 we extended the AbstractPortlet class, but the PortletAdapter class provides a default implementation of the AbstractPortlet class, and is recommended instead.
Remove any doXXX() servlet methods, for example in our case doGet() and doPost(), and replace them with the doView() method instead. Consider adding other processing methods doEdit(), doHelp() and doConfig() to the portlet as appropriate.
Change servlet API calls to the HTTPSession object to use the portlet API instead. For example change:
UserInfoBean userInfo = (UserInfoBean)httpSesion.getAttribute(userInfoID);
to
UserInfoBean userInfo = (UserInfoBean)request.getAttribute(userInfoID);
Change other servlet API calls to use the equivalent portlet API calls, some examples:
HttpServlet to PortletAdapter
HttpServletRequest to PortletRequest
HttpServletResponse to PortletResponse
ServletException to PortletException
HttpServletSession to PortletSession
Set JSPs FORM ACTION fields for runtime determination by JSPs, for example:
PortletURI traderURI = response.createURI(); PortletAction newAction = new DefaultPortletAction("logon"); traderURI.addAction(newAction); request.setAttribute("actionVal", traderURI.toString());
Render the JSPs to display portlet views, for example:
getPortletConfig().getContext().include("/jsp/Logon.jsp", request, response);
Add any state handling to the portlet for user interaction, for example to handle cases where users switch portal pages away from the portlet and the return again within the same session.
Example 6-4 shows the complete source code listing of the TraderServlet before we converted it into a TraderPortlet:
Example 6-4: The servlet code before modification
package itso.cics.eci.j2ee.trader.servlet; import itso.cics.eci.j2ee.trader.*; import java.io.*; import javax.ejb.*; import javax.servlet.http.*; import javax.servlet.*; public class TraderServlet extends javax.servlet.http.HttpServlet { // HttpSession IDs private final static String traderID = "Trader"; private final static String companiesID = "Companies"; private final static String userInfoID = "userInfo"; // constants used in JSPs // field names private final static String fieldDoPerformLogon = "doPerformLogon"; private final static String fieldDoShowQuotes = "doShowQuotes"; private final static String fieldDoShowBuy = "doShowBuy"; private final static String fieldDoPerformBuy = "doPerformBuy"; private final static String fieldDoShowSell = "doShowSell"; private final static String fieldDoPerformSell = "doPerformSell"; private final static String fieldDoShowCompanies = "doShowCompanies"; private final static String fieldDoShowLogoff = "doShowLogoff"; private final static String fieldJndiName = "jndiName"; private final static String fieldNameService = "nameService"; private final static String fieldProviderURL = "providerURL"; private final static String fieldUserID = "userid"; private final static String fieldPassword = "password"; private final static String fieldCompany = "company"; private final static String fieldNumberOfShares = "numberOfShares"; // JSPs private final static String jspLogon = "Logon.html"; private final static String jspCompanySelection = "CompanySelection.jsp"; private final static String jspQuotes = "Quotes.jsp"; private final static String jspBuy = "Buy.jsp"; private final static String jspSell = "Sell.jsp"; private final static String jspTraderError = "TraderError.jsp"; // beans private final static String beanCompanies = "companiesBean"; private final static String beanQuotes = "quotesBean"; private final static String beanUserInfo = "userInfoBean"; private final static String beanErrorMessage = "errorMessageBean"; /** * Process incoming HTTP GET requests * * @param request Object that encapsulates the request to the servlet * @param response Object that encapsulates the response from the servlet */ public void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { performTask(request, response); } /** * Process incoming HTTP POST requests * * @param request Object that encapsulates the request to the servlet * @param response Object that encapsulates the response from the servlet */ public void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { performTask(request, response); } private String getCurrentCompany(HttpServletRequest request) throws Exception { HttpSession httpSesion = request.getSession(); UserInfoBean userInfo = (UserInfoBean)httpSesion.getAttribute(userInfoID); return userInfo.getCompany(); } public String getServletInfo() { return super.getServletInfo(); } public String handleShowBuy(HttpServletRequest request) throws Exception { // query company and update it updateCompany(request); // return user info returnUserInfo(request); // show buy panel return jspBuy; } public String handleShowError(HttpServletRequest request, String errorText) throws Exception { request.setAttribute(beanErrorMessage, new ErrorMessageBean(errorText)); return jspTraderError; } public String handleShowError(HttpServletRequest request, String errorText, String stackTrace) throws Exception { request.setAttribute(beanErrorMessage, new ErrorMessageBean(errorText, stackTrace)); return jspTraderError; } public String handleShowSell(HttpServletRequest request) throws Exception { // query company and update it updateCompany(request); // return user info returnUserInfo(request); // show sell panel return jspSell; } /** * Initializes the servlet. */ public void init() { // insert code to initialize the servlet here } /** * Process incoming requests for information * * @param request Object that encapsulates the request to the servlet * @param response Object that encapsulates the response from the servlet */ public void performTask(HttpServletRequest request, HttpServletResponse response) { try { // declare tader Trader trader = null; // obtain HttpSession HttpSession httpSesion = request.getSession(); // if logon request if ( request.getParameter(fieldDoPerformLogon) != null) { // create new trader session bean and keep it in session trader = createTrader(request); httpSesion.setAttribute(traderID, trader); } else {// for all other request trader must already exist // retrieve trader from session trader = (Trader)httpSesion.getAttribute(traderID); } // check which request we got and dispatch to appropriate method String nextJsp = null; if ( request.getParameter(fieldDoPerformLogon) != null ) nextJsp = handlePerformLogon(request, trader); else if ( request.getParameter(fieldDoShowQuotes) != null ) nextJsp = handleShowQuotes(request, trader); else if ( request.getParameter(fieldDoShowCompanies) != null ) nextJsp = handleShowCompanies(request, trader); else if ( request.getParameter(fieldDoShowBuy) != null ) nextJsp = handleShowBuy(request); else if ( request.getParameter(fieldDoPerformBuy) != null ) nextJsp = handlePerformBuy(request, trader); else if ( request.getParameter(fieldDoShowSell) != null ) nextJsp = handleShowSell(request); else if ( request.getParameter(fieldDoPerformSell) != null ) nextJsp = handlePerformSell(request, trader); else if ( request.getParameter(fieldDoShowLogoff) != null ) nextJsp = handleShowLogoff(request, trader); else nextJsp = handleShowError(request, "Got unknown request to process, check the JSPs."); // now process JSP ServletContext sc = getServletContext(); RequestDispatcher rd = sc.getRequestDispatcher("/" + nextJsp); rd.forward(request, response); } catch(Throwable theException) { try { trace( "We got an exception in TraderServlet.performTask" ); theException.printStackTrace(); ByteArrayOutputStream bas = new ByteArrayOutputStream(); PrintStream ps = new PrintStream( bas ); theException.printStackTrace(ps); String nextJsp = handleShowError(request, "Error processing bean: " + theException.toString(), bas.toString() ); ServletContext sc = getServletContext(); RequestDispatcher rd = sc.getRequestDispatcher("/" + nextJsp); rd.forward(request, response); } catch( Throwable t ) { System.out.println( "Fatal error, exception in exception handling!" ); t.printStackTrace(); } } } private void returnUserInfo(HttpServletRequest request) throws Exception { // return user info HttpSession httpSesion = request.getSession(); UserInfoBean userInfo = (UserInfoBean)httpSesion.getAttribute(userInfoID); request.setAttribute(beanUserInfo, userInfo); } private void trace(String txt) { System.err.println( txt ); } private void updateCompany(HttpServletRequest request) { String company = request.getParameter(fieldCompany); HttpSession httpSesion = request.getSession(); UserInfoBean userInfo = (UserInfoBean)httpSesion.getAttribute(userInfoID); userInfo.setCompany(company); request.getSession().setAttribute(userInfoID, userInfo ); } private Trader createTrader(HttpServletRequest request) throws javax.naming.NamingException, javax.ejb.CreateException, java.rmi.RemoteException { // retrieve fields String jndiName = request.getParameter(fieldJndiName); String nameService = request.getParameter(fieldNameService); String providerURL = request.getParameter(fieldProviderURL); // create initial context java.util.Hashtable properties = new java.util.Hashtable(2); properties.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, nameService); properties.put(javax.naming.Context.PROVIDER_URL, providerURL); javax.naming.InitialContext ctx = new javax.naming.InitialContext(properties); // lookup TraderHome Object obj = ctx.lookup(jndiName); // Use the JNDI name from HTML page // narrow to TraderHome and create Trader TraderHome traderHome = (TraderHome)javax.rmi.PortableRemoteObject.narrow((org.omg.CORBA.Object)obj, TraderHome.class); Trader trader = traderHome.create(); return trader; }public String handlePerformBuy(HttpServletRequest request, Trader trader) throws Exception { // get the number of shares String numberOfShares = request.getParameter(fieldNumberOfShares); String company = getCurrentCompany(request); // convert to integer int shares = 0; try { shares = Integer.parseInt(numberOfShares); } catch( Exception e ) { } // buy shares trader.buy( company, shares ); // return user info returnUserInfo(request); // go back to companies selction return handleShowCompanies(request, trader); }public String handlePerformLogon(HttpServletRequest request, Trader trader ) throws Exception { // retrieve fields String userID = request.getParameter(fieldUserID); String password = request.getParameter(fieldPassword); // check if userID and password are provided if( userID.equals("") || password.equals("") ) return handleShowError(request, "You have to specify userID AND password" ); // now logon to the application trader.logon( userID, password); // store user info, we need it later UserInfoBean userInfo = new UserInfoBean(); userInfo.setUserID(userID); request.getSession().setAttribute(userInfoID, userInfo ); // show companies now return handleShowCompanies(request, trader); }public String handlePerformSell(HttpServletRequest request, Trader trader) throws Exception { // get the number of shares String numberOfShares = request.getParameter(fieldNumberOfShares); String company = getCurrentCompany(request); // convert to integer int shares = 0; try { shares = Integer.parseInt(numberOfShares); } catch( Exception e ) { } // sell shares trader.sell( company, shares ); // return user info returnUserInfo(request); // go back to companies selction return handleShowCompanies(request,trader); }public String handleShowCompanies(HttpServletRequest request, Trader trader) throws Exception { // check for local copy HttpSession httpSesion = request.getSession(); CompaniesBean companies = (CompaniesBean)httpSesion.getAttribute(companiesID); if( companies == null ) {// if not stored locally companies = trader.getCompanies(); // to improve response time, store it locally request.getSession().setAttribute(companiesID, companies ); } request.setAttribute(beanCompanies, companies); return jspCompanySelection; }public String handleShowLogoff(HttpServletRequest request, Trader trader) throws Exception { // logoff from trader trader.logoff(); // remove trader object in EJB server trader.remove(); // remove session variables request.getSession().removeAttribute(traderID); request.getSession().removeAttribute(companiesID); request.getSession().removeAttribute(userInfoID); // return to logon panel return jspLogon; }public String handleShowQuotes(HttpServletRequest request, Trader trader) throws Exception { // query company and update it updateCompany(request); // get the quotes String company = request.getParameter(fieldCompany); QuotesBean quotes = trader.getQuotes(company); // return quotes request.setAttribute(beanQuotes, quotes); // return user info returnUserInfo(request); // show the quotes return jspQuotes; } }
Example 6-5 shows the complete source code listing of new TraderPortlet modified from the original servlet where we have highlighted new or modified lines. Note that we tried to leave the code logic intact but have re-ordered the methods to a more meaningful order (init, doView, performTask, and so on):
Example 6-5: The Portlet code after modification
package itso.cics.eci.j2ee.trader.servlet; import itso.cics.eci.j2ee.trader.*; import java.io.*; import javax.ejb.*; // Added for portlet import org.apache.jetspeed.portlet.*; import org.apache.jetspeed.portlets.*; /** * TraderServlet converted to run as a portlet */ public class TraderServlet extends org.apache.jetspeed.portlet.PortletAdapter { // PortletSession IDs private final static String traderID = "Trader"; private final static String companiesID = "Companies"; private final static String userInfoID = "userInfo"; // constants used in JSPs // field names private final static String fieldDoPerformLogon = "doPerformLogon"; private final static String fieldDoShowQuotes = "doShowQuotes"; private final static String fieldDoShowBuy = "doShowBuy"; private final static String fieldDoPerformBuy = "doPerformBuy"; private final static String fieldDoShowSell = "doShowSell"; private final static String fieldDoPerformSell = "doPerformSell"; private final static String fieldDoShowCompanies = "doShowCompanies"; private final static String fieldDoShowLogoff = "doShowLogoff"; private final static String fieldJndiName = "jndiName"; private final static String fieldNameService = "nameService"; private final static String fieldProviderURL = "providerURL"; private final static String fieldUserID = "userid"; private final static String fieldPassword = "password"; private final static String fieldCompany = "company"; private final static String fieldNumberOfShares = "numberOfShares"; private String state = null; // JSPs private final static String jspLogon = "Logon.jsp"; private final static String jspCompanySelection = "CompanySelection.jsp"; private final static String jspQuotes = "Quotes.jsp"; private final static String jspBuy = "Buy.jsp"; private final static String jspSell = "Sell.jsp"; private final static String jspTraderError = "TraderError.jsp"; // beans private final static String beanCompanies = "companiesBean"; private final static String beanQuotes = "quotesBean"; private final static String beanUserInfo = "userInfoBean"; private final static String beanErrorMessage = "errorMessageBean"; /** * Initializes the portlet. */ public void init(PortletConfig portletConfig) throws UnavailableException { super.init(portletConfig); plog("TraderPortlet - init()"); } /** * doView method added for the portlet, (instead of doGet, doPost) */ public void doView(PortletRequest request, PortletResponse response) throws PortletException, java.io.IOException { // Set state for processing JSP's state = (String)request.getPortletSession().getAttribute("state"); plog("TraderPortlet - doView(), state = " + state); // If first time through? update state, show Logon JSP if (state == null) { state = "logon"; request.getPortletSession().setAttribute("state", state); // Create URI for the logon JSP FORM action attribute PortletURI traderURI = response.createURI(); PortletAction newAction = new DefaultPortletAction("logon"); traderURI.addAction(newAction); // save the URI so the JSP can get it request.setAttribute("actionVal", traderURI.toString()); // Display logon JSP to render getPortletConfig().getContext().include("/jsp/Logon.jsp", request, response); } // Not first time thorough, process task(s) from JSP else { performTask(request, response); } } /** * Process incoming requests for information * (session handling changed for portlet and state tracking) */ public void performTask(PortletRequest request, PortletResponse response) { plog("TraderPortlet - performTask()"); try { // declare tader Trader trader = null; // if logon request if ( request.getParameter(fieldDoPerformLogon) != null) { // create new trader session bean and keep it in session trader = createTrader(request); request.getPortletSession().setAttribute(traderID, trader); } else { // for all other request trader must already exist // so retrieve trader from session trader = (Trader)request.getPortletSession().getAttribute(traderID); } // check which request we got and dispatch to appropriate method String nextJsp = null; if ( request.getParameter(fieldDoPerformLogon) != null ){ nextJsp = handlePerformLogon(request, trader); request.getPortletSession().setAttribute("state", fieldDoPerformLogon); } else if ( request.getParameter(fieldDoShowQuotes) != null ){ nextJsp = handleShowQuotes(request, trader); request.getPortletSession().setAttribute("state", fieldDoShowQuotes); } else if ( request.getParameter(fieldDoShowCompanies) != null ){ nextJsp = handleShowCompanies(request, trader); request.getPortletSession().setAttribute("state", fieldDoShowCompanies); } else if ( request.getParameter(fieldDoShowBuy) != null ){ nextJsp = handleShowBuy(request); request.getPortletSession().setAttribute("state", fieldDoShowBuy); } else if ( request.getParameter(fieldDoPerformBuy) != null ){ nextJsp = handlePerformBuy(request, trader); request.getPortletSession().setAttribute("state", fieldDoPerformBuy); } else if ( request.getParameter(fieldDoShowSell) != null ){ nextJsp = handleShowSell(request); request.getPortletSession().setAttribute("state", fieldDoShowSell); } else if ( request.getParameter(fieldDoPerformSell) != null ){ nextJsp = handlePerformSell(request, trader); request.getPortletSession().setAttribute("state", fieldDoPerformSell); } else if (request.getParameter(fieldDoShowLogoff) != null){ nextJsp = handleShowLogoff(request, trader); request.getPortletSession().setAttribute("state", fieldDoShowLogoff); } // unknown request, try to get back to where we were else { state = (String)request.getPortletSession().getAttribute("state"); if (state.equals(fieldDoPerformLogon)) { nextJsp = handlePerformLogon(request, trader); } else if (state.equals(fieldDoShowQuotes)) { nextJsp = handleShowQuotes(request, trader); } else if (state.equals(fieldDoShowCompanies)) { nextJsp = handleShowCompanies(request, trader); } else if (state.equals(fieldDoShowBuy)) { nextJsp = handleShowBuy(request); } else if (state.equals(fieldDoPerformBuy)) { nextJsp = handlePerformBuy(request, trader); } else if (state.equals(fieldDoShowSell)) { nextJsp = handleShowSell(request); } else if (state.equals(fieldDoPerformSell)) { nextJsp = handlePerformSell(request, trader); } else if (state.equals(fieldDoShowLogoff)) { nextJsp = handleShowLogoff(request, trader); } } plog("TraderPortlet in PerformTask(), nextJSP = " + nextJsp); // Changed for portlet // Create URI for the logon JSP FORM action attribute PortletURI traderURI = response.createURI(); PortletAction newAction = new DefaultPortletAction("company"); traderURI.addAction(newAction); // save the URI so the JSP can get it request.setAttribute("actionVal", traderURI.toString()); // Display next JSP to render getPortletConfig().getContext().include("/jsp/" + nextJsp, request, response); } catch(Throwable theException) { try { trace( "WebSphere Portal : we got an exception in TraderPortlet.performTask" ); theException.printStackTrace(); ByteArrayOutputStream bas = new ByteArrayOutputStream(); PrintStream ps = new PrintStream( bas ); theException.printStackTrace(ps); String nextJsp = handleShowError(request, "Error processing bean: " + theException.toString(), bas.toString() ); // Changed for portlet /// Display next JSP to render getPortletConfig().getContext().include("/jsp/" + nextJsp, request, response); } catch(Throwable t) { t.printStackTrace(); } } } /** * Extract current Company from UserInfoBean. */ private String getCurrentCompany(PortletRequest request) throws Exception { plog("TraderPortlet - getCurrentCompany()"); // Session access changed for portlet UserInfoBean userInfo = (UserInfoBean)request.getPortletSession().getAttribute(userInfoID); return userInfo.getCompany(); } /** * Process buy request */ public String handleShowBuy(PortletRequest request) throws Exception { plog("TraderPortlet - handleShowBuy()"); // query company and update it updateCompany(request); // return user info returnUserInfo(request); // show buy panel return jspBuy; } /** * Process an error */ public String handleShowError(PortletRequest request, String errorText ) throws Exception { request.setAttribute(beanErrorMessage, new ErrorMessageBean(errorText)); return jspTraderError; } /** * Process an error with Stack */ public String handleShowError(PortletRequest request, String errorText, String stackTrace ) throws Exception { request.setAttribute(beanErrorMessage, new ErrorMessageBean(errorText, stackTrace)); return jspTraderError; } /** * Process sell request */ public String handleShowSell(PortletRequest request) throws Exception { plog("TraderPortlet - handleShowSell()"); // query company and update it updateCompany(request); // return user info returnUserInfo(request); // show sell panel return jspSell; } /** * Get user info and set in UserInfoBean */ private void returnUserInfo(PortletRequest request) throws Exception { plog("TraderPortlet - returnUserInfo()"); // Session handling changed for portlet, get bean from session UserInfoBean userInfo = (UserInfoBean)request.getPortletSession().getAttribute(userInfoID); //set UserInfoBean for JSP request.setAttribute(beanUserInfo, userInfo); } /** * Print exceptions in error log */ private void trace(String txt) { System.err.println(txt); } /** * Added for portlet, log data to Portal Log */ private void plog(String txt) { PortletLog log = getPortletConfig().getContext().getLog(); if (log.isDebugEnabled()) log.debug(txt); if (log.isWarnEnabled()) log.warn(txt); if (log.isInfoEnabled()) log.info(txt); if (log.isErrorEnabled()) log.error(txt); } /** * Get company selected and save in UserInfoBean */ private void updateCompany(PortletRequest request) { plog("TraderPortlet - updateCompany()"); // Get company selected by user String company = request.getParameter(fieldCompany); // Session handling changed for portlet UserInfoBean userInfo = (UserInfoBean)request.getPortletSession().getAttribute(userInfoID); // save in UserInfoBean userInfo.setCompany(company); // save in session for JSP request.getPortletSession().setAttribute(userInfoID, userInfo ); } /** * Lookup and accesss Trader EJB */ private Trader createTrader(PortletRequest request) throws javax.naming.NamingException, javax.ejb.CreateException, java.rmi.RemoteException { // Retrieve fields from logon page String jndiName = request.getParameter(fieldJndiName); String nameService = request.getParameter(fieldNameService); String providerURL = request.getParameter(fieldProviderURL); // create initial context java.util.Hashtable properties = new java.util.Hashtable(2); properties.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, nameService); properties.put(javax.naming.Context.PROVIDER_URL, providerURL); javax.naming.InitialContext ctx = new javax.naming.InitialContext(properties); Object obj = ctx.lookup(jndiName); // Use the JNDI name from logon JSP // narrow to TraderHome and create Trader TraderHome traderHome = (TraderHome)javax.rmi.PortableRemoteObject.narrow((org.omg.CORBA.Object)obj, TraderHome.class); Trader trader = traderHome.create(); return trader; } /** * Process buy request */ public String handlePerformBuy(PortletRequest request, Trader trader) throws Exception { plog("TraderPortlet - handlePerformBuy()"); // get the number of shares String numberOfShares = request.getParameter(fieldNumberOfShares); String company = getCurrentCompany(request); // convert to integer int shares = 0; try { shares = Integer.parseInt(numberOfShares); } catch( Exception e ) { } // buy shares trader.buy(company, shares); // return user info returnUserInfo(request); // go back to companies selection return handleShowCompanies(request, trader); } /** * Process logon request */ public String handlePerformLogon(PortletRequest request, Trader trader ) throws Exception { plog("TraderPortlet - handlePerformLogon()"); // retrieve input fields user and password String userID = request.getParameter(fieldUserID); String password = request.getParameter(fieldPassword); // check if userID and password are provided if( userID.equals("") || password.equals("") ) return handleShowError(request, "You have to specify userID AND password" ); // now logon to the application trader.logon(userID, password); // store user info, we need it later UserInfoBean userInfo = new UserInfoBean(); userInfo.setUserID(userID); // session changed for portlet request.getPortletSession().setAttribute(userInfoID, userInfo ); // show companies now return handleShowCompanies(request, trader); } /** * Process sell request */ public String handlePerformSell(PortletRequest request, Trader trader) throws Exception { plog("TraderPortlet - handlePerformSell()"); // get the number of shares String numberOfShares = request.getParameter(fieldNumberOfShares); String company = getCurrentCompany(request); // convert to integer int shares = 0; try { shares = Integer.parseInt(numberOfShares); } catch( Exception e ) { } // sell shares trader.sell( company, shares ); // return user info returnUserInfo(request); // go back to companies selection return handleShowCompanies(request,trader); } /** * Show companies list */ public String handleShowCompanies(PortletRequest request, Trader trader) throws Exception { plog("TraderPortlet - handleShowCompanies()"); // check for local copy of bean (session handling changed for portlet) CompaniesBean companies = (CompaniesBean)request.getPortletSession().getAttribute(companiesID); // if not stored locally in session if( companies == null ) { companies = trader.getCompanies(); // to improve response time, store it locally request.getPortletSession().setAttribute(companiesID, companies ); } // set bean for JSP request.setAttribute(beanCompanies, companies); return jspCompanySelection; } /** * Handle logoff */ public String handleShowLogoff(PortletRequest request, Trader trader) throws Exception { plog("TraderPortlet - handleShowLogoff()"); // logoff from trader trader.logoff(); // remove trader object in EJB server trader.remove(); // remove session variables (session handling changed for portlet) request.getPortletSession().removeAttribute(traderID); request.getPortletSession().removeAttribute(companiesID); request.getPortletSession().removeAttribute(userInfoID); // return to logon panel return jspLogon; } /** * Show requested quotes */ public String handleShowQuotes(PortletRequest request, Trader trader) throws Exception { plog("TraderPortlet - handleShowQuotes()"); // query company and update it updateCompany(request); // get the company requested String company = request.getParameter(fieldCompany); // and get the quotes QuotesBean quotes = trader.getQuotes(company); // return quotes request.setAttribute(beanQuotes, quotes); // return user info returnUserInfo(request); // show the quotes return jspQuotes; } }
We have already covered the first four of the following items but list them here for convenience:
Add the Portal API available from the WebSphere Portal tag library <%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %> then the JSP can use the Portal API. An example of this is shown in the next item to define a unique form namespace. If the JSP uses the Portal tag library then the library must be available in the WAR file directory /WEB-INF/tld.
Ensure the HTML object namespace is unique within WebSphere Portal We did not do this, but an example applied to the INPUT field of the form in Buy.jsp is:
<INPUT type="submit" name='<portletAPI:encodeNamespace value="Buy"/>'>
Set the action attribute of the forms in the JSPs from the doView, doPost and doGet methods in the portlet:
PortletURI traderURI = response.createURI(); request.setAttribute("actionVal", traderURI.toString());
Then in the JSP form the attribute must be evaluated, for example:
<FORM action='<%=actionVal%>'>
In the JSPs with forms, retrieve the URI for the action attribute:
<jsp:useBean scope="request" />
Add desired Portal themes and skins to the portlet We did not do this.
Cleanup file names and variables to remove references to servlet We did not do this.
Add additional mark-up language support to the portlet We did not do this.
Import an EJB application client JAR if the portlet references EJBs For the Trader application, as we have seen in Figure 6-34 on page 224 the Web application part of the servlet, and therefore the TraderPortlet java code, performs a lookup of the JNDI namespace for the EJB home of the session bean used to wrapper the EJB that connects to CICs. At this stage this was reflected in the many cross reference errors we were seeing in the Application Developer tasks list when building the portal project.
From the portlet perspective of the TraderEJB project in Application Developer we identified file imported_classes_EJB.jar which we found in the Application Developer directory structure file-system, and subsequently imported into the /lib directory of TraderPortlet project. This immediately resolved all of our build errors caused by missing references to the EJB.
Tip | Additional information on portlet development is available in redbook IBM WebSphere Portal Developers Handbook, SG24-6897. |
At this stage with building the portlet project, assuming there are no errors, listed by Application Developer as tasks, it is ready for either debug and testing using the development environment we described in Chapter 3, "Installing the Portal Toolkit" on page 104, or deployment directly to WebSphere Portal on z/OS for testing.
After deploying TraderServlet to WebSphere Portal, and adding it to a page we had created in out of CICS' test application place, we used the logon page rendered from our Logon.jsp and shown in Figure 6-63 on page 279 to sign on to the Trader application.
Figure 6-63: TraderPortlet inside the Portal
As we have seen in Figure 6-36 on page 226 the EJB location parameters specified by the JNDI name are specified globally and are the same as the one we used for the original Trader Web application.
After signing on to the Trader application the Company Selection screen is received as shown in Figure 6-64 on page 280.
Figure 6-64: Company Selection screen from the Trader Portlet
To test and exercise the portlet we chose to buy and sell some shares, first by obtaining some quotes from IBM as seen in Figure 6-65 on page 281.
Figure 6-65: Quotes screen from the Trader Portlet
Finally the "sell shares" portlet view is shown from the portlet in Figure 6-66 on page 282.
Figure 6-66: Sell Shares screen from the Trader Portlet
That concluded our exercise in portalizing an existing Web application to run as a portlet on WebSphere Portal. Although this was quite a simple example to work with, it demonstrated several things:
The logic in the servlet being converted can remain largely intact.
No deployment changes are needed at all to an existing J2EE application consisting of Web applications and EJBs.
Most of the work was in handling the portlet API for the servlet/portlet/JSPs, as might be expected. For this it helps if the developer has a working knowledge of servlet/JSP programming and understands the indirection added by the portlet API.
The Model-View-Controller design pattern is applicable to portlet programming, where JSPs are used for the View, portlets used for the Controller and Beans/EJBs used for the Model.
| < Day Day Up > |
|