Building hybrid mobile applications with PhoneGap and IBM ...

7 downloads 88 Views 820KB Size Report
Summary: Learn how IBM® WebSphere® Portlet Factory and PhoneGap can be used to quickly develop hybrid mobile applications. Using an example mobile ...
Building hybrid mobile applications with PhoneGap and IBM WebSphere Portlet Factory

Sam Alexander Software Developer, IBM Software Services for Lotus IBM Collaboration Solutions IBM Software Group Littleton, MA US April 2011 © Copyright International Business Machines Corporation 2011. All rights reserved. Summary: Learn how IBM® WebSphere® Portlet Factory and PhoneGap can be used to quickly develop hybrid mobile applications. Using an example mobile application that a Realtor might use to list property data and photos, this white paper demonstrates how to leverage the strengths of both these technologies to quickly build a hybrid mobile application for the Android platform.

Table of Contents 1 Introduction..............................................................................................................................2 1.1 Prerequisites.....................................................................................................................2 2 Hybrid mobile apps and frameworks........................................................................................3 3 PhoneGap...............................................................................................................................3 4 WebSphere Portlet Factory.....................................................................................................4 5 Example Realtor application....................................................................................................4 6 The Portlet Factory application................................................................................................7 6.1 Provider model..................................................................................................................7 6.2 The consumer model: Main UI..........................................................................................9 6.3 Consumer model: Using the PhoneGap camera API to snap photos..............................11 6.4 The Android application...................................................................................................16 7 Conclusion.............................................................................................................................17 8 Appendix: Configuring the Portlet Factory and Android environments...................................17 9 Resources.............................................................................................................................18 About the author........................................................................................................................19

1

1 Introduction While persevering long supermarket checkout lines or crammed rush-hour commuter-rail rides, if you look around, chances are you'll find more than a few people with smart phones or tablets browsing news headlines, interacting with friends via social Web sites, or reading digital books. Thus it is no surprise that the market for mobile applications (mobile apps) is exploding. As such, many software developers are increasingly focused on mobile app development. Modern mobile browsers and Web technologies already allow developers to create attractive Web sites suitable for mobile devices. However, mobile Web applications have limitations in that they cannot interact with the platform's features, such as the camera, accelerometer, or contacts, and they can't be easily monetized and deployed to an app store. These requirements force developers to tackle steep learning curves for each platform's Software Development Kit (SDK), often resulting in longer time-to-market and complexities such as maintaining separate code streams for each platform. To address these problems, new “hybrid” mobile application-development frameworks have emerged allowing developers to leverage familiar Web technologies––HTML, cascading style sheets (CSS), and JavaScriptTM––to build cross-platform, native applications that can interact with the platform's features and be deployed to an app store. One such hybrid framework is PhoneGap. Just as PhoneGap is a powerhouse in the hybrid development framework space, IBM WebSphere Portlet Factory (hereafter called “Portlet Factory”) is a powerhouse in the portlet and JavaTM 2 Platform, Enterprise Edition (J2EE) application development space. Using wizard-like components called builders, Portlet Factory developers can rapidly snap together builders to construct dynamic, data-driven Web applications for IBM WebSphere Portal and IBM WebSphere Application Server (WAS). In this paper, using an example of a mobile App that a Realtor might use to list property data and photos, we demonstrate how to leverage the strengths of both these technologies to quickly build a hybrid mobile application for the Android platform.

1.1 Prerequisites This paper is intended for Web developers, mobile developers, application architects, and IT decision makers. It is assumed that readers have Portlet Factory experience, HTML/CSS/JavaScript knowledge, and some knowledge of Smartphone development. The following software is required: • • • •

WebSphere Portlet Factory Next Beta2 or later WAS Community Edition (comes with WebSphere Portlet Factory Next Beta2) or WebSphere Application Server Android SDK and ADT Plug-in for Eclipse PhoneGap version 0.9.4 or later

2

The attached Portlet Factory sample application project can be downloaded and imported into Portlet Factory Designer. The minimal Android wrapper code is not included in the download but can easily be created in your Android environment. See the Appendix section of this paper for instructions. Let's start with a brief discussion of hybrid mobile apps.

2 Hybrid mobile apps and frameworks As the name suggests, hybrid mobile apps are part native application, part Web application; specifically, they are applications built with HTML/ CSS/JavaScript that can interact with the platform's features. A native portion provides a wrapper around the underlying Web application. As mentioned above, PhoneGap is one such hybrid framework. Hybrid mobile apps sport many benefits. Already familiar with the Web technologies, Web developers are spared from learning the nuances of a platform's SDK. Since most of the app is built with HTML, CSS, and JavaScript, cross-platform goals are more easily achievable. Also, by using widgets from modern JavaScript libraries like Dojo, developers can create hybrid mobile apps that are often indistinguishable from purely native apps. There are also drawbacks to consider, however, in that hybrid mobile app frameworks do not expose all native API's, and though hybrid mobile apps can be built to meet performance requirements, performance of purely native applications is often better.

3 PhoneGap PhoneGap is an open-source hybrid mobile app framework. Currently supporting multiple platforms––including Android, iOS, BlackBerry, Palm, Symbian, and Microsoft® Windows® Mobile––PhoneGap provides JavaScript interfaces to the platform's features. For example, using PhoneGap's JavaScript APIs, the application can access device information, native events, cellular and wi-fi connectivity information, local storage, device notifications, accelerometer data, geolocation data, and compass information. With minimal coding to the platform's SDK (in our Android example, only a few lines of Java code), PhoneGap allows the user to build a native “wrapper” around the Web application. The wrapper wraps the platform's Web browser, resulting in a native application that can be deployed to an app store and installed on the device. To address the desire for cross-platform applications, PhoneGap has recently introduced PhoneGap Build service. This new feature allows developers to simply write their application using HTML/CSS/JavaScript, upload it to the PhoneGap build service, and receive a native application for the supported devices. This saves the developer from installing SDKs and writing/compiling the native wrapper code. PhoneGap has two licensing options, the “MIT License” and the “New BSD License.” The details of these options can be found on Phone Gap's License page.

3

4 WebSphere Portlet Factory Portlet Factory is an application development tool for rapidly creating dynamic enterprise applications for WebSphere and WebSphere Portal. At design time, developers construct their application by assembling components called builders. Each builder is configured via a wizardlike user interface (UI) and automatically generates the code of the application. Two specific types of builders are integration builders and UI builders. The integration builders provide access to backend data while shielding the developer from learning backend APIs like the Java Database Connectivity (JDBC) or Domino Java API. The integration builders create XML schemas that can then be leveraged by UI builders, which allow developers to quickly build the UI of their application. Builders capture design patterns that Web developers often encounter. For example, a typical use case is displaying an initial view of data and letting users select a specific record for which to display more details, as well as letting them add new records. Portlet Factory's View and Form builder captures this pattern. Given an XML schema of the data, Portlet Factory designer automatically builds list and details pages, as well as HTML forms for entering new records. Now that we are familiar with PhoneGap and Portlet Factory, let's turn to the example application and see how it is built.

5 Example Realtor application Upon visiting a property, a Realtor could use our example application to list a new property, including capturing data about the property and, more interestingly, capturing a picture. As shown in figure 1, when first loaded, the application displays a list of properties including a thumbnail, address, town, and price. Adhering to good mobile design, each row is easily clickable, using a thumb, and clicking a row takes you to details about the selected property.

4

Figure 1. App screen when first loaded

Clicking the Create button displays a page in which the realtor can enter property metadata, as shown in figure 2. This is a standard HTML form. At the top of the screen, a message indicates no picture has been taken yet. This message will be replaced by a thumbnail once the image is taken. Figure 2. Form page

5

The Take Picture button allows the user to launch the device's camera. In this example, it will launch the Android Camera Intent, as shown in figure 3. The Camera Intent lets you use the viewfinder and shutter button to snap a picture of the property. Note that, when an emulator is used, the Camera Intent will display a checkered dummy image through the viewfinder. Figure 3. Camera in Android

Once you click the shutter button and click OK to accept the picture, the “No Picture Yet” message back on the form page is replaced with a thumbnail of the snapped image (see figure 4). Once you submit the form and picture, a new row appears back on the List page. Figure 4. Thumbnail of image

6

Portlet Factory handles page and form generation, persisting the data to a backend database and building the UI tailored for a mobile application. PhoneGap allows us to interact with the camera. With that in mind, let's see how the application is built, starting with the Portlet Factory application.

6 The Portlet Factory application The Portlet Factory application is built in the recommended consumer/provider pattern whereby data services are exposed to a consumer model from a provider model.

6.1 Provider model The provider model (/models/samples/mobile_real_estate/data/PropertiesProvider.model) is responsible for exposing list, create, retrieve, update, and delete services for the real estate data and contains a single builder to achieve this, namely, SQL Table Create (see figure 5). New in Portlet Factory 7.0, the SQL Table Create builder is a high-level SQL builder that automatically configures list, search, create, retrieve, update, and delete operations for simple SQL tables with a single unique key. At design time, the developer uses builder inputs to fetch and select a datasource defined by the application server. Rather than writing SQL statements in discreet lower-level SQL builders, the developer uses the Table Columns input to define a table name and desired columns and their data types. Figure 5. SQL Table Create builder

7

In this example, we've defined columns to hold property data such as the town and price. Also note that we've defined an imgfilename column, which will hold the file name of the property image we'll upload using PhoneGap. Using the Create Table and Delete Table buttons, we can create and delete the table in the database. Checking the Create on Demand input instructs the builder to dynamically create the database at runtime, if it doesn't already exist. This feature is especially helpful; it relieves the developer from the burden of adding lower-level SQL builder and logic to check for and create the database if necessary. Figure 6 shows the Application Tree, in which this single builder automatically creates and exposes Portlet Factory service operations with XML interfaces that we'll consume in the consumer model. Figure 6. Application Tree window

SQL Table Create also automatically configures and exposes the provider's “Logical Operations”. A new feature in Portlet Factory 7.0, the Logical Operation metadata can be consumed by new UI builders that use this data to automatically generate UI components. You can see the logical operations of a Data Service by selecting it in the Application Tree, as shown in figure 7. You'll soon see the power of Logical Operations when we discuss the consumer model.

8

Figure 7. Logical Operations window

6.2 The consumer model: Main UI The consumer model (WEB-INF\models\samples\mobile_real_estate\PropertiesUI.model) consumes the property services from our provider model and defines the UI of the application. It is also where we'll add JavaScript to invoke PhoneGap APIs. The bulk of the UI in the consumer model is generated by the Data Services UI (DSUI) builder. Using well-defined service operations in the provider, the DSUI builder generates pages, tables, HTML forms, buttons, and other UI components for interacting with the data. From the previous section you'll recall that the provider model exposes its Logical Operations. The DSUI builder uses this logical-operation metadata to populate the “Choose Pages / Operations” input and also to wire the pages together. For example, the RetrieveList operation produces the PROP_ID field, and the RetrieveOne operation requires a PROP_ID. Using this information, the DSUI builder can make the PROP_ID field in the record clickable and, more importantly, use it to pass it to the retrieve operation. In addition to integration builders and UI builders, Portlet Factory contains powerful modification builders––builders that modify artifacts in the Web app. To modify the pages generated by the DSUI builder into a layout more suitable for a mobile device, the consumer model uses a powerful new modification builder available in WebSphere Portlet Factory Next Beta2 called the Data Layout builder (see figure 8). The Data Layout builder allows the developer to modify the UI to be more suitable for another environment. After selecting the container field to modify, the developer is able to select a template, and then map fields to named targets in the selected template. The builder then uses this template to modify the layout of a page generated by other builders. In this example, the Data Layout builder modifies the List page created by the DSUI builder.

9

Figure 8. Data Layout builder

Developers can choose from several predefined templates or create their own. In this example, we've chosen the “Thumbnail Multi-Line List” template that lets the developer generate a list in which each item (row) contains three columns of data. Conveniently, this template also makes the entire row clickable, so that a mobile user can use a finger to easily open the record details. Based on the selected template, the Data Layout input allows us to define the values that will go into those spots. In this example, the template defines several locations: left_image, center_top, center_middle, center_bottom, and right_middle. Also, we've chosen to map our ADDRESS, TOWN, and PRICE elements to center_top, center_middle, and center_bottom fields, respectively. To better visualize the effects of this builder, consider the pages shown in figure 9. The page on the left-hand side was generated by the DSUI builder and is best suited for a traditional computer browser. The page on the right-hand side is the page after the layout is modified by the Data Layout builder, which is more suitable for the smaller screen of a mobile device.

10

Figure 9. Browser page vs. Data Layout-modified page

So far, Portlet Factory builders have created our complete UI. Now we just need to add the ability to take photos.

6.3 Consumer model: Using the PhoneGap camera API to snap photos The DSUI builder has created all of our pages, including our Create page. By default, the Create page contains a form to capture our property data and a Submit button to perform a form submit that calls the “create” data service to persist the property data. Recall that the Create page contained a section at the top of the page that included a Take Picture button, a No Picture Yet place-holder message, and a thumbnail of the snapped photo. This section was created using an HTML builder, which lets developers add blocks of code to a named element on a page. In this case, we add a
to the Create page, just under the header tag at the top of the page (see listing 1). Note that the defines the location of the Take Picture button, the No Picture Yet place-holder message (which will be hidden later via JavaScript), and the thumbnail image (which will be unhidden later via JavaScript). Listing 1. Create page code
(No picture yet)


11

With these locations defined, we use a Button builder to place an HTML button on the takePictureButton tag. For the Action Type input, we select “Run a Script”, and for the Script to Execute input, we call a JavaScript function named takePicture(), as shown in listing 2. This function is defined in WebContent\samples\phonegap\RealEstateJavaScript.js and is added to the page with a Client JavaScript builder. Listing 2. takePicture function code function takePicture() { if (!navigator.camera){ // The app has to be run through the native shell interface created by PhoneGap. // Can't run through a regular brow ser, since it w ill be missing phonegap.jar alert("Verify you are running this through an Android emulator and that you have installed PhoneGap correctly."); } else { navigator.camera.getPicture(onTakePicSuccess,onTakePicFail,{quality: 0, destinationType: Camera.DestinationType.FILE_URI , sourceType : Camera.PictureSourceType.CAMERA}); } }

TakePicture calls PhoneGap's Camera.getPicture() API to take a photo, using the device's default camera application. Several options are passed, including: •

The destinationType: Camera.DestinationType.FILE_URI parameter tells PhoneGap to send our application the file URI, as opposed to a memory-consuming, Base64-encoded string.



The sourceType: Camera.PictureSourceType.CAMERA sets the source type to Camera, as opposed to an image from the Gallery.

In addition to these options, the Camera.getPicture() method takes a success-and-fail callback function. The success callback function, here onTakePicSuccess, is called by PhoneGap when the picture is taken, and PhoneGap passes the image data to the success callback (see listing 3).

12

Listing 3. onTakePicSuccess function function onTakePicSuccess(imageURI) { // Save the Image URI representing the picture. We'll // upload it shortly globalImageURL = imageURI; // Show a thumbnail of the snapped image var thumbnailimage = document.getElementById('smallImage'); thumbnailimage.src = imageURI; thumbnailimage.style.display = 'block'; // Hide the "No Pic Yet" placeholder document.getElementById("noPicYet").style.display = "none"; // Generate a unique file name based on curr time var uniqueNum = genUniqueNum(); var imgName = "img" + uniqueNum +".jpg"; // Set the hidden filename input to the unique file name // This will be stored in the db var form = document.forms["filter_form"]; form.IMGFILENAME.value = imgName; form.PROP_ID.value = uniqueNum; }

When our callback is called, the imageURI is saved to a global variable, which we'll soon use to call another PhoneGap API, to stream the image to the server. Then, we programmatically unhide and set the thumbnail tag to the snapped image and hide the No Picture Yet placeholder on the Create page. Finally, we construct a unique file name that we'll use for the imgfilename input in the Create form. (Recall that in the provider model's SQL Table Create builder we defined an imgfilename column in the database). Now that the user has taken a picture, the thumbnail image has displayed, and the user has finished entering the property data such as the address and number of bedrooms, it's time to submit the form data and image to the server. This is done by use of a Button builder to replace the default Submit button created by the DSUI builder. After selecting the PropertiesDSUICreate page, we select the tag name of the existing Submit button on this page. Then, for the Script to Execute input, we call uploadPhotoAndData(), as shown in figure 10.

13

Figure 10. Button builder

Listing 4 shows how the uploadPhotoAndData() JavaScript function is also defined in WebContent\samples\phonegap\RealEstateJavaScript.js. Listing 4. uploadPhotoAndData() JavaScript function function uploadPhotoAndData() { var url = document.location.pathname; var urlPrefix = url.substring(0,url.lastIndexOf("/")); // Get the IMGFILENAME from hidden form input. It will be name of uploaded picture var form = document.forms["filter_form"]; var imgName = form.IMGFILENAME.value; // Setup PhoneGap's FileUpoadOptions to pass to the FileTransfer API var options = new FileUploadOptions(); options.fileKey="file"; options.fileName=imgName; options.mimeType="image/jpeg"; // Use PhoneGap FileTransfer API to Upload the Photo. // Portlet Factory's File Upload functionality checks for uploaded // files on any form submit, so any form action will do. var ft = new FileTransfer(); ft.upload(globalImageURL, "http://" + window.location.hostname + ":" + window.location.port + urlPrefix + "/Action!fileupload", onUploadSuccess, onUploadFail, options); // Let Portlet Factory handle form submission of the rest of the data. form.action = urlPrefix + "/Action!PropertiesDSUICreate_NextAction"; form.submit(); }

14

When the Submit button is clicked, this function will submit the form data and also call PhoneGap's FileTransfer.upload API to upload the image to the server via an HTTP multi-part POST request. But before calling the upload() API, we must first define the options for FileUpload. PhoneGap's FileUploadOptions object allows us to specify the file name and MIME type of the image we'll upload. Recall from earlier that the success callback function generated a unique file name and used it to set the value of the imagefilename input in the form. For the filename option, we use the value of the imagefilename. Now that our FileUploadOptions object is created, we call the FileTransfer.upload API. The first parameter it takes is a file to be uploaded to the server. Recall that PhoneGap passed our success callback function the imageURL, and our callback function saved it to a variable for later. We'll now use this variable as the image file we'll upload to the server. The second parameter is the target URL for the multi-part POST. In this example, we call a Portlet Factory action called “filetransfer”, defined in an Action List builder. Note that this Action List builder call is a placeholder and doesn't explicitly perform actions itself. However, when any Portlet Factory action is called in the context of a form submission, the Portlet Factory runtime verifies that file uploading has been enabled; and, if so, it checks the request for files being uploaded and saves them to a designated place on the file system. To use Portlet Factory's built-in file upload functionality, you must enable and configure it with properties in the project's web-inf/config/override.properties file. Listing 5 shows an example. Listing 5. Example of enable and config of upload functionality # Enable file upload, regardless if the File Upload builder is # being used. New in PF 7.0.1 bowstreet.upload.checkSessionToken=false # File Upload Settings # This determines whether file upload is active for this server. bowstreet.upload.enabled=true # This sets the directory where uploaded files will be stored upon successful upload. bowstreet.upload.destinationPath=${bowstreet.rootDirectory}/../realestateimages # This determines the maximum file size that can be uploaded at one time in KiloBytes. bowstreet.upload.maxFileSizeK=500000

Finally, after uploading the file, we call the Portlet Factory action that was the original target URL for the form submission. Now that our Web application built with Portlet Factory and PhoneGap is complete, we can turn our attention to the Android wrapper application.

15

6.4 The Android application As mentioned earlier, the Android native wrapper application consists of a mere few lines of code. Use PhoneGap's Android Get Started guide to create a new Android project and modify the project's default Java file. Listing 6 is an example of how you might modify the Java file to create the native wrapper to call the Portlet Factory realtor application. First, we create statics to define the URL to our deployed Portlet Factory application. Notice that the URL begins with http://10.0.2.2. If your app server and emulator are running on the same machine, you can simply use this convenient loopback rather than using a hardcoded hostname or IP address. If you deploy your application to a real device, don't forget to change the SERVER variable to a real hostname or IP address. Then, we extend from Droidgap and call super.loadURL, passing in the URL of the deployed Portlet Factory application. Listing 6. Example of modified Java file import android.os.Bundle; import com.phonegap.DroidGap; public class MyRealtorApp extends DroidGap { // The URL to a Portlet Factory UI model // Note: Use 10.0.2.2 to easily allow emulator to // connect to app server running on same machine as emulator public static final String SERVER = "http://10.0.2.2:8080"; public static final String APP = "Realtor/webengine/samples/mobile_real_estate/PropertiesUI"; public static final String URL = SERVER + "/" + APP; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); super.setBooleanProperty("loadInWebView", true); super.loadUrl(URL); } }

If everything is configured correctly, you can click Run, in Eclipse, to deploy and launch your native wrapper in your Android emulator.

16

After testing in your emulator, try deploying the application to your phone. Don't forget to change the SERVER variable in your native wrapper code to point to the URL of your app server.

7 Conclusion You've just seen how to leverage the strengths of both Portlet Factory and PhoneGap to rapidly build a cross-platform, hybrid mobile application that can be deployed to an app store or phone. Leveraging these two technologies can lead to faster development and less complexity, as developers are insulated from learning platform-specific APIs and from maintaining multiple code streams. As with building normal Portlets and J2EE applications with Portlet Factory, the majority of the application can be built with traditional Portlet Factory builders and new mobile-specific builders such as Data Layout. Then, by adding JavaScript callbacks, we can modify the application to call PhoneGap's JavaScript rich set of interfaces to the devices features. So the next time you're caught in long supermarket lines or subway commutes, take a peek at the app running on your neighbor's device; as the lines between purely native apps and Web apps continue to blur, you'll likely be unable to tell whether the app is purely native or hybrid.

8 Appendix: Configuring the Portlet Factory and Android environments Importing the sample application into WebSphere Portlet Factory Beta2 1. Create a new Portlet Factory project and, when prompted, enable the Mobile feature set (see figure 1). Figure 1. Create WebSphere Portlet Factory Project window

2.

Download the sample app and import it as a WebSphere Portlet Factory Archive.

3.

Copy the phonegap.0.9.4.js file to your Portlet Factory project's WebContent/samples/phonegap directory. 17

4.

Open the Client JavaScript builder call named phonegap.js and verify that the “Script URL or File Location” input points to the above directory; change it, if necessary.

5.

Enable Portlet Factory's File Upload functionality by adding the properties shown in listing 1 to web-inf/config/override.properties.

6.

Finally, redeploy the Portlet Factory application to the application server.

Listing 1. Enable File Upload functionality # File Upload Settings

bowstreet.upload.checkSessionToken=false # This determines whether file upload is active for this server.

bowstreet.upload.enabled=true # This sets the directory where uploaded files will be stored upon successful upload.

bowstreet.upload.destinationPath=$ {bowstreet.rootDirectory}/../realestateimages # This determines the maximum file size that can be uploaded at one time in KiloBytes.

bowstreet.upload.maxFileSizeK=500000

Configuring Android SDK and PhoneGap • Follow PhoneGap's Android Get Started guide for detailed instructions on downloading and configuring PhoneGap and the Android SDK. NOTE: Instead of installing a separate version of Eclipse for Android SDK as the guide suggests, you can install the Android SDK into the Eclipse that comes with Portlet Factory Beta Next. This allows you to use Portlet Factory Designer and the Android SDK in one integrated development environment (IDE). •

Note that the Android wrapper application code is not included in the accompanying sample application. It is recommended to refer to PhoneGap's Android Get Started guide to create an initial Android project, and then modify the few lines of code to point to your deployed application, as discussed in the Android Application section above.



It is recommended to start by running your application on the Android 2.1 emulator. Although the camera function works with an actual device running Android 2.2, the camera in Android 2.2 emulator does not work with this sample.

9 Resources Beta Download: WebSphere Portlet Factory Next Beta2: http://www14.software.ibm.com/webapp/download/preconfig.jsp?id=2011-0131+01:08:18.414216R&S_TACT=&S_CMP= developerWorks® WebSphere Portlet Factory product page: http://www.ibm.com/developerworks/websphere/zones/portal/portletfactory/ 18

PhoneGap home page: http://www.phonegap.com PhoneGap documentation: http://docs.phonegap.com IBM Redbooks publication, “Portal Application Development Using WebSphere Portlet Factory:” http://www.redbooks.ibm.com/abstracts/sg247525.html wiki article, “Developing Web Applications for Smartphones with IBM WebSphere Portlet Factory 7.0:” http://www10.lotus.com/ldd/pfwiki.nsf/dx/Developing_Web_Applications_for_Smartphones_with_IBM_Web Sphere_Portlet_Factory_7.0

About the author Sam Alexander is a Managing Consultant for IBM Software Services for Lotus. A 10-year veteran of IBM, he has worked on several IBM software products, including serving on the WebSphere Portlet Factory development team. In addition to computer technology, his interests include family activities, marathon running, and traveling.

Trademarks •

developerWorks, IBM, and WebSphere are trademarks or registered trademarks of IBM Corporation in the United States, other countries, or both.



Microsoft and Windows are registered trademarks of Microsoft Corporation in the United States, other countries, or both.



Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.



Other company, product, and service names may be trademarks or service marks of others.

19

Suggest Documents