Lotus® Expeditor Developing Applications for Lotus Expeditor
Lotus® Expeditor
Developing Applications for Lotus Expeditor
���
Lotus® Expeditor
Developing Applications for Lotus Expeditor
���
Note
Before using this information and the product it supports, read the information in “Notices,” on page 395.
Third Edition (November 2006)
This edition applies to Version 6, Release 1 of IBM Lotus Expeditor and to all subsequent releases and modifications
until otherwise indicated in new editions.
© Copyright International Business Machines Corporation 2004, 2006. All rights reserved.
US Government Users Restricted Rights – Use, duplication or disclosure restricted by GSA ADP Schedule Contract
with IBM Corp.
Contents
Product overview . . . . . . . . . . . 1
What’s new in Lotus Expeditor, version 6.1 . . . . 2
Lotus Expeditor Client for Desktop . . . . . . 2
Runtime and tooling environment . . . . . 2
Application types . . . . . . . . . . . 3
Security . . . . . . . . . . . . . . 3
User interface . . . . . . . . . . . . 3
Document services . . . . . . . . . . 4
Communication . . . . . . . . . . . 4
Data . . . . . . . . . . . . . . . 5
Lotus Expeditor Client for Devices . . . . . . 5
Runtime and tooling environment . . . . . 6
Application types . . . . . . . . . . . 6
User interface . . . . . . . . . . . . 6
Communication . . . . . . . . . . . 6
Overview of the managed client platform . . . . . 6
Managed client services . . . . . . . . . . 7
Access services . . . . . . . . . . . . 8
Interaction services . . . . . . . . . . . 9
Lotus Expeditor Client for Desktop . . . . . 9
Lotus Expeditor Client for Devices . . . . 10
Platform management . . . . . . . . . . 10
Lotus Expeditor Toolkit . . . . . . . . . . 11
Getting started with the Lotus Expeditor Toolkit 11
Lotus Expeditor Toolkit overview . . . . . 11
Supported platforms and prerequisite software 12
Understanding the development platforms . . 12
Setting up the Lotus Expeditor Toolkit . . . 12
Setup for Desktop development . . . . 12
Setup for Device development . . . . . 13
Creating a sample Client Services project . . 13
Running your project on development
runtimes . . . . . . . . . . . . 13
Setting Toolkit preferences . . . . . . . 14
Turning on build automatically . . . . . . 14
Concepts . . . . . . . . . . . . . 15
Client Services Project . . . . . . . . 15
Managing Client Services project
dependencies . . . . . . . . . . . . 15
Target Definition . . . . . . . . . 15
Target features . . . . . . . . . . 17
Secondary Dependencies . . . . . . . 17
Automatic management of manifest
package dependencies . . . . . . . . 17
Configuring the Client Services launcher . . 18
Configuring the Lotus Expeditor Toolkit
environment for Sametime . . . . . . . . 18
Creating and using Client Services applications 19
Creating a Client Services project . . . . . 19
Creating a Client Services fragment project . . 20
Converting a Java project into a Client
Services project . . . . . . . . . . . 21
Updating the buildpath and classpath for
converted Client Services projects . . . . . 21
Setting Client Services project properties . . . 21
Launching Client Services launch
configurations from the manifest editor . . . 22
Using the samples . . . . . . . . . . . . 22
Installing samples using the Rational Software
Development Platform . . . . . . . . . . 22
Installing samples using Eclipse (without RSDP) 23
Cloudscape sample . . . . . . . . . . 24
Setup instructions . . . . . . . . . 24
Troubleshooting . . . . . . . . . . 25
DB2 Everyplace sample . . . . . . . . 25
Setup instructions . . . . . . . . . 26
Troubleshooting . . . . . . . . . . 26
Echo sample . . . . . . . . . . . . 27
Setup instructions . . . . . . . . . 27
Echo Secure sample . . . . . . . . . 30
Setup instructions . . . . . . . . . 30
Eclipse Preferences sample . . . . . . . 31
Setup instructions . . . . . . . . . 31
Running the sample . . . . . . . . 32
Embedded Transaction Container sample . . 32
Setup instructions . . . . . . . . . 33
ISync sample . . . . . . . . . . . . 34
Setup instructions . . . . . . . . . 34
Troubleshooting . . . . . . . . . . 38
JMS with Lotus Expeditor micro broker
provider sample . . . . . . . . . . . 39
Setup instructions . . . . . . . . . 39
Option A: Using the in-memory-based
micro broker instance . . . . . . . . 40
Option B: Using the file-persistence-based
micro broker instance . . . . . . . . 41
JMS with MQe provider sample . . . . . 42
Setup instructions . . . . . . . . . 42
Troubleshooting . . . . . . . . . . 43
JNDI sample . . . . . . . . . . . . 44
Setup instructions . . . . . . . . . 45
Troubleshooting . . . . . . . . . . 45
Log and Log Reader sample . . . . . . . 46
Setup instructions . . . . . . . . . 46
Troubleshooting . . . . . . . . . . 47
MQ Everyplace sample . . . . . . . . 48
Setup instructions . . . . . . . . . 48
Troubleshooting . . . . . . . . . . 49
Order Entry sample . . . . . . . . . 50
Setup instructions . . . . . . . . . 50
Order Entry Rich Client sample . . . . 51
Order Entry Web Application sample . . . 52
OSGi Preferences Service sample . . . . . 53
Setup instructions . . . . . . . . . 53
Pizza JSP sample . . . . . . . . . . 54
Setup instructions . . . . . . . . . 55
Portlet Communication sample . . . . . . 55
Setup instructions . . . . . . . . . 56
Rich Application sample . . . . . . . . 57
Setup instructions . . . . . . . . . 57
Troubleshooting . . . . . . . . . . 58
© Copyright IBM Corp. 2004, 2006 iii
Rich Text Editor sample . . . . . . . . 58
Setup instructions . . . . . . . . . 58
Secured Web application sample . . . . . 59
Setup instructions . . . . . . . . . 59
Running the sample . . . . . . . . 59
Service Tracker sample . . . . . . . . 60
Setup instructions . . . . . . . . . 60
Simple Portlet sample . . . . . . . . . 63
Setup instructions . . . . . . . . . 63
Simple Portlet Viewer sample . . . . . . 64
Setup instructions . . . . . . . . . 64
Web Page Aggregation of Portlets sample . . 65
Setup instructions . . . . . . . . . 65
Troubleshooting . . . . . . . . . . 66
Web Application sample . . . . . . . . 66
Setup instructions . . . . . . . . . 66
Web Application Log sample . . . . . . 67
Setup instructions . . . . . . . . . 67
XML Parser sample . . . . . . . . . . 68
Setup instructions . . . . . . . . . 68
Developing applications . . . . . . . 71
Application models . . . . . . . . . . . . 71
Application design considerations . . . . . . . 71
End-to-End applications . . . . . . . . . 71
Topology . . . . . . . . . . . . . . 73
Business logic . . . . . . . . . . . . 73
Persistence . . . . . . . . . . . . . 74
Messaging . . . . . . . . . . . . . . 74
Management . . . . . . . . . . . . . 75
Serviceability . . . . . . . . . . . . . 75
Interaction . . . . . . . . . . . . . . 75
Cross platform APIs . . . . . . . . . . 75
Packaging . . . . . . . . . . . . . . 76
Components . . . . . . . . . . . . 76
Fragments . . . . . . . . . . . . . 77
Features . . . . . . . . . . . . . 77
Class loading . . . . . . . . . . . . . 78
Developing the application user interface . . . . 81
Understanding the user interface . . . . . . 81
Eclipse . . . . . . . . . . . . . . 81
UI toolkits . . . . . . . . . . . . . 82
Visual Editor for Java . . . . . . . . . 82
User interaction in the Lotus Expeditor . . . 82
User interface organization . . . . . . . 83
Title bar . . . . . . . . . . . . 83
Menu bar . . . . . . . . . . . . 83
Banner bar . . . . . . . . . . . 84
Data area . . . . . . . . . . . . 84
Coolbar/Toolbar . . . . . . . . . . 84
Status bar . . . . . . . . . . . . 84
Application Launcher . . . . . . . . 84
Using widgets on devices . . . . . . . . 84
Functional partitioning . . . . . . . 84
Device normalization . . . . . . . . 84
Mobile extensions widgets . . . . . . 85
eSWT programming tips . . . . . . . 87
Using the Restricted Workbench . . . . . . 87
Applicability and benefits . . . . . . . 88
Using ILockdownService . . . . . . . . 88
Acquiring the ILockdownService . . . . 88
Using the ILockdownService to remove the
window title trim . . . . . . . . . 89
Using the ILockdownService to lock the
window . . . . . . . . . . . . 89
Using the ILockdownService to exit the
Restricted Workbench and logoff the OS . . 89
Using personalities . . . . . . . . . . . 89
Creating a personality . . . . . . . . . 89
Lifecycle events . . . . . . . . . . . 91
Creating a global toolbar . . . . . . . . 91
Developing composite applications . . . . . . 91
Understanding composite applications . . . . 91
Creating composite application projects . . . . 92
Creating an SWT view component . . . . . 93
Creating a portlet component . . . . . . 96
Developing composite application logic . . . . 97
Developing data access applications . . . . . . 98
Understanding embedded database development 98
Databases . . . . . . . . . . . . . 98
Embedded databases . . . . . . . . 99
DB2 Everyplace and Cloudscape
comparison . . . . . . . . . . . 99
Deployment and synchronization . . . . . 100
Security considerations . . . . . . . 101
Database lifecycle management . . . . . 102
Enabling projects for data access application
development . . . . . . . . . . . . 102
Client Services target profile features . . . 102
Developing database logic . . . . . . . . 103
Data access application development best
practices . . . . . . . . . . . . . 103
Database Lifecycle Management . . . . . 104
Accessing a defined managed datasource 104
Monitoring operations on a managed
datasource . . . . . . . . . . . 104
Accessing the default managed datasource 104
Developing Embedded Transaction applications 105
Understanding the Embedded Transaction
Container . . . . . . . . . . . . . . 105
Concepts . . . . . . . . . . . . . 106
EJB Container . . . . . . . . . . 106
Home Interfaces . . . . . . . . . 106
Finder Methods . . . . . . . . . 107
Component interfaces . . . . . . . 107
Transaction management . . . . . . 107
Container managed transactions . . . . 107
Programmatic transaction management 107
DataSource/TxnDataSource . . . . . 107
Creating Embedded Transaction projects . . . 108
Using a Client Services Embedded
Transaction project versus an EJB project . . 108
Creating a Client Services Embedded
Transaction project . . . . . . . . . 108
Converting an EJB project to a Client Services
Embedded Transaction project . . . . . . 109
Embedded Transaction Container preferences 109
Developing Embedded Transaction Container
logic . . . . . . . . . . . . . . . 110
Implementing finder methods . . . . . . 110
Configuring and using data sources . . . . 112
iv Lotus Expeditor: Developing Applications for Lotus Expeditor
Creating and binding DataSource
instances . . . . . . . . . . . . 112
Locating and connecting to a DataSource 112
Locating EJBs . . . . . . . . . . . 113
Finding EJB homes . . . . . . . . 113
Conserving JDBC resources . . . . . . . 113
Working with user managed transactions . . 113
Advanced topics . . . . . . . . . 114
Providing custom bundle activation . . . . 114
Advanced topics . . . . . . . . . 114
Creating Session and Entity Beans . . . . 114
Creating a Container Managed Persistence
(CMP) bean . . . . . . . . . . . 115
Creating EJB CMP 1.1 beans . . . . . 116
Creating a stateless session bean . . . . 116
Creating a Bean Managed Persistence
(BMP) bean . . . . . . . . . . . 117
Customizing for target data base (DB2e and
Derby) . . . . . . . . . . . . . . 118
Packaging and deploying Embedded
Transaction applications . . . . . . . . . 119
Invoking deployment . . . . . . . . . 119
Embedded Transaction Deployment
Descriptor . . . . . . . . . . . . 119
Embedded Transaction Deployment Editor 120
Debugging and testing Embedded Transaction
applications . . . . . . . . . . . . . 120
Enabling logging and tracing with the
Embedded Transaction Container . . . . . 120
Run or debug Client Services Embedded
Transaction Container projects using the
Client Services launcher . . . . . . . . 121
Run or debug a Client Services Embedded
Transaction Container project on the Lotus
Expeditor runtime . . . . . . . . . . 121
Run or debug a non-Client Services EJB
project on the Lotus Expeditor runtime . . . 121
Run or debug a Client Services Embedded
Transaction Container project on a non-Lotus
Expeditor runtime . . . . . . . . . . 122
Setting breakpoints on generated code . . . 122
Developing management applications . . . . . 122
Developing applications to drive the Enterprise
Management Agent . . . . . . . . . . 122
Accessing the OSGiAgentService object . . . 123
Developing Enterprise Management Agent
SyncML tree extensions . . . . . . . . . 123
SyncML tree extensions overview . . . . 123
Creating an Extension to the SyncML Tree 123
Creating an Extension to register the
command for Exec Leaf . . . . . . . . 124
Developing update manager applications . . . 125
Developing messaging applications . . . . . . 125
Understanding messaging applications . . . . 125
Publish and subscribe messaging . . . . . 126
Topics and hierarchical topic names . . . 128
Publication and subscription messages 130
Point-to-point – queues and queue managers 132
Java Message Service . . . . . . . . . 132
WebSphere MQ Everyplace . . . . . . . 133
Lotus Expeditor micro broker . . . . . . 133
Scenarios and applications . . . . . . 134
Understanding the micro broker
components . . . . . . . . . . . 135
Micro broker topologies . . . . . . . 137
MQTT . . . . . . . . . . . . . . 139
Quality of Service . . . . . . . . . 140
Clean session . . . . . . . . . . 140
WebSphere MQ Everyplace and micro broker
comparison . . . . . . . . . . . . 141
Enabling projects for messaging . . . . . . 141
Client Services target profile features . . . 141
Developing messaging application logic . . . 142
MQ Telemetry Transport . . . . . . . 142
Developing network aware applications . . . . 142
Understanding network aware applications . . 142
Enabling network aware applications . . . . 143
Developing network aware application logic . . 143
Component registration . . . . . . . . 143
Creating and configuring handlers . . . . 145
Adding and configuring customized handlers 146
Using the NetStatus API to detect the real
network status . . . . . . . . . . . 147
Using enhanced HttpClient . . . . . . . 147
Monitoring remote resources . . . . . . 148
Using the notification and check models . . 149
Notification model . . . . . . . . 149
Check model . . . . . . . . . . 150
Developing portlet applications . . . . . . . 150
Understanding Portlet applications . . . . . 150
URL Addressability . . . . . . . . . 151
Client Services Portlet projects . . . . . . 152
Portlet API and Type support . . . . . . 152
Unsupported features . . . . . . . . 153
Using the Portlet Aggregation Tag Library 153
Creating Portlet projects . . . . . . . . . 154
Creating a Client Services Portlet project . . 154
Converting a J2EE portlet project to a Client
Services Portlet project . . . . . . . . 154
Importing Client Services portlet projects . . 155
Adding a portlet to a Client Services Portlet
project . . . . . . . . . . . . . . 155
Developing Portlet application logic . . . . . 155
Aggregating portlets to JSPs . . . . . . 155
Securing Portlet application resources . . . 156
Transport level security (HTTPS) . . . . 156
Programmatic security . . . . . . . 157
Debugging and testing Client Services Portlet
applications . . . . . . . . . . . . . 157
Debugging and testing Client Services Portlet
projects on a test environment . . . . . . 157
Debugging and testing Client Services Portlet
projects on non-Lotus Expeditor runtimes . . 157
Developing Rich Client applications . . . . . . 157
Creating a simple Rich Client Platform
application . . . . . . . . . . . . . 158
Creating a simple Rich Client Application for
devices . . . . . . . . . . . . . . 160
Creating a Rich GUI application for devices 160
Setup . . . . . . . . . . . . . 160
Creating the basic parts of a Rich GUI
application . . . . . . . . . . . 160
Contents v
Running your application on the
development runtime . . . . . . . . 162
Creating an eRCP workbench application
from an existing project . . . . . . . . 162
Deploying an eRCP workbench application 163
Installing an eRCP workbench application 164
Running an eRCP eWorkbench application 165
Managing an eRCP eWorkbench application 165
Extending the capabilities of your application 165
Customizing the user interface . . . . . 165
Using themes . . . . . . . . . . 165
Custom widgets . . . . . . . . . 167
Using SWidgets . . . . . . . . . . 197
Managing contributions to the user interface 197
Accessing SWidgets and JFaceEX objects . . 198
Adding and contributing menus . . . . . 198
Menu contributions . . . . . . . . 199
Creating a top-level menu . . . . . . 200
Creating views . . . . . . . . . . . 201
Applying Capitalization and punctuation
guidelines . . . . . . . . . . . . 201
Creating helpful messages . . . . . . . 202
Critical . . . . . . . . . . . . 202
Warning . . . . . . . . . . . . 202
Customizing existing applications . . . . . 202
Activities . . . . . . . . . . . . . 203
Using activities . . . . . . . . . . 203
Integrating existing RCP applications into Lotus
Expeditor . . . . . . . . . . . . . . 204
Developing with user interface widgets . . . 204
Adding spell checking to applications . . . 204
Using dictionaries supported by a given
locale . . . . . . . . . . . . . 204
Using dictionaries supported by the
platform default locale . . . . . . . 205
Using given dictionaries . . . . . . . 205
Using given dictionaries and a customized
user dictionary . . . . . . . . . . 205
Using the getSuggestions string . . . . 205
User dictionary manager . . . . . . 206
Contributing custom spell checking
services . . . . . . . . . . . . 208
Implementing an embedded Web browser 216
Creating an embedded browser . . . . 217
Enhancing an embedded browser view 217
Setting browser preferences . . . . . . 219
Using the Rich Text Editor . . . . . . . 225
Creating a custom Rich Text Editor . . . 225
Using and controlling the custom Rich
Text Editor . . . . . . . . . . . 225
Accessing a Web address with the integrated
browser application . . . . . . . . . 227
Customizing the integrated browser
application with Eclipse preferences . . . 228
Using the Portlet Viewer . . . . . . . 228
Portlet Viewer extension examples . . . 229
Using the Portlet Viewer with WSRP
portlets . . . . . . . . . . . . 229
Widgets for devices . . . . . . . . . 230
Creating help for the application . . . . . . 230
Developing synchronization applications . . . . 230
SyncML . . . . . . . . . . . . . . 230
Understanding SyncML development . . . 230
Technology overview . . . . . . . . 231
SyncML4J common . . . . . . . . 231
SyncML4J data synchronization . . . . 231
SyncML4J device management . . . . 232
Enabling projects for SyncML development 232
Client Services target definition
components . . . . . . . . . . . 232
SyncManager . . . . . . . . . . . . 232
Understanding the SyncManager . . . . . 232
Enabling projects for the SyncManager . . . 233
SyncService extension point . . . . . 233
TypeService extension point . . . . . 233
SchedulerService extension point . . . . 235
Developing SyncManager application logic 235
Developing Web applications . . . . . . . . 238
Understanding Web Applications . . . . . . 239
Creating Web Application projects . . . . . 239
Using a Client Services Web project versus a
Dynamic Web project . . . . . . . . . 240
Creating a Client Services Web project . . . 240
Converting a Dynamic Web project to a
Client Services Web project . . . . . . . 242
Developing Web Application logic . . . . . 242
Accessing resources . . . . . . . . . 242
Using JSP Standard Tag Libraries . . . . . 243
Java Server Faces (JSF) development . . . . 243
Using the Sun JSF Reference
Implementation . . . . . . . . . 243
Struts development . . . . . . . . . 243
Securing Web Application resources . . . . 244
Configuring a Web Application . . . . 244
Supported authentication mechanisms . . 245
Using the User Admin Service to create
users and roles . . . . . . . . . . 245
Using the Admin Utility for OSGi to
create users and roles . . . . . . . . 246
Exporting Web Application bundles . . . . 247
WAB Utility . . . . . . . . . . . . 247
WAB Utility installation . . . . . . . 247
WAB Utility usage and parameters . . . 247
Using Lotus Expeditor servers . . . . . . 249
Servicing Web Applications . . . . . . . . 249
Web Container Logging . . . . . . . . 249
Configuring the Web Container Logging 250
Debugging and testing Web Applications . . . 250
Running and debugging using Client
Services launcher . . . . . . . . . . 250
Running and debugging using ″Run on
Server″ style . . . . . . . . . . . . 250
Running or debugging Client Services
Web Projects on the Client Services
runtime . . . . . . . . . . . . 250
Run or debug a non-Client Services Web
Project on the Client Services runtime . . 250
Running or debugging a Client Services
Web Project on a non-Client Services
runtime . . . . . . . . . . . . 251
Developing Web Services . . . . . . . . . 251
Understanding Web Services . . . . . . . 251
vi Lotus Expeditor: Developing Applications for Lotus Expeditor
Technologies . . . . . . . . . . . . 251
Web Services Description Language
(WSDL) . . . . . . . . . . . . 251
Simple Object Access Protocol (SOAP) . . 252
JAX-RPC . . . . . . . . . . . . 252
The Web Services Client Programming
Model . . . . . . . . . . . . . 252
Tools . . . . . . . . . . . . . . 253
Tools for Mobile Web Services
development . . . . . . . . . . 253
Web Services Resource Framework . . . . . 253
Understanding WSRF applications . . . . 253
Technologies . . . . . . . . . . . 253
WS-Resource . . . . . . . . . . 254
WSRF Runtime . . . . . . . . . . 254
WSRF tools . . . . . . . . . . . 255
Creating WS-Resource projects . . . . . 255
Developing WS-Resource providers . . . 256
Creating a WS-Resource client . . . . . 258
Supported data types . . . . . . . . 259
Developing WSRF application logic . . . . 260
WS-Resource provider application . . . 260
WS-Resource client applications . . . . 264
WS-Resource creation operations . . . . 264
WS-Resource LifeTime operations . . . 265
WS-Resource properties operations . . . 266
WS-Resource service method operations 270
Packaging and deploying WS-Resource
bundles . . . . . . . . . . . . . 271
Deploying a WS-Resource provider . . . 271
Deploying a WS-Resource client . . . . 272
Accessing a WS-Resource from Java
applications . . . . . . . . . . . 272
Securing a WS-Resource . . . . . . . . 273
Enabling security for WS-Resource
providers . . . . . . . . . . . . 273
Enabling security for WS-Resource clients 274
Developing custom authenticators . . . 274
Mobile Web Services . . . . . . . . . . 275
Creating Mobile Web Services . . . . . . 275
Creating Mobile Web Services providers 275
Creating Mobile Web Services clients . . 277
Developing Mobile Web Services logic . . . 283
Custom serialization (marshalling) . . . 283
Securing Mobile Web Services . . . . . . 289
Creating secure Mobile Web Services
providers . . . . . . . . . . . . 289
Securing pre-existing Mobile Web Services
providers . . . . . . . . . . . . 290
Creating secure Mobile Web services
clients . . . . . . . . . . . . . 290
Securing pre-existing Mobile Web Services
clients . . . . . . . . . . . . . 292
Editing the Mobile Web Services security
configuration . . . . . . . . . . 293
Deploying Mobile Web Services . . . . . 299
Deploying Mobile Web Services providers 299
Deploying Mobile Web Services clients 300
Axis Web Services . . . . . . . . . . . 301
Creating Axis Web Services . . . . . . . 301
Creating an Apache Axis based Web
Services client . . . . . . . . . . 301
Directly obtaining an instance of the
interface stub . . . . . . . . . . 302
Obtaining an instance of the interface stub
using the JNDI mechanism . . . . . . 302
Defining the extensions to register the
Service Interface to JNDI provider . . . 303
Programmatically creating accounts for
Apache Axis Web Services clients . . . . 304
Developing wired applications . . . . . . . 305
Portlet communication . . . . . . . . . 305
Defining actions and properties . . . . . 305
Wiring portlets . . . . . . . . . . . 306
Wire extension examples . . . . . . . 306
Using the portlet wiring tool . . . . . . 307
Creating Cooperative Components with the
Lotus Expeditor Property Broker . . . . . . 307
Creating your components . . . . . . . 308
Registering your definitions with the broker 309
Working with the Property Broker WSDL file 309
Using the Composite Application
Infrastructure . . . . . . . . . . . 311
Declarative wiring with the Portal Admin
tool . . . . . . . . . . . . . . . 311
Specifying a custom owner for your SWT
action . . . . . . . . . . . . . . 311
Developing for serviceability . . . . . . . . 312
Understanding serviceability . . . . . . . 312
Enabling projects for serviceability . . . . . 312
Developing serviceability logic . . . . . . 313
JSR47 Logging (java.util.logging) . . . . . 313
JSR47 Tracing (java.util.logging) . . . . . 314
OSGi Logging . . . . . . . . . . . 314
OSGi Tracing . . . . . . . . . . . 314
Eclipse Logging . . . . . . . . . . 314
Eclipse Tracing . . . . . . . . . . . 315
Apache Commons Logging . . . . . . . 315
Java.util.logging best practices . . . . . . 315
Migrating applications . . . . . . . . . . 319
Automated migration . . . . . . . . . 319
Manual migration . . . . . . . . . . . 319
Migrating component logic . . . . . . . . 320
Compatibility plug-ins provided . . . . . 320
Plug-ins removed in this release . . . . . 320
Metatype Service changes . . . . . . . 322
Changes to plug-in startup . . . . . . . 322
Migrating OSGi services . . . . . . . . . 322
Debugging and testing applications 323
Local debugging and testing . . . . . . . . 323
Client Services Launcher . . . . . . . . 323
Client Services Server . . . . . . . . . 324
Creating a server . . . . . . . . . . 324
Editing a server . . . . . . . . . . 324
Adding projects to a server . . . . . . . 324
Starting a server . . . . . . . . . . 325
Remote debugging and testing . . . . . . . 325
Packaging and deploying applications 327
Contents vii
Packaging applications for distribution . . . . . 327
Understanding application lifecycle . . . . . 327
Understanding methods of installation . . . . 328
Local installation . . . . . . . . . . 328
Enterprise installation . . . . . . . . 328
Understanding the types of install artifacts . . 328
Installer/Uninstaller . . . . . . . . . 329
Update site . . . . . . . . . . . . 329
Features . . . . . . . . . . . . . 329
Plug-ins . . . . . . . . . . . . . 330
Native libraries . . . . . . . . . . . 330
Configuration file updates . . . . . . . 331
Installation instructions . . . . . . . . 331
Enterprise distribution instructions . . . . 331
Using Ant tasks to build a deployable bundle 331
Deploying projects for local testing . . . . . . 331
Exporting plug-ins using the PDE . . . . . 332
Globalizing your application . . . . . 333
Support for multiple locales . . . . . . . . 333
IBM language groups . . . . . . . . . . . 333
Supporting preferred fonts and bidirectional
layouts . . . . . . . . . . . . . . . 335
Creating translatable plug-ins . . . . . . . . 335
Securing applications and data . . . . 337
Accounts framework . . . . . . . . . . . 337
Adding accounts . . . . . . . . . . . 338
Retrieving accounts . . . . . . . . . . 339
Updating accounts . . . . . . . . . . 339
Listening for account changes . . . . . . . 340
Implementing a custom account type . . . . 340
Managing secure passwords . . . . . . . 340
SSL and accounts . . . . . . . . . . . 341
Login configurations . . . . . . . . . . . 341
Logging into the platform . . . . . . . . 342
Logging into remote servers . . . . . . . 342
Using HTTP basic authentication . . . . . 343
Using form-based authentication . . . . . 343
Using platform single sign-on . . . . . . . 345
Implementing single sign-on with remote
servers . . . . . . . . . . . . . . 345
Contributing a login configuration . . . . . 346
Enabling applications for
configuration . . . . . . . . . . . 349
Using preferences . . . . . . . . . . . . 349
Creating preference pages . . . . . . . . 349
Using the Managed Settings framework . . 349
Creating a managed settings-aware
application . . . . . . . . . . . 350
Using the Portal Policy API on the client 351
Creating Eclipse Preference Sets with the
Policy Type Editor . . . . . . . . . 351
Understanding preference options . . . . . 358
Eclipse preferences . . . . . . . . . 358
Configuration admin . . . . . . . . . 358
OSGi preference service . . . . . . . . 359
Using the XML parser services . . . . . . . 359
Locating the Web Container ports using the
HttpSettingListener Service . . . . . . . . . 361
Using the Meta Type Service . . . . . . . . 362
Reference information . . . . . . . 365
Lotus Expeditor top level menus . . . . . . . 365
File menu . . . . . . . . . . . . . 365
View menu . . . . . . . . . . . . . 365
Help menu . . . . . . . . . . . . . 366
OSGi . . . . . . . . . . . . . . . . 366
OSGi specification . . . . . . . . . . . 366
Working with OSGi bundles . . . . . . . 366
Creating OSGi bundles . . . . . . . . 366
Bundles . . . . . . . . . . . . 366
Conventions for creating bundles . . . . 367
Creating manifest files . . . . . . . 367
Packages . . . . . . . . . . . . 368
Understanding services . . . . . . . . 369
Registering and unregistering a service
with the OSGi Framework . . . . . . 369
Getting and un-getting services from the
OSGi Framework . . . . . . . . . 370
Lotus Expeditor Toolkit . . . . . . . . . . 372
Wizards . . . . . . . . . . . . . . 372
New Client Services Project Wizard . . . . 372
New Client Services Fragment Project Wizard 373
Convert Project to Client Services Project
Wizard . . . . . . . . . . . . . 375
Client Services Project Properties page . . . 376
Dialogs . . . . . . . . . . . . . . 376
Client Services Launch Configuration dialog 376
Debugging a remote Client Services
runtime . . . . . . . . . . . . 377
Lotus Expeditor Toolkit Preference Dialog 377
Tag library . . . . . . . . . . . . . . 378
Aggregation tag library . . . . . . . . . 378
Message reference . . . . . . . . . . . . 380
Web Container messages . . . . . . . . 380
Appendix. Notices . . . . . . . . . 395
Trademarks . . . . . . . . . . . . . . 397
viii Lotus Expeditor: Developing Applications for Lotus Expeditor
Product overview
Developing Applications for Lotus® Expeditor is your tool for developing powerful managed client
applications that run on desktops, laptops, tablets, and handheld devices. If you are experienced in
developing Java™ Enterprise Edition (JEE) applications, portlet applications, Web Services, or Eclipse
applications, then you are ready to develop applications for Lotus Expeditor.
With the Lotus Expeditor, you can move key components of your applications from the server to desktop,
laptop, tablet, and handheld clients by using standard APIs and services. Moving application components
to run on a client can have dramatic results for business. End-users benefit from improved application
response time because applications perform business operations locally on the client. As a result, there is
a reduction in network traffic between clients and servers, and in server workload. Furthermore, mobile
end-users can continue to productively use their applications from their clients even when they are at a
location that does not have network connectivity, such as a customer site. You can also utilize the local
graphical user interface (GUI) capabilities of the client devices to deliver a richer user experience than can
be supported by a Web browser.
The Lotus Expeditor Toolkit provides a complete, integrated set of tools that allows you to develop,
debug, test, package and deploy client applications that use the Client Services. This toolkit is built on
Eclipse technology and extends the powerful Rational® suite of development tools so you can leverage
your existing skills and software components. Eclipse is an award-winning, open source platform for the
construction of powerful software development tools and rich desktop applications. Leveraging the
Eclipse plug-in framework to integrate technology on the desktop saves technology providers time and
money by enabling them to focus their efforts on delivering differentiation and value for their offerings.
Full details on Eclipse are available at http://www.eclipse.org.
The toolkit also provides Ant tasks so you can create Ant scripts to automate the building of your
applications. In addition, the toolkit provides program samples to help jump start your application
development projects.
The combination of the Lotus Expeditor Client and the Lotus Expeditor Server provide the client and
server middleware ″connectors″ necessary to deliver and manage end-to-end applications (see the below
figure). System Administrators use the Lotus Expeditor server to install and configure the server
middleware, so client applications can securely perform assured transactions and database
synchronization with Enterprise applications and data. For more information on the server platform,
please refer to the Lotus Expeditor Server documentation.
New for Lotus Expeditor Client for Desktop, Portal administrators can now use WebSphere® Portal Server
to control managed client applications available to end-users based on access rights defined by the
administrator. For more information, please refer to the Assembling and Deploying Lotus Expeditor
Applications.
Lotus Expeditor Toolkit End-to-end Tools
End-to-end Applications
End-to-end Services
Enterprise and Portal Applications(Extended)
Lotus Expeditor Client
Desktop: jclDesktopDevice: jclDevice
Eclipse, AST, Rational SoftwareDevelopment Platform
Enterprise and Portal Applications
Lotus Expeditor Server
Websphere ApplicationServer / JEE
© Copyright IBM Corp. 2004, 2006 1
With Lotus Expeditor Client for Desktop, you can develop managed client applications that run on the
following operating systems:
v Microsoft® Windows® XP Professional Service Pack 1 and 2
v Microsoft Windows XP Home Edition Service Pack 1 and 2
v Microsoft Windows XP Tablet PC Edition 2005
v RedHat EL 4.0 WS with GTK support – Update 3
v Novell Linux® Desktop 9 (NLD9) Service Pack 3
v Microsoft Windows 2000 Service Pack 4
With Lotus Expeditor Client for Devices, you can develop managed client applications that run on the
following operating systems:
v Windows Mobile 5.0 (PocketPC and Phone)
v Windows Mobile 2003 SE (PocketPC and Phone)
In summary, the powerful client platforms, toolkit, and server platform enable you to develop compelling
managed client applications that run on a variety of clients and securely access e-business on demand™
applications, services, and data. You can use programming skills you have already acquired to develop
these applications. This guide provides the information you need to deliver these applications to your
customers.
What’s new in Lotus Expeditor, version 6.1
This section describes what’s new in Lotus Expeditor, version 6.1
Lotus Expeditor Client for Desktop
The Lotus Expeditor Client for desktop, version 6.1 provides many new application functions over the
previous release, the WebSphere Everyplace® Deployment for Windows and Linux client, version 6.0.
Here are the highlights of the new application features:
Runtime and tooling environment
jclDesktop
J9 2.3 with jclDesktop is a custom configuration that provides a reduced-footprint runtime
environment for the client platform. jclDesktop is the default configuration for desktops.
Eclipse Rich Client Platform (RCP) 3.2.1
The client platform includes the Eclipse Rich Client Platform, version 3.2.1, which implements the
OSGi Release 4 specification.
Eclipse Integrated Development Environment (IDE) 3.2.1
The Lotus Expeditor Toolkit extends the tooling provided by the Eclipse Plug-in Development
Environment (PDE) and works with the Eclipse Visual Editor provided by the Eclipse Integrated
Development Environment (IDE), version 3.2.1.
Eclipse Web Tools Project (WTP) 1.5.1
The Lotus Expeditor Toolkit extends the Web tooling in WTP so you can use the existing Web
tooling provided by WTP to develop Web applications. This environment does not support
development of Java Server Faces (JSF) applications.
WebSphere Application Server Toolkit (AST) 6.1.1
The Lotus Expeditor Toolkit extends the Web, EJB, and portlet tooling in AST so you can continue
to use existing tools to develop Web, Embedded Transaction, and portlet client applications
respectively. This environment does not support development of JSF applications.
Rational Software Development Platform 7.0
The Lotus Expeditor Toolkit extends the Web, EJB, and portlet tooling provided by Rational
2 Lotus Expeditor: Developing Applications for Lotus Expeditor
Application Developer 7.0 and Rational Software Architect 7.0 so you can continue to use existing
tools to develop Web, Embedded Transaction, and portlet client applications respectively.
Application types
Portlets (JSR 168)
The portlet container runs portlets that comply with the JSR 168 specification. The portlet
container also supports “wiring” (i.e. communications among portlets) using wire definitions
defined on the portal server with the existing portal wiring tool. The wiring information is
included in the markup sent to the client for the portal-defined perspective represented by that
page.
Composite Applications
Composite applications enable the integration of information from multiple applications,
providing end users with the content they need based on their role and task. Lotus Expeditor
enables you to integrate portlets and Eclipse applications through the use of the Property Broker.
Compatibility
Lotus Expeditor Client for Desktop continues to support Web, Embedded Transaction, Eclipse,
messaging, database, and Web Services applications.
Security
Key Store
The key store provides an encrypted local repository for user ids, passwords, certificates, and
other credentials. Applications can store their credentials in the key store to reduce the number of
times a user is prompted for credentials (e.g. the user is prompted once to unlock the key store).
The implementation is based on standard JSE key stores and the Java Authentication and
Authorization Service (JAAS).
Single Sign-on
The single sign-on service uses operating specific key stores for the key. User authentication with
the operating system allows the managed client to access the key and unlock the platform. Users
can also log into the key store without being prompted for a password.
Accounts
Accounts provide an interface for accessing account information, regardless of account type and
data store. Accounts contain information for accessing a given service, such as server, user name,
password, protocol, port, SSL details, and other preferences.
User interface
Eclipse Rich Client Platform (RCP) 3.2.1
RCP 3.2.1 supports the Standard Widget Toolkit (SWT), which provides a cross-platform API that
tightly integrates with the native widgets of the operating system; JFace, which provides
convenience features such as dialogs; and the Workbench, which enables you to add your
applications to the application desktop.
Personality
A personality extends the concept of a WorkbenchAdvisor to allow for different
WorkbenchWindow configurations to run within one running Virtual Machine (VM). Lotus
Expeditor provides a personality, which is a standalone personality that supports Eclipse and
composite application-driven perspectives.
Workbench Personality with Application Launcher
The application switcher is enhanced to support application hierarchies. Applications may be
placed in folders (i.e. cascading submenus). For portal-defined perspectives, the application
hierarchy maps to the page group hierarchy defined on the portal server. “Native” applications
may also be added to the launcher and accessed from the workbench. Native applications
launched from the workbench will appear in their own windows as they do when launched from
Product overview 3
the operating system. Windows for applications launched from the workbench will appear in the
application switcher. The launcher also supports the following functionality:
v Supports multiple user workspaces via command line –data option
v Supports launching multiple personalities
v Supports bring-to-front of application instead of launch of multiple instances
The workbench can also launch eRCP applications.
Access to “native” applications and selected system services from the workbench
The workbench and application launcher are enhanced to (optionally) provide access to the
following features. In the portal-managed client environment, access to these functions is
restricted based on portal access control and configured using the “Launcher administration
portlets”.
v Launch an application installed on the client (e.g. Notes®, Office, etc...)
v Shutdown the operating system
v Log off the operating system
v Change the keyboard layout
v Change the current operating system locale
v Lock the screen (using screen lock program included in the operating system)
User Interface Widgets
The user interface widgets provide a common look and feel for all user interfaces that use the
managed client platform. These widgets provide the following features:
v Renders the toolbar skin, which means it formats the toolbar to make its style consistent with
the style defined for the application it displays in
v Manages drag and drop of items within the toolbar
v Saves and loads the last state of toolbar items using the Eclipse memento pattern, which uses
the saveState and restoreState methods to remember the position of each toolbar item when
the user shuts down the client so that the same toolbar items display when the user restarts it
Embedded Browser
The Embedded Browser extends an application’s functions, such as browser launch, handles
events, and preference user interface, etc. The Embedded Browser is based on SWT browser
widget in Eclipse, which can support Internet Explorer on Windows and Mozilla on Linux.
Document services
Rich Text Editor
Rich Text Editor is a DOM-based Editor, which provides HTML and plain text editing. It is a very
thin layer on top of the DOM browser, and the editing operation is achieved primarily by using
DOM calls.
Text Analyzer
The Text Analyzer framework can be extended to provide Spell Checker functionality. Spell
Checker functionality is used to check misspelled words in a document and supports 26
languages. The Spell Checker functionality can be used by many editors, by implementing the
document interfaces.
Communication
Axis Web Services
The managed client platform also supports JAX-RPC compliant clients by bundling the Apache
Axis runtime components. The Apache Axis 1.3 tools available in the Rational Application
Developer (RAD) should be used to generate the code needed to consume Web services via
JAX-RPC. You can use Axis Web Services when a JSR 172 implementation cannot meet your
application requirements. For example, use Axis Web Services when your application requires
automatic two-way conversions between Java Collections and SOAP Arrays. The Axis Web
4 Lotus Expeditor: Developing Applications for Lotus Expeditor
Services support SSL and Basic Authentication. Lotus Expeditor Client for Desktop continues to
support Web Services client applications that conform to the JSR 172 specification.
Network Layer
The Network Layer includes a main framework for handling network faults. This service
provides a handler extension point for applications to extend and includes the following
functionality:
v Failures can be handled at the HTTP protocol level - provides a service for querying the state
of the client
v Manages multiple server states and provides a service to query the state of the servers
v Detects the network adapter status
v Detects the operating system power events (standby or hibernate) and provides an interface
layer on top of os.events to be used by other plug-ins to query the system power status
Micro broker
The micro broker is a very small footprint, 100% Java message broker, capable of running in
resource-constrained environments. It is suitable for embedding in applications and solutions that
have a need for messaging, notification and event services. Micro broker supports publish and
subscribe messaging paradigm. It provides a messaging infrastructure, which enables lightweight
messaging clients to communicate with each other, on one host or across a network, as well as
with enterprise brokers through its bridging capabilities. Micro broker uses the MQ Telemetry
Transport (MQTT) protocol over TCP/IP. Micro broker allows for the bridging of messages to
WebSphere MQ servers.
Property Broker
Property Broker wires are created via calls to the property broker APIs. Wires are tied to
navigation elements and are enabled or disabled when a perspective is activated or deactivated
respectively. Wires enable different components to communicate with each other while remaining
completely decoupled. Currently, the client only supports PROPERTY-TO-ACTION wires.
Although Property Broker supports multiple wire types, the portal projection model supports
only this type.
OSGi Event Admin
Nearly all the bundles in an OSGi framework must deal with events, either as an event publisher
or as an event handler. The Event Admin service provides an inter-bundle communication
mechanism. It is based on an event publish and subscribe model, popular in many message based
systems.
Data
Database Lifecycle Management
Database Lifecycle Management provides interoperability with a relational database in a uniform
and transparent manner, while providing the platform administrators and developers a
declarative way of specifying the required database structure and the ability to initially populate
that database prior to client access, as well as supporting migration from one database structure
to another.
Sync Manager
Synchronization Manager allows users and applications to initiate, control and monitor
synchronization of local data stores using one or more synchronization services. Synchronization
Manager integrates multiple synchronization services under the same framework. Lotus
Expeditor provides synchronization services for DB2e, ISync, SyncML, and composite application
integration.
Lotus Expeditor Client for Devices
The Lotus Expeditor Client for Devices provides many new application functions over the previous
release, the Workplace Client Technology™ Micro Edition 5.7.2. These enhancements focus on updating
Product overview 5
the capabilities of the managed client platform to support Windows Mobile 5.0 and Windows Mobile
2003 SE devices with the most current technologies. Here are the highlights of the new application
features:
Runtime and tooling environment
jclDevice
J9 2.3 with jclDevice is a custom configuration that provides an optimal reduced-footprint
runtime environment for the client platform that supports Java Micro Environment (JME)
Foundation Profile 1.1.
Eclipse Embedded Rich Client Platform (eRCP) 1.0
The client platform includes the Eclipse Embedded Rich Client Platform, version 1.0 for handheld
devices, such as PDA’s and smart phones. eRCP implements the OSGi Release 4 specification.
Tooling
You can use the Lotus Expeditor Toolkit to develop device applications by using any of the same
tooling environments supported for the desktop client.
Application types
Eclipse Embedded Rich Client Platform (eRCP) applications
Lotus Expeditor Client for Devices now enables you to run eRCP applications.
Embedded Transaction Applications
Lotus Expeditor Client for Devices now enables you to run business logic that conforms to a
subset of the EJB 2.0 specification.
Compatibility
Lotus Expeditor Client for Devices continues to support Web, messaging, database, and Web
Services applications.
User interface
Eclipse Embedded Rich Client Platform (eRCP) 1.0
eRCP supports a subset of APIs and capabilities available in the RCP with the benefit of smaller
resource requirements. eRCP includes the embedded SWT (eSWT), embedded JFace (eJFace) and
Mobile Extensions.
Communication
Lotus Expeditor Client for Devices supports the Micro Broker and OSGi Event Admin. Lotus Expeditor
Client for Devices continues to support Web Services client applications that conform to the JSR 172
specification.
Overview of the managed client platform
The managed client platform provides the following set of standards-based Client Services for the
development of your managed client applications:
v Managed Client Services including a selection of runtime environments, a robust component
framework, and additional component services, all of which enable Java applications to run on
multiple operating systems and clients.
v Platform Management including the Eclipse Update Manager and an Enterprise management agent to
install and update applications and services on the client platform.
v Access Services including data and synchronization services, transactional messaging, Web Services, a
Web container to run local Web applications, an embedded transaction container to run local embedded
Enterprise Java Beans (EJB’s), a portlet container to run local portlets, and more.
v Interaction Services including integrated browser controls to launch Web applications, Eclipse
technology to support GUI applications, a portlet viewer to launch portlets, and a Workbench that
enables end-users to install and launch one or more applications.
6 Lotus Expeditor: Developing Applications for Lotus Expeditor
The client platform provides a set of standards-based APIs that you use to invoke these services.
Managed Client Applications
ClientServices Interaction Services
Access Services
Platform Management
Managed Client Services
Windows, Linux, Windows Mobile
Managed client services
The client platform provides a choice of runtime environments that enable Java applications to run across
multiple operating systems. For Lotus Expeditor Client for Desktop, the runtime environment is
jclDesktop, which is a custom runtime environment with reduced footprint (i.e. jclDesktop does not
include AWT). For Lotus Expeditor Client for Device, the runtime environment is jclDevice, which is a
custom runtime environment that runs applications written to the JME Foundation Profile 1.1
specification.
The client platform provides a Service Framework that implements the OSGi R4 framework specification
and provides a service-oriented architecture on top of the runtime environments. The OSGi framework
specification is provided by the OSGi Alliance. The OSGi Alliance’s mission is to specify, create, advance,
and promote wide industry adoption of an open service delivery and management platform.
Incorporating the OSGi standard into the client platform provides four very important capabilities:
v It enables multiple applications and components to share a single Virtual Machine (VM) that
implements the Java specifications. This saves valuable resources on the client when running multiple
applications because only one instance of the VM is launched rather than multiple instances of the VM.
v It enables applications to share services and packages, which further reduces resource requirements on
devices.
v It separates service interface from service implementation and provides publish, find, and bind
operations in support of a service-oriented architecture. This capability enables integration of business
applications on the same device.
v It enables dynamic life-cycle management without a VM restart so components can be updated without
impacting other unrelated components that are running at the same time.
The Eclipse framework is built on the Service Framework, which provides Eclipse with powerful
capabilities, such as the ability to dynamically load and unload components without restarting the Eclipse
framework and robust life cycle management of components.
The client platform also provides optional OSGi services, such as UserAdmin, LogService, Configuration
Management, and more.
Product overview 7
Access services
Access Services provide a familiar programming model for JEE developers so they can reuse their skills
and software components to develop applications that run on managed clients. Additionally, Access
Services enable managed client applications to support offline operations. Access Services also enable you
to move key components of your application to the client platform through the use of standard APIs.
For desktops, the client platform provides an embedded Web container to run JEE Web applications that
support the Servlet 2.3/2.4, JSP 1.2/2.0, JSF 1.1, JSTL, and Struts specifications. For devices, the Web
container supports Servlet 2.3 and JSP 1.2 to conserve resources. The Web container enables you to move
your Web applications from the server to clients to preserve the existing browser user interface, leverage
your existing Web components, and provide a richer user experience through support of local and offline
operations.
The client platform also provides an embedded Transaction Container to run JEE Enterprise Java Beans
(EJB’s) that conform to any of the following specifications: 1.1 and 2.0 Stateless Session Beans, Container
Managed Persistence (CMP) Entity Beans, and Bean Managed Persistence (BMP) Entity Beans. This
container enables you to move your business logic from the server to clients so you can leverage your
existing beans to make business logic available to client applications, including Web applications, and
support local and offline operations. These business logic components are referred to as Embedded
Transaction applications.
In addition, the desktop client provides a portlet container to run portlets that support the JSR 168
specification.
There are four key services that support local and offline operations.
First, you can use the JDBC API with DB2® Everyplace or IBM® Cloudscape™ as a local SQL database
when more advanced data manipulations are required than can be supported by placing data in a local
file store. These databases can periodically synchronize with Enterprise databases to capture data on the
client for use by the client application when the user is offline. These databases can also protect local data
through data encryption.
DB2 Everyplace is an extremely small footprint relational database (200-300 KB). It is especially suitable
for embedded devices, where large databases and sophisticated queries are not normally required, but
can also be used on larger devices. DB2 Everyplace provides transaction support covering updates to
multiple tables within a single transaction, encrypted tables, and zero client administration.
IBM Cloudscape is a 100% pure Java relational database, providing SQL-92, partial SQL-99, and SQLJ
support, indexes, triggers, transactions, encryption, and the standard features that one expects of a
relational database. Because IBM Cloudscape contains a larger number of features, it is approximately 2
MB in size. Therefore, IBM Cloudscape might not be suitable for smaller, resource-constrained devices.
Second, you can also use the Java Message Service (JMS) API with WebSphere MQ Everyplace (MQe) to
send and receive messages. MQe provides once-only, assured messaging and supports offline operations
with local message queues that hold messages when the device is offline and then sends these queued
messages to Enterprise applications when the device is back online. Similarly, messages destined for
client applications are held in server-side message queues and then sent to the client applications when
the device is back online. MQe encrypts messages to protect content over the network. As a result, the
client platform enables your users to conduct secure e-business on demand transactions.
Third, you can use the JMS API with the micro broker, which is suitable for applications that require
messaging, notification and event services. The micro broker supports publish and subscribe messaging
in which publishers generate messages containing information about a particular subject, subscribers
express interest in messages containing information on a particular subject, and a broker receives
8 Lotus Expeditor: Developing Applications for Lotus Expeditor
messages from publishers and delivers messages on a particular subject to the subscribers registered for
that subject. You can support offline operations through defined quality-of-service levels and durable
subscriptions.
Fourth, for the desktop client, you can use the Network Layer API to determine the status of the network
and remote resources when running your applications. You can then execute your application logic
accordingly.
For online operations, the client platform supports Web Services so client applications can consume and
provide Web Services in a secure manner. As a result, your users have access to a broad range of business
data and consumer information. The client platform implements Web Services similar to those defined in
JSR 172 and provides support for document literal encoded streams that exchange well-typed data objects
so client applications can consume Web Services. You can also develop an OSGi service and, during
registration of the service, indicate that it is also available as a Web Service. For the desktop client, you
can also use Axis Web Services so client applications can consume Web Services, with full support for
JAX-RPC (JSR 101).
The SyncML4J (SyncML for Java) toolkit enables you to develop data synchronization and device
management client applications based on the Open Mobile Alliance (OMA) Data Synchronization (DS)
and Device Management (DM) standard protocols. As a framework, SyncML4J supports user-defined
data sources. Data sources can range from simple resources, such as memos and images, to complex
schema-aware data types, such as relational databases or PIM databases.
For the desktop client, security services support: a key store, which provides an encrypted local
repository for user security information; accounts, which allows access to user account information (e.g.
user id and password); and single sign-on, which minimizes logon prompts. Additional services include:
database lifecycle management, which provides uniform interoperability with different relational
databases; and synchronization manager, which allows users and applications to initiate, control and
monitor synchronization of local data stores using one or more synchronization services.
Interaction services
This section provides information on interaction services.
Lotus Expeditor Client for Desktop
The Lotus Expeditor Client for Desktop is built on the Eclipse Rich Client Platform (RCP) so you can
deliver applications that provide a rich user experience across multiple platforms. The client platform
provides the Workbench, Standard Widget Toolkit (SWT), JFace, Help and Preferences interaction services.
The Workbench provides an integrated application desktop window so end-users can install, manage and
launch one or more applications within a single window. The Workbench presents each application
individually in its own perspective, only one of which is visible at any given time. When an end-user
selects an application from the Workbench, the Workbench launches the perspective for that application.
You specify an extension point for each of your applications so the Workbench can correctly launch the
perspective for your application.
The desktop client supports Web technology so users can interact with local Web Applications through a
Web browser. Each Web application installed onto the Workbench runs in a browser perspective. When
an end-user selects a Web application from the Workbench, the Workbench launches a browser
perspective which in turn launches a local Web browser to run the Web application within the
Workbench window. When you specify the extension point for Web applications, the Workbench
automatically handles launching your Web applications in the browser perspective. You can also use this
extension point to enable the Workbench to launch a Web application on a remote server.
The desktop client also supports rich client applications, which interact with end-users through a
graphical user interface (GUI). Each rich client application installed onto the Workbench runs in an
application perspective. In this case, each application must contribute its own perspective to the
Product overview 9
Workbench. In each perspective, an application provides the collection of views, layout of views, and
actions appropriate for the tasks that end-users will perform with the application. You use SWT and the
JFace toolkit to develop the GUI for rich client applications. SWT provides a cross-platform API that
tightly integrates with the native widgets of the operating system and, therefore, gives your applications
a look and feel that makes them virtually indistinguishable from native applications. The JFace toolkit
provides a set of components and helper utilities that simplify many of the common tasks in developing
SWT user interfaces. When an end-user selects a rich client application from the Workbench, the
Workbench launches the appropriate perspective to run the application within the Workbench window.
When you specify the extension point for rich client applications, the Workbench automatically handles
launching the perspectives for your rich client applications.
The desktop client also provides services that enable you to contribute Helps and Preferences for your
applications so end-users can understand and configure your applications respectively within the
Workbench.
A personality extends the concept of a WorkbenchAdvisor to allow for different WorkbenchWindow
configurations to run within one running VM. The desktop client provides a personality. However, you
can also provide your own personalities.
The user interface widgets provide a common look and feel for all user interfaces that run on the desktop
client platform. These widgets provide the following features:
v Renders the toolbar skin, which means it formats the toolbar to make its style consistent with the style
defined for the application it displays in
v Manages drag and drop of items within the toolbar
v Saves and loads the last state of toolbar items using the Eclipse memento pattern, which uses the
saveState and restoreState methods to remember the position of each toolbar item when the user
shuts down the client so that the same toolbar items display when the user restarts it
The Embedded Browser extends an application’s functions, such as browser launch, handles events, and
preference user interface, etc. The Embedded Browser is based on SWT browser widget in Eclipse, which
can support Internet Explorer on Windows and Mozilla on Linux.
Lotus Expeditor Client for Devices
The Lotus Expeditor Client for Devices is built on the Eclipse embedded Rich Client Platform (eRCP) so
you can deliver applications that provide a rich user experience across multiple handheld devices. The
device client provides the embedded Workbench (eWorkbench), embedded Standard Widget Toolkit
(eSWT), embedded JFace (eJFace) interaction services. These services support a subset of the Eclipse RCP
APIs.
The device client provides a customized eRCP eWorkbench. When running in the development
environment, this workbench displays just like the normal eRCP eWorkbench. However, when running
on a device, the customized eWorkbench is invisible to the user. It runs in the background and does not
provide user interface. Instead, it allows eRCP applications to seamlessly integrate with the native GUI
and shows running eRCP applications in the Windows task list. The user clicks the link for an
application, and it displays like a normal application on the device.
The device client also supports Web technology so users can interact with local Web applications through
a Web browser.
Platform management
Platform Management installs, maintains, and configures applications and services on the client. There
are two platform management services.
The Update Manager enables end-users to directly install applications and components from standard
Eclipse update sites onto managed clients.
10 Lotus Expeditor: Developing Applications for Lotus Expeditor
The Enterprise Management Agent works cooperatively with the Device Management Server provided by
the Lotus Expeditor Server to perform management operations. The agent and server use the
SyncML/DM protocol defined by the Open Mobile Alliance to communicate management requests. An
administrator can schedule management jobs for devices that include software installation, update, and
configuration. When installing and updating software components, the management system determines
which components are already on the device and then installs only the missing components.
Lotus Expeditor Toolkit
The Lotus Expeditor Toolkit provides a complete, integrated set of tools that allows you to develop,
debug, test, package and deploy client applications that use Client Services. You can use the toolkit to
develop the following types of client applications:
v Eclipse Rich Client Platform applications (desktop client only)
v Eclipse embedded Rich Client Platform applications
v Web applications
v Embedded Transaction applications
v Portlet applications (desktop client only)
v Database applications
v Messaging applications
v Web Services applications
The toolkit provides wizards that enable you to create Client Services projects to develop client
applications. The toolkit uses Target Definitions to provide a convenient method for you to specify the
runtime environment, the build-time environment, and the set of components that can run on the
platform. For example, when you create a Client Services project, you select a Target Definition and set of
Features from a list of available targets and the toolkit automatically sets up the Java Build Path and
runtime for your project. You can then edit, compile, and debug your project. The toolkit provides a
default list of Target Definitions; however, you can create your own definitions.
You can also use the toolkit to build custom client platforms for your devices. However, custom
platforms require an OEM license from IBM.
The toolkit is built on Eclipse 3.2.1 and extends the familiar application development tools so you can
leverage your existing skills and software components.
Getting started with the Lotus Expeditor Toolkit
The information in this section guides you through the process of setting up the Lotus Expeditor Toolkit
and creating a sample plug-in with Lotus Expeditor Toolkit.
Lotus Expeditor Toolkit overview
The Lotus Expeditor Toolkit provides the tools necessary to create and test OSGi plug-ins, Web
Applications, Embedded Transaction Applications, Web Services client and providers, and Portlet
applications for use on the Lotus Expeditor platform. Lotus Expeditor is built on Eclipse 3.2.1, which is
built on top of OSGi. For this release, Eclipse 3.2.1 plug-ins run as OSGi plug-ins. The Plug-in Developer
Environment (PDE) provided with Eclipse 3.2.1 provides many features that are useful in the
development of OSGi plug-ins. The Lotus Expeditor Toolkit is built upon the solid base provided by the
Eclipse PDE.
Lotus Expeditor Toolkit allows developers to focus on the creation of plug-ins, without requiring them to
become OSGi plug-in internals experts. In its simplest form, the toolkit is designed for the developer who
wants to develop and store ten or twenty plug-ins with automated assistance purely within the Eclipse
environment.
Product overview 11
Using the Lotus Expeditor Toolkit, developers build applications and services as ″plug-ins″ that run on
Lotus Expeditor runtime. A plug-in may be packaged as a JAR file with information in the manifest file
that relates information to Lotus Expeditor Toolkit about the plug-in, such as the services and packages
the plug-in imports and/or exports. A plug-in may also be packaged in a plug-in structure. For more
information, see “Packaging and deploying applications” on page 327.
Supported platforms and prerequisite software
The Lotus Expeditor Toolkit runs on the following platforms and supports application development for
the Lotus Expeditor runtime on these same platforms:
v Windows XP
v RedHat EL 4.0 WS with GTK support Update 3
The toolkit supports application development for the Lotus Expeditor runtime on these same platforms
plus Windows Mobile 2003 SE and Windows Mobile 5 PPC and Phone Editions.
In addition, Lotus Expeditor Toolkit can be installed into any of the following development tools:
v Rational Application Developer (RAD) 7.0
v Rational Software Architect (RSA) 7.0
v Eclipse 3.2.1 + Web Tools Project (WTP) 1.5.1
v WebSphere Application Server Toolkit (AST) 6.1.1
Understanding the development platforms
Developing Applications for Lotus Expeditor makes several references to the Rational Software
Development Platform. The Rational Software Development Platform is a powerful set of integrated
development tools that supports open standards. The Platform is based on the Eclipse open source
platform and runs across multiple platforms including Linux. The tools Rational Application Developer
(RAD) and Rational Software Architect (RSA) are used as base tools for the Lotus Expeditor Toolkit. You
can install the Lotus Expeditor Toolkit on top of RAD or RSA. When this documentation mentions the
Rational Software Development Platform, it means specifically the RAD and RSA tools.
For a list of supported development tools, refer to “Supported platforms and prerequisite software.”
Setting up the Lotus Expeditor Toolkit
This section provides Desktop and Device installation instructions for the Lotus Expeditor Toolkit.
Setup for Desktop development: Before you use the Lotus Expeditor Toolkit, verify that you have done
the following:
Note: Upon the first startup of a workspace, you will see a dialog that prompts you to automatically set
the preferences for the Lotus Expeditor Toolkit. Selecting yes in this dialog, causes steps 1 and 2
below to be done automatically. You may also skip them when performing a manual set up.
1. Set the plug-in development target platform location to the Lotus Expeditor development runtime.
a. Select Window > Preferences > Plug-in Development > Target Platform.
b. Set the Location field for the target platform to <toolkit_install_location>/eclipse/plugins/com.ibm.pvc.wct.runtimes_ 6.1.0.0-<date>/eclipse.
c. Select OK.2. Set the default workbench JRE to be the Lotus Expeditor JRE. Lotus Expeditor runtime ships with
jclDesktop.
a. Select Window > Preferences > Java > Installed JREs.
b. Click the check box next to jclDesktop to select it as the default workbench JRE.
c. Click OK.
12 Lotus Expeditor: Developing Applications for Lotus Expeditor
Note: The PDE Target Platform page provides a method of selecting ″Pre-defined Targets″ and applying
those targets to the environment. In general, this option should not be used when building
applications with the Lotus Expeditor Toolkit. This may reset the target platform to point to the
IDE environment in which you are developing.
Setup for Device development: When installing the Lotus Expeditor Toolkit, install both the desktop
and device features. Upon the first startup of a workspace, you will see a dialog that prompts you to
automatically set the preferences for the Lotus Expeditor Toolkit. Select yes in this dialog. Next, prepare
the IDE to build applications for devices:
1. Select Window > Preferences > Client Services.
2. Change Default Target Selection to Default Device Target.
3. Expand Java and click Installed JREs.
4. Check jclDevice Win32 x86.
5. Expand Plug-in Development and click Target Platform.
6. Select Default Device Target under Pre-defined Targets and click Load Target. Alternately, Browse to
eclipse_home\plugins\com.ibm\pvc.wct.device.runtimes_6.1.0.0-date\rcp\eclipse and click
Reload.
7. Click OK to exit.
Creating a sample Client Services project
Refer to the following instructions to create a sample Client Services project that includes the Client
Services Pizza JSP Web Application Sample project:
1. Select Window > Open Perspective > Other > Plug-in Development from the menu bar.
2. Select Help > Samples Gallery.
3. Select Application Samples >Lotus Expeditor samples > Pizza JSP Web Application.
4. Select Import.
Running your project on development runtimes: This section contains information on running projects
on both desktop and device runtimes.
Running on the Desktop runtime: To run your project on development runtimes, perform the following
procedure:
1. Select Finish on the Pizza JSP Web Application Sample project naming dialog.
The Pizza JSP Web Application Sample project appears in the Package Explorer view.
2. Launch the Lotus Expeditor runtime.
a. Select either Run > Run... or Debug > Debug...
b. In the Launch Configuration Dialog that comes up, select Client Services. Click New Launch
Configuration.
c. Select the Target tab and ensure that Default Target is selected.
d. Click Run.
Note you did not have to explicitly install the Pizza JSP Web Application into Lotus Expeditor
runtime. This is because, as part of standard Eclipse behavior, the Lotus Expeditor runtime launches
all plug-ins in the user’s workspace (plus any enabled external plug-ins found in the Target Platform
folder).This behavior is controlled by the Plug-ins tab of your Lotus Expeditor.
3. Now, with the Pizza JSP Web Application already running on the Lotus Expeditor runtime, verify its
behavior by launching the application in the runtime’s browser. Click Open to display Pizza JSP Web
Application as a selectable application to launch from the Lotus Expeditor runtime.
Running on the Device runtime: You can run your application on the Lotus Expeditor for Devices runtime
by launching the workbench. The workbench will detect your application and list it as an application you
can start. To run the workbench:
Product overview 13
1. Select Run > Run...
2. Double-click Client Services.
3. Change the new configuration’s name.
4. Switch to the Profile tab, and select Default Device Target.
5. For Runtime JRE, select jclDevice Win32 x86.
6. Switch to the Plug-ins tab and click Select All.
7. Switch to the Configuration tab and select Use an existing config.ini file as a template, then click
Variables.... Select rcp_devicebase and then click OK. ${rcp_devicebase}/config.ini should appear
in the path field.
8. Click Run.
Setting Toolkit preferences
After the Lotus Expeditor Toolkit installation, users may change the Client Services default values by
selecting Window > Preferences... > ClientServices. The following Client Services preferences can be
modified:
Auto-Management Preference
v Search for dependencies automatically upon resource changes
Select this option to enable the tools to search for package dependencies whenever the user modifies
source files. When this option is deselected, the tooling will not search for any unresolved or unused
dependencies in your project.
v Attempt to automatically resolve Manifest dependencies
Select this option to enable the tools to automatically manage the package dependency information in
the manifest file. Package dependencies in your project’s Java code will automatically be reflected
through proper updates to the manifest file. When this option is deselected, package dependencies that
are not properly reflected in the manifest are flagged with problem markers, along with quick fixes to
resolve the problems.
v Give preference to Required-Bundle
Require-Bundle will be used to automatically resolve a package dependency in cases where either
Require-Bundle or Import-Package can be used.
v Give preference to Import-Package
Import-Package will be used to automatically resolve a package dependency in cases where either
Require-Bundle or Import-Package can be used.
Default Target Selection
Use the drop-down list to choose the default Target Definition. This means that when creating a new
Client Services project or a new Client Services launch configuration, the default Target Definition
selection will be obtained from this setting.
Show plug-in objects in editors and dialogs using...
v Identifiers
This selection displays the plug-ins and features objects using their given ID attribute. A plug-in or
feature will always have an ID value associated with them. This is not the case with the NAME.
v Presentation names
This selection displays the plug-ins and features objects using their given NAME attribute.
Turning on build automatically
For optimum launch performance, the ’build automatically’ preference should be enabled. To enable
’build automatically’, select Project from the main menu bar. If Build Automatically is checked, the
preference is already enabled. If it is not checked, select the option to enable it.
14 Lotus Expeditor: Developing Applications for Lotus Expeditor
Concepts
The Lotus Expeditor Toolkit extends the Eclipse and Rational integrated development environment to
support the development, testing, and deployment of Eclipse plug-ins and OSGi plug-ins. The Lotus
Expeditor Toolkit adds wizards and editors that collectively provide a developer with the tools needed to
create, build, test, and package applications for the Lotus Expeditor runtime.
Client Services Project: A Client Services project contains a set of plug-ins and an associated Target
Definition. All or some of the Target Definition’s features may be selected. In addition, Client Services
projects can:
v Automatically update the Java Build Path.
Lotus Expeditor Toolkit can automatically update the project’s Java Build Path to reflect the project’s
Target Definition and Features settings. Refer to “Target Definition” and “Target features” on page 17
for more information.
v Provide a default plug-in activator.
The toolkit can create a default bundle activator class. You can tailor the default plug-in activator class
by editing the source file for the class folder.
v Automatically update the Manifest file.
The toolkit can automatically update the Manifest file in the Client Services project to contain
appropriate OSGi metadata for the project. Client Services manages or provides initial default values
for the metadata fields by:
– Setting Bundle-Name to the project name on project creation
– Setting Bundle-Version to 1.0.0 on project creation
– Setting Bundle-Activator to the default bundle activator if one was created
– Updating Import-Packages and Require-Bundle to reflect the packages imported by the project’s
classes. Refer to “Automatic management of manifest package dependencies” on page 17 for more
information
Managing Client Services project dependencies
The following describes how to best use the tooling provided by the Lotus Expeditor Toolkit to manage
the dependencies in a Client Services project. These dependencies include the Java Build Path and the
manifest file. When developing Eclipse plug-ins or OSGi plug-ins, the Java Build Path, the packages used
by the plug-in’s code, and the manifest are all related.
v The Java Build Path must contain the necessary libraries and plug-ins that contribute the packages and
classes used by the project’s plug-in code during the compilation process. If this is not the case, the
tools will tag the code with problem markers indicating that a referenced package or class cannot be
found.
v The manifest must contain references to the packages and plug-ins that the plug-in code is using. This
is how the OSGi framework manages the class path of the plug-in at runtime. A reference to a
particular plug-in implementation is done through a Require-Bundle manifest entry. A reference to a
required package is done through an Import-Package manifest entry. Failure to properly resolve these
dependencies in the manifest can cause the plug-in to fail at runtime with a “class not found” error
(NoClassDefFoundError).
The following mechanisms are available to help manage both the Java Build Path and the manifest file:
Target Definition: A target definition specifies all aspects of a target - including its location, constituent
plug-ins and environment. Please refer to the Eclipse Documentation for more information on Target
Definitions. The Client Services project wizard provides a method for defining the Target’s features and
each feature’s plug-ins to be used at runtime. Note that each Target’s feature is composed with a set of
one or more plug-ins.
Product overview 15
A Target Definition defines a set of Target features. Target features enable you to focus on the logical
service requirements of the service instead of the requirements of the actual underlying Client Services
plug-ins.
When you create a Client Services project, you select a Target Definition and a set of Target features for
the project. The Lotus Expeditor Toolkit updates the Java Build Path for the project to reflect the Java
Runtime Environment (JRE) of the platform, based on the Target Definition and Target features you
selected. The Target Features you selected are automatically added to the Java Build Path of the project.
The Lotus Expeditor Toolkit provides Target Definitions that represent the bundles available in the Lotus
Expeditor runtime.
Creating a Target Definition: The Lotus Expeditor Toolkit has extended the functionality of a regular PDE
target definition. To support this new functionality, Lotus Expeditor Toolkit has additional elements and
attributes not found in a traditional PDE target definition. The PDE contains a graphical target definition
editor. Since the editor is not aware of these additional elements and attributes, it will remove them when
you use it to modify the target definition. Therefore, you should use the graphical editor to create the
initial content of the target definition, and then add additional content with the text editor.
The additional attributes and elements created by the Lotus Expeditor Toolkit are used to modify the
behavior of the Client Services Launcher. A target can contain a list of feature and plug-in elements.
These elements are displayed on the Profile tab of the Client Services Launcher. By default, all listed
elements are required and cannot be deselected. If a feature or plug-in is not required for launching but
can add optional functionality, add the attribute optional="true". For example:
<feature id="com.ibm.db2e.feature" optional="true"/>
A target definition may also specify the config.ini location to be used during launch. You can do this by
adding a config element with a location attribute. The location can be absolute or contain variables. The
value of the location will set the location of the config.ini template to be used for launching. A default
personality can also be specified with a personality element with an id attribute. The value of the id
attribute should be the personality ID to be specified during launch. For example:
<config location="${rcp_base}/config.ini"/>
Additional launcher settings include specifying environment variables. To do this, create an envVariables
element. If you prefer the variables values to replace the existing environment variables, add the attribute
replace=“true”. Under the envVariables element, create a separate child element called variable. Each
variable element has a value attribute and optionally, an os attribute if it is specific to a particular
operating system. For example:
<envVariables>
<variable name="PATH" value= "${isynch_win32}/os/win32/x86;${db2e_win32}/os/win32/x86"
os="win32"/>
<variable name="LD_LIBRARY_PATH" value=
"${isynch_linux}/os/linux/x86:${db2e_linux}/os/linux/x86"
os="linux"/>
</envVariables>
Lastly, a target definition can define the default product and application for launching. This is specified in
a program element. To specify a product, create a prodId attribute and set the value to the product ID. To
specify an application, create an appId attribute and set the value to the application ID. If both are
specified, a useProd attribute should also be included. The value should be set to true if the product
takes precedence, otherwise it should be set to false. For example:
<program prodId="com.ibm.rcp.platform.personality.branding.DefaultProduct"
appId="com.ibm.rcp.personality.framework.RCPApplication" useProd="true"/>
You can replace the default Lotus Expeditor branding with a custom branding, by replacing the Default
Branding feature (com.ibm.rcp. personality default..branding.feature) with your own feature.
16 Lotus Expeditor: Developing Applications for Lotus Expeditor
Branding is the look and feel of the product - it includes splash screens and window decorations. For
details on creating your own branding feature, refer to the documentation Assembling and Deploying
Lotus Expeditor Applications.
To launch with your custom branding, you must create a custom target definition:
1. Create a general project (File > New > Project > General Project).
2. Create a target definition within your general project (File > New > Target Definition).
a. Select the general project as the parent folder.
b. Specify a name for the target.
c. Use an existing target definition, and select Default.
d. Select Finish. The target definition editor appears with your new target definition.3. Customize your new target definition:
a. On the Overview page, select the Features tab
b. Search for com.ibm.rcp.personality.personality.default.branding.feature and select Remove.
c. Select Add and select your custom branding feature.
d. Save your target definition.4. To launch your new target definition, select Run > Run... The run wizard appears.
a. Double click Client Services.
b. On the Main tab, locate the Program to Run box. Select your custom product from the drop down
list.
c. On the Profile tab, Browse to find and select your custom target definition.
d. Review the features that are selected. You can only deselect features that are not required.
e. Select Run.
Lotus Expeditor launches with your custom branding.
Target features: A Target feature represents a logical service, such as an XML parser or logging service
that consists of one or more plug-ins. Target features enable you to focus on the logical requirements of
the service instead of the requirements of the actual underlying plug-ins.
Secondary Dependencies: Secondary Dependencies allow users to include plug-ins in the build path
without adding references to the Manifest. By using this mechanism to include these plug-ins in the build
path, users can compile and develop their code with the convenient features of Eclipse JDT. Once the
user references one of the packages in a plug-in in the Secondary Dependency list, Lotus Expeditor
Toolkit can display a warning to inform the user the project is compiling against classes which are not
available during runtime (For information on how to change the severity of this notification, see “Setting
Toolkit preferences” on page 14). The Lotus Expeditor Toolkit also provides “quick fixes” to resolve this
problem by adding corresponding references to the Manifest. The user may also configure the Lotus
Expeditor Toolkit to automatically add dependencies to the Manifest, whenever they are found. Please see
“Automatic management of manifest package dependencies.”
The Target features and plug-ins selected from a project’s Target Definition are added to the Secondary
Dependencies for dependency management.
Note: For the Lotus Expeditor Toolkit to find possible dependencies in your project’s code, you must use
the Automated Management of Dependencies table on the Dependencies page of the Plug-in
Manifest Editor or select features and/or plug-ins from the project’s Target definition.
Automatic management of manifest package dependencies: Manifest package dependencies are
automatically managed for Client Services projects by default. This capability can be disabled through the
new project wizard when creating a Client Services project and through the Client Services properties tab
of an existing project. When enabled, this option automatically updates the Require-Bundle or
Product overview 17
Import-Package entries in the project’s manifest file based on changes in the project’s Java code. The
header to which the dependencies are added is specified by a per project preference. You can change this
preference on the Dependencies Page of the Plug-in Manifest Editor, the project’s Client Services property
page under the options tab, or the first page of the Client Services Project creation wizard.
When new package dependencies are added to the project’s Java code, the manifest file is automatically
updated with the proper entries. It is important to note that this mechanism requires the developer to use
the Target Definition or secondary dependencies to represent the project’s plug-in dependencies. Plug-ins
that are placed on the Java Build Path through the project’s Java properties page will not be handled by
this mechanism.
If this option is disabled, the tools will flag the manifest file with error markers if packages are used in
the Java code without being referenced in the manifest file. These errors can then be selectively fixed
through the quick fix mechanism.
Lotus Expeditor Toolkit will search for manifest package dependencies by default. This capability can be
disabled through the new project wizard when creating a Client Services project and through the Client
Services properties tab of an existing project. If this option is disabled, the user can still manually search
the project for manifest dependencies by using the Add dependencies link in the Automated
Management of Dependencies panel on the Dependencies page of the Plug-in Manifest Editor.
Configuring the Client Services launcher
To configure the Client Services launcher, perform the following procedure:
1. 1. From the Java perspective, select Run > Run... to invoke the Launch Configuration Dialog wizard
2. Create a new Client Services launch configuration.
At this point, you can run this configuration with default values or proceed to configure it farther.
3. In the Main tab of this new Client Services launch configuration, select the Workspace Data,
Personality, Program to Run, Java Runtime Environment and National Language Support groups for
this launch.
4. The Argument tab will contain default values for the Program and VM argument data of this Target
Definition.
5. In the Profile tab, select any necessary pre-defined Target Definitions for this launch.
6. In the Plug-ins tab, individual plug-ins may be selected in addition to the list already defined in the
Profile tab.
7. The Configuration tab contains default configuration data for the location and format of this
configuration file.
8. The Tracing tab displays a list of plug-ins that support tracing.
9. Use the Environment tab to define environment variables for the configuration.
10. Use the Source tab to add any additional sources or archives to the configuration.
11. The Common tab contains default configuration data for the OSGi console.
12. Click Run to launch the new Client Services launch configuration.
Configuring the Lotus Expeditor Toolkit environment for Sametime
These instructions assume sametime is installed c:\sametime.
1. Set the target platform:
a. From within the Eclipse IDE, select Window > Preferences.
b. Expand Plug-in Development.
c. Select Target Platform.
d. In the Location edit control, type C:\sametime.
e. Click the Reload button.
f. Click OK.
18 Lotus Expeditor: Developing Applications for Lotus Expeditor
2. Set the JRE:
a. From within the Eclipse IDE, select Window > Preferences.
b. Expand Java.
c. Select Installed JREs.
d. Check the box next to jclDesktop.
e. Click OK.3. Set the Compliance Compiler level:
a. From within the Eclipse IDE, select Window > Preferences.
b. Expand Java.
c. Select Compiler.
d. Set the Compiler Compliance Level to 1.4.
e. Click OK.4. Set the default platform:
a. From within the Eclipse IDE, select Window > Preferences.
b. Select Client Services.
c. Change the Default Target Selection to Sametime Target.
d. Click OK.5. Create a new runtime configuration:
a. From within the Eclipse IDE, select Run > Run...
b. Select Client Services.
c. Click New.
d. Enter a name (for example, sametime).
e. Select Clear workspace data before launching.
f. Click Run.
The Sametime® target sets all VM arguments. The tabs on the client services launcher function similar to
the Eclipse PDE launcher, with a few additions. If you want to add or remove plug-ins from the launch
configuration at a feature level, change to the profile tab and select the features to add and remove.
Creating and using Client Services applications
This section describes the tasks for creating and using Client Services projects.
Creating a Client Services project
Complete the following steps to create a new Client Services project:
1. Select File >New >Project.
The Select a wizard page displays.
2. Select Client Services > Client Services Project.
3. Click Next.
The Client Services Plug-in Project panel displays.
4. Specify a project name in the Project name field.
5. Click Next.
The Client Services Content page displays.
6. Modify the Plug-in Properties as necessary .
7. Modify the Plug-in Options as necessary.
8. Modify the Auto-Management Preference as necessary. Click Next.
9. The Target Profile page displays. The selected Target Definition is the target specified in the
Preference page. You may select another Target via the Combo list.
Product overview 19
A description of the Target Definition you selected displays in the Description field.
10. Select the features and plug-ins to be included for this project by checking the check boxes beside
the features names or IDs. You may select Finish here, or click Next to view the Template page.
11. Create a Client Services project using one of the three templates – Preference page Basic Application,
Rich Basic Application or Text-Editor Sample Application:
a. Perform steps 1 through.10 above, but do not select Finish. Rather, select Next to view the
Template’s page
b. Select one of three templates and click Finish or select Next to change the default Application
name, Package name, and so on for this project.
Note: If the Target Definition that you selected in step 9 on page 19 requires specific services, the
services are automatically selected in the Target features field. You can select any additional
services that the application uses.
12. Click Next. The Templates panel is displayed. You may select a Template for this application.
Note: In some cases, a Client Services Project cannot automatically determine the correct Manifest
dependency settings. For example, if your code uses the java.lang.Class.forName(...) API,
the tool cannot correctly calculate the required Manifest dependency settings at compile time.
In this case you should disable the Attempt to automatically resolve Manifest dependencies
option and configure the dependency section of the Manifest manually by using the Manifest
Editor.
13. Click Finish.
Lotus Expeditor Toolkit creates a Client Services project.
Creating a Client Services fragment project
1. Select File > New > Project.
The Select a wizard page displays.
2. Expad Client Services and select Client Services Fragment Project.
3. Click Next.
The Client Services Fragment Project page displays.
4. Specify a project name in the Project name field. Click Next.
5. The Fragment Content page displays. Modify the Fragment Properties as necessary.
6. Modify the Host Plug-in.
Note: A host plug-in ID must be selected.
Modify the Auto-Management Preference as necessary.
7. Click Next.
Note: In some cases, a Client Services project cannot automatically determine the correct Manifest
dependency settings. For example, if your code uses the java.lang.Class.forName(...) API,
the tool cannot correctly calculate the required Manifest dependency settings at compile time.
In this case, you should disable the Attempt to automatically resolve Manifest dependencies
option and configure the dependency section of the Manifest manually by using the Manifest
Editor.
8. The Target Profile page displays. The selected Target Definition is the target specified in the
Preference page. You may select another Target via the Combo list.
9. Select the features and plug-ins to be included for this project by checking the check boxes besides
the features names or IDs.
20 Lotus Expeditor: Developing Applications for Lotus Expeditor
Note: If the Target Definition that you selected above requires specific services, the services are
automatically selected in the Target Features field. You can select any additional services that
the application uses.
10. Click Finish.
The Lotus Expeditor Toolkit creates a Client Services Fragment project.
Converting a Java project into a Client Services project
Complete the following steps to convert a Java project into a Client Services project:
1. Select File->New->Other....
The Select a wizard page displays.
2. Expand Client Services and select Convert Project to Client Services Project.
3. Select Next.
The Convert Existing Project page displays.
4. Select the project that you want to convert from the list of Available projects.
Note: You may check the Copy project before conversion checkbox to make a copy of the selected
project and convert the copy.
5. Click Next.
The Target Profile page displays. The selected Target Definition is the target specified in the Preference
page. You may select another Target Definition via the Combo list.
6. Select the features and plug-ins to be included for this project by checking the check boxes besides the
Features names or IDs.
7. Click Finish.
The Lotus Expeditor Toolkit converts the Java project into a Client Services project.
Note: Any project of a Java, WEB, Plug-in, or EJB nature can be converted to a Client Services project.
For more information about natures, please refer to the Eclipse documentation.
Updating the buildpath and classpath for converted Client Services projects
To update the buildpath and classpath for projects that have been converted into Client Services projects,
perform the following procedure:
1. Import the external jar into the Client Services Project, using File > Import > File System wizard.
2. Add the external jar to the Bundle-ClassPath of the manifest.mf file.
3. Open the manifest.mf file with the Manifest Editor.
4. Click on the Runtime tab, and add the external jar file to the Classpath section.
5. Add the Export-Package Statement to the manifest.mf file. To add the Export-Package statement, from
the Bundle Manifest Editor select the Runtime tab, and add the packages in the Exported Packages
section for any other projects that must reference this external jar.
6. Add a Require-Bundle/Import-Package to Manifest to reference the dependent project. To add the
Require-Bundle statement, from the Manifest Editor select the Dependencies tab, and add the
referenced bundle to the Required Plug-ins section. To add the Import-Package statement, from the
Manifest Editor open the Dependencies tab, and add the packages to the Import Packages section.
Setting Client Services project properties
Client Services projects have some project-specific properties. To modify the project properties, complete
the following steps:
1. Right-click on your Client Services project and select Properties.
2. Select Client Services.
The Target Profile tab allows you to modify the previously selected Target Definition, Target features,
plug-ins and Feature plug-ins.
Product overview 21
The Options tab allows you to modify the Auto-Management preferences - Require-Bundle or
Import-package.
See the table, Client Services Project Options in “New Client Services Project Wizard” on page 372 for
additional information.
3. Make the changes you desire and click OK to commit your changes.
Launching Client Services launch configurations from the manifest editor
To launch a Client Services launch configuration from the Client Services project’s manifest editor,
perform the following procedure:
1. Navigate to the Overview tab of the Client Services project’s manifest editor, and select Launch Client
Services application.
2. A list of Client Services launch configurations displays if more than one Client Services launch
configurations are currently defined.
If there is only one launch configuration, it will be the one launched. If there is no configuration, one
is created and launched for the project.
3. From the list of Client Services launch configurations, select a configuration.
4. Click OK. The selected configuration is launched.
Using the samples
To get started using the client platform or specific features of the client platform, review the collection of
client platform samples. The pre-built samples are provided by the Lotus Expeditor Toolkit.
Installing samples using the Rational Software Development Platform
The Samples Gallery is a facility that is available in the Rational product set. It acts as a centralized
location or repository for samples. The gallery is accessible from both the Welcome page and from the
Help menu. The samples in the gallery are split into three categories:
Showcase samples
Demonstrate end-to-end applications that follow best practices for application development.
Application samples
Demonstrate more than one tool or API.
Technology samples
Demonstrate a single tool or API.
When you open a sample, you see a short description of the sample, setup instructions, and a link to
import the sample into your tooling workspace. Samples are located in the Lotus Expeditor section of
each category. Additional samples are also available on the IBM developerWorks® Web site
at:http://www.ibm.com/developerworks
To import one or more samples from the Samples Gallery, follow these steps:
1. Select Help > Samples Gallery.
2. Select a category (Showcase, Application, or Technology samples).
3. Select the [+] next to the Lotus Expeditor samples. This displays a list of the samples, and some
additional categories, that are available.
4. Select one of the samples. The overview page is displayed, providing a short description of the
sample.
5. Do one of the following:
v If you are new to the sample, select the Setup instructions link. This page provides detailed
instructions on prerequisites and information on how to run the sample. You can also import the
sample from the Setup instructions page.
22 Lotus Expeditor: Developing Applications for Lotus Expeditor
v If you are familiar with the sample and it’s prerequisites, select the Import the sample link. This
displays a dialog to prompt you to install the sample.
Installing samples using Eclipse (without RSDP)
The sample projects are provided by the Lotus Expeditor Toolkit. The samples are provided as archive
files containing pre-built projects. Prior to beginning the steps to import the projects, you must know the
install location of the com.ibm.rcp.tools.samplegallery plug-in provided by the Lotus Expeditor Toolkit.
If you are unsure of the location, go to Help > Software Updates > Manage Configuration. The dialog
shows the Eclipse SDK, followed by a set of directories. Locate the Lotus Expeditor Toolkit, and note the
directory. The com.ibm.rcp.tools.samplegallery plug-in resides in the directory + /plugins.
Important: Samples that contain portlets, or that use the embedded transaction container, require the use
of a Rational Software Development Platform (RSDP) with the Lotus Expeditor Toolkit
installed. These projects might import successfully on an Eclipse with the WTP environment,
but full function, including Run on Server launch capabilities, are not available.
To import one or more samples, follow these steps:
1. Select File > Import ...
2. On the Import Wizard, select General, then Existing Projects into Workspace, then Next.
3. Select the radio button for Archive file.
4. Enter the name of a specific jar, or browse to the com.ibm.rcp.tools.samplegallery plug-in directory,
then the archive directory, and select one of the archive files.
Table 1. Sample file names
File name Sample
cloudscape.jar “Cloudscape sample” on page 24
db2e.jar “DB2 Everyplace sample” on page 25
echo.jar “Echo sample” on page 27
echosecure.jar “Echo Secure sample” on page 30
eclipseprefs.jar “Eclipse Preferences sample” on page 31
ejbtestdriver.jar “Embedded Transaction Container sample” on page 32
isync.jar “ISync sample” on page 34
jmsmb.jar “JMS with Lotus Expeditor micro broker provider
sample” on page 39
jms.jar “JMS with MQe provider sample” on page 42
jndi.jar “JNDI sample” on page 44
log.jar “Log and Log Reader sample” on page 46
mqe.jar “MQ Everyplace sample” on page 48
orderentry.jar “Order Entry sample” on page 50
osgiprefs.jar “OSGi Preferences Service sample” on page 53
pizzajsp.jar “Pizza JSP sample” on page 54
portletcomm.jar “Portlet Communication sample” on page 55
richapp.jar “Rich Application sample” on page 57
rte.jar “Rich Text Editor sample” on page 58
secwebapp.jar “Secured Web application sample” on page 59
servicetracker.jar “Service Tracker sample” on page 60
calcportlet.jar “Simple Portlet sample” on page 63
Product overview 23
Table 1. Sample file names (continued)
File name Sample
portletviewer.jar “Simple Portlet Viewer sample” on page 64
simpleaggr.jar “Web Page Aggregation of Portlets sample” on page 65
webapplication.jar “Web Application sample” on page 66
weblog.jar “Web Application Log sample” on page 67
xmlparser.jar “XML Parser sample” on page 68
5. The dialog shows the projects present in the archive jar. Select Finish to install the projects into your
workspace.
Specific instructions for each of the samples is provided in the following sections.
Cloudscape sample
This sample illustrates the use of the local Cloudscape database.
You can use this sample only on the desktop runtime.
Time requested: 2 minutes
This sample consists of a single Client Services project, com.ibm.rcp.samples.cloudscape. Key concepts
demonstrated in this sample are as follows:
v Creation of a Cloudscape DataSource object
v Creation of a database within the current workspace
v Creation of a database using SQL specified in the Java code
v Use of PreparedStatement objects to execute SQL statements
For additional information about this sample, refer to the Cloudscape.java class in the
com.ibm.rcp.samples.cloudscape package.
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, cloudscape.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To configure and run the sample:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as Cloudscape.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.cloudscape plug-in is selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.
24 Lotus Expeditor: Developing Applications for Lotus Expeditor
3. To start the sample, enter start com.ibm.rcp.samples.cloudscape in the Console view.
osgi> start com.ibm.rcp.samples.cloudscape
osgi> ***Created table: dish
***Inserted three records into table: dish
***Selected records from table: dish
Dish NO.=1111, Dish Name=Noodle, Dish Price=12.25
Dish NO.=2222, Dish Name=Fish, Dish Price=32.24
Dish NO.=3333, Dish Name=Pizza, Dish Price=25.32
***Deleted table: dish
Troubleshooting:
Problem:
When attempting to run the start com.ibm.rcp.samples.cloudscape command in the console, the
following message is displayed:
Cannot find bundle com.ibm.rcp.samples.cloudscape
Cause:
The com.ibm.rcp.samples.cloudscape bundle is not available in your runtime.
Action:
Edit your launch configuration and verify that the com.ibm.rcp.tools.samples.cloudscape bundle is
selected.
Problem:
When attempting to run the start com.ibm.rcp.samples.cloudscape command in the console, the
following message is displayed:
org.osgi.framework.BundleException: The bundle could not be resolved.
Reason: Missing Constraint: Require-Bundle: org.apache.derby.core; bundle-version="0.0.0"
Cause:
The org.apache.derby.core bundle is not available in the runtime.
Action:
Edit your launch configuration and verify that the org.apache.derby.core bundle is selected. Related samples:
See DB2e sample on using an alternative databaseThis sample illustrates a use of the local DB2 Everyplace database.
See ISync sample to synchronize content from a relational database on the server to a local
Cloudscape or DB2e databaseThis sample demonstrates how to use the ISync APIs to synchronize relational data from a DB2
Everyplace Sync Server to a local DB2 Everyplace or Cloudscape database.
See JNDI sample to declaratively define (bind) a DataSource objectThis sample demonstrates the use of JNDI APIs and declarative JNDI.
DB2 Everyplace sample
This sample illustrates a use of the local DB2 Everyplace database.
Time required: 2 minutes
This sample consists of a single Client Service project, com.ibm.rcp.samples.db2e. Key concepts
demonstrated in this sample are as follows:
v Creation of a DB2 Everyplace DataSource object
v Creation of a database within the current workspace
v Creation of a database using SQL specified in the Java code
Product overview 25
For additional information about this sample, refer to the DB2e.java class in the
com.ibm.rcp.samples.db2e package.
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, db2e.jar, or browse to the com.ibm.rcp.tools.samplegallery
plug-in directory, then the archive directory, and select the archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as DB2e.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.db2e plug-in is selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To start the sample, enter start com.ibm.rcp.samples.db2e in the Console view.
Note: To stop the sample, use the stop com.ibm.rcp.samples.db2e command.
Output is similar to the following:
osgi> start com.ibm.rcp.samples.db2e
osgi> ***Created table: employee
***Inserted three records into table: employee
***Selected all records from table: employee
Employee NO.=112233, FirstName=John, AGE=22
Employee NO.=445566, FirstName=Mary, AGE=33
Employee NO.=778899, FirstName=Lily, AGE=44
***Deleted table: employee
Troubleshooting:
Problem:
When attempting to perform the start com.ibm.rcp.samples.db2e command in the console, the
following message is displayed:
Cannot find bundle com.ibm.rcp.samples.db2e
Cause:
The com.ibm.rcp.samples.db2e bundle is not available in your runtime.
Action:
Edit your launch configuration and verify that the com.ibm.rcp.tools.samples.db2e bundle is
selected.
Problem:
When attempting to perform the start com.ibm.rcp.samples.db2e command in the console, the
following message is displayed:
26 Lotus Expeditor: Developing Applications for Lotus Expeditor
org.osgi.framework.BundleException: The bundle could not be resolved.
Reason: Missing Constraint: Require-Bundle: com.ibm.db2e;
bundle-version="0.0.0"
Cause:
The com.ibm.db2e bundle is not available in the runtime.
Action:
Edit your launch configuration and verify that the com.ibm.db2e bundle is selected.
Problem:
When attempting to perform the start com.ibm.rcp.samples.db2e command in the console, the
following message is displayed:
java.lang.UnsatisfiedLinkError: Can’t find library db2ejdbc (db2ejdbc.dll) in
sun.boot.library.path or java.library.path
Cause:
The operating system specific fragment for com.ibm.db2e bundle is not available in the runtime.
Action:
Edit your launch configuration and verify that the com.ibm.db2e.win32.x86 or com.ibm.db2e.linux.x86
fragment is selected. Related samples:
See the Cloudscape sample for an alternative database included with the platformThis sample illustrates the use of the local Cloudscape database.
See ISync sample to synchronize content from a relational database on the server to a local
Cloudscape or DB2e databaseThis sample demonstrates how to use the ISync APIs to synchronize relational data from a DB2
Everyplace Sync Server to a local DB2 Everyplace or Cloudscape database.
See the JNDI sample to declaratively define (bind) a DataSource objectThis sample demonstrates the use of JNDI APIs and declarative JNDI.
Echo sample
This sample demonstrates the Mobile Web Services capabilities.
Time required: 2 minutes
You can use this sample only on the desktop runtime.
This sample contains three projects: com.ibm.rcp.samples.ws.echo.client,
com.ibm.rcp.samples.ws.echo.marshal, and com.ibm.rcp.samples.ws.echo.server.
Key concepts demonstrated in this sample are as follows:
v Expose an OSGi service as a Web Service
v Use custom serialization and deserialization (also referred to as custom marshalling)
v Access a Web Service using a dynamic client
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
Product overview 27
d. Enter the name of the sample jar file, echo.jar, or browse to the com.ibm.rcp.tools.samplegallery
plug-in directory, then the archive directory, and select the archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To configure and run the sample:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as Echo.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.ws.echo.server,
com.ibm.rcp.samples.ws.echo.marshal, and com.ibm.rcp.samples.ws.echo.client plug-ins are
selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To start the sample, enter a start bundle_name command for each plug-in in the Console view. Bundle
names are as follows:
v com.ibm.rcp.samples.ws.echo.marshal – Starts the marshal plug-in to register the custom serializer
implementations.
v com.ibm.rcp.samples.ws.echo.server – Starts the Web service provider bundle.
v com.ibm.rcp.samples.ws.echo.client – Starts the Web service client bundle.
Attention: You must start these bundles in the order shown and stop bundles in the reverse order.
To stop a bundle, use the stop bundle_name command.
Output is similar to the following:
28 Lotus Expeditor: Developing Applications for Lotus Expeditor
osgi> start com.ibm.rcp.samples.ws.echo.marshal
osgi> start com.ibm.rcp.samples.ws.echo.server
osgi> start com.ibm.rcp.samples.ws.echo.client
osgi> refs[i]: {com.ibm.rcp.samples.ws.echo.service.EchoService}={ws.location=http://localhost:3760/ws/pid/echoSvc, serv
ice.id=107}
SERVER_SIDE=null
Bundle ws:http://localhost:3760/ws/pid/echoSvc?wsdl says:
{req1=Counter = 1}
{req1=Counter = 1}@java.util.GregorianCalendar[time=1150078860000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zo
ne=sun.util.calendar.ZoneInfo[id="America/Chicago",offset=-21600000,dstSavings=3600000,useDaylight=true,transitions=235,
lastRule=java.util.SimpleTimeZone[id=America/Chicago,offset=-21600000,dstSavings=3600000,useDaylight=true,startYear=0,st
artMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endD
ayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2006,MONTH=5,WEEK_OF_YEA
R=24,WEEK_OF_MONTH=3,DAY_OF_MONTH=11,DAY_OF_YEAR=162,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=9,HOUR_OF_DAY=21,
MINUTE=21,SECOND=0,MILLISECOND=0,ZONE_OFFSET=-21600000,DST_OFFSET=3600000]
refs[i]: {com.ibm.rcp.samples.ws.echo.service.EchoService}={service.pid=echoSvc, SERVER_SIDE=true, service.id=106}
SERVER_SIDE=true
This is the server side bundle, skipping...
------------
refs[i]: {com.ibm.rcp.samples.ws.echo.service.EchoService}={ws.location=http://localhost:3760/ws/pid/echoSvc, service.id
=107}
SERVER_SIDE=null
Bundle ws:http://localhost:3760/ws/pid/echoSvc?wsdl says:
{req2=Counter = 2, req1=Counter = 1}
{req2=Counter = 2, req1=Counter = 1}@java.util.GregorianCalendar[time=1150078860000,areFieldsSet=true,areAllFieldsSet=tr
ue,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Chicago",offset=-21600000,dstSavings=3600000,useDaylight=tru
e,transitions=235,lastRule=java.util.SimpleTimeZone[id=America/Chicago,offset=-21600000,dstSavings=3600000,useDaylight=t
rue,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMont
h=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2006,M
ONTH=5,WEEK_OF_YEAR=24,WEEK_OF_MONTH=3,DAY_OF_MONTH=11,DAY_OF_YEAR=162,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR
=9,HOUR_OF_DAY=21,MINUTE=21,SECOND=0,MILLISECOND=0,ZONE_OFFSET=-21600000,DST_OFFSET=3600000]
refs[i]: {com.ibm.rcp.samples.ws.echo.service.EchoService}={service.pid=echoSvc, SERVER_SIDE=true, service.id=106}
SERVER_SIDE=true
This is the server side bundle, skipping...
------------
refs[i]: {com.ibm.rcp.samples.ws.echo.service.EchoService}={ws.location=http://localhost:3760/ws/pid/echoSvc, service.id
=107}
SERVER_SIDE=null
Bundle ws:http://localhost:3760/ws/pid/echoSvc?wsdl says:
{req3=Counter = 3, req2=Counter = 2, req1=Counter = 1}
{req3=Counter = 3, req2=Counter = 2, req1=Counter = 1}@java.util.GregorianCalendar[time=1150078860000,areFieldsSet=true,
areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Chicago",offset=-21600000,dstSavings=36000
00,useDaylight=true,transitions=235,lastRule=java.util.SimpleTimeZone[id=America/Chicago,offset=-21600000,dstSavings=360
0000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0
,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1
,ERA=1,YEAR=2006,MONTH=5,WEEK_OF_YEAR=24,WEEK_OF_MONTH=3,DAY_OF_MONTH=11,DAY_OF_YEAR=162,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MO
NTH=2,AM_PM=1,HOUR=9,HOUR_OF_DAY=21,MINUTE=21,SECOND=0,MILLISECOND=0,ZONE_OFFSET=-21600000,DST_OFFSET=3600000]
refs[i]: {com.ibm.rcp.samples.ws.echo.service.EchoService}={service.pid=echoSvc, SERVER_SIDE=true, service.id=106}
SERVER_SIDE=true
This is the server side bundle, skipping...
------------
refs[i]: {com.ibm.rcp.samples.ws.echo.service.EchoService}={ws.location=http://localhost:3760/ws/pid/echoSvc, service.id
=107}
SERVER_SIDE=null
Bundle ws:http://localhost:3760/ws/pid/echoSvc?wsdl says:
{req3=Counter = 3, req2=Counter = 2, req1=Counter = 1, req4=Counter = 4}
{req3=Counter = 3, req2=Counter = 2, req1=Counter = 1, req4=Counter = 4}@java.util.GregorianCalendar[time=1150078860000,
areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Chicago",offset=-2160000
0,dstSavings=3600000,useDaylight=true,transitions=235,lastRule=java.util.SimpleTimeZone[id=America/Chicago,offset=-21600
000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=72000
00,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minima
lDaysInFirstWeek=1,ERA=1,YEAR=2006,MONTH=5,WEEK_OF_YEAR=24,WEEK_OF_MONTH=3,DAY_OF_MONTH=11,DAY_OF_YEAR=162,DAY_OF_WEEK=1
,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=9,HOUR_OF_DAY=21,MINUTE=21,SECOND=0,MILLISECOND=0,ZONE_OFFSET=-21600000,DST_OFFSET=
3600000]
refs[i]: {com.ibm.rcp.samples.ws.echo.service.EchoService}={service.pid=echoSvc, SERVER_SIDE=true, service.id=106}
SERVER_SIDE=true
This is the server side bundle, skipping...
------------
refs[i]: {com.ibm.rcp.samples.ws.echo.service.EchoService}={ws.location=http://localhost:3760/ws/pid/echoSvc, service.id
=107}
SERVER_SIDE=null
Bundle ws:http://localhost:3760/ws/pid/echoSvc?wsdl says:
{req3=Counter = 3, req2=Counter = 2, req1=Counter = 1, req5=Counter = 5, req4=Counter = 4}
{req3=Counter = 3, req2=Counter = 2, req1=Counter = 1, req5=Counter = 5, req4=Counter = 4}@java.util.GregorianCalendar[t
ime=1150078860000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Chicag
o",offset=-21600000,dstSavings=3600000,useDaylight=true,transitions=235,lastRule=java.util.SimpleTimeZone[id=America/Chi
ff 21600000 d S i 3600000 D li h Y 0 M d 3 M h 2 D 8 D OfW k
Product overview 29
Related sample:
See the Echo Secure sample for Mobile Web Services security enablementThis sample demonstrates Mobile Web Services Security.
Echo Secure sample
This sample demonstrates Mobile Web Services Security.
Time required: 2 minutes
You can use this sample only on the desktop runtime.
This sample contains three projects: com.ibm.rcp.samples.wssecurity.echo.client,
com.ibm.rcp.samples.wssecurity.server, and com.ibm.rcp.samples.wssecurity.registration.
Key concepts demonstrated in this sample are as follows:
v Expose an OSGi service as a Web Service
v Access a Web Service using a dynamic client
v Use of the OSGi UserAdmin service to define users that can access the service
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, echosecure.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as Echo Secure.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.wssecurity.registration, ,
com.ibm.rcp.samples.wssecurity.echo.client, and com.ibm.rcp.samples.wssecurity.echo.server
plug-ins are selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To start the sample, enter a start bundle_name command for each plug-in in the Console view. Bundle
names are as follows:
v com.ibm.rcp.samples.wssecurity.registration – Starts the registration plug-in to create user
definitions.
v com.ibm.rcp.samples.wssecurity.echo.client – Starts the Web service client bundle.
v com.ibm.rcp.samples.wssecurity.echo.server – Starts the Web service provider bundle.
Note: To stop a bundle, use the stop bundle_name command.
Output is similar to the following:
30 Lotus Expeditor: Developing Applications for Lotus Expeditor
osgi> start com.ibm.rcp.samples.wssecurity.registration
osgi> start com.ibm.rcp.samples.wssecurity.echo.server
osgi> start com.ibm.rcp.samples.wssecurity.echo.client
start SecureEchoClient bundle...
return = HelloWorld!
osgi> stop com.ibm.rcp.samples.wssecurity.echo.client
osgi> stop com.ibm.rcp.samples.wssecurity.echo.server
osgi> stop com.ibm.rcp.samples.wssecurity.registration
Related samples:
See the Echo sample for use of the Mobile Web Services custom marshallingThis sample demonstrates the Mobile Web Services capabilities.
Eclipse Preferences sample
This technology sample demonstrates usage of the Eclipse Preferences capabilities.
Time required: 2 minutes
You can use this sample only on the desktop runtime.
Two projects are included in this sample: com.ibm.rcp.samples.preferences.eclipse and
com.ibm.rcp.samples.preferences.eclipse.ui.
Key concepts demonstrated in this sample:
v Use of the org.eclipse.core.runtime.Plugin.getPluginPreferences API
v Use of the org.eclipse.core.runtime.preferences extension point to initialize default preference values
v Use of the org.eclipse.ui.preferencePages extension point to define a preference page
v Use of the org.eclipse.ui.preferences.ScopedPreferenceStore class
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, eclipseprefs.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as Eclipse Preferences.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.preferences.eclipse and
com.ibm.rcp.samples.preferences.eclipse.ui plug-ins are selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
Product overview 31
f. Click Apply and then Run.
Running the sample:
You will use both the console and the workbench to use this sample. First, a preferences page has been
contributed to the workbench. Select File > Preferences, then Sample Preference Page to view the
preference page. The location field shows the current default value. Change the value of the location field
to xxxxx. Select OK to save the updated preference value. (The Restore Defaults button causes the
default value to be redisplayed.) Now switch to the console window. Output similar to the following is
displayed:
osgi> Current storage location : file:/C:/Documents and Settings/fred/runtime-test/
Default storage location : file:/C:/Documents and Settings/fred/runtime-test/
This information show the current value of the preference and the default value. Since these statements
were output prior to changing the preference, they show the same value. Because you changed the
current value of the preference, you can display the updated values by first stopping and then restarting
the com.ibm.rcp.samples.preferences.eclipse plug-in. Perform the following steps using the console:
1. Enter stop com.ibm.rcp.samples.preferences.eclipse to stop the sample plug-in.
2. Enter start com.ibm.rcp.samples.preferences.eclipse to start the sample plug-in.
Note: There is a slight delay between the time that you enter the start command and the messages
appears. This is a condition of the sample code that enables the PreferenceInitializer and the
BundleActivator to run before attempting to display the information.
Output similar to the following is displayed:
osgi> Current storage location : file:/C:/Documents and Settings/fred/runtime-test/
Default storage location : file:/C:/Documents and Settings/fred/runtime-test/
osgi> stop com.ibm.rcp.samples.preferences.eclipse
osgi> start com.ibm.rcp.samples.preferences.eclipse
osgi> Current storage location : xxxxx
Default storage location : file:/C:/Documents and Settings/fred/runtime-test/
Related sample:
See the OSGi Preferences Services sample for the use of the OSGi Preferences Service to store
preferencesThis sample demonstrates the use of the OSGi Preferences Service to save and retrieve preference
information.
Embedded Transaction Container sample
This sample demonstrates capabilities of the Embedded Transaction Container.
Time required: 2 minutes
The sample contains three Client Services Embedded Transaction projects and one Client Services project.
com.ibm.rcp.samples.txncontainer.entitybean.cmp11
Sample Embedded Transaction project containing a bean conforming to Container Managed
Persistence (CMP) from the EJB 1.1 specification.
com.ibm.rcp.samples.txncontainer.entitybean.cmp20
Sample Embedded Transaction project containing a bean conforming to Container Managed
Persistence (CMP) from the EJB 2.0 specification.
com.ibm.rcp.samples.txncontainer.sessionbean
Sample Embedded Transaction project containing a Stateless Session Bean.
32 Lotus Expeditor: Developing Applications for Lotus Expeditor
com.ibm.rcp.samples.txncontainer.testdriver
Test bundle that calls each of the other beans.
Key concepts demonstrated in this sample are as follows:
v Implementation of Embedded Transaction Container Entity Beans
v Implementation of Embedded Transaction Container Session Beans
v Use of JNDI to locate beans
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, ejbtestdriver.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as TxnContainer.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.txncontainer.entitybean.cmp11,
com.ibm.rcp.samples.txncontainer.entitybean.cmp20,
com.ibm.rcp.samples.txncontainer.sessionbean, and com.ibm.rcp.samples.txncontainer.testdriver
plug-ins are selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To start the sample, enter start com.ibm.rcp.samples.txncontainer.testdriver in the Console view.
Output is similar to the following:
osgi> start com.ibm.rcp.samples.txncontainer.testdriver
* ************************************ *
* Testing sample Stateless Session EJB
* ************************************ *
Stateless Session EJB JNDI lookup successful
JNDI lookup returned: com.ibm.rcp.samples.txncontainer.sessionbean.IBMCustomerDatabaseHome
Stateless Session EJB Object created successfully
create() returned: com.ibm.rcp.samples.txncontainer.sessionbean.IBMCustomerDatabase
Executing Stateless Session EJB business methods
Attempting connection to CUSTOMER
Attempting connection to CUSTOMER
list() returned:
0) Smith, Sam
Stateless Session EJB business methods executed successfully
* ********************************* *
* Testing sample CMP 1.1 Entity EJB
* ********************************* *
Product overview 33
CMP 1.1 Entity EJB JNDI lookup successful
JNDI lookup returned: com.ibm.rcp.samples.txncontainer.entitybean.cmp11.PVCCustomerJDBCHome
CMP 1.1 Entity EJB Object created successfully
create() returned: com.ibm.rcp.samples.txncontainer.entitybean.cmp11.PVCCustomerJDBC
Executing CMP 1.1 Entity EJB business methods
getId() returned: 1157639752696
getLastName() returned: Holmes
getFirstName() returned: Hank
CMP 1.1 Entity EJB business methods executed successfully
* ********************************* *
* Testing sample CMP 2.0 Entity EJB
* ********************************* *
CMP 2.0 Entity EJB JNDI lookup successful
JNDI lookup returned: com.ibm.rcp.samples.txncontainer.entitybean.cmp20.PVCCustomer20JDBCHome
CMP 2.0 Entity EJB Object created successfully
create() returned: com.ibm.rcp.samples.txncontainer.entitybean.cmp20.PVCCustomer20JDBC
Executing CMP 2.0 Entity EJB business methods
getId() returned: 1157639752865
getLastName() returned: Baxter
getFirstName() returned: Beth
CMP 2.0 Entity EJB business methods executed successfully
Executing CMP 2.0 Entity EJB finder methods
findByFirstName() returned 1 EJB objects
1157639752865) Beth
findByLastName() returned: 1 EJB objects
1157639752865) Baxter
CMP 2.0 Entity EJB finder methods executed successfully
osgi> stop com.ibm.rcp.samples.txncontainer.testdriver
ISync sample
This sample demonstrates how to use the ISync APIs to synchronize relational data from a DB2
Everyplace Sync Server to a local DB2 Everyplace or Cloudscape database.
Time required: 2 minutes
You can use this sample on both desktop and device runtimes. However, only DB2 Everyplace can be
used as a client on the device runtimes. To run this sample, you must have access to an IBM DB2
Everyplace Sync Server installation.
A key concept demonstrated in this sample is the use of the ISync APIs.
Setup instructions:
Before importing and running this sample, you must have DB2 Everyplace Sync Server installed or have
access to one.
The DB2 Everyplace Sync Server can be installed as a standalone product, or as part of the Lotus
Expeditor Server installation. If installed as a standalone product, the demo applications must be installed
to properly configure the user IDs required by this sample.
To verify accessibility to the DB2 Everyplace Sync Server, use a Web browser to connect to the URL
http://server:port/db2e/db2erdb, where server and port are the correct server and port for your
installation. The server responds with the page content:
DB2e SyncServer (Thu Jan 26 18:56:25 CST 2006)
1. To import the sample:
34 Lotus Expeditor: Developing Applications for Lotus Expeditor
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, isync.jar, or browse to the com.ibm.rcp.tools.samplegallery
plug-in directory, then the archive directory, and select the archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.Notes:
v If the server is not installed on the local machine on port 8080, then you must update the server.url
property value in the db2sync_db2e.properties or db2sync_db2j.properties file contained in the
com.ibm.rcp.samples.isync project. Set the value to http://xxxx:8080, where xxxx is the IP address
of the DB2 Everyplace Sync Server.
v By default the project is set up to use DB2 Everyplace as the local target for the synchronization
operation. If you prefer to use Cloudscape as the local target, edit the ISyncBundle.java file and
change the propertyUrl in the start method from String propertyUrl =
"com.ibm.rcp.samples.isync.db2sync_db2e" to String propertyUrl =
"com.ibm.rcp.samples.isync.db2sync_db2j"
2. To launch the test runtime:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as ISync.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.isync plug-in is selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To start the sample, enter start com.ibm.rcp.samples.isync in the Console view.
Note: To stop a bundle, use the stop com.ibm.rcp.samples.isync command.
Output is similar to the following:
osgi> start com.ibm.rcp.samples.isync
osgi> *********************
SubsSet:
Subs:
SubsType: 0
Event Type: 1
Event Code: 1001
Progress: 0
**********************
*********************
SubsSet: Configuration
Subs:
SubsType: 0
Event Type: 1
Event Code: 1007
Progress: 0
**********************
*********************
SubsSet: Configuration
Subs: Configuration
SubsType: 100
Event Type: 1
Event Code: 1008
Product overview 35
Progress: 0
**********************
*********************
SubsSet: Configuration
Subs: Configuration
SubsType: 100
Event Type: 1
Event Code: 1002
Progress: 0
**********************
*********************
SubsSet: Configuration
Subs: Configuration
SubsType: 100
Event Type: 1
Event Code: 1003
Progress: 0
**********************
*********************
SubsSet: Configuration
Subs: Configuration
SubsType: 100
Event Type: 1
Event Code: 1004
Progress: 0
**********************
*********************
SubsSet: Configuration
Subs: Configuration
SubsType: 100
Event Type: 1
Event Code: 1005
Progress: 0
**********************
*********************
SubsSet: Configuration
Subs: Configuration
SubsType: 100
Event Type: 1
Event Code: 1004
Progress: 0
**********************
*********************
SubsSet: Configuration
Subs:
SubsType: 0
Event Type: 1
Event Code: 1011
Progress: 0
**********************
*********************
SubsSet: SUBSCRIPTION_SET1
Subs:
SubsType: 0
Event Type: 1
Event Code: 1007
Progress: 0
**********************
*********************
36 Lotus Expeditor: Developing Applications for Lotus Expeditor
SubsSet: SUBSCRIPTION_SET1
Subs: JDBCSUB1
SubsType: 102
Event Type: 1
Event Code: 1008
Progress: 0
**********************
Sample set subs .
*********************
SubsSet: SUBSCRIPTION_SET1
Subs: JDBCSUB1
SubsType: 102
Event Type: 1
Event Code: 1002
Progress: 0
**********************
*********************
SubsSet: SUBSCRIPTION_SET1
Subs: JDBCSUB1
SubsType: 102
Event Type: 1
Event Code: 1003
Progress: 0
**********************
*********************
SubsSet: SUBSCRIPTION_SET1
Subs: JDBCSUB1
SubsType: 102
Event Type: 1
Event Code: 1004
Progress: 50
**********************
*********************
SubsSet: SUBSCRIPTION_SET1
Subs: JDBCSUB1
SubsType: 102
Event Type: 1
Event Code: 1005
Progress: 50
**********************
*********************
SubsSet: SUBSCRIPTION_SET1
Subs: JDBCSUB1
SubsType: 102
Event Type: 1
Event Code: 1004
Progress: 100
**********************
*********************
SubsSet: SUBSCRIPTION_SET1
Subs:
SubsType: 0
Event Type: 1
Event Code: 1011
Progress: 100
**********************
*********************
SubsSet:
Subs:
SubsType: 0
Product overview 37
Event Type: 1
Event Code: 1012
Progress: 100
**********************
Synchronization succeeded
Subscription Set: SUBSCRIPTION_SET1 Status: COMPLETED
osgi> stop com.ibm.rcp.samples.isync
Troubleshooting:
Problem:
When attempting to perform the start com.ibm.rcp.samples.isync command in the console, the
following message is displayed:
Cannot find bundle com.ibm.rcp.samples.isync
Cause:
The com.ibm.rcp.samples.isync bundle is not available in your runtime.
Action:
Edit your launch configuration and verify that the com.ibm.rcp.samples.isync bundle is selected.
Problem:
When attempting to perform the start com.ibm.rcp.samples.db2e command in the console, the
following message is displayed:
org.osgi.framework.BundleException: The bundle could not be resolved.
Reason: Missing Constraint: Require-Bundle: com.ibm.db2e; bundle-version="0.0.0"
Cause:
The com.ibm.db2e bundle is not available in the runtime.
Action:
Edit your launch configuration and verify that the com.ibm.db2e bundle is selected.
Problem:
When attempting to perform the start com.ibm.rcp.samples.db2e command in the console, the
following message is displayed:
java.lang.UnsatisfiedLinkError: Can’t find library db2ejdbc (db2ejdbc.dll)
in sun.boot.library.path or java.library.path
Cause:
The operating system specific fragment for com.ibm.db2e bundle is not available in the runtime.
Action:
Edit your launch configuration and verify that the com.ibm.db2e.win32.x86 or com.ibm.db2e.linux.x86
fragment is selected.
Problem:
When attempting to perform the start com.ibm.rcp.samples.isync command in the console, the
following message is displayed:
java.lang.UnsatisfiedLinkError: Can’t find library isync4j (isync4j.dll)
in sun.boot.library.path or java.library.path
Cause:
The operating system specific fragment for com.ibm.mobileservices.isync bundle is not available in
the runtime.
Action:
Edit your launch configuration and verify that the com.ibm.mobileservices.isync.win32.x86 or
com.ibm.mobileservices.isync.linux.x86 fragment is selected.
38 Lotus Expeditor: Developing Applications for Lotus Expeditor
Problem:
The com.ibm.rcp.samples.isync bundle starts and output to the console begins. The last message
output to the console is as follows:
Exception code: 304 304 com.ibm.mobileservices.isync.ISyncException: 304
Cause:
The user ID and password combination used to access the server is not valid.
Action:
The default users, nurse1 and nurse2, are defined as part of the DB2e Everyplace Sync Server sample
installation. If the samples were not installed on the server, either install the samples now, or use
another ID that has been configured at the server. The user ID and password are specified in the
db2sync_db2e.properties or db2sync_db2j.properties files using the isync.user and isync.password
properties.
Problem:
The com.ibm.rcp.samples.isync bundle starts and output to the console begins. The last message reads
as follows:
Exception code: 315 315 com.ibm.mobileservices.isync.ISyncException: 315
Cause:
The user ID has previously synchronized the database to another system or location.
Action:
The user ID is not configured to synchronize to multiple devices. Edit the user ID to delete its current
devices, or modify the DB2 Everyplace Sync Server configuration to allow synchronization to
multiple devices. Related samples:
See the DB2e sample for use of the DB2 Everyplace database without ISync technologyThis sample illustrates a use of the local DB2 Everyplace database.
See the Cloudscape sample for an alternative database included with the platformThis sample illustrates the use of the local Cloudscape database.
JMS with Lotus Expeditor micro broker provider sample
This sample demonstrates the use of the Java Message Service (JMS) publish and subscribe messaging
interfaces with the micro broker JMS provider.
Time required: 2 minutes
Key concepts demonstrated in this sample are as follows:
v Use of micro broker APIs to create a micro broker instance
v Use of JMS APIs with micro broker provider
v Use of declarative JNDI to define connection factory and topic objects
Setup instructions:
There are two options for using this sample:
v “Option A: Using the in-memory-based micro broker instance” on page 40
v “Option B: Using the file-persistence-based micro broker instance” on page 41
In both options, the com.ibm.rcp.samples.microbroker.jmspubsub project contains code that publishes
messages to a topic and subscribes to a topic to receive messages.
Option A uses the com.ibm.rcp.samples.microbroker.setup project to create and start a
memory-only-based micro broker instance. When the com.ibm.rcp.samples.microbroker.setup bundle is
stopped, the micro broker instance is stopped and the definition deleted. If the
Product overview 39
com.ibm.rcp.samples.microbroker.setup is started and stopped after the com.ibm.micro.gettingstarted
bundle was used, the micro broker instance defined by com.ibm.micro.gettingstarted is stopped and the
definition removed.
Option B uses the evaluation bundle, com.ibm.micro.gettingstarted, to create and start a
file-persistence-based micro broker instance. When the com.ibm.micro.gettingstarted bundle is stopped,
the micro broker instance continues to run and the definition remains.
While many micro broker instances can be created and configured, only a single micro broker instance
can be started within a process. You cannot use this sample if another micro broker instance is in use,
with the exception of the FirstBroker instance defined by and started by the
com.ibm.micro.gettingstarted or the com.ibm.rcp.samples.microbroker.setup bundles.
Option A: Using the in-memory-based micro broker instance:
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, jmsmb.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To configure and run the sample:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as JMS Micro broker Memory.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.microbroker.setup and
com.ibm.rcp.samples.microbroker.jmspubsub plug-ins are selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To start the sample, enter a start bundle_name command for each plug-in in the Console view. Bundle
names are as follows:
v com.ibm.rcp.samples.microbroker.setup
v com.ibm.rcp.samples.microbroker.jmspubsub
Note: When finished with the sample, enter stop com.ibm.rcp.samples.microbroker.jmspubsub
followed by stop com.ibm.rcp.samples.microbroker.jmspubsub.
This completes the sample and removes the micro broker instance that might conflict with
other samples.
Output is similar to the following:
osgi> start com.ibm.rcp.samples.microbroker.setup
FirstBroker started.
osgi> start com.ibm.rcp.samples.microbroker.jmspubsub
osgi> PublisherThread: Starting
SubscriberThread: Starting
SubscriberThread: Received message This is message 0
SubscriberThread: Received message This is message 1
40 Lotus Expeditor: Developing Applications for Lotus Expeditor
SubscriberThread: Received message This is message 2
SubscriberThread: Received message This is message 3
SubscriberThread: Received message This is message 4
osgi> stop com.ibm.rcp.samples.microbroker.jmspubsub
osgi> stop com.ibm.rcp.samples.microbroker.setup
Option B: Using the file-persistence-based micro broker instance:
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, jmsmb.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To configure and run the sample:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as JMS Micro broker Persistence.
d. Select the Plug-ins tab and verify that the com.ibm.micro.gettingstarted and
com.ibm.rcp.samples.microbroker.jmspubsub plug-ins are selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To start the sample, enter a start bundle_name command for each plug-in in the Console view. Bundle
names are as follows:
v com.ibm.micro.gettingstarted
v com.ibm.rcp.samples.microbroker.jmspubsub
Note: When finished with the sample, enter stop com.ibm.rcp.samples.jmspubsub followed by stop
com.ibm.micro.gettingstarted.
This completes the sample.
Output is similar to the following:
osgi> start com.ibm.micro.gettingstarted
osgi> start com.ibm.rcp.samples.microbroker.jmspubsub
PubisherThread: Starting
osgi> SubscriberThread: Starting
SubscriberThread: Received message This is message 0
SubscriberThread: Received message This is message 1
SubscriberThread: Received message This is message 2
SubscriberThread: Received message This is message 3
SubscriberThread: Received message This is message 4
osgi> stop com.ibm.rcp.samples.microbroker.jmspubsub
osgi> stop com.ibm.micro.gettingstarted
Product overview 41
Related sample:
See the JMS with MQe provider sample for use of the MQe provider using JMS APIsThis sample demonstrates the use of the Java Message Service (JMS) point-to-point messaging
interfaces with the MQ Everyplace provider.
JMS with MQe provider sample
This sample demonstrates the use of the Java Message Service (JMS) point-to-point messaging interfaces
with the MQ Everyplace provider.
Time required: 2 minutes
Key concepts demonstrated in this sample are as follows:
v Use of JMS APIs with MQ Everyplace provider
v Use of declarative JNDI to define MQ Everyplace Queue Manager and Queue factory objects
Setup instructions:
This sample relies on the ability to create its own MQ Everyplace Queue Manager. Only a single MQ
Everyplace Queue Manager is allowed to be active at any one time. You cannot use this sample at the
same time as the MQ Everyplace sample, the Order Entry sample, or any other application that might
have attempted to create a queue manager.
Before running this sample, you must make the following updates:
1. Update the mqe.ini configuration file. To do so, open the mqe.ini file, located in the jmsQM directory.
For the DirName property, replace c:\sample_workspace with the location of the current project
workspace.
2. Update the plugin.xml file. To do so, open the plugin.xml file for the project, and switch to the
Extensions tab in the editor. In the All Extensions list, expand the first
com.ibm.pvc.jndi.provider.java.genericobject extension point and continue expanding until
(method-parameter) is shown. Select (method-parameter) and in the Extension Details panel, update
the Value field, and replace c:\sample_workspace with the location of the current project workspace.
Note: Keep in mind that Linux users must reformat the workspace locations appropriately.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, jms.jar, or browse to the com.ibm.rcp.tools.samplegallery
plug-in directory, then the archive directory, and select the archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To configure and run the sample:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as JMS.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.jms plug-in is selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To start the sample, enter start com.ibm.rcp.samples.jms in the Console view.
42 Lotus Expeditor: Developing Applications for Lotus Expeditor
4. When the sample has completed, enter stop com.ibm.rcp.samples.jms. This completes the sample and
removes the queue manager definitions that might conflict with other samples.
Output is similar to the following:
osgi> PTPSample - simple example of sending and receiving a message.
osgi> start com.ibm.rcp.samples.jms
Creating a ConnectionFactory
Retrieving a ConnectionFactory from JNDI
Creating a Connection
Starting the Connection
Creating a non-transactional Session
Retrieving a Destination from JNDI
Creating a MessageProducer
Creating a MessageConsumer
Creating a TextMessage
Adding Text
Sending the message to ExampleQM+SYSTEM.DEFAULT.LOCAL.QUEUE
Reading the message back again
Got message:
HEADER FIELDS
------------------------------------------------------------------
JMSType: jms_text
JMSDeliveryMode: 2
JMSExpiration: 0
JMSPriority: 4
JMSMessageID: ID:00007e5db5f52d3cfffffef6ebdd5f44
JMSTimestamp: 1138504147131
JMSCorrelationID: null
JMSDestination: ExampleQM:SYSTEM.DEFAULT.LOCAL.QUEUE
JMSReplyTo: null
JMSRedelivered: false
PROPERTY FIELDS (read only)
------------------------------------------------------------------
JMSXRcvTimestamp : 1138504147301
JMSXDeliveryCount : 1
MESSAGE BODY (read only)
------------------------------------------------------------------
A simple text message from PTPSample
Reply string equals original string
Closing Connection
finished
osgi> stop com.ibm.rcp.samples.jms
Troubleshooting:
Problem:
When attempting to perform the start com.ibm.rcp.samples.jms command in the console, the
following message is displayed:
Cannot find bundle com.ibm.rcp.samples.jms
Cause:
The com.ibm.rcp.samples.jms bundle is not available in your runtime.
Action:
Edit your launch configuration and verify that com.ibm.rcp.samples.jms is selected.
Problem:
When the com.ibm.rcp.samples.jms bundle starts, the following message is displayed:
javax.jms.JMSException: Cannot start a queue manager for JMS: .ini
file c:\workspaces\test\com.ibm.rcp.samples.jms\jmsQM\mqe.ini not found
Product overview 43
Cause:
The MQ Everyplace .ini file that contains the queue manager configuration is unable to be opened.
Action:
Edit the plugin.xml file residing in the project directory, and verify that the DirName parameter
correctly references the target workspace.
Problem:
When the com.ibm.rcp.samples.jms bundle starts, the following message is displayed:
javax.jms.JMSException: Cannot start a queue manager for JMS
Cause:
The directory containing the Queue Manager definition is not accessible.
Action:
Edit the mqe.ini file residing in the jmsQm directory, and verify that the DirName parameter correctly
references the target workspace.
Problem:
When the com.ibm.rcp.samples.jms bundle starts, the following message is displayed:
javax.jms.InvalidDestinationException: Cannot resolve ExampleQM/SYSTEM.DEFAULT.LOCAL.QUEUE
Cause:
A Queue Manager already exists in the platform that does not contain the Queue definition
Action:
This sample defines its own queue manager, so if another bundle or application (such as Order Entry
or OFN Teller) has already started, the expected queue manager cannot be defined.
Stop this sample using the stop com.ibm.rcp.samples.jms command. Stop all other samples that you
might have started to remove their queue manager definitions. Restart the sample.
Problem:
When the com.ibm.rcp.samples.jms bundle starts, the following message is displayed:
javax.jms.JMSException: Cannot create JNDI InitialContext!
Cause:
The JNDI Provider has not been started; therefore, JNDI lookups are not available.
Action:
Verify that the com.ibm.pvc.jndi.provider.java bundle is included in your runtime or launch
configuration. Related samples:
See the MQe sample for use of the MQe client using MQe-specific APIsThis technology sample demonstrates the use of IBM WebSphere MQ Everyplace (MQe).
See the JMS with Lotus Expeditor micro broker provider sample for use of the Java Message Service
(JMS) publish and subscribe messaging interfacesThis sample demonstrates the use of the Java Message Service (JMS) publish and subscribe messaging
interfaces with the micro broker JMS provider.
JNDI sample
This sample demonstrates the use of JNDI APIs and declarative JNDI.
Time required: 2 minutes
You can use this sample on both desktop and device runtimes. However, only the DB2 Everyplace
DataSource can be returned on the device runtimes.
Key concepts demonstrated in this sample are as follows:
v Use of the JNDI bind() and lookup() APIs for binding and locating a simple object
44 Lotus Expeditor: Developing Applications for Lotus Expeditor
v Use of the JNDI extension points to define a data source object
v Use of the lookup() API to locate a resource defined via extension points
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, jndi.jar, or browse to the com.ibm.rcp.tools.samplegallery
plug-in directory, then the archive directory, and select the archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as JNDI.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.jndi plug-in is selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To start the sample, enter start com.ibm.rcp.samples.jndi in the Console view.
Note: To stop the sample, enter stop com.ibm.rcp.samples.jndi.
Output is similar to the following:
osgi> start com.ibm.rcp.samples.jndi
osgi> Object SampleSimpleObject bound with value: SimpleObject
Object retrieved from lookup: SimpleObject
Object SampleSimpleObject unbound.
The object lookup for SampleDataSourceDB2e returned com.ibm.db2e.jdbc.DB2eDataSource@69326932
The object lookup for SampleDataSourceDerby returned org.apache.derby.jdbc.EmbeddedDataSource@44024402
osgi> stop com.ibm.rcp.samples.jndi
Troubleshooting:
Problem:
When attempting to perform the start com.ibm.rcp.samples.jndi command in the console, the
following message is displayed:
Cannot find bundle com.ibm.rcp.samples.jndi
Cause:
The com.ibm.rcp.samples.jndi bundle is not available in your runtime.
Action:
Edit your launch configuration and verify that com.ibm.rcp.samples.jndi is selected.
Problem:
When attempting to perform the start com.ibm.rcp.samples.jndi command in the console, the
following message is displayed:
Product overview 45
javax.naming.NoInitialContextException: Cannot instantiate
class: com.ibm.pvc.jndi.provider.java.InitialContextFactory. Root exception is
java.lang.ClassNotFoundException: com.ibm.pvc.jndi.provider.java.InitialContextFactory
Cause:
The com.ibm.pvc.jndi.provider.java plug-in is either not available in the runtime or has not been
started.
Action:
Edit your launch configuration and verify that com.ibm.pvc.jndi.provider.java bundle is selected. Related sample:
See the JMS sample for use of the JNDI extensions points for JMS and MQe objectsThis sample demonstrates the use of the Java Message Service (JMS) point-to-point messaging
interfaces with the MQ Everyplace provider.
Log and Log Reader sample
This sample demonstrates the use of the OSGi Log and LogReader services.
Time required: 2 minutes
This sample contains two projects: com.ibm.rcp.samples.log and com.ibm.rcp.samples.logreader.
The logreader project shows how to register a listener with the OSGi LogReader service to receive
notification of LogEvents.
The log project shows how to create OSGi Log messages. The resulting messages will be displayed by the
logreader.
Key concepts demonstrated in this sample are as follows:
v Use of Log Service APIs to log information to a common log service
v Use of the LogReader service APIs to receive notification of new log events
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, log.jar, or browse to the com.ibm.rcp.tools.samplegallery
plug-in directory, then the archive directory, and select the archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as Log Reader.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.log and
com.ibm.rcp.samples.logreader plug-ins are selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.
46 Lotus Expeditor: Developing Applications for Lotus Expeditor
3. To start the sample, enter a start bundle_name command for each plug-in in the Console view. Bundle
names are as follows:
v com.ibm.rcp.samples.logreader – Starts the Log Reader sample plug-in.
v com.ibm.rcp.samples.log – Starts the Log sample plug-in.
Note: When finished with the sample, enter stop com.ibm.rcp.samples.logreader or all log messages
are repeated on the console.
Output is similar to the following:
osgi> start com.ibm.rcp.samples.logreader
LogReader received LogEntry: BundleEvent STARTED
Time: 1138593859708
Source Bundle: com.ibm.rcp.samples.logreader
Level: INFO
osgi> start com.ibm.rcp.samples.log
LogReader received LogEntry: BundleEvent STARTED
Time: 1138593867550
Source Bundle: com.ibm.rcp.samples.log
Level: INFO
LogReader received LogEntry: This is a log message!
Time: 1138593867551
Source Bundle: com.ibm.rcp.samples.log
Level: WARNING
osgi> LogReader received LogEntry: This is a log message with exception
Time: 1138593867552
Source Bundle: com.ibm.rcp.samples.log
Level: ERROR
Exception:
java.lang.Exception: An Exception
at com.ibm.rcp.samples.log.Log.doLogMessage(Log.java:63)
at com.ibm.rcp.samples.log.Log.run(Log.java:35)
at java.lang.Thread.run(Thread.java:567)
LogReader received LogEntry: This is another log message!
Time: 1138593867553
Source Bundle: com.ibm.rcp.samples.log
Level: INFO
osgi> stop com.ibm.rcp.samples.logreader
Troubleshooting:
Problem:
When attempting to perform the start com.ibm.rcp.samples.logreader command in the console, the
following message is displayed: Cannot find bundle com.ibm.rcp.samples.logreader
Cause:
The com.ibm.rcp.samples.logreader bundle is not available in your runtime.
Action:
Edit your launch configuration and verify that the com.ibm.rcp.samples.logreader is selected.
Problem:
When attempting to perform the start com.ibm.rcp.samples.log command in the console, the
following message is displayed: Cannot find bundle com.ibm.rcp.samples.log
Cause:
The com.ibm.rcp.samples.log bundle is not available in your runtime.
Action:
Edit your launch configuration and verify that the com.ibm.rcp.samples.log is selected.
Product overview 47
Related sample:
See the Echo Secure sample for Mobile Web Services security enablementThis sample demonstrates Mobile Web Services Security.
MQ Everyplace sample
This technology sample demonstrates the use of IBM WebSphere MQ Everyplace (MQe).
Time required: 2 minutes
This sample contains two Client Services projects: com.ibm.rcp.samples.mqeclient and
com.ibm.rcp.samples.mqeserver.
The mqeclient project sends MQ Everyplace messages to a queue.
The mqeserver project starts a Queue Manager and listens for messages sent by the mqeclient.
Key concepts demonstrated in this sample are as follows:
v Creation of a local MQe Queue Manager
v Creation of listeners to enable a local MQe Server
v Accessing an existing Queue Manager
v Sending messages
v Receiving messages
Setup instructions:
This sample relies on the ability to create its own MQ Everyplace Queue Manager. Only a single MQ
Everyplace Queue Manager is allowed to be active at any one time. You cannot use this sample at the
same time as the JMS with MQe Provider sample, the Order Entry sample, or any other application that
might have attempted to create a queue manager.
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, mqe.jar, or browse to the com.ibm.rcp.tools.samplegallery
plug-in directory, then the archive directory, and select the archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To configure and run the sample:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as MQe.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.mqeserver and
com.ibm.rcp.samples.mqeclient plug-ins are selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To start the sample, enter a start bundle_name command for each plug-in in the Console view. Bundle
names are as follows:
v start com.ibm.rcp.samples.mqeserver – Starts the MQe server plug-in.
48 Lotus Expeditor: Developing Applications for Lotus Expeditor
v start com.ibm.rcp.samples.mqeclient – Starts the MQe client plug-in.4. When the sample has completed, enter stop com.ibm.rcp.samples.mqeclient. Next, enter stop
com.ibm.rcp.samples.mqeserver. This completes the sample and removes the queue manager
definitions that might conflict with other samples.
Output is similar to the following:
osgi> start com.ibm.rcp.samples.mqeserver
com.ibm.rcp.samples.mqeserver: MqeServer starting in 2 seconds...
osgi> com.ibm.rcp.samples.mqeserver: connection administration message created
com.ibm.rcp.samples.mqeserver: create administration message put to AdminQ
com.ibm.rcp.samples.mqeserver: listener created
com.ibm.rcp.samples.mqeserver: connection administration message created
com.ibm.rcp.samples.mqeserver: create administration message put to AdminQ
com.ibm.rcp.samples.mqeserver: listener started
com.ibm.rcp.samples.mqeserver: ..MQeServerBundle - registering message listener
com.ibm.rcp.samples.mqeserver: MqeServer has been started OK
osgi> start com.ibm.rcp.samples.mqeclient
com.ibm.rcp.samples.mqeclient: MqeClient - starting in 2 seconds...
com.ibm.rcp.samples.mqeclient: ..Create a message with id: 1 and data:This is a test message
com.ibm.rcp.samples.mqeclient: ..Put the message to QM/queue: MQeServer/SYSTEM.DEFAULT.LOCAL.QUEUE
osgi> com.ibm.rcp.samples.mqeserver: *****************************************************************
com.ibm.rcp.samples.mqeserver: A new message arrived on Queue SYSTEM.DEFAULT.LOCAL.QUEUE with ID = 1
com.ibm.rcp.samples.mqeserver: Message = This is a test message
com.ibm.rcp.samples.mqeserver: *****************************************************************
osgi> stop com.ibm.rcp.samples.mqeclient
com.ibm.rcp.samples.mqeclient: MqeClient has been stopped
osgi> stop com.ibm.rcp.samples.mqeserver
com.ibm.rcp.samples.mqeserver: stop listener administration message created
com.ibm.rcp.samples.mqeserver: stop listener administration message put to AdminQ
com.ibm.rcp.samples.mqeserver: listener stopped
com.ibm.rcp.samples.mqeserver: delete listener administration message created
com.ibm.rcp.samples.mqeserver: delete listener administration message put to AdminQ
com.ibm.rcp.samples.mqeserver: listener deleted
com.ibm.rcp.samples.mqeserver: Stopping QM
com.ibm.rcp.samples.mqeserver: Deleting QM
com.ibm.rcp.samples.mqeserver: MqeServer has been stopped.
Troubleshooting:
Problem:
When attempting to perform the start com.ibm.rcp.samples.mqeserver command in the console, the
following message is displayed:
Cannot find bundle com.ibm.rcp.samples.mqeserver
Cause:
The com.ibm.rcp.samples.mqeserver bundle is not available in your runtime
Action:
Edit your launch configuration and verify that com.ibm.rcp.samples.mqeserver is selected.
Problem:
When attempting to perform the start com.ibm.rcp.samples.mqeclient command in the console, the
following message is displayed:
Cannot find bundle com.ibm.rcp.samples.mqeclient
Cause:
The com.ibm.rcp.samples.mqeclient bundle is not available in your runtime.
Product overview 49
Action:
Edit your launch configuration and verify that com.ibm.rcp.samples.mqeclient is selected.
Problem:
When the com.ibm.rcp.samples.mqeserver bundle starts, the following message is displayed:
com.ibm.rcp.samples.mqeserver: ERROR - java.lang.Exception: ERROR - QM Already Running
Cause:
A Queue Manager already exists in the platform that does not contain the Queue definition.
Action:
This sample defines its own queue manager, so if another bundle or application (such as Order Entry
or OFN Teller) has already started, the expected queue manager cannot be defined. Stop this sample
using the stop com.ibm.rcp.samples.mqeclient and stop com.ibm.rcp.samples.mqeserver commands.
Stop all other samples that you might have started to remove their queue manager definitions.
Restart the sample. Related sample:
See the JMS sample for use of the JMS APIs with MQeThis sample demonstrates the use of the Java Message Service (JMS) point-to-point messaging
interfaces with the MQ Everyplace provider.
Order Entry sample
Order Entry is a three-tier, sample application that demonstrates the following components and
capabilities: Web container, Java Servlet 2.3, OSGi Preferences, MQe, JDBC, DB2e, RCP views, and
preference pages.
Time required: 10 minutes
You can use both the Rich and Web Application views on the desktop runtime, but you can only use the
Web Application on the device runtime.
Order Entry illustrates the following scenario:
A delivery employee drives a soda truck to three different customers: SpeedyMarket, Marine Mart, and
Quick Stop. As he travels to each of these locations to offload his stock, he determines how many bottles
and cans of soda to order for the next week and enters that information into his device. The order is
stored in the local DB2 Everyplace database and queued for transmission to the server using MQ
Everyplace. The order is transmitted to the server only when a network connection exists and the server
is ready to receive; otherwise, it is held on the device. After the order is sent from the device to the
backend server, a confirmation message is displayed on the device.
In this scenario, the Order Entry client processes input from the user and generates requests to the Order
Entry server, which represents an inventory management system. A DB2 Everyplace database stores the
requests and sends the requests through MQ Everyplace to the Order Entry server. When the Order Entry
server receives a message, it confirms the order and sends a response to the client. When the response
message is received, Order Entry updates the status of the order in the client database.
Note: This sample provides both a rich client and a Web application, but uses a common service project
for accessing the server.
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and configure it.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
50 Lotus Expeditor: Developing Applications for Lotus Expeditor
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, orderentry.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. Follow steps for one of the following sample applications:
v “Order Entry Rich Client sample”
v “Order Entry Web Application sample” on page 52
Order Entry Rich Client sample:
The Order Entry Rich Client application consists of the following bundles:
v Client side bundle providing shared packages:
com.ibm.rcp.samples.orderentry.common
v Client side service implementation:
com.ibm.rcp.samples.orderentry.service
v Client side desktop application:
com.ibm.rcp.samples.orderentry.richapp
v Server side demo application:
com.ibm.rcp.samples.orderentry.server
To configure and run the Order Entry Rich Client application, follow these steps:
1. Select Run > Run from the main menu.
2. Select Client Services and click New.
3. Type a unique name for your configuration, such as Order Entry.
4. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.orderentry.common,
com.ibm.rcp.samples.orderentry.service, and com.ibm.rcp.samples.orderentry.richapp plug-ins are
selected.
5. Click Run. After the runtime starts, click Open > Order Entry Rich Client Sample to open the
application perspective. This new perspective contains two views: Submit Order, and Order Status.
a. In the Submit Order view, select a customer from the list.
b. Select a quantity for cans and bottles and click Submit.
Note: The Order Status view displays any existing orders for this customer. If orders are
displayed, the order status for those orders is also displayed.
In addition, if this application is connected to a server-side application, the order’s status
changes from Queued to Confirmed. Click Refresh to synchronize orders and update status.
6. Repeat this procedure to submit orders for any additional customers.
7. To close the application, right-click the Perspective tab and select Close.
To configure and run the server, follow these steps while the client runtime is running. The server
application causes the order status to be changed from Queued to Confirmed in the Order Status view.
1. Select Run > Run from the main menu.
2. Select Java Application and click New.
3. Type a name for your configuration, such as Order Entry Server.
Product overview 51
4. Click Browse to identify the project and then select com.ibm.rcp.samples.orderentry.server from the
list.
5. Click Search to identify the main class.
6. Select Server from the displayed list to select com.ibm.rcp.samples.orderentry.server.Server as the
class containing the main method.
7. Click Run to start the application. The Order Entry Server application takes a few seconds to start.
The server application is ready to begin handling messages when the following message is displayed:
DEBUG -- BaseMQeTransport: Transport thread started
If you have already created orders, the following message is displayed in the Server application
console:
DEBUG -- PROCESS ORDER
8. In the Order Status view, click Refresh to update the order status. As response messages are received
from the server application, the following message is displayed in the console:
DEBUG -- RESPONSE RECEIVED
Order Entry Web Application sample:
The Order Entry Web Application consists of the following bundles:
v Client side bundle providing shared packages:
com.ibm.rcp.samples.orderentry.common
v Client side service implementation:
com.ibm.rcp.samples.orderentry.service
v Client side Web application:
com.ibm.rcp.samples.orderentry.webapp
v Server side demo application:
com.ibm.rcp.samples.orderentry.server
To configure and run the Order Entry Web application, follow these steps:
1. Select Run > Run from the main menu.
2. Select Client Services and click New.
3. Type a unique name for your configuration, such as Order Entry.
4. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.orderentry.common,
com.ibm.rcp.samples.orderentry.service, and com.ibm.rcp.samples.orderentry.webapp plug-ins are
selected.
5. Click Run. After the runtime starts, click Open > Order Entry Web Sample to open the application. A
browser perspective opens and displays the first page of the application.
6. To use the application, follow these steps:
a. Click Start.
b. Select a customer from the list.
c. Select New Order.
d. Select a quantity for cans and bottles and click Submit.
Note: If this application is connected to a server-side application, the order’s status changes from
Queued to Confirmed. Click Refresh to synchronize orders and update status.
7. Repeat this procedure to submit orders for any additional customers.
8. To close the application, right-click the Perspective tab and select Close.
52 Lotus Expeditor: Developing Applications for Lotus Expeditor
To configure and run the server, follow these steps while the client platform is running. The server
application causes the order status to be changed from Queued to Confirmed in the Order Status view.
1. Select Run > Run from the main menu.
2. Select Java Application and click New.
3. Type a name for your configuration, such as Order Entry Server.
4. Click Browse to identify the project and then select com.ibm.rcp.samples.orderentry.server from the
list.
5. Click Search to identify the main class.
6. Select Server from the displayed list to select com.ibm.rcp.samples.orderentry.server.Server as the
class containing the main method.
7. Click Run to start the application. The Order Entry Server application takes a few seconds to start.
The server application is ready to begin handling messages when the following message is displayed:
DEBUG -- BaseMQeTransport: Transport thread started
If you have already created an order, the following message is displayed in the Server application
console:
DEBUG -- PROCESS ORDER
8. In the Order Status view, click Refresh to update the order status. As response messages are received
from the server application, the following message is displayed in the console:
DEBUG -- RESPONSE RECEIVED
Related sample:
See the Echo Secure sample for Mobile Web Services security enablementThis sample demonstrates Mobile Web Services Security.
OSGi Preferences Service sample
This sample demonstrates the use of the OSGi Preferences Service to save and retrieve preference
information.
Time required: 2 minutes
Three Client Services projects are included in this sample: com.ibm.rcp.samples.preferences.osgi.service,
com.ibm.rcp.samples.preferences.osgi.serviceimpl, and
com.ibm.rcp.samples.preferences.osgi.serviceconsumer.
The service project defines an interface for a service.
The serviceimpl project provides an implementation of the service, and the serviceconsumer project
requests and uses the service. The serviceimpl project contains a service that stores some preference or
configuration information using the OSGi Preferences service.
The serviceconsumer project calls the service, causing preference information to be retrieved and stored.
Key concepts demonstrated in this sample are as follows:
v Use of the OSGi Preferences Service
v Bundle componentization into service consumer, service implementer, and service interfaces,
decoupling consumer from implementer
v Registration of OSGi Services
v Use of the OSGi Service Tracker
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
Product overview 53
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, osgiprefs.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as OSGi Preferences.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.preferences.osgi.service, ,
com.ibm.rcp.samples.preferences.osgi.serviceimpl, and
com.ibm.rcp.samples.preferences.osgi.serviceconsumer plug-ins are selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To run the sample:
a. Enter start com.ibm.rcp.samples.preferences.osgi.serviceimpl to start the sample service
implementation.
b. Enter start com.ibm.rcp.samples.preferences.osgi.serviceconsumer to start the sample service
consumer. The service consumer calls the service implementation’s run() method, causing
messages to be printed to the console, and preference information to be updated.
c. Enter stop com.ibm.rcp.samples.preferences.osgi.serviceconsumer to stop the sample service
consumer.
d. Enter start com.ibm.rcp.samples.preferences.osgi.serviceconsumer to start the sample service
consumer again. Note the change to the value of the last execution time from the previous time
that you entered the start command.
Output is similar to the following:
osgi> start com.ibm.rcp.samples.preferences.osgi.serviceimpl
osgi> start com.ibm.rcp.samples.preferences.osgi.serviceconsumer
SampleService data location: file:/C:/Documents and Settings/james/workspace/
This service has not been previously executed by james
osgi> stop com.ibm.rcp.samples.preferences.osgi.serviceconsumer
osgi> start com.ibm.rcp.samples.preferences.osgi.serviceconsumer
SampleService data location: file:/C:/Documents and Settings/james/workspace/
This service was last executed by james on 1138074724210
Related sample:
See the Eclipse Preferences sample for use of the Eclipse preferences API and extension pointsThis technology sample demonstrates usage of the Eclipse Preferences capabilities.
Pizza JSP sample
This sample demonstrates the usage of Java Server Pages (JSPs) within a Web application intended for
deployment on the Web Container. The sample application is a pizza-ordering Web application.
Time required: 5 minutes
54 Lotus Expeditor: Developing Applications for Lotus Expeditor
Key concepts demonstrated in this sample are as follows:
v Use of the WctWebApplication extension point
v Use of JSP
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, pizzajsp.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Switch to the J2EE perspective.
b. Right-click com.ibm.rcp.samples.webapp.pizzajsp.
c. Select Run > Run on Server from the main menu.
d. Select a Client Services server from the list. If a Client Services server is not on the list, select
Manually define a server and choose it from the list of server types.
e. Click Finish.3. To run the sample, use the Web browser opened by the tools. The Web application URL is displayed.
You can also view the Web application in the client runtime. To do so, select Open > Sample Pizza
JSP Web Application. To end the runtime, select File > Exit from the menu.
Note that this sample does not require use of the console to run the sample.
Related samples:
See the Web Application sample for a simple non-secured Web applicationThis sample demonstrates the usage of Java Server Pages (JSPs) within a Web application intended for
deployment on the local Web Container. The sample application is a Hello World application.
See the Web Application Log sample for a Web application that provides a user interface to log entriesThis sample demonstrates the usage of Java Server Pages (JSPs) within a web application intended for
deployment on the local Web Container. The sample application displays log contents obtained from
the OSGi LogReader service.
See the Secure Web Application sample for a Web application secured using form-based
authenticationThis sample demonstrates a local, secured Web application intended for deployment on the local Web
container.
Portlet Communication sample
This sample demonstrates two Java Specification Request (JSR) 168 portlets exchanging data.
Time required: 2 minutes
You can use this sample only on the desktop runtime.
This sample contains two Client Services projects: com.ibm.rcp.samples.portletcommunication.portlets and
com.ibm.rcp.portletcommunications.views.
Product overview 55
The portlets project contains the two portlets that are used in this sample. The views project contains the
user interface objects required to display the two portlets using the portlet viewer capabilities.
Key concepts demonstrated in this sample are as follows:
v Use of the PortletViewer view
v Wiring of two portlets for data exchange
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, portletcomm.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.
Note: The WSDL files necessary to define the property broker bindings for the portlets use extensions
to the WS-I standard for WSDL. As a result, WSDL validation warnings are listed in the
Problems view on resources P2PSearchResult.wsdl and P2PQuickSearch.wsdl in the
com.ibm.rcp.samples.portletcommunication.portlets project. These warnings do not affect the
ability to run the sample successfully.
2. To launch the test runtime:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as Portlet Communication.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.portletcommunication.portlets
and com.ibm.rcp.samples.portletcommunication.views plug-ins are selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To start the sample:
a. When the workbench displays, select Open > Portlet Communication Sample to open its
perspective.
b. Enter search text in the Search Portlet View, and select Search. The result is displayed in the Result
Portlet View.
c. To end the runtime, select File > Exit from the menu.
This sample does not require use of the console to run.
Related samples:
See the Simple Portlet sample to create a simple portletThis sample demonstrates a local JSR 168 portlet enabled for local portlet container.
See the Portlet Aggregation sample to lay out multiple portlets on a single Web pageThis sample demonstrates the use of a Web page aggregation of portlets.
56 Lotus Expeditor: Developing Applications for Lotus Expeditor
See the Portlet Viewer sample as an alternative mechanism for viewing local portletsThis sample demonstrates the use of the Portlet Viewer, a predefined view enabling display of Java
Specification Request (JSR) 168 and WSRP-compliant portlets. This sample demonstrates the viewing
of JSR 168 portlets only.
Rich Application sample
This sample creates a Client Services project to illustrate the basic elements of a rich application.
Time required: 2 minutes
You can use this sample only on the desktop runtime.
Key concepts demonstrated in this sample are as follows:
v Use of the WctApplication extension point
v Assembly of an application with one perspective, two views, and three actions
v Use of view options to prevent closing or moving
v Use of global menu actions
v Use of application-specific (perspective) actions
v Use of view-specific actions
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, richapp.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as Rich Application.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.richapp plug-ins is selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. This sample makes several contributions to the client user interface and menu system. To view these
capabilities:
v After the workbench starts, select Sample Menu > Run Global Action to perform this action. The
action results in a dialog box containing the message Global Action. Global actions are available
even when no perspectives are open.
v Select Open > Sample Rich Application to open the application. The application demonstrates a
perspective containing two views. Primary View is a non-closable, non-movable view. Secondary
View can be closed or moved within the perspective. The sample application was contributed to the
launcher list by implementing the WctApplication extension point.
Product overview 57
v With the sample application opened, close the Secondary View. Then select Sample Menu > Open
Secondary View to reopen the view. This demonstrates actions (Open Secondary View) that are
specific to an application (perspective).
v Click Secondary View to highlight this view. Select Sample Menu > Run View Action to run a
view specific action. This results in a dialog box containing the message View Action. This
demonstrates an action that is specific to a view.
Troubleshooting:
Problem:
The sample application is not available in the Open menu.
Cause:
The com.ibm.rcp.samples.richapp plug-in is not present or does not resolve in the client platform.
Action:
Verify that the com.ibm.rcp.samples.richapp plug-in is contained in the runtime. If the plug-in is
present, check the status of the plug-in. Go to the console view, and enter the b
com.ibm.rcp.samples.richapp command. The second line of output indicates the plug-in status. If the
status is not RESOLVED or ACTIVE, then there is a problem with the plug-in and its dependencies.
Use the diag com.ibm.rcp.samples.richapp command to identify any unsatisfied dependencies.
Resolve any missing dependencies and restart the platform.
Rich Text Editor sample
This sample demonstrates how to include the Rich Text Editor within an Eclipse view.
Time required: 2 minutes
You can use this sample only on the desktop runtime.
The key concept demonstrated in this sample is the use of Rich Text Editor APIs to create toolbar and
editor widgets.
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, rte.jar, or browse to the com.ibm.rcp.tools.samplegallery
plug-in directory, then the archive directory, and select the archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To configure and run the sample:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as Rich Text Editor.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.richtexteditor plug-in is selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.
58 Lotus Expeditor: Developing Applications for Lotus Expeditor
3. To start the sample, when the workbench displays, select Open > Rich Text Editor Sample to open its
perspective. Enter text within the editor view. You may use the coolbar icons within the Rich Text
Editor View to format the text.
This sample does not require use of the console to run.
Related sample:
See the Echo Secure sample for Mobile Web Services security enablementThis sample demonstrates Mobile Web Services Security.
Secured Web application sample
This sample demonstrates a local, secured Web application intended for deployment on the local Web
container.
Time required: 5 minutes
You can use this sample only on the desktop runtime.
This sample contains three projects: com.ibm.rcp.samples.ws.echo.client,
com.ibm.rcp.samples.ws.echo.marshal, and com.ibm.rcp.samples.ws.echo.server.
Key concepts demonstrated in this sample are as follows:
v Expose an OSGi service as a Web Service
v Use custom serialization and deserialization (also referred to as custom marshalling)
v Access a Web Service using a dynamic client
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, secwebapp.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Locate the project com.ibm.rcp.samples.webapp.secured.form in the Project Explorer under the
Dynamic Web Projects folder.
b. Right-click the project and select Run > Run on Server. The Server Selection dialog is displayed.
c. Select a Client Services server from the list. If a Client Services server is not on the list, select
Manually define a server and choose it from the list of server types.
d. Click Finish to launch the runtime.
Running the sample:
To run the sample, follow these steps:
1. View the Web browser opened by the tools. Select Create Users to create the necessary users for this
sample.
2. Select one of the available pages (admin.jsp, user.jsp, or guest.jsp). The login page is displayed.
Product overview 59
3. Enter the Userid and Password for one of the users listed on the page. If the Userid is not enabled to
access a particular page, an error page is displayed.
4. Use the browser’s Back button to return to the Web application main page (/secured_form/index.jsp).
5. Click Logout to log out of the application, allowing another user to be selected.
The following table shows the accessibility of the pages within the application:
User index.jsp admin.jsp user.jsp guest.jsp
not logged in U
nancy (admin) U U U U
bob (user) U U U
fred (guest) U U
Before exiting the runtime, select Delete Users from /secured_form/index.jsp to clean up the users
added for this sample.
To end the runtime, select File > Exit from the menu.
Note: This sample does not require use of the console to run.
Related samples:
See the Web Application sample for a simple, non-secured Web applicationThis sample demonstrates the usage of Java Server Pages (JSPs) within a Web application intended for
deployment on the local Web Container. The sample application is a Hello World application.
See the Web Application Log sample for another Web application that provides a user interface to log
entriesThis sample demonstrates the usage of Java Server Pages (JSPs) within a web application intended for
deployment on the local Web Container. The sample application displays log contents obtained from
the OSGi LogReader service.
See the Pizza JSP sample for a JSP-based Web applicationThis sample demonstrates the usage of Java Server Pages (JSPs) within a Web application intended for
deployment on the Web Container. The sample application is a pizza-ordering Web application.
Service Tracker sample
This sample creates two Client Services project to demonstrate how to use OSGi service trackers.
Time required: 2 minutes
This sample creates the following projects: com.ibm.rcp.samples.servicetracker, and
com.ibm.rcp.samples.servicetracker.service.
The servicetracker project creates and starts two service trackers for the
com.ibm.pvc.webcontainer.listeners.HttpSettingListener service. One tracker is a simple implementation
that returns an instance of the service, if one is available. The second tracker is a more sophisticated
implementation that receives notifications of changes in service life cycle.
The service bundle registers an instance of the com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service. By starting and stopping this bundle during run time, you can view output to see how the events
are processed.
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
60 Lotus Expeditor: Developing Applications for Lotus Expeditor
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, servicetracker.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as Service Tracker.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.servicetracker and
com.ibm.rcp.samples.servicetracker.service plug-ins are selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To start the sample:
a. In the Console view, enter ss to list the bundle IDs and locate the servicetracker ID.
b. Enter a start bundle_name command for each plug-in in the Console view. Bundle names are as
follows:
v com.ibm.rcp.samples.servicetracker – Starts the sample bundle. This causes the
CustomServiceTracker to identify all of the HttpSettingListener service implementations that are
registered. In addition, a message is displayed every 10 seconds showing the current set of
HttpSettingListener services that are currently registered.
v com.ibm.rcp.samples.servicetracker.service – Starts the sample service bundle. This registers
an additional instance of the HttpSettingListener service, causing output from the
CustomServiceTracker. After two seconds, the service will modify itself causing additional
output from the CustomServiceTracker.
Note: To stop a bundle, use the stop bundle_name command.
Output is similar to the following:
osgi> start com.ibm.rcp.samples.servicetracker
CustomServiceTracker: Service added:
objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service.id:39
CustomServiceTracker: Service added:
service.vendor:IBM
service.description:HttpSettingListener for the Web Http Service 1.0 IBM Implementation
objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service.id:46
CustomServiceTracker: Service added:
objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service.id:65
CustomServiceTracker: Service added:
objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
Product overview 61
service.id:66
osgi> SimpleServiceTracker: Tracking service at 0 seconds.
ServiceTracker monitoring 4 service instances
Service instance returned if getService() called:
objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service.id:39
SimpleServiceTracker: Tracking service at 10 seconds.
ServiceTracker monitoring 4 service instances
Service instance returned if getService() called:
objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service.id:39
osgi> start com.ibm.rcp.samples.servicetracker.service
CustomServiceTracker: Service added:
service.vendor:IBM
service.ranking:-2147483648
service.description:Service part of com.ibm.rcp.samples.servicetracker
objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service.id:94
osgi> CustomServiceTracker: Service modified: service.update:1149994259253
service.vendor:IBM
service.ranking:-2147483648
service.description:Service part of com.ibm.rcp.samples.servicetracker
objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service.id:94
SimpleServiceTracker: Tracking service at 20 seconds.
ServiceTracker monitoring 5 service instances
Service instance returned if getService() called:
objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service.id:39
osgi> stop com.ibm.rcp.samples.servicetracker.service
CustomServiceTracker: Service removed: service.update:1149994259253
service.vendor:IBM
service.ranking:-2147483648
service.description:Service part of com.ibm.rcp.samples.servicetracker
objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service.id:94
osgi> SimpleServiceTracker: Tracking service at 30 seconds.
ServiceTracker monitoring 4 service instances
Service instance returned if getService() called:
objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service.id:39
osgi> stop com.ibm.rcp.samples.servicetracker
CustomServiceTracker: Service removed: service.vendor:IBM
62 Lotus Expeditor: Developing Applications for Lotus Expeditor
service.description:HttpSettingListener for the Web Http Service 1.0 IBM Implementation
objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service.id:46
CustomServiceTracker: Service removed: objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service.id:65
CustomServiceTracker: Service removed: objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service.id:39
CustomServiceTracker: Service removed: objectClass:
com.ibm.pvc.webcontainer.listeners.HttpSettingListener
service.id:66
Simple Portlet sample
This sample demonstrates a local JSR 168 portlet enabled for local portlet container.
Time required: 2 minutes
You can use this sample only on the desktop runtime.
Key concepts demonstrated in this sample are as follows:
v Definition of a JSR 168 portlet for use on the local portlet container
v Use of the WctWebApplication extension point to enable access to a single portlet
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, calcportlet.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Locate the project com.ibm.rcp.samples.portlets.calculatorportlet in the Project Explorer under the
Dynamic Web Projects folder.
b. Right-click the project and select Run > Run on Server. The Server Selection dialog is displayed.
c. Select a Client Services server from the list. If a Client Services server is not on the list, select
Manually define a server and choose it from the list of server types.
d. Click Finish to launch the runtime.
3. To run the sample, use the Web browser opened by the tools. The portlet URL is displayed. You can
also view this portlet in the client runtime. To do so, select Open > Sample Calculator Portlet. To end
the runtime, select File > Exit from the menu.
This sample does not require use of the console to run.
Related samples:
Product overview 63
See the Portlet Aggregation sample to lay out multiple portlets on a single Web pageThis sample demonstrates the use of a Web page aggregation of portlets.
See the Portlet Viewer sample as an alternative mechanism for viewing local portletsThis sample demonstrates the use of the Portlet Viewer, a predefined view enabling display of Java
Specification Request (JSR) 168 and WSRP-compliant portlets. This sample demonstrates the viewing
of JSR 168 portlets only.
See the Portlet Communication sample to wire two portlets together for action processingThis sample demonstrates two Java Specification Request (JSR) 168 portlets exchanging data.
Simple Portlet Viewer sample
This sample demonstrates the use of the Portlet Viewer, a predefined view enabling display of Java
Specification Request (JSR) 168 and WSRP-compliant portlets. This sample demonstrates the viewing of
JSR 168 portlets only.
Time required: 2 minutes
You can use this sample only on the desktop runtime. This sample requires the use of the “Simple Portlet
sample” on page 63.
Key concepts demonstrated in this sample are as follows:
v Use of the com.ibm.rcp.portletviewer.portlets extension point to define the portlet for viewing by the
viewer
v Use of compound view IDs for defining the layout of a simple perspective
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, portletviewer.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as Portlet Viewer.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.portletviewer and
com.ibm.rcp.samples.portlets.calculatorportlet plug-ins are selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To start the sample when the workbench displays, select Open > Sample PortletViewer to open its
perspective.
This sample does not require use of the console to run.
Related samples:
64 Lotus Expeditor: Developing Applications for Lotus Expeditor
See the Simple Portlet sample to create a simple portletThis sample demonstrates a local JSR 168 portlet enabled for local portlet container.
See the Portlet Aggregation sample to lay out multiple portlets on a single Web pageThis sample demonstrates the use of a Web page aggregation of portlets.
See the Portlet Communication sample to wire two portlets together for action processingThis sample demonstrates two Java Specification Request (JSR) 168 portlets exchanging data.
Web Page Aggregation of Portlets sample
This sample demonstrates the use of a Web page aggregation of portlets.
Time required: 2 minutes
You can use this sample only on the desktop runtime. This sample requires the use of the “Simple Portlet
sample” on page 63.
This sample contains two Client Services projects: com.ibm.rcp.samples.portlets.portletaggregator and
com.ibm.rcp.portlets.dateportlet.
The portletaggregator project is a Client Services Web project that contains a JavaServer Page that
aggregates the content of two portlets onto a single Web page.
Key concepts demonstrated within this sample are as follows:
v Definition of a JSR 168 portlet for use on the local portlet container
v Use of the WctWebApplication extension point to enable access to a single portlet
Setup instructions:
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, simpleaggr.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Locate the project com.ibm.rcp.samples.portlets.portletaggregator in the Project Explorer under
the Dynamic Web Projects folder.
b. Right-click the project and select Run > Run on Server. The Server Selection dialog is displayed.
c. Select a Client Services server from the list. If a Client Services server is not on the list, select
Manually define a server and choose it from the list of server types.
d. Use the Default Target to launch the sample and click Next.
e. On the Add and Remove Projects page, the Configured projects list must contain the
com.ibm.rcp.samples.portlets.portletaggregator, com.ibm.rcp.samples.portlets.calculatorportlet and
com.ibm.rcp.samples.portlets.dateportlet projects. If these projects are not listed, select each of
these projects in the Available projects list and then click Add.
f. Click Finish to launch the server.
Product overview 65
3. To run the sample, use the Web browser opened by the tools. The Web application URL is displayed.
You can also view the Web application in the client runtime. To do so, select Open > Sample Portlet
Web Page Aggregation. To end the runtime, select File > Exit from the menu.
This sample does not require use of the console to run.
Troubleshooting:
Problem:
The Web page displays an error that the page cannot be displayed due to an HTTP 500 - Internal
Server error or the following message:
Error 500: Unable to get RequestDispatcher for Context:
[/.com.ibm.rcp.samples.portlets.dateportlet] and URL:
[/DatePortlet/default/ver=1.0]. Verify values and/or enable cross context access.
An error is also logged in the runtime and can be viewed in the console.
Cause:
The com.ibm.rcp.samples.portlets.dateportlet sample has been not added to the launch.
Action:
From the Servers view, right-click the Client Services server and then select Open. From the Server
Overview page, select Advanced to open the launch configuration. Select the Plug-ins tab and ensure
that the com.ibm.rcp.samples.portlets.dateportlet plug-in is selected.
Problem:
The Web page displays an error that the page cannot be displayed due to an HTTP 500 - Internal
Server error or the following message:
Error 500: Unable to get RequestDispatcher for Context: [/CalculatorPortlet]
and URL: [/CalculatorPortlet/default/ver=1.0]. Verify values and/or
enable cross context access.
An error is also logged in the runtime and can be viewed in the console.
Cause:
The com.ibm.rcp.samples.portlets.calculatorportlet sample has been not added to the launch.
Action:
From the Servers view, right-click the Client Services server and then select Open. From the Server
Overview page, select Advanced to open the launch configuration. Select the Plug-ins tab and ensure
that the com.ibm.rcp.samples.portlets.calculatorportlet plug-in is selected. Related samples:
See the Simple Portlet sample to create a simple portletThis sample demonstrates a local JSR 168 portlet enabled for local portlet container.
See the Portlet Communication sample to wire two portlets together for action processingThis sample demonstrates two Java Specification Request (JSR) 168 portlets exchanging data.
See the Simple Portlet Viewer sample for an alternative mechanism for viewing local portletsThis sample demonstrates the use of the Portlet Viewer, a predefined view enabling display of Java
Specification Request (JSR) 168 and WSRP-compliant portlets. This sample demonstrates the viewing
of JSR 168 portlets only.
Web Application sample
This sample demonstrates the usage of Java Server Pages (JSPs) within a Web application intended for
deployment on the local Web Container. The sample application is a Hello World application.
Time required: 2 minutes
Setup instructions:
66 Lotus Expeditor: Developing Applications for Lotus Expeditor
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, webapplication.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Locate the project com.ibm.rcp.samples.webapp.hello in the Project Explorer under the Dynamic
Web Projects folder.
b. Right-click the project and select Run > Run on Server. The Server Selection dialog is displayed.
c. Select a Client Services server from the list. If a Client Services server is not on the list, select
Manually define a server and choose it from the list of server types.
d. Click Finish to launch the runtime.3. To run the sample, use the Web browser opened by the tools. The message Hello World is displayed
in the browser. You can also view this portlet in the client runtime. To do so, select Open > Sample
Hello JSP Web Application. To end the runtime, select File > Exit from the menu.
This sample does not require use of the console to run.
Related samples:
See the Secured Web Application sample for a Web application secured using form-based
authenticationThis sample demonstrates a local, secured Web application intended for deployment on the local Web
container.
See the Web Application Log sample for a Web application that provides a user interface to log entriesThis sample demonstrates the usage of Java Server Pages (JSPs) within a web application intended for
deployment on the local Web Container. The sample application displays log contents obtained from
the OSGi LogReader service.
See the Pizza JSP sample for a JSP-based Web applicationThis sample demonstrates the usage of Java Server Pages (JSPs) within a Web application intended for
deployment on the Web Container. The sample application is a pizza-ordering Web application.
Web Application Log sample
This sample demonstrates the usage of Java Server Pages (JSPs) within a web application intended for
deployment on the local Web Container. The sample application displays log contents obtained from the
OSGi LogReader service.
Time required: 2 minutes
Key concepts demonstrated in this sample are as follows:
v Use of the WctWebApplication extension point
v Use of JSP
v Use of the OSGi LogReader service
Setup instructions:
Product overview 67
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, weblog.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Switch to the J2EE perspective.
b. Right-click com.ibm.rcp.samples.webapp.weblog.
c. Select Run > Run on Server from the main menu.
d. Select a Client Services server from the list. If a Client Services server is not on the list, select
Manually define a server and choose it from the list of server types.
e. Click Finish.3. To run the sample, use the Web browser opened by the tools. The Web application URL is displayed.
You can also view the Web application in the client runtime. To do so, select Open > Sample Web
Log Web Application. To end the runtime, select File > Exit from the menu.
This sample does not require use of the console to run the sample.
Related samples:
See the Web Application sample for a simple, non-secured Web applicationThis sample demonstrates the usage of Java Server Pages (JSPs) within a Web application intended for
deployment on the local Web Container. The sample application is a Hello World application.
See the Secure Web Application sample for a Web application secured using form-based authenticationThis sample demonstrates a local, secured Web application intended for deployment on the local Web
container.
See the Pizza JSP sample for a JSP-based Web applicationThis sample demonstrates the usage of Java Server Pages (JSPs) within a Web application intended for
deployment on the Web Container. The sample application is a pizza-ordering Web application.
See the Log and Log Reader sample for use of the OSGi Log Service and LogReader ServiceThis sample demonstrates the use of the OSGi Log and LogReader services.
XML Parser sample
This sample creates a Client Services project to demonstrate how to use a Simple API for XML (SAX)
parser to get information stored in XML files.
Time required: 2 minutes
Key concepts demonstrated in this sample are as follows:
v Accessing a SAX Parser using the OSGi XML Parsing Service
v Loading resources from Bundle objects
v Preparing InputSource objects to enable DTD or Schema verification for resources contained in the
bundle
Setup instructions:
68 Lotus Expeditor: Developing Applications for Lotus Expeditor
Before you can run the sample, you must first import the sample into your workspace and launch the
test runtime.
1. To import the sample:
a. Select File > Import.
b. From the Import Wizard, select General and then select Existing Projects into Workspace. Click
Next.
c. Select the Archive file radio button.
d. Enter the name of the sample jar file, xmlparser.jar, or browse to the
com.ibm.rcp.tools.samplegallery plug-in directory, then the archive directory, and select the
archive file.
e. The dialog shows the projects present in the archive jar file. Select Finish to install the projects
into your workspace.2. To launch the test runtime:
a. Select Run > Run from the main menu.
b. Select Client Services and click New.
c. Type a unique name for your configuration, such as XML Parser.
d. Select the Plug-ins tab and verify that the com.ibm.rcp.samples.xmlparser plug-in is selected.
e. Select the Arguments tab and verify that –console is in the Program Arguments field.
f. Click Apply and then Run.3. To start the sample, enter start com.ibm.rcp.samples.xmlparser in the Console view.
Note: To stop the sample, enter stop com.ibm.rcp.samples.xmlparser.
Output is similar to the following:
osgi> start com.ibm.rcp.samples.xmlparser
osgi> ***Begin Document***
[bookshelf]
[TechnologyBooks]
[Book]
[Name]
JAVA 2 Programming
[/Name]
[Price]
$30.99
[/Price]
[TotalPages]
325
[/TotalPages]
[/Book]
[Book]
[Name]
J2EE Design and Development
[/Name]
[Price]
$52.49
[/Price]
[TotalPages]
583
[/TotalPages]
[/Book]
[/TechnologyBooks]
[/bookshelf]
***End Document***
Product overview 69
70 Lotus Expeditor: Developing Applications for Lotus Expeditor
Developing applications
This section provides comprehensive and detailed information on developing applications for Lotus
Expeditor.
Application models
There are several application patterns that are recommended for use in the managed client platform. One
pattern is the browser user interface pattern, which is supported by the Web Application Model. Web
applications present their user interface through the use of generated scripting language such as HTML,
which is rendered for display by a browser.
Another pattern is to build a graphical user interface using Eclipse, to aggregate display components into
views, and views into perspectives. Applications will be defined using extension points to define the
actions, views, and perspective that provide the user interface. This pattern is the rich client user interface
pattern, which is supported by the Rich Client Application Model.
For Lotus Expeditor Client for Desktop, there are additional application patterns. The portlet pattern is
supported by the Portlet Application Model. This pattern is based on JSR 168.
Another pattern is the Composite Application pattern, which is supported by the Composite Application
Model. In this model, multiple applications cooperate by using inter-component communications. For
example, you can use the Property Broker to communicate among portlets and/or Eclipse applications
running in the same client process. Or, you can use the micro broker to communicate among applications
running in the same or different processes.
Application design considerations
You can design client applications in much the same way that you design standard Enterprise
applications. However, there are unique considerations for designing client applications. The list of
considerations in this section is not necessarily comprehensive for all possible decision points; however,
this list provides key considerations for developing client applications.
End-to-End applications
The client and server platforms enable the development of end-to-end applications through an end-to-end
programming model (see figure below) that connects managed client applications to Enterprise
applications, services and data. An end-to-end application can be distributed between a client device and
a server in which case there are two nodes in the application. However, an end-to-end application might
be distributed across more nodes. The exact design of an end-to-end application depends on your specific
requirements; however, this section discusses considerations on how you might construct these
applications.
© Copyright IBM Corp. 2004, 2006 71
WebUI
RichUI
Local BusinessLogic Services
Web Services
SyncMLLibraries
MQe
DB2e orCloudscape
EnterpriseManagement
Agent
Update Manager
Portal Services(Desktop only)
WebServices
[SyncMLLibraries]
MQGateway
DB2eSync Server
DeviceManagement
Server
Update sites
Portal Server
Consume and publishWeb Services
Synchronize objects
Send and receivesecure transactions
Synchronizerelational data
Server-managed SWinstall & maintenance
User-initiated SWinstall & Maintenance
Aggregation, role-basedaccess control
Sync
ESB
Replicate
Data
MessagingApplications
RDB's
Lotus Expeditor "Connectors"
Lotus Expeditor - Client
Lotus Expeditor - Server
Managed client applications can use WebSphere MQ Everyplace (MQe) to exchange secure transactional
messages with Enterprise applications that support MQ messaging. MQ Everyplace operates in many
topologies, from peer-to-peer, to client, to server using the MQ Everyplace gateway technology. For
example, a JEE application can implement Message Driven Beans (MDB) to exchange messages with a
managed client application. This exchange can occur through an MQ Everyplace Gateway-MQ Server
configuration. However, managed client applications can also use MQe to exchange messages directly
with other MQe applications in the network.
DB2 Everyplace and Cloudscape are both capable of synchronizing with the DB2 Everyplace (DB2e) Sync
Server, using the IBM ISync technology provided by the client platform. A System Administrator
configures the DB2e Sync Server to synchronize data with Enterprise databases. The initial
synchronization activity creates the local database schema, and also populates the initial set of data in the
local database on a device. When a client application updates the local database, synchronization can
transfer that data to Enterprise databases that are configured to receive it. When Enterprise applications
update data in an Enterprise database, synchronization can transfer that data to local device databases
that are configured to receive it. Database administrators set up the DB2e Sync Server with the necessary
subscriptions for synchronization, and can set up filtering of data to limit the amount of data distributed
between nodes. The DB2e Sync Server supports synchronizing relational data on the client with relational
data on the following Enterprise databases: DB2 Universal Database™ Informix® Dynamic Server, IBM
Cloudscape, Lotus Domino® Server, Oracle, Microsoft SQL Server, and Sybase Adaptive Server Enterprise.
72 Lotus Expeditor: Developing Applications for Lotus Expeditor
Note: Administrators can use the Lotus Expeditor server to install and configure the DB2 Everyplace
Synchronization Server, WebSphere MQ Everyplace, including the function necessary for
WebSphere MQ Everyplace to act as a gateway server to the WebSphere MQ products, and the
Device Management Server.
Managed client applications can also consume and provide Web Services. This requires an active
connection between the Web services consumer and provider.
An optional capability, which some customers have chosen to implement, is another IBM product called
Lotus Mobile Connect. Lotus Mobile Connect enables client applications to operate over secure,
optimized, roaming network connections on wireless and wire-line networks. Lotus Mobile Connect
installs below TCP/IP APIs so TCP/IP applications can continue to run without change and benefit from
these capabilities.
Topology
When you build a client application, you should determine which types of clients your application will
support. For example, will your application run on high-powered clients, such as desktops and laptops,
or handheld devices, such as PDA’s, or all of the above?
After you select the client(s), you should consider the capabilities of these client(s). For example,
handheld devices typically have slower processors and less memory than desktop or laptop clients, so
applications that require intensive floating point calculations or substantial memory are not appropriate
for handheld devices, but are perfectly fine for desktops and laptops. DB2e and Cloudscape are both
embedded databases. However, DB2e is much smaller than Cloudscape, so you might choose DB2e for
handheld devices such as PDAs and choose Cloudscape, which has greater database capabilities, for more
powerful clients, such as desktops and laptops.
Device form factors - such as screen size, color, user input types - also present challenges. For example,
complex forms requiring a lot of specific data entry might not be appropriate for a device that doesn’t
have a keyboard and uses a stylus to point at a keyboard.
You should also consider the network connectivity of your clients. Is the client always connected to the
network, intermittently connected to the network, or never connected to the network? You should also
consider speed, reliability, and cost of the network connection. For example, client applications requiring
large data transfers might not be suitable for a cell phone connection that charges by the byte, so a good
design point might be to handle large transfers of data when the user is present in an office environment
with a high-speed, no-cost network connection.
Business logic
When you build an end-to-end application, you must decide how to distribute the business logic across
the nodes that comprise the application so your mobile users are productive when they are offline. The
amount of business logic in the client application must be sufficient to perform all necessary work.
However, you might consider moving business logic components that require frequent updating to a
server node to avoid network traffic and administration costs associated with managing these same
components on clients.
In addition, client applications can provide multiple levels of capability, reserving some capability for
when a reliable connection exists to an Enterprise server, and disabling that capability if the server is
unavailable.
Business logic can be packaged within a client application or made available as a separate component,
such as a plug-in, an Embedded Transaction, or local Web Service.
Developing applications 73
Persistence
Most applications manipulate data. This can take the form of read-only access of databases to retrieve
catalog items, or of database update for creation of orders that need to be processed. Data can take the
form of files distributed on disk, or relational database capability. When dealing with databases, you can
choose to use databases only as a local data repository, or as a repository that actively synchronizes with
an Enterprise database.
Consider the following design possibilities:
v Use a database as a local repository when your application requires more advanced data organization
and access capabilities than can be supported by a local file store – especially if a relatively large
amount of data is stored on the device.
v Use a local database to protect, or encrypt, data in case a device is lost or stolen .
v Use database synchronization to exchange the current state of data between local databases and an
Enterprise database, when transaction boundaries or the order of state changes is not important.
v Consider how much data needs to be distributed and when (once only at initialization, one-way from
one node to another only on an infrequent basis, frequent exchange between nodes). Balance these
considerations with the storage capabilities at each node, and the networking requirements that would
permit the exchange to take place.
v Consider database organization, filtering, and conflict resolution policies if you choose to use database
synchronization.
Messaging
Messaging links client applications to Enterprise applications, services, and data. Messaging can take
various forms, whether a plain socket-based application, Web Services, or a more sophisticated store and
forward transaction messaging capability that supports connected as well as disconnected usage. When
choosing a form of messaging, you should consider the following requirements in the design of your
end-to-end applications:
v Online vs. offline operation
v Security, including message confidentiality, non-repudiation, and authentication
v Synchronous vs. asynchronous messaging
v Once-only assured transactions
v Configuration of the messaging solution
Web Services support secure, online, synchronous access to information; however, an online connection
must be active between the Web Services consumer and provider to access information across the
network. Security features include message confidentiality, integrity, and authentication.
MQe provides transaction messaging that supports online and offline operation, security features
(message confidentiality, non-repudiation, authentication), synchronous and asynchronous messaging, and
once-only assured transactions. Transaction messaging provides a convenient mechanism for defining or
identifying transaction boundaries when performing such actions as creating or updating orders,
particularly if the transaction requires updates across multiple resources in the Enterprise such as
inventory management, shipping and billing systems.
Note: Certain nodes in the end-to-end system might not be able to manage or commit transactions
because these nodes might not have transaction coordination and, therefore, do not have the
master copy of all of the data.
The micro broker implements publish and subscribe messaging, which supports online and offline
operations, and synchronous and asynchronous messaging.
74 Lotus Expeditor: Developing Applications for Lotus Expeditor
Management
Management covers a wide range of activities, from initial device provisioning to application
management. When you design your application, you should consider the implications of your design in
regards to application management, specifically in two key areas: componentization and data formats.
First, if you design a monolithic application, then the management system must distribute and install a
complete new copy of the application to update nodes with the latest version of the application.
Depending on the size of the application and the frequency of updates, this design might adversely affect
network capacity and disrupt users. If you design and package the application as multiple installable
components, then the management system distributes and installs only those components that require an
update. You might also be able to reuse components in different applications that run on the same or
different nodes. There is a trade-off between the granularity of the components and the complexity of
administering the set of components that comprise an application.
Second, you must consider the effect on data when updating applications or components. If you design
your components and data format so that local data is upwardly compatible, then users can continue to
access their data after application and component updates. Otherwise, you must provide a mechanism to
update the existing data to match a new or revised format and ensure that all installed components that
consume this data can process this format.
Serviceability
Distributed applications pose additional issues of serviceability as compared to applications running on a
single node. Logging and problem resolution might be difficult if the application is running on one node,
and the node only occasionally connects to a central logging repository. In these situations, you must
consider how to transfer logging information from a node to the central logging repository, how to track
user usage, and help in problem resolution.
Interaction
You must first decide if your application will support user interactions and, if so, which interaction
model to use. You might choose the Web Application or Portlet Application model when moving a Web
application or portlet from the server to the client to reduce development and training costs. You might
choose the Rich Client Application model when you require more control over the user experience.
If your application requires a user interface, then you must consider the device characteristics in the
design of the user interface. For example, if you are developing a Web application for PDA’s and laptops,
then you can design the layout of the Web pages to fit within the constraints of the PDA screen size.
When you run the application on the PDA, the Web browser will render the markup for the PDA screen
size. When you run the same Web application on a laptop, the Web browser will render the same
markup, which should fit within the larger screen size. Of course, there might be cost vs. usability
tradeoffs in supporting a common user interface across multiple device types instead of tailoring the user
interface for each device type. For example, multiple Web pages might be required to perform a set of
related business operations on the smaller screen size of a PDA while a single Web page might suffice for
the larger screen of a laptop.
Cross platform APIs
This section shows you the common API functionality that is supported across the Lotus Expeditor Client
for Desktop and Lotus Expeditor Client for Devices, to assist you in the development of your managed
client applications. For common API functionality, Lotus Expeditor Client for Devices either supports the
same APIs or a proper subset of APIs available on Lotus Expeditor Client for Desktop. As a result, Lotus
Expeditor provides a programming model that scales across desktops, laptops, tablets, and handheld
devices.
Developing applications 75
API Lotus Expeditor Client for Desktop Lotus Expeditor Client for Devices
Java jclDesktop (default) jclDevice (supports JME Foundation
1.1)
OSGi OSGi R4 core plus optional services:
v User Admin Service
v Log Service
v Configuration Admin Service
v Metatype Service
v Preferences Service
v Event Admin Service
OSGi R4 core plus optional services:
v User Admin Service
v Log Service
v Configuration Admin Service
v Metatype Service
v Preferences Service
v Event Admin Service
Eclipse RCP 3.2.1 eRCP 1.0
Data Sync SyncML4J 2.6 SyncML4J 2.6
Database Access JDBC 3.0 with DB2e or Cloudscape JDBC JSR 169 with DB2e
Database Sync ISync with DB2e or Cloudscape ISync with DB2e or * Cloudscape
Messaging – Point-to-Point JMS 1.1 with MQe JMS 1.1 with MQe
Messaging – Pub-Sub JMS 1.1 with micro broker JMS 1.1 with micro broker
Web Services Client JSR 172, JSR 101 JSR 172
Web Applications Servlet 2.3/2.4
JSP 1.2/2.0
Servlet 2.3
JSP 1.2
Embedded Transaction Applications EJB 2.0 subset EJB 2.0 subset
Name Lookup JNDI JNDI
* Available via OEM licensing
Packaging
Client applications and Target Features are packaged as features, each of which consists of one or more
components. The client platform cannot directly run J2EE packaging artifacts such as EAR and WAR files.
Components
The Eclipse framework, and therefore the client platform, is organized around a plug-in and extension
point model. The framework provides a core set of components. Additional components are provided in
a directory or JAR file organized in a specific structure, and implement instantiations of the various
extension points. The framework reads the component declarative information, and incorporates the
components into the correct locations in the framework.
A plug-in is the level at which components are declared to the Eclipse framework. A plug-in is a JAR file
with a plug-in manifest file named plugin.xml. The plug-in manifest describes the plug-in to the
framework and enables a plug-in to consume and/or provide extensions from/to other plug-ins. For
example, a plug-in can provide user interface extensions, such as perspectives, views, editors, and
wizards. It can also provide business logic or core services to other plug-ins, but contribute no extensions
to the user interface.
A bundle is the level at which components are declared to the OSGi Service Framework. A bundle is a
JAR file with a bundle manifest file named MANIFEST.MF. The bundle manifest describes the bundle to the
service framework and enables a bundle to consume and/or provide packages and services from/to
other bundles. Bundles can also include a Bundle Activator class. The Bundle-Activator header in the
bundle manifest file identifies the class to the framework. At startup time, the framework creates an
instance of this class and calls its start() method. The Bundle Activator can then publish services, start
its own threads, and so on. When the bundle shuts down, the framework calls the activator’s stop()
76 Lotus Expeditor: Developing Applications for Lotus Expeditor
method. While the bundle shuts down, the Bundle Activator can release resources that are obtained since
the start method was called and revoke any services it has published.
Recall that the Eclipse framework is built on the OSGi Service Framework. Therefore, you can define each
component in your applications as a plug-in, a bundle, or both depending on your requirements.
Note: For a component to be recognized by the client platform, the toolkit and the Eclipse Plug-in
Development Environment (PDE), it must have a unique name and version. If you develop a
plug-in, specify a unique value for the name attribute and a version number for the version
attribute in the plug-in manifest. If you develop a bundle, specify a unique value for the
Bundle-SymbolicName attribute and a version number for the Bundle-Version attribute in the bundle
manifest.
A component can generally be organized in one of three ways:
v A directory containing at least a plugin.xml file. The directory may also contain a MANIFEST.MF file
located in the META-INF directory, additional files, as well as Java code contained within JAR files.
A plugin.xml file is required if the component defines extension points for use by other plug-ins or
implements extension points provided by other plug-ins.
v A directory containing at least a MANIFEST.MF file in the META-INF directory. The directory will also
contain Java code contained in JAR files. The MANIFEST.MF will refer to the JARs by referencing them
via the Bundle-Classpath attribute.
Components that provide only business logic or OSGi services and do not intend to provide or
implement any extension points can use this format. Components without plugin.xml files that need to
be available when building other components or when launching the client platform by using either
the toolkit or the PDE must be organized in this format.
v A single JAR file containing at least a META-INF\MANIFEST.MF or a plugin.xml file.
Components may be provided for use in the client platform by collecting all of the component artifacts
into a single JAR file. While this organization will run successfully, this organization is not compatible
with the toolkit and Eclipse PDE.
Fragments
A component may not always provide a complete implementation. In some cases, fragments may be used
to complete or extend a component.
For example, the primary component may provide an implementation that contains translatable text in a
default language. Fragments are then used to provide translations for additional languages.
A second case where fragments are often used is to provide platform (processor/operating system)
specific implementations.
Fragments contain either a fragment.xml (similar to a plugin.xml), or a MANIFEST.MF, or both. A fragment
is associated with, or dependent upon, a specific primary component, but still maintains a unique
identity. Querying a list of components will also return fragments, so that these fragments can be
individually started and stopped.
Fragments generally add classes or resources to the class path normally used by the primary component.
Fragments do not contain Bundle-Activator classes. Since fragments are only extensions to a component,
they cannot be required or imported by another component.
Features
On disk, an Eclipse-based product is structured as a collection of components and fragments. Each
component or fragment contains the code that provides some of the product’s functionality. The code and
other files for a component or fragment are installed on the local computer, and get activated
automatically as required. A product’s components are grouped together into features. A feature is the
smallest unit of separately downloadable and installable functionality
Developing applications 77
The fundamentally modular nature of the Eclipse platform makes it easy to install additional features and
components into an Eclipse-based product, and to update the product’s existing features and components.
You can do this either by using traditional native installers running separately from Eclipse, or by using
the Eclipse platform’s own Update Manager. The Eclipse Update Manager can be used to discover,
download, and install updated features and components from special web based Eclipse update sites.
The basic underlying mechanism of the Update Manager is simple: the files for a feature or component
are always stored in a sub-directory whose name includes a version identifier (e.g., ″2.0.0″). Different
versions of a feature or component are always given different version identifiers, thereby ensuring that
multiple versions of the same feature or component can co-exist on disk. This means that installing or
updating features and components requires adding more files, but never requires deleting or overwriting
existing files. Once the files are installed on the local computer, the new feature and component versions
are available to be configured. The same installed base of files is therefore capable of supporting many
different configurations simultaneously; installing and upgrading an existing product is reduced to
formulating a configuration that is incrementally newer than the current one. Important configurations
can be saved and restored to active service in the event of an unsuccessful upgrade.
Large Eclipse-based products can organize their features into trees starting from the root feature that
represents the entire product. This root feature then includes smaller units of functionality all the way
down to leaf features that list one or more plug-ins and fragments. The capability to group features
hierarchically allows products to be built on top of smaller products by including the smaller products
and adding more features.
Some included features may be useful add-ons but not vital to the proper functioning of the overall
product. Feature providers can elect to mark these features as optional. The Update Manager allows users
to choose whether or not to install optional features. If not installed right away, optional features can be
added at a later date.
Class loading
This section explains how classes are located and loaded by the client platform. A class loader is
responsible for loading classes. A class is loaded by a hierarchy of cooperating class loaders as shown in
the figure below.
FrameworkClass Loader
BundleClass Loader
Boot Class Loader(bootclasspath)
ExtensionClass Loader(java.ext.dirs)
ApplicationClass Loader(classpath)
SystemBundle
OtherBundles
Thread(Content
ClassLoader)
Public
Class
Space
(osgi.framework.systempackages)
default(change via osgi.parentClassloader)
Export-Package
Import-Package
Require-Bundle
Figure 1. Class loaders
78 Lotus Expeditor: Developing Applications for Lotus Expeditor
A typical Java application has a global name space that consists of the contents of the JARs in a single,
well-defined class path. A set of class loaders cooperates to locate and load classes based on this class
path. These class loaders include the Application Class Loader to load application classes (normally
found in the CLASSPATH), the Extension Class Loader to load standard extension classes (normally in the
jre/lib/ext directory, which is specified in the java.ext.dirs property), and the Boot Class Loader to
load system classes (normally from rt.jar in the jre/lib directory).
Class loading functions differently in the client platform because the client platform is built on the OSGi
Service Framework. Since the mechanics for supporting plug-ins are implemented by using the OSGi
Service Framework, a plug-in is the same as an OSGi bundle for the purpose of this explanation. The
bundle and its associated classes specify and implement the process for Java class loading, prerequisite
management, and the bundle’s life cycle.
Each bundle installed and resolved in the OSGi Service Framework must have a class loader created
when the first attempt is made to load a class from that bundle. This class loader, called the Bundle Class
Loader, provides each bundle with its own name space to avoid name conflicts and enables package
sharing with other bundles.
The Bundle Class Loader searches for classes and resources in the bundle’s class path as defined by the
Bundle-Classpath header in the bundle’s manifest. The Bundle Class Loader has a parent class loader as
specified in the osgi.parentClassloader property. By default, the parent class loader is the Extension
Class Loader for the client platform. However, the Extension Class Loader also has a parent class loader -
the Boot Class Loader. As a result, the parent of the Bundle Class Loader actually consists of the Boot
Class Loader and the Extension Class Loader.
A bundle can export the classes and resources in one or more of its packages by specifying each such
package name in the Export-Package header in its manifest. The classes and resources in each exported
package become part of the Public Class Space and are made available to other bundles with permission
to use the package A bundle can import one or more packages by specifying each package name in the
Import-Package header in its manifest. If the bundle has permission to import these packages, then the
bundle can use the classes and resources in these packages as defined in the Public Class Space. A
package can be shared based on its name and, optionally, its version. Multiple bundles can share (export)
a package with the same name. Each package in the Public Class Space is unique based on its package
name, exporting bundle’s symbolic name, and exporting bundle’s version. As a result, multiple versions
of the same package can exist in the Public Class Space.
A bundle may access a package from the Public Class Space by importing the package (using
Import-Package) or requiring the bundle which exports the package (using Require-Bundle). A bundle
that imports a package must know the name of the package it needs to import and may explicitly control
which bundle provides the package it actually uses by specifying additional matching attributes to select
a particular exporter of a package. A bundle can explicitly use all packages exported by another bundle
by specifying the required bundle in the Require-Bundle manifest in its header. The Require-Bundle
manifest header contains a list of bundle symbolic names that need to be searched after the imports are
searched but before the bundle’s class path is searched.
The figure below illustrates the search order used to locate classes and resources.
Developing applications 79
The OSGi Framework must adhere to the following rules for class or resource loading. When a bundle’s
class loader is requested to load a class or find a resource, the search must be performed in the following
order:
1. If the class or resource is in a java.* package, the request is delegated to the parent class loader;
otherwise, the search continues with the next step. If the request is delegated to the parent class
loader and the class or resource is not found, then the search terminates and the request fails.
2. If the class or resource is from a package included in the boot delegation list
(org.osgi.framework.bootdelegation), then the request is delegated to the parent class loader. If the
class or resource is found there, the search ends. The default for the platform is * which indicates that
all packages are delegated to the parent class loader.
yes
Failure
yes
Start
yes
yes
no
no
no
no
no
yes
yes
yes
yes
nono
no
yes
no
no
no
yes
yes
Success
java. ?*
bootdelegation?
imported?
Search Requiredbundles
Search bundleclass path
Search fragmentsbundle class path
packageexported?
found?
found?
found?
dynamicimport?
Delegate towire's exporter
Delegate towire's exporter
Delegate toparent class loader
Delegate toparent class loader
found?
found?
found?
found?1
2
3
4
5
6
7
8
9
Figure 2. Search order used to locate classes and resources
80 Lotus Expeditor: Developing Applications for Lotus Expeditor
3. If the class or resource is in a package that is imported using Import-Package or was imported
dynamically in a previous load, then the request is delegated to the exporting bundle’s class loader;
otherwise the search continues with the next step. If the request is delegated to an exporting class
loader and the class or resource is not found, then the search terminates and the request fails.
4. If the class or resource is in a package that is imported from one or more other bundles using
Require-Bundle, the request is delegated to the class loaders of the other bundles, in the order in
which they are specified in this bundle’s manifest. If the class or resource is not found, then the
search continues with the next step.
5. The bundle’s own internal bundle class path is searched. If the class or resource is not found, then the
search continues with the next step.
6. Each attached fragment’s internal bundle class path is searched. The fragments are searched in
ascending bundle ID order. If the class or resource is not found, then the search continues with the
next step.
7. If the class or resource is in a package that is exported by the bundle or the package is imported by
the bundle (using Import-Package or Require-Bundle), then the search ends and the class or resource
is not found.
8. Otherwise, if the class or resource is in a package that is imported using DynamicImport-Package, then
a dynamic import of the package is now attempted. An exporter must conform to any implied
package constraints. If an appropriate exporter is found, a wire is established so that future loads of
the package are handled in Step 3. If a dynamic wire is not established, then the request fails.
9. If the dynamic import of the package is established, the request is delegated to the exporting bundle’s
class loader. If the request is delegated to an exporting class loader and the class or resource is not
found, then the search terminates and the request fails.
When delegating to another bundle class loader, the delegated request enters this algorithm at Step 3.
Developing the application user interface
This section provides information on application user interface development.
Understanding the user interface
This section provides introductory information for understanding the user interface.
Eclipse
The Eclipse 3.0 SDK provides an open framework to enhance functionality to the Integrated Development
Environment (IDE). These features (or plug-ins) are easily versioned and dynamically installed or
updated without restarting the IDE. Software developers using Eclipse have often wished for a similar
model for their desktop applications. With previous versions of Eclipse, this was possible but difficult,
especially when heavily customizing menus, layouts, and other user interface elements.
Eclipse Version 3 introduces the Rich Client Platform (RCP), a refactoring of the fundamental parts of the
UI, enabling RCP to be used as a general-purpose application platform. Its internals are the same OSGi
run time and GUI toolkit provided by the Eclipse IDE, but now these are easily used by application
developers to provide robust, customizable, and portable Java applications. Because of its Eclipse open
source license, you can use the technologies that went into Eclipse to create your own commercial-quality
programs. The GUI toolkits used by Eclipse RCP are the same used by the Eclipse IDE and enable
applications with optimal performance that have a native look and feel on any platform that they run on.
The client platform provides the default product and workbench. The workbench provides the default
look-and-feel that incorporates the base menus, images, and application launcher. Application developers
can create new applications that run within the confines of the platform. The workbench provides the
facilities that developers can use to enable applications to be launched.
Developing applications 81
There are two main application user interface types that can be hosted within the platform. Web
application based user interfaces are displayed within an embedded browser view that is part of a
predefined perspective provided by the platform. The web application can either be locally hosted within
the provided web container environment, or be running on a remote server. For more information on
creating web applications, refer to “Developing Web applications” on page 238.
The second main application user interface type is an application built using Java-based widgets. In order
to contribute to the workbench, applications must provide a perspective and declare the perspective to
the workbench using the WctApplication extension point. The perspective is a standard Eclipse
perspective and is composed of one or more views (or editors). Views (and editors) are constructed from
UI widgets such as tables, buttons, text fields and more. Wizards and dialogs can also be created by the
application to perform specific tasks. Wizards and dialogs can be launched by widgets (buttons) within a
view, or from various menu bars that are available within the platform.
UI toolkits
The following UI toolkits are used by the Eclipse IDE and plug-ins, and work equally well for RCP
applications.
The Standard Widget Toolkit (SWT) provides a completely platform-independent API that is tightly
integrated with the operating system’s native windowing environment. Java widgets actually map to the
platform’s native widgets. This gives Java applications a look and feel that makes them virtually
indistinguishable from native applications. In cases where native function is not provided, the SWT
emulates it in a manner in keeping with the platform’s normal look and feel. This toolkit overcomes
many of the design and implementation trade-offs that developers face when using the Java Abstract
Window Toolkit (AWT) or Java Foundation Classes (JFC). AWT gives the least common denominator
approach and is therefore functionally limited. JFC is more flexible, but because all widgets are painted
by the toolkit, JFC always seems to have trouble precisely emulating a native look and feel.
The JFace toolkit is a platform-independent user interface API that extends and interoperates with the
SWT. This library provides a set of components and helper utilities that simplify many of the common
tasks in developing SWT user interfaces. For example, it provides the dialogs, wizards, and rich text
editors used by the Eclipse IDE. JFace also has tables and trees that utilize a model view controller
(MVC) architecture to separate data access login from data display logic. JFace also provides the
mechanisms by which plug-ins programmatically contribute to the workbench, which is further discussed
in the next topic.
Lotus Expeditor for Devices provides subsets of the full SWT and JFace widgets sets. It also provides
Mobile Extensions which are particularly useful on devices.
Visual Editor for Java
The visual editor for Java is a code-centric Java editor that helps you design applications that have a
graphical user interface (GUI). The visual editor is based on the JavaBeans™ component model and
supports visual construction using the Standard Widget Toolkit (SWT), the Abstract Window Toolkit
(AWT), or Swing. A developer can use the visual editor for Java to create SWT composites. These SWT
composites can be used inside of Eclipse views and perspectives and used in Lotus Expeditor.
User interaction in the Lotus Expeditor
The default user interface provided for the Lotus Expeditor is similar in appearance and action to other
user interfaces, such as Microsoft Windows, Macintosh, and Motif. Users employ a ″selection/action″
model to interact with it.
In a selection/action model, selection pertains to each view and is independent of other selections in
other views. The view retains what is called selection memory. For example, when a user selects one or
more items in a list in one view, and goes to a different view (in a different plug-in or the same one), and
then returns to the list in the original view, the selected items are still selected. This selection model helps
users remember where they were and what they were doing in the view.
82 Lotus Expeditor: Developing Applications for Lotus Expeditor
Inactive selection refers to the display of a previous selection at the same time that the active selection is
displayed. All actions only take place on active selections, but continuing to display the inactive selection
helps users remember the choices they have made.
User interface organization
The following figure illustrates the organization of the user interface.
The following parts of the Lotus Expeditor user interface are displayed by default:
v Title bar
v Menu bar
v Status bar
The main data area contains only a default image when the client platform starts. Once applications have
been opened, then the views associated with the application will be displayed.
Title bar: The Title bar displays the program title and icon. The displayed information is part of the
branding elements that can be changed. .
Menu bar: The menu bar contains the set of actions that have been provided by either the default
workbench or by other applications.
Usability guidelines recommend that all features of an application be available from the menu items on
the menu bar. They also can be available from buttons or context menus. Context menus are displayed
when a user right-clicks the background of a user interface component, such as a view or document.
In general, menus should display all menu items that are applicable to the current view.
If Then
A menu item is not applicable to what is currently
selected, but the user can take an action while in the
same tab or window to enable the menu item
That menu item should appear dimmed
A menu item is not applicable to what is in the view That menu item should be hidden
Title Bar
Menu Bar
Main Data Area
Status Bar
Toolbar/Coolbar
View View ViewLauncher
Sidebar
Figure 3. User interface organization
Developing applications 83
Pull-right menus should always be enabled, even if none of the items in the pull-right menu are
available. Users should be able to view the contents of the pull-right menu, even if none of the actions
are available.
Banner bar: The banner bar can optionally display a graphic and the application name. The
configuration and graphical image used by the banner bar can be changed.
Data area: The primary data area of the user interface contains the perspectives and views associated
with an application.
Coolbar/Toolbar: The Coolbar/Toolbar area is used to display icons for actions that are available.
Usability guidelines suggest that all actions available on the Coolbar or toolbar are also available via
actions on the menu bars. The Coolbar/Toolbar area is optionally displayed and is configurable by the
administrator. Users can also elect to display or hide the Coolbar/Toolbar area.
Status bar: The status area can be used by applications to display information regarding the status of
their application. The components in the status area are associated with a particular perspective and view.
The status bar is optionally displayed and is configurable by the administrator.
Application Launcher: The Launcher is represented in the user interface as a button with a drop down
menu that contains the list of applications available to the user. The user selects a menu entry to open or
launch the application.
Using widgets on devices
The following sections contain information on Rich GUI programming for devices.
Functional partitioning: Embedded Rich GUI applications are composed of widgets and graphics
provided by eSWT which is a strict subset of the Standard Widget Toolkit (SWT) used for Rich GUI
desktop applications. The eSWT API is partitioned into two sets of function. The “Core” set contains the
minimal function required to run basic applications. The “Expanded” set contains additional function
which may be more appropriate for higher end devices with larger displays and more memory resources.
Since eSWT based applications are using subsets of SWT, these applications also function on any desktop
SWT implementation.
Mobile Extensions is a set of function primarily targeted for mobile devices, although some widgets it
provides may also be useful on desktop platforms. Lotus Expeditor Client for Desktops provides a
Mobile Extensions library for Win32 platforms.
Devices may potentially carry these functional sets in various configurations or packaging schemes. For
instance, low end devices may include a Core library and a Mobile library. Higher end devices may carry
a converged library that contains all three sets of function. For this reason, eRCP workbench applications
should always declare eSWT and Mobile Extensions dependencies by using “Import Packages” rather
than “Required Plug-ins” statements in their manifest which tie an application to a specific plug-in. Lotus
Expeditor Client for Devices includes a converged eSWT library.
Device normalization: Unlike desktop machines which all have large screens and pointer mechanisms,
mobile devices come in a wide range of shapes and sizes and have a variety of input mechanisms. As
much as possible, we would like to write applications that run well on any kind of mobile device. Note
that there is a big difference between simply running on a device and running well. Usability is a vital
concern for mobile devices where environments vary and expectations for ease of use are very high.
eSWT and Mobile Extensions attempts to normalize devices so that the application programmer does not
have to do a lot of work to handle the differences among devices. It does this in two ways: implicitly, by
providing a device’s native look and feel that a user is familiar with, and explicitly by providing
mechanisms that abstract input and output through the actual device hardware.
84 Lotus Expeditor: Developing Applications for Lotus Expeditor
Implicit normalization automatically provides some level of device adaptation by giving applications
indirect access to a device’s native widgets. Since eSWT widgets are implemented using a platform’s
native widgets, eSWT widgets appear and behave similarly to widgets in native applications. The
end-user can recognize and interact with these widgets as they’re use to. The programmer gains these
benefits simply by using eSWT widgets.
Explicit normalization is provided via specific mechanisms that a programmer is encouraged to use.
These generally fall into two categories: organizing output on a display and handling different input
mechanisms.
Display normalization:
1. Use of flow based layouts is strongly encouraged – Layouts like RowLayout and GridLayout
position widgets independently of screen size.
2. Use of absolute coordinates is highly discouraged – Display sizes and aspect ratios can vary
considerably. To guarantee that widgets are fully displayed, the programmer would have to calculate
proper coordinates for each different display size.
Even though layouts help considerably in adapting to different screen sizes, a program that runs well on
all devices should also perform the following:
1. Check if the computed layout is larger than the available screen size, and if so, add scroll bars to
allow scrolling the content.
2. Check for high aspect ratios which may restrict layout or allow for additional content. For example,
on a mobile device with a long, narrow landscape display, content may be better displayed in two
columns or in side by side panes.
Input normalization:
1. Use of the Mobile Extensions Command feature is highly encouraged – Commands are an
abstraction that the Mobile Extensions library maps to a specific mechanism depending upon the
device capabilities. This will usually be pointer driven menus or soft keys.
2. Use of buttons is discouraged – Many devices do not have pointer devices. Therefore, buttons on
these devices must be navigated to using jog controls or arrow keys and then selected. Jogging
through numerous widgets is more time consuming and may also be cumbersome depending on the
device controls. If buttons are highly desired, then the application can check via the MobileDevice
class to see if the screen is a touch screen. This allows use of buttons when they can be easily selected
and use of Commands or some other mechanism when they can not be.
3. Use of menus is discouraged – Of minor consideration is that the width of the menu bar may be
quite limited on some devices. It is difficult for your application to determine how many top level
menu items can be supported. Menus may also be somewhat difficult to navigate on non-pointer
devices. However, a more important reason not to use menus is that you may be bypassing the
device’s most natural or efficient input mechanism.
Mobile extensions widgets:
CaptionedControl
Determining which control has focus can be difficult on mobile devices where lighting conditions
are often less than optimal. (For instance, hairline width cursors in a text field can be very
difficult to see even under good conditions.) This widget “captions” a control with a label that
shows focus highlighting whenever the control has focus. With an entire label highlighted, it is
easy to see where focus is.
ContrainedText
A convenience widget which sets the initial input mode of a text field and also limits the
characters than can be entered within the field.
Developing applications 85
DateEditor
Allows the user to select a date, time, time offset or duration in a platform specific manner.
Constructor styles allow the program to choose FULL or COMPACT display modes, or rely on
the platform default.
HyperLink
Displays a label in a style which indicates it can be selected. Selecting the label (containing valid
link text) will cause an appropriate platform function to be launched. For instance, selecting an
e-mail address link will launch the platform’s e-mail program, selecting a URL will launch the
platform’s browser and selecting a phone number will cause the platform’s dialer to dial that
number. Note that not all platforms may have corresponding platform function. For instance,
PDAs may not have a built in phone.
ListBox
This is a Model-View-Controller (MVC) style widget which displays a list of items in the most
efficient/useful way for a given platform. The application may specify many hints as to how the
list should be presented, but the details of the actual presentation are device dependent.
ListView
This is an advanced List widget which allows inclusion of an image with each list item. The
ListView layout can also be changed at runtime in order to display more or fewer items
depending on the user’s preferences.
MobileDevice
This object allows the application to query information about the specific device the application is
running on and be informed about device state changes as they occur. For instance, an event is
sent when the device is closed, or a remote keyboard becomes available.
MobileShell
This widget allows applications to create a full screen window on a device, such as might be
desired for a slide show or game. The window created can also dynamically change its trim to
provide a window with more or less application area. MobileShell can also be used to poll for
key state as commonly done within game execution loops.
MultiPageDialog
This dialog allows the application to show multiple pages which are displayed one at a time. It
provides function similar to TabFolder in desktop SWT, but in a more platform dependent
fashion. The more restricted nature of this dialog over TabFolder allows it to map more readily to
native platform functionality.
QueryDialog
This dialog allows the application to query the user for a single piece of data and is provided as
a convenience class.
SortedList
This list widget automatically sorts its items in ascending or descending order. In addition, the
end user can filter the list. That is, by entering characters, items not containing that subset of
characters will not be displayed in the list. Such function is useful on devices where scrolling
through a list may not be as quick or easy to do as on a desktop.
TaskTip
This class provides an asynchronous indicator that a long running task is progressing. The
indication is similar in purpose to a mouse-over generated ToolTip which provides a bit of extra
information that is generally unobtrusive in nature. An application may open and close a TaskTip
as desired.
TextExtension
Basically, the Text widget with some mobile specific features. It allows a program to set the
semantic meaning of the text field so that platform specific features may be utilized to aid text
entry. For instance, if the field is intended to hold an e-mail address, the widget may access the
system e-mail application to retrieve possible e-mail address completions that the user may select
86 Lotus Expeditor: Developing Applications for Lotus Expeditor
from. The initial input mode may also be set to a particular character set. The user can change the
input mode as needed. Finally, the widget allows the setting of an initial casing mode, so that text
can be entered in all upper case or one of several other modes. Again, the user may change the
casing mode.
TimedMessageBox
This dialog shows one of several styles and audible patterns. It automatically closes after a short
period of time, thus reducing the need for user interaction to dismiss the dialog. For instance,
such a message may be useful to inform the user than someone wants to start a chat session.
eSWT programming tips:
v Disposal of Colors - You should dispose of the colors you create when there is no longer a need for
that color. You should not create the color, set it in the GC, dispose the color, then attempt to use the
GC. In this instance, you are disposing of the color before you are done using it.
v Layouts – Standard SWT practice is to either call layout() or pack() on a container with a layout
before opening the container. Otherwise, the layout calculation is not performed and elements may not
be visible.
v Commands – The Command constructor type parameter may only be used for mapping Commands to
softkeys, thus priority values should be used as well as type for appropriate ordering when
Commands are displayed within a menu.
v Command activation – Remember that Commands may not be the only (or best) interaction method
on all devices. Direct selection of items should also be supported. For example, a list widget may
support several Commands such as Open, Edit, and Delete. Direct selection (clicking) of a list item
should invoke the default Command action (Open). In some cases, a double click action may also be
supported (double clicking an item may invoke the Edit action).
v PaintListeners – Listeners should be added before a window is opened. Otherwise, the window will
not paint correctly when opened.
v Key Events - Do not assume there will be an elapse time between key pressed and key released events.
On a device with virtual keyboard these events may be sent back to back.
v Virtual Keyboard – On PPC, the virtual keyboard covers the bottom of display. Do not place text fields
requiring keyboard input in the bottom portion of a window, or if you do, ensure that the window is
scrollable so that the user may scroll the text field into view. Other platforms provide better
mechanisms such as providing an output field integrated with the virtual keyboard so the user can see
what is typed regardless of where the widget is positioned.
v Screen Size – Don’t assume that the screen will always be small. Some devices are capable of
connecting to large displays, where a few widgets, expanded to take up the entire screen will look very
odd. If going over a certain panel size would look odd, then limit your panel size, or adjust its content.
v Using images - When loading and using images, do not use the ImageData class unless you specifically
need to modify the image’s pixels. Image loading that does not involve ImageData employs an
optimized code path that is much faster than paths involving ImageData. Please note that many SWT
snippets show code like:
ImageData imageData = new ImageData("filename");
Image image = new Image(display, imageData);
Do not use code like this unless you intend to manipulate the image. Instead use:
Image image = new Image(display, "filename");
Using the Restricted Workbench
The Restricted Workbench provides an environment in which the user is restricted from accessing the
operating system and the Lotus Expeditor Workbench is used as the desktop shell.
The Lotus Expeditor Workbench alters its behavior, look, and feel when the Lockdown Feature is
installed on a Lotus Expeditor client machine. Once the Lockdown Feature is installed, the Lotus
Expeditor Workbench uses the Lockdown Service to achieve the restricted nature described below:
Developing applications 87
v No title bar
v No sizing borders
v Maximized to fill the screen
v Pinned down in the Z-order such that no other windows can be drawn beneath it
v Cannot be closed
v Cannot be resized
v Cannot be minimized
v File > Exit is removed from the Menu Bar
The above workbench environment is described as the Restricted Workbench.
Applicability and benefits
The Restricted Workbench allows system administrators to restrict the functionality of the user’s
workstation to a set of applications and OS services configured by the system administrator; in essence
creating a managed client. Target deployments include centrally managed clients such as bank branch
teller terminals and retail point of sales devices as well as customer facing kiosks such as ATMs.
The Restricted Workbench supports the following platforms:
v Windows XP SP1&2
v Red Hat Enterprise Linux Workstation 4
v NLD 9.0
The fundamental behavior of the Restricted Workbench is the same across platforms. Implementation
details differ between the Windows and Linux operating systems.
The key components to both implementations of the Restricted Workbench are:
v Code to pin down the workbench window by removing borders and isolating the window to the
bottom of the z-order
v Code that blocks keystrokes to prevent a user from circumventing the Restricted Workbench
v Installing scripts, registry updates, policy files and other install pieces that are executed by the
Lockdown Feature Install Handler and are also part of the Restricted Workbench
v The Restricted Workbench and the rcplauncher process work together to determine the nature of
workbench close events so that the user is not left with an empty desktop while the Lotus Expeditor
Workbench is restricted.
Using ILockdownService
To initiate a Restricted Workbench, the ILockdownService OSGi service is used.
The ILockdownService OSGi service is intended to be used by subclasses of WorkbenchWindowAdvisor in
order to lock down the initial workbench window. This service will lock one and only one window, and
it will effectively become the user’s desktop shell. The preWindowOpen method is intended to be called
during preWindowOpen of the WorkbenchWindowAdvisor and the postWindowOpen method is intended to be
called during postWindowOpen of the WorkbenchWindowAdvisor.
When using ILockdownService, take care not to do the following:
v Allow unrestricted access to the file system through applications running in the Workbench as there are
opportunities for circumventing lockdown by allowing users to have unrestricted file system access
v Allow applications to exit the workbench without using the exitLockDown method call
Acquiring the ILockdownService: The following code illustrate how to acquire the ILockdownService:
88 Lotus Expeditor: Developing Applications for Lotus Expeditor
public ILockdownService getLockdownService(BundleContext ctx){
ILockdownService rWorkbenchService = null;
ServiceReference ref = ctx.getServiceReference(ILockdownService.class.getName());
if(ref != null)
rWorkbenchService = (ILockdownService) ctx.getService(ref);return rWorkbenchService;
}
Using the ILockdownService to remove the window title trim: The following code sample illustrates
how to use the ILockdownService to remove the window title trim during preWindowOpen for a subclass of
WorkbenchWindowAdvisor:
ILockdownService rWorkbench = getLockdownService(bContext);
if(rWorkbench != null && !rWorkbench.isLockedDown())
rWorkbench.preWindowOpen(getWindowConfigurer());
Using the ILockdownService to lock the window: The following code sample illustrates how to use the
ILockdownService to lock a window during postWindowOpen for a subclass of WorkbenchWindowAdvisor:
if(rWorkbench != null && !rWorkbench.isLockedDown())
rWorkbench.postWindowOpen(getWindowConfigurer());
Using the ILockdownService to exit the Restricted Workbench and logoff the OS: The following code
sample illustrates how to use the ILockdownService to exit the Restricted Workbench and logoff the OS:
ILockdownService rWorkbench = getLockdownService(bContext);
if(rWorkbench != null){
rWorkbench.exitLockDown(ILockdownService.EXIT_DATA_LOGOFF);
PlatformUI.getWorkbench.close();
}
Using personalities
The personality of an application defines the framework the platform uses to determine what
perspectives or windows, menus, action bar items and status line controls are displayed when the
application starts. It can also determine what services are available, an event sequence, or what life cycle
should be applied to the objects associated with that application.
The layout of the window that contains the views and folders included in your application is defined by
a perspective, which is the Eclipse equivalent of a window. You set the desired perspective by specifying
a personality for your application.
A personality may be needed for one of two reasons. The first is that your application needs to provide a
different UI than other applications running on that platform. The other is to have fine control over the
application’s startup sequence.
Personalities are activated by specifying the personality ID on the command line. For example:
richclient -personality personality.id.
In addition, a default personality can be specified using the plugin_customization.ini file with the
following key:
com.ibm.rcp.personality.framework/DEFAULT_PERSONALITY_ID=
com.ibm.myexample.personality
Creating a personality
Personalities are contributed to the platform via the com.ibm.rcp.personalty.framework.personalities
extension point. Use the following procedure to create a personality:
1. Create an extension of the org.eclipse.ui.application.WorkbenchWindowAdvisor class. Refer to
Eclipse documentation for lifecycle events.
public class DefaultWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor {
public DefaultWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) {
super(configurer);
Developing applications 89
}
public void createWindowContents(final Shell shell) {
// Create your layout and menus here. See Eclipse documentation
for details on WorkbenchWindowAdvisor.createWindowContents
}
public void postWindowOpen() {
// Do stuff like logging into the platform etc
try {
doLogin();
}
catch (LoginException e) {
// Do stuff
}
}
}
2. Create a class that implements com.ibm.rcp.personality.framework.IWorkbenchWindowAdvisorFactory.
The create(IWorkbenchWindowConfigurer) method should return an instance of your
WorkbenchWindowAdvisor subclass created in the first step.
import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
import org.eclipse.ui.application.WorkbenchWindowAdvisor;
import com.ibm.rcp.personality.framework.IWorkbenchWindowAdvisorFactory;
public class WorkbenchWindowAdvisorFactory implements
IWorkbenchWindowAdvisorFactory {
public WorkbenchWindowAdvisorFactory() {
super();
}
public WorkbenchWindowAdvisor create(IWorkbenchWindowConfigurer configurer) {
return new DefaultWorkbenchWindowAdvisor(configurer);
}
}
3. Add an extension to the com.ibm.rcp.personality.framework.personalities extension point,
specifying your factory class created in the second step.
<extension point="com.ibm.rcp.personality.framework.personalities">
<personality
allowMultipleWindows="false"
icon="icons/icon_notes.gif"
class=""
factory="com.ibm.myexample.personality.
WorkbenchWindowAdvisorFactory"
name="%PlatformPersonalityName"
id="com.ibm.myexample.personality"/>
</extension>
4. Launch Lotus Expeditor and specify the new personality by personality ID.
The extension point startBundles enables defining a set of bundles that should be associated with a
Personality.
90 Lotus Expeditor: Developing Applications for Lotus Expeditor
The following example would associate the org.eclipse.equinox.event bundle to the Personality
identified by the extension point com.ibm.myexample.personality.
<extension point="com.ibm.rcp.lifecycle.personality.startBundles">
<personality id="com.ibm.myexample.personality">
<bundle id="org.eclipse.equinox.event"/>
</personality>
</extension>
Note: You can invoke additional personalities which will join the currently running Lotus Expeditor
Client environment rather than creating another full instance.
For information on contributing a personality to a menubar, refer to “Lotus Expeditor top level menus”
on page 365.
Lifecycle events
The events in the startup lifecycle in a personality are:
v preWindowOpen
v fillActionBars
v createWindowContents
v postWindowCreate
v preWindowOpen
v postWindowOpen
v Contributed personality startup tasks are executed
The events in the shutdown lifecycle of a window that are used in a personality are:
v preWindowShellClose
v postWindowClose
Creating a global toolbar
When a global toolbar is created in a Lotus Expeditor personality, the following steps occur:
DefaultWorkbenchWindowAdvisorFactory implements IWorkbenchWindowAdvisorFactory. In its create
method, DefaultWorkbenchWindowAdvisor is instantiated. DefaultWorkbenchWindowAdvisor overrides the
CreateActionBarAdvisor method, and instantiates DefaultActionBarAdvisor. DefaultActionBarAdvisor
overrides the fillActionBars method, and calls the actionBuilder.fillCoolBar and
ControlSetHelper.getInstance().fillCoolBar methods in it.
Developing composite applications
This section provides information on composite application development.
Understanding composite applications
Composite applications are applications that enable independently-developed components which provide
specific business functions to run together in a single application.
They are considered Portal-managed applications in that they are described and deployed on WebSphere
Portal and can be centrally managed from WebSphere Portal. They are supported on the Lotus Expeditor
client by the composite application infrastructure, which provides the following services:
v APIs that are common between Lotus Expeditor and WebSphere Portal, such as:
– Property broker – In WebSphere Portal, the property broker allows two portlets to communicate
with each other through the use of a Property and Action registry and a wiring mechanism. This
functionality has been ported to the client to support Property to Action model communication. The
architecture built into the client supports additional action-based communication systems, such as
Developing applications 91
Standard Widget Toolkit (SWT), Abstract Window Toolkit (AWT), and OSGI events. You can extend
the property broker API to support different Action-based targets through the Action Handler
Extension Point.
– Synchronization – Composite applications whose components implement the Synchronizable
interface are able to synchronize their data so that the application can be available for use while the
client is offline or when a connection to the WebSphere Portal server is not available. When an
application’s components implement this interface, users of the application have the ability to
register the application for synchronization. After an application is registered, when a
synchronization job runs on the client, the synchronization code for the application is called. The
code that synchronizes the data must be provided by the application components, using SyncML or
another synchronization service registered with the Synchronization Manager.
– Managed settings – A framework that retrieves policy and preference settings, or any other name
and value pairs defined by you on a back-end system, and stores them in the Eclipse preference
store implemented on the client. For composite applications, which are managed by WebSphere
Portal, this means that the framework manages portal policies on the client. A portal policy is a
collection of settings that influences the behavior of a portal resource and the experience that users
have when working with it. Using policy, you can limit or extend the functionality provided by a
resource based on the role and associated permissions defined for a given user. The portal policy
settings you define for a resource in WebSphere Portal are likewise applied to that resource on the
client. Policies are kept up-to-date through scheduled synchronization jobs. The client implements a
federated policy architecture in which policy providers identify a precedence order for policy
retrieval.v Serialization service – On Websphere Portal, components that contain custom code call the Serialization
SPI to serialize their data and generate a custom application definition in the form of an XML
fragment. The server exposes Web Services and Representational State Transfer (REST)-based services
that provide application-specific information to the XML-based application definition. When the
application is provisioned to the client, WebSphere Portal passes the XML fragment to the client. The
client reads the XML fragment and calls the components that implement the Deserializable SPI to
create instances of the custom domain objects defined by the components.
v Application definition - The client uses a standard composite XML document structure referred to as
the composite application instance to define the application. You can use the application instance to
specify application-specific information to create an importable CAI XML file for the client. The basic
construction of this XML package is a high level container hosting embedded XML fragments called
domain objects. These domain objects are the definitions for the various components within a
composite application and can be reconstructed on the client using the Deserializable interface.
Creating composite application projects
This topic provides a list of steps that you must complete to create a composite application project which
is made up of a SWT view component and a portlet component.
The sample application that is described in this section is one of the many types of composite
applications you can build. This section does not attempt to provide details for the steps required to
build every type of composite application supported on the client.
One step that you must complete to create both application components is to create a portlet. You can use
development tools to create a fully functioning portlet, or, to create a portlet that represents an SWT view
on the client and is not a fully functioning portlet, you can create a dummy portlet and edit it. The
dummy portlet serves as a container to which you can add custom preferences, such as
com.ibm.rcp.viewId or com.ibm.rcp.folder, using the rich client layout portlet. Views that implement
technologies such as Property Broker (for inter-portlet communication), WSRP, or Managed Browser APIs
require fully-functioning portlets, no dummy portlets. To create the SWT view component, we will start
with a dummy portlet and enhance it to enable it to register actions and properties using a WSDL file.
92 Lotus Expeditor: Developing Applications for Lotus Expeditor
Note: Some of the steps in these procedures ask you to use an Eclipse-based development tool. You can
use any of the following tools, among others:
v Rational Application Developer
v IBM Workplace™ Designer
v Eclipse
v Eclipse Web Tools Platform (WTP) project, which extends the Eclipse platform with tools for
developing J2EE Web applications.
To build a simple composite application made up of a SWT view component and a portlet component,
perform the following steps:
1. Create the SWT view component. See Creating a SWT view component.
2. Create the portlet component. See Creating a portlet component.
3. On the client machine, from the Preferences window, select Home Portal Account and provide the
Portal account connection information.
Creating an SWT view component
The SWT view component consists of an Eclipse plug-in containing one or more views and
corresponding portlets to represent each of the views that are built using the dummy portlet. In this
example, we are creating just one view and one corresponding portlet.
To create an SWT view component, complete the following steps:
1. In an Eclipse-based development tool, create a dummy portlet by using the portlet wizard. Leave the
resulting portlet as is.
2. Create a WSDL file to define the actions that you want to make available. See Defining actions and
properties . The IBM Workplace Designer tool provides a graphical wizard for creating WSDL files.
The following sample WSDL content defines the following items:
v An action – produceURL
v An input parameter:
name – URL
type – tns:BaseURL
v Output parameters:
name – URL From Tree
type – tns:BaseURL
name – Progress
type – tns:Progress<definitions name="LoadURLInBrowser_Service"
targetNamespace="http://www.ibm.com/wps/c2a"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:portlet="http://www.ibm.com/wps/c2a"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.ibm.com/wps/c2a"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<types>
<xsd:schema targetNamespace="http://www.ibm.com/wps/c2a">
<xsd:simpleType name="BaseURL">
<xsd:restriction base="xsd:string">
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="Progress">
<xsd:restriction base="xsd:string">
</xsd:restriction>
</xsd:simpleType>
Developing applications 93
</xsd:schema>
</types>
<message name="LoadURLRequest">
<part name="urlFromTree" type="tns:BaseURL"/>
</message>
<message name="OurResponse">
<part name="loadedURL" type="tns:BaseURL"/>
<part name="progress" type="tns:Progress"/>
</message>
<portType name="LoadURL_Service">
<operation name="loadURL_Operation">
<input message="tns:LoadURLRequest"/>
<output message="tns:OurResponse"/>
</operation>
</portType>
<binding name="ManyOutParamsBinding" type="tns:LoadURL_Service">
<portlet:binding/>
<operation name="loadURL_Operation">
<portlet:action name="produceURL"
type="standard"
caption="Load the new URL"
description="Load.a.new.url.in.the.main.window"
actionNameParameter="ACTION_NAME"/>
<input>
<portlet:param name="URL" partname="urlFromTree" caption="url.loader"/>
</input>
<output>
<portlet:param name="URL From Tree" partname="loadedURL" caption=
"Browser URL"/>
<portlet:param name="Progress" partname="progress" caption=
"Browser Progress"/>
</output>
</operation>
</binding>
</definitions>
3. Reference the WSDL file in the portlet preferences by updating the deployment descriptor
(portlet.xml) file of the portlet. For example:
<portlet>
<portlet-preferences>
...
<preference>
<name>com.ibm.portal.propertybroker.wsdllocation</name>
<value>/wsdl/ManagedBrowser.wsdl</value>
</preference>
</portlet-preferences>
</portlet>
4. Using an Eclipse-based development tool, create the SWT view plug-in for which the portlet serves
as a placeholder.
5. Code the view to publish properties and code the action to update the view on property changes.
The property broker framework supports the following handler interfaces:
v Core Eclipse commands that implement the IHandler (org.eclipse.core.commands.IHandler)
interface. Use this interface if your application can not depend on any features that require user
interface packages. For example, use this implementation if your product runs in a device that
does not have SWT or AWT available. Implement the execute() method. The PropertyChangeEvent
94 Lotus Expeditor: Developing Applications for Lotus Expeditor
is set as the event trigger and can be accessed by calling ExecutionEvent.getTrigger(). The
following sample code processes a property broker change event in an Eclipse IHandler action:
public Object execute(ExecutionEvent event) throws ExecutionException
{
if (event.getTrigger() instanceof PropertyChangeEvent){
final Display display = Display.getDefault();
final PropertyChangeEvent pce = PropertyChangeEvent)event.getTrigger();
...
}
}
v SWT based handlers that implement the IHandler (org.eclipse.core.commands.IHandler)
interface. Use this interface if you are writing visual components with SWT. Implement the
execute() method in your handler. The event passed into this method is of type Event
(org.eclipse.swt.widgets.Event) and implements the PropertyChangeEvent
(com.ibm.rcp.propertybroker.event.PropertyChangeEvent) interface. Unlike the IHandler
implementation the SWT event passed in can simply be typecast to the PropertyChangeEvent
interface. The following sample code gets the broker’s event interface from an SWT IAction:
public void runWithEvent(Event event) {
//Make sure the Event is from the Property Broker
if (event instanceof PropertyChangeEvent){
final Display display = Display.getDefault();
final PropertyChangeEvent finalEvent = (PropertyChangeEvent)event;
...
}
}
The SWT component should use the SWTHelper (com.ibm.rcp.propertybroker.swt.api.SWTHelper)
class to publish its properties. This helper class removes the complexity of identifying the SWT
View instance the property came from by having the caller simply pass in a ″this″ pointer to the
view. The following sample code publishes a URL on the broker from an SWT based view:
PropertyValue value = PropertyFactory.createPropertyValue(prop, selection);
SWTHelper.changedProperties(new PropertyValue[]{value}, this);
The core broker also has a version of the changedProperties() method that enables the property
broker to resolve the specific instance property change to a specific instances action at runtime. A
Java String is passed into the changedProperties() method as the owner context of the property
change. The string should match the EntityID of the source in the wire.
Use the SWTHelper methods to help in retrieving the correct ViewPart. For example:
public void runWithEvent(Event event) {
//Make sure the Event is from the Property Broker
if (event instanceof PropertyChangeEvent){
final Display display = Display.getDefault();
final PropertyChangeEvent finalEvent = (PropertyChangeEvent)event;
display.asyncExec(new Runnable() {
public void run( ) {
//Get the wire definition from the event
Wire def = finalEvent.getWireDefinition();
//Our view object type
PreviewView pView = null;
//view for this action.
PreviewView pView =
Developing applications 95
(PreviewView)SWTHelper.locateView(def.getTargetEntityId());
...
}
v AWT components that implement the Component (java.awt.Component) class. 6. To enable view-to-view communication, register your action with the broker by extending the
com.ibm.rcp.propertybroker.PropertyBrokerAction extension point. In the class attribute, reference
the Java class that will be instantiated by the broker and that you created in the previous Step. In the
file attribute, specify the name of the WSDL file you created previously.
7. Create an Eclipse Feature project that contains the view plug-in you created. Your plug-in can be
bundled into an existing feature. If you do add it to an existing feature, update the version number
of the feature to ensure that the plug-in is installed with the next client update.
8. Create an Eclipse Update Site project and import your feature, and then deploy your Eclipse Update
site to the WebSphere Portal server or another HTTP server. The supporting files required for the
client to run the SWT view are now available.
9. Return to the SWT view portlet and, using Rational Application Developer or J2EE Web application
tools provided by the Eclipse Web Tools Platform (WTP) project, bundle the portlet into a Web
Application Archive (WAR) file. Adding the portlet to a WAR file makes it easier to deploy, and
subsequently update.
10. From the Administration page, navigate to the WebSphere Portal → Portlet Management → Web
Modules page, and then click Install to install your WAR file as a Web module.
11. Verify the portlet was installed by searching for it on the Portlet Management → Portlets page.
12. In the Templates → Application page, create a new application and make the following edits:
a. In the Content tab, define the layout for the application and add the portlet to the page.
b. In the Rich Client tab, select This portlet represents an SWT view on the rich client, and then
type the ID of the SWT view plug-in into the Eclipse view id field.
c. In the Wires tab, create wires between your portlets by specifying the source portlet, the output
property, the target page, which defaults to current, the target portlet, the target property, and
whether or not the wire is public or private. If you want to implement cross-page wires, which
are wires that open an event in a second page, you must designate them as global actions. To do
so, click Manage Actions and then select the Global check box.
Creating a portlet component
This topic explains how to develop a portlet that will run locally and communicate with an SWT based
component after both have been projected to the client from a WebSphere Portal server.
You must complete the steps in Creating a SWT view component before you can perform the steps below.
To create a portlet component, complete the following steps:
1. Using an Eclipse-based development tool, create a fully functioning portlet, not a dummy portlet. It
must use the IBM Portal property broker functionality to publish properties to the SWT component.
2. Reference the same WSDL file that you created in the previous procedure in the portlet preferences
in the portlet.xml deployment descriptor file.
3. Using Rational Application Developer or the J2EE Web application tools provided by the Eclipse
Web Tools Platform (WTP) project, bundle the portlet into a Web Application Archive (WAR) file.
4. From the Administration page, navigate to the WebSphere Portal → Portlet Management → Web
Modules page, and then click Install to install your WAR file as a Web module.
5. Verify the portlet was installed by searching for it on the Portlet Management → Portlets page.
6. In the Templates → Application page, create a new application and make the following edits:
a. In the Content tab, define the layout for the application and add the portlet to the page.
96 Lotus Expeditor: Developing Applications for Lotus Expeditor
b. In the Rich Client tab, select This portlet runs locally on the rich client (requires client
bundle), and then type the directory of the Web module for the portlet into the Portlet context
root field to configure your portlet to be exposed in the client through the local JSR-168 container.
c. In the Wires tab, create wires between this portlet and the portlet representing the SWT view by
specifying the source portlet, the output property, the target page, which defaults to current, the
target portlet, the target property, and whether or not the wire is public or private. If you want to
implement cross-page wires, which are wires that open an event in a second page, you must
designate them as global actions. To do so, click Manage Actions and then select the Global
check box. 7. You must add the portlet to the feature for the SWT view plug-in. Use the Lotus Expeditor
command line tool (WAB Tool) to compile the portlet WAR file into an Eclipse-based plug-in. The
WAB tool is provided in the Lotus Expeditor Development kit. Go to the plugins directory, and look
in the com.ibm.pvc.tools.web.translator plug-in for the wab.exe file. See WAB Utility for information
on using this tool.
8. Add the resulting plug-in to the feature plug-in you created for the SWT view.
9. From the Portlet Management → Portlets page, find the portlet you just created, and click the
Configure icon to open it, and then add the feature ID to the portlet properties to ensure that the
feature is installed when the application is installed from the portal catalog.
10. Redeploy the update site with the newly added plug-in to the HTTP server. If you already deployed
the feature and are adding this plug-in to an existing feature, be sure to increment the version
number so that the client can pick up your changes with its next update.
Developing composite application logic
This section provides an overview of the steps you must complete to create a basic composite application.
The types of components that can comprise a composite applications, the tools you can use to build
them, and the technologies that they can employ are wide-ranging. For example, you can build a
contributing component of a composite application that employs views based on any of the following
technologies:
v Standard Widget Toolkit (SWT)
v JSR 168 Viewer
v WSRP Viewer
v WEB container, JSP
v Embedded Browser
v Abstract Window Toolkit (AWT)
In addition, the client implementation of the property broker API enables the following subsection of
components to communicate with one another:
v Standard Widget Toolkit (SWT)
v JSR 168 Viewer
v Abstract Window Toolkit (AWT)
This section provides instructions that are based on steps that were used to create the sample application,
Managed Browser, which you can find in the samples gallery.
The sample composite application employs the following two supported composite application
technologies and enables them to communicate with one another:
v SWT view-based components – Built as a standard widget toolkit view designed in RAD or Eclipse
that is installed on a deployed Eclipse Update Site. You create a portlet to represent the view and
define values for a set of custom portlet properties that identify it as an SWT-based view. You can use
the administrative tools provided by WebSphere Portal or the Lotus Expeditor Rich Client Layout tool
to organize the layout of this and other portlets into a page design that is customized for the users of
Developing applications 97
your application. When the component is downloaded to the client, the client infrastructure constructs
the user interface for your component based on the customizations you defined in WebSphere Portal.
v Portlet-based components – Built as a portlet in RAD or Eclipse. The portlet component is bundled into
an Eclipse-style plug-in to be downloaded to the client using either the Lotus Expeditor WAB tool or
the Lotus Expeditor Client Services Toolkit. When the component is downloaded, the client uses the
JSR 168 portlet viewer, which reconstructs the portlet as an SWT view, to display it.
The sample application uses WebSphere Portal to represent an SWT-based view in a portal based
application, and then projects it to the client. It also uses WebSphere Portal to project a portlet to the
client. In both cases, what ultimately runs on the client are standard Eclipse-based SWT views.
By projecting the views through WebSphere Portal, you can:
v Leverage portal policy, which exposes different application features to users based on their job
responsibilities.
v Wire components together at runtime instead of at development time.
v Use a common deployment and aggregation model for both Web-based and client-based applications.
The sample application exercises the following services:
v The composite application infrastructure to deploy an SWT based application.
v Dynamic provisioning and integration into the application catalog. When you open the application
from the catalog, it is downloaded and then launched without requiring the user to restart the client.
v Reading portlet preferences from the SWT view.
v Drag and drop capability using property broker property values.
v Declarative wiring through the property broker on the portal and client.
v Cross-page wiring, which means that a view action opens another page and sends the event to a view
on it.
v Click to Action control, which is a right-click menu that displays a list of any compatible property
broker actions that are available.
v Portlet component to SWT view component communication.
Developing data access applications
This section provides information on data access application development.
Understanding embedded database development
This section provides information on understanding embedded database development.
Databases
Database application developers use JDBC, the application programming interface that makes it possible
to access relational databases from Java programs. The JDBC API is part of the Java 2 Platform, Standard
Edition (J2SE) and is not specific to any particular database implementation, such as Cloudscape or DB2
Everyplace. It consists of the java.sql and javax.sql packages, which are sets of classes and interfaces
that make it possible to access databases (from a number of different vendors) from a Java application.
The JDBC specification defines several interfaces and types that application developers use to access the
database.
v A DataSource is the primary definition of a database, and typically defines database access properties
and locations.
v A Connection is the communication object that enables queries and updates to be performed against
the database. The preferred method for obtaining a connection in JDBC 3.0 is with a DataSource object,
as opposed to using DriverManager in a JDBC 2.0 specification implementation.
98 Lotus Expeditor: Developing Applications for Lotus Expeditor
v A Statement enables the application developer to affect specific actions on the database. There are
specialized types known as PreparedStatement or CallableStatement that provide additional advantages
and capabilities. A Statement is created from a Connection object.
v A ResultSet represents the result of a query. The ResultSet object is returned upon successful execution
of a query in a Statement.
As previously mentioned, the JDBC APIs are part of the J2SE specification. In order to get a clear picture
of the current JDBC APIs it is useful to review the history of them and their relationship to the J2SE
specification. J2SE 1.2 defined JDBC 2.0, which included definitions in the java.sql package. The main
way of accessing databases was with the java.sql.DriverManager interface. Sun then defined the JDBC
2.0 extension package, which introduced the javax.sql package and a new DataSource interface for
accessing databases in Java, as well as support for connection pooling. JDBC 3.0 was subsequently
defined and combined the two components of JDBC 2.0 into one JDBC specification. This was first
included in J2SE 1.4.
In addition to knowledge of the development of JDBC APIs, database application developers also need to
have a detailed understanding of the Structured Query Language (SQL). SQL is the standard query
language used with relational databases and is not tied to a particular programming language. No matter
how a particular relational database management system (RDBMS) has been implemented, the user can
design databases and insert, modify, and retrieve data using the standard SQL statements and
well-defined data types. SQL-92 is the version of SQL standardized by ANSI and ISO in 1992. Entry-level
SQL-92 is a subset of full SQL-92 specified by ANSI and ISO that is supported by nearly all major DBMSs
today. In 1999, another update to the SQL standard was made available called SQL-1999 or SQL-99.
Embedded databases: Lotus Expeditor provides two relational databases that are accessible using JDBC
interfaces, DB2 Everyplace and Cloudscape.
v DB2 Everyplace features a small footprint relational database and high performance data
synchronization solution that enables enterprise applications and data to be securely extended to
mobile devices such as personal digital assistants (PDAs), smart phones, other embedded mobile
devices, and desktops. With DB2 Everyplace, the mobile work force in industries such as health care,
telecommunications, retail, distribution, transportation, and hospitality can now easily access the
information they need to perform their work from any location, at any time, right from the palm of
their hand. It is especially suitable for embedded devices, where large databases and sophisticated
queries are not normally required, but can also be used on desktop platforms. DB2 Everyplace
provides transaction support covering updates to multiple tables within a single transaction, encrypted
tables, and zero client administration.
v Cloudscape is a 100% pure Java relational database, providing SQL-92, partial SQL-99, and Structured
Query Language for Java (SQLJ) support, indexes, triggers, transactions, encryption, and the standard
features that one expects of a relational database.
DB2 Everyplace and Cloudscape comparison: DB2 Everyplace and Cloudscape are similar, but have
features that might make one a better choice for client needs.
Table 2. DB2 Everyplace and Cloudscape comparison
DB2 Everyplace Cloudscape
Implementation Type High performance native
implementation (see DB2E
documentation for a complete list of
supported devices)
Java-based (platform independent)
implementation
On-Disk Footprint 250 KB 2 MB
Number of connections supported Allows multiple concurrent
connections to a database from the
same VM
Allows multiple concurrent
connections to a database
Developing applications 99
Table 2. DB2 Everyplace and Cloudscape comparison (continued)
DB2 Everyplace Cloudscape
SQL Support Limited set of SQL data types Full SQL-92 support, partial SQL-99
support
Schema Support No Yes
Database Creation Requirements Directory for database tables must be
created prior to use
Cloudscape creates directory
JDBC URL jdbc:db2e:location
If no database exists at the location, a
new database structure is created
jdbc:derby:location
Database creation requires the
addition of an explicit create=true
attribute to the URL
In addition, the database location can
also refer to a zip or JAR file in the
case of a read-only database
Lifecycle management provider
available
No Yes
Refer to the product documentation for more complete information about these products.
Deployment and synchronization
When dealing with databases, you can choose to use a database only as a local data repository or as a
repository that actively synchronizes with another node in the topology. In either case, if data needs to be
distributed to a database, you need to balance considerations of how much data needs to be distributed
and when (once only at initialization, one way from one node to another only on an infrequent basis,
frequent exchange between nodes), with the storage capabilities at each node in question, and the
networking requirements that would permit the exchange to take place. In addition, if you choose
synchronization, application developers should consider database organization, filtering, and conflict
resolution policies. Synchronization is useful for exchanging the current state of data between nodes,
where transaction boundaries or the order of state changes are not important.
When you use data synchronization, database tables are automatically created. If a database is used only
for local storage and will not be synchronized with a server database, you must perform the additional
step of creating the initial database.
There are a few options for creating an initial database without using database synchronization:
v Incorporate code to create the initial database within the application. The advantage of this is that the
application is fully responsible for creating the database. In addition, if there is a need to rebuild the
database, the code is already present. No additional steps are required by the user of the database. One
disadvantage is that the code must be carried along with the application, even though it might never
be used again. Another disadvantage is that the population of initial data into the database might be
too large, or not appropriate to include within Java code. For read-only databases, this is generally not
appropriate; if the data were available to populate into the database, then you probably would not
need a database. Database creation code could be provided in a separate OSGi bundle that is then
removed from the framework after the database has been constructed.
v Distribute the database files with the application. The database is constructed and populated outside of
the client environment. The resulting database files are then distributed with the application, either in
directory format or in a zip or JAR file. The advantage of this is that the code to build and populate
the database runs in another environment; it does not need to be distributed with the application. This
is an ideal choice for distribution of a read-only database, as the data can be distributed using CD,
memory cards, or other distribution mechanisms. The disadvantages are that the distribution of the
files might be more difficult than distributing code. Also, updates to the database typically require
redistribution of database files.
100 Lotus Expeditor: Developing Applications for Lotus Expeditor
v Distribute Data Definition Language (DDL) (a set of database statements) and require the installation
application or the end user to create the database. While this overcomes some of the disadvantages of
incorporating database creation code within the application, it requires that the end user be sufficiently
knowledgeable to create the database, or the installation application becomes more complex. In
addition, this also typically requires additional tools (such as the command-line tools DB2eCLP for DB2
Everyplace, or ij for Cloudscape) to be present on the client or device.
Another option for database creation, and for continual update, is to use database synchronization
facilities. DB2 Everyplace and Cloudscape are both capable of synchronizing with the DB2 Everyplace
Sync Server, using the IBM ISync technology provided with Lotus Expeditor. The initial synchronization
activity creates the local database tables and also populates the initial set of data. As data is updated on
the client device, synchronization transfers that data to the DB2 Everyplace Sync Server and then to other
client devices that are configured to receive it. Database administrators set up the necessary subscriptions
for synchronization, and can also set up filtering of data to limit the amount of data distributed to client
devices.
Database application developers can use the ISync APIs provided with Lotus Expeditor to control the
synchronization process for their specific databases. This includes initiating the sync, monitoring events
during the process, and managing any conflicts or errors that occur.
A simple iSync sample is available in the Rational Samples Gallery under Technology Samples > Lotus
Expeditor.
It is also important to note some differences in database application programming when using database
synchronization:
v Any changes made to the local copy might appear much later in the server. Synchronization requires
the application or user to initiate the sync to the sync server; the replication cycle must kick in so that
changes are pushed back to the back end database server. Furthermore, the local changes can be
rejected for many reasons, for example, because conflicts were found at the database server side.
v There is a latency between synchronizing changes from a client to the server and then from the server
to other clients. In other words, if a change was made to a local database, these changes do not appear
on another client device until the local database is synchronized successfully to the sync server. Then
the sync server successfully replicates the changes to the back end database, the changes come back
down the sync server for other clients; the changes show up on another client device when it
successfully synchronizes.
v Database synchronization is based on row-level updates synchronized to and from the DB2 Everyplace
Sync Server and the client devices. Database synchronization captures only the current state of data for
synchronization. Because the database row contains only the current state of the data, there are two
situations in which the application might need to provide additional capabilities. The first situation is
when the ordering of updates that were applied to the database is important. The second situation is
when a historical record of the changes to the rows in the database is required. In either of these
situations, the application needs to provide this capability. The application can store each change in
value in a separate row in the main table, or use additional tables for history purposes. Optionally, the
application can use assured messaging to send the various updates to the server.
Security considerations: Server databases typically reside in a well-secured zone, with limited access to
applications residing in other network topologies. As a result, data in the database is secured because of
network location and access rights. In addition, database backup or sophisticated data management exists
to protect against data loss.
Databases existing on client systems have different levels of protection. Physical device security is the
first barrier in preventing unauthorized access to databases. This includes locking up or securing the
physical device as well as providing secure passwords to protect against others illicitly using the device
to access data. An additional way to protect the data in the database is to use the encryption technologies
provided by the databases. The entire database, or specific tables, can be encrypted using a key
Developing applications 101
(password) that must be provided by the user. This protects against someone being able to open the
database if they were to obtain the physical media storing the database (such as a CD for read-only
databases, memory keys, Compact Flash cards, and so on).
To protect against data loss, especially for local databases or local synchronized databases, you should
put a backup or synchronization strategy in place to ensure that data is synchronized on an appropriate
schedule. Incidentally, this also reduces the chances of data on the device becoming out of date.
Database lifecycle management
The Database Lifecycle Management framework offers Lotus Expeditor applications the ability to
interoperate with a relational database in a uniform and transparent manner (using the notion of
managed datasources). It provides the platform administrator/developer:
v A declarative way of specifying the required database structure
v The ability to initially populate databases prior to client access
v Supports migration from one database structure to another
The database population and migration is handled either by scripts or specialized database programming
tasks that have the power to manipulate a database’s contents. The granularity of this task falls along the
line of a datasource which simply refers to a database definition and its associated schemas (there are
also virtual datasources which allow for the reuse of already defined datasources). This allows for a finer
control over schema lifecycle within the framework. The framework empowers platform administrators to
define simple scripts that handle migration related tasks while being completely transparent to the
application developer. The application developer can access the framework via a standard JNDI context.
The framework automatically performs the necessary functions of the platform administrator.
The framework also defines a method to allow application developers to monitor changes to a
datasource. This can be useful when migration tasks take a long time and the administrator may offer
some relevant visual feedback to the end-user.
Another feature of this framework is the ability to define other database providers. By default, the
platform includes a default datasource that is based on the Apache Derby database. If a platform
administrator requires another database type, they have the power to define their own provider to be
accessed by datasources platform-wide. A good example of when a new database provider would be
defined is in the embedded space where Apache Derby is not the best database to use due to memory
constraints.
Included in the framework is a defined platform default datasource which is based on the Apache Derby
embedded database. The reason this datasource exists is because many applications have a need to store
encrypted data on the Lotus Expeditor platform so having a reusable platform datasource can allow
applications to do so. Applications can provide their own schemas through virtual datasources in order to
interact with the platform default datasource.
On the whole, the Database Lifecycle Management framework aims to simplify the tasks of a platform
administrator by offering lifecycle management and making the whole process transparent to the
application developer.
Enabling projects for data access application development
This section discusses enabling projects for data access application development.
Client Services target profile features
The Lotus Expeditor Toolkit provides Client Services target definition support. These target definitions
simplify the creation and configuration of database application projects, enabling you to select the
target-embedded database, and provide automatic management of the requisite JDBC libraries. When
developing a data access application, any of the Client Services target definitions can be selected for your
Client Services project. However, ensure you select the Cloudscape target feature for project access to the
102 Lotus Expeditor: Developing Applications for Lotus Expeditor
Cloudscape database engine, or the DB2 Everyplace client target feature for the DB2 Everyplace database
engine. If you are interested to developing code leveraging the DB2 Everyplace ISync APIs, be sure to
select the DB2 Everyplace Client Sync or the Cloudscape Client Sync for project access to the ISync APIs.
Finally, if you are developing implementations/extensions for the database lifecycle management
framework then select the Database Lifecycle Management target feature. For more information on Client
Services projects, refer to “Lotus Expeditor Toolkit” on page 11.
Developing database logic
This section details database logic development.
Data access application development best practices
There are typically two reasons for creating an application that needs to make use of database
technologies. One reason for using database technologies is to create a new lightweight client application.
Another reason is to adapt an existing server-side application for use as a lightweight client application.
Enterprise Java developers who have written applications requiring the use of JDBC typically rely on
obtaining access to the database through a DataSource object. The DataSource object will have already
been bound by the Java Naming and Directory Interface (JNDI). The application developer simply needs
to locate the DataSource using JNDI, and obtain from the DataSource a connection to the database. The
application deployer cooperates with the system administrator to define the mapping and access to a
physical database, causing the DataSource object to be bound into JNDI.
Also, in the server environment, database management often falls within the administration realm of a
database administrator. A database administrator is typically responsible for creating the physical
database, partitioning the database for use among several applications, creating tables and indexes for a
particular application, managing the access rights to a database, and monitoring the performance of the
database. When the application developer has successfully located a DataSource using JNDI, the
developer can obtain a Connection, and begin performing actions against the database.
When adapting a server-side application to run on a client, the application developer often takes more
responsibility, performing some roles typically handled by a database administrator. The application
developer might be responsible for initially creating the database, creating tables, and configuring the
database. In addition, depending upon the set of components available on the client, the application
developer might also be responsible for creating the DataSource objects, either directly within the
application, or within a JNDI environment on the client. If no JNDI support is available in the client
runtime environment, the application developer should use the standard JDBC 3.0 javax.sql.DataSource
creation methods. Regardless of how the DataSource object is created or located, application developers
still obtain Connection objects from the DataSource, and create Statement objects from the Connection.
In order to ensure minimum changes to applications that use JDBC, some best practices should be
followed:
v Use DataSource objects as they exist across the JDBC 3.0 specifications; DriverManager does not exist
in the JDBC Optional Package for CDC/Foundation Profile. Limiting the JDBC application usage to the
JDBC Optional Package for CDC Foundation Profile subset of JDBC 3.0 provides the most portability
from desktops to devices.
v Isolate the DataSource creation or location to a single class. Later, if the environment changes, a change
needs to be made in only one place.
v Ensure you close all objects (ResultSet, Statement, Connection) when you have completed work.
Servers typically include more sophisticated connection management known as connection pooling,
which can accommodate mismanagement of connections. However, on the clients, application
developers are directly responsible for the life cycle of the objects. By promptly closing objects, memory
requirements will remain at a minimum.
v Do not hard code a schema identifier during statement creation. DB2 Everyplace does not support
schema names, so all statements would need to be changed if an application needed to be migrated to
DB2 Everyplace.
Developing applications 103
v The SQL statements supported by a particular database might not match the statements used within an
existing application. The application either needs to adapt statements depending upon database type,
or obtain statement information from externalized information.
Lotus Expeditor includes the Lotus Expeditor Toolkit, which provides Target Profile support. These Target
Profiles simplify the creation and configuration of database application projects, enabling you to select the
target-embedded database, and provide automatic management of the requisite JDBC libraries. In an
Eclipse SDK plug-in development environment, you can use the standard plug-in dependency tooling to
provide the necessary access to DB2e and Cloudscape libraries during database application development.
Eclipse is an award-winning, open source platform for the construction of powerful software
development tools and rich desktop applications.
While for Lotus Expeditor development we recommend the use of the DataSource interface for JDBC
programming, there may also be cases where the DriverManager interface must be used for legacy code. If
this is the case, please note that while in previous releases of the Lotus Expeditor runtime, the platform
would load the database driver classes at platform startup, this is no longer the case. The method for
accessing a database via JDBC using the DriverManager interface involves 2 steps:
1. Loading the database driver via a Class.forName() method call.
2. Connecting to the database.
The DriverManager class to be loaded for IBM Cloudscape is org.apache.derby.jdbc.EmbeddedDriver and
the DriverManager class to be loaded for DB2 Everyplace is com.ibm.db2e.jdbc.DB2eDriver.
For more information on JDBC programming please see the SUN JDBC tutorial at http://java.sun.com/docs/books/tutorial/jdbc/index.html.
Database Lifecycle Management
This section provides information on Database Lifecycle Management.
Accessing a defined managed datasource: To look up a datasource, invoke a standard JNDI lookup:
InitialContext context = new InitialContext();
DataSource source = (DataSource) context.lookup("jdbc/MyDataSource");
Connection connection = source.getConnection();
For more information about JNDI, please see Sun’s documentation: http://java.sun.com/j2se/1.5.0/docs/api/javax/naming/package-summary.html.
Monitoring operations on a managed datasource: To define a listener, first create a class that
implements IProgressMonitor:
public class TestProgressMonitor implements IProgressMonitor {
...
public void done() {
System.out.println("Processing is done!");
}
...
}
Next, implement the com.ibm.rcp.database.core.listener extension point. The following sample listener
listens against the datasource jdbc/MyDataSource.
<extension point="com.ibm.rcp.database.core.listener">
<listener class= "com.ibm.rcp.database.tests.listeners.TestProgressMonitor"
jndiName="jdbc/MyDataSource"/>
</extension>
Accessing the default managed datasource: By default, the platform includes a default datasource based
on the Apache Derby database. To access it, perform a JNDI lookup using its defined JNDI name
(jdbc/DerbyDS):
104 Lotus Expeditor: Developing Applications for Lotus Expeditor
InitialContext context = new InitialContext();
DataSource source = (DataSource) context.lookup("jdbc/DerbyDS");
Connection connection = source.getConnection();
Developing Embedded Transaction applications
The Embedded Transaction capability of the Lotus Expeditor platform enables the development and
deployment of business logic components by supporting a subset of the Enterprise Java Bean (EJB)
specification. These business logic components are referred to as Embedded Transaction applications, and
are run by the platform’s Embedded Transaction Container. The Lotus Expeditor platform only supports
execution of and access to Embedded Transaction Applications executing within the Lotus Expeditor
runtime.
Note: Use of the Embedded Transaction development tools requires Rational Application Developer,
Rational Software Architect, or Application Server Tools. The Embedded Transaction development
tools are not supported with just the Web Tools Platform.
Embedded Transaction applications can be developed using many of the same EJB development tools
provided by the Rational Software Development platform. You should therefore refer to the Rational
online help section “Developing Enterprise applications” as your initial development tools reference. The
following topics discuss the additional development considerations and tool usage required when
targeting an Embedded Transaction application for the Lotus Expeditor platform.
The following table provides pointers to information on tasks that are unique to, or require special
consideration when developing Embedded Transaction applications for the Lotus Expeditor platform.
Table 3. Embedded Transaction application tasks
Task Reference
Understanding Embedded Transaction concepts,
including which elements of the EJB specification are
supported, and which are not.
“Understanding the Embedded Transaction Container”
Working with Client Services Embedded Transaction
projects versus EJB projects, and when to use one versus
the other.
“Creating Embedded Transaction projects” on page 108
Developing Embedded Transaction logic. This
encompasses any special development considerations
when coding and constructing the embedded transaction
logic.
“Developing Embedded Transaction Container logic” on
page 110
Performing embedded transaction deployment. The
Embedded Transaction container requires additional
deployment information beyond that provided for an
EJB.
“Packaging and deploying Embedded Transaction
applications” on page 119
Debugging and testing the Embedded Transaction
application.
“Debugging and testing applications” on page 323
Deploying the Embedded Transaction application to a
runtime.
“Deploying projects for local testing” on page 331
Understanding the Embedded Transaction Container
The Embedded Transaction Container provides tooling and runtime support for local Enterprise Java
Beans (EJBs). The current version supports the following features of the EJB 2.1 specification:
v Remote and Local Homes for local EJBs
v Stateless Session Beans
Developing applications 105
v Entity Beans, both bean managed persistence (BMP) and container managed persistence (CMP) at both
the EJB 1.1 and EJB 2.1 specification levels (local homes, use of abstract persistence schema)
v Container-managed transactions
v Entity Bean tooling container managed persistence (CMP) support for container managed field types
that implement java.io.Serializable.
v CMP Support for DB2e 8.2.1 and Derby 10.0.2
v JDBC DataSource support
v JNDI support
v Container-managed Relationships
The following features are not supported:
v Stateful Session Beans
v Pass-by-Copy semantics for mutable serializable objects when running in a single address space
v For EJB 1.1, the Embedded Transaction Container does not persist references to an EJB’s remote or
remote home interfaces. Note that this capability is not required of EJB 2.1
v Message-driven Beans
v Java Security support
v EJB Query Language
v Home methods
v Select methods
v Bean managed transactions
v Enumeration return type for finders. Collection return type is supported.
v Specification of transaction isolation level
v Support for Collections and Iterators outside of the transactions in which they were created
The programming models for the Embedded Transaction container and WebSphere’s J2EE EJB
server/container are very similar. But, there are differences between the two models, primarily in how
they reduce runtime resource requirements. The development differences are covered in “Developing
Embedded Transaction Container logic” on page 110.
Concepts
The Embedded Transaction Container runtime is based on an extended subset of the EJB 2.1 spec. The
following are descriptions of EJB 2.1 concepts related to the Embedded Transaction Container.
EJB Container: The container provides runtime support for EJB components. This includes handling
such tasks as persistence, transaction scoping and management, database connection management, and
EJB instantiation/caching.
Home Interfaces: These interfaces define the EJB’s life-cycle methods, such as creating, locating, and
removing EJBs.
In the J2EE EJB programming model, there is a need to access data stored locally and/or stored on a
remote server. As a result, there are two types of home interfaces: Local Home Interfaces and (Remote)
Home Interfaces.
While the Embedded Transaction Container only allows access to data stored locally, both Local and
(Remote) Home Interfaces are supported for accessing that data. This provides more affinity with the
J2EE EJB 2.1 programming model. While J2EE EJB implementations recommend using the local home
interface for accessing local data for performance reasons, either local or remote home interfaces can be
used in the Embedded Transaction Container with little (if any) performance differences. Since the
Embedded Transaction Container never allows access to EJBs on other servers, the Embedded Transaction
Container’s (remote) home interface does not have the same overhead of the J2EE EJB’s implementations.
106 Lotus Expeditor: Developing Applications for Lotus Expeditor
Finder Methods: The home interface must contain a findByPrimaryKey method to locate/return an EJB
based on its primary key. Other finder methods locate/return EJB(s) based on other search criteria.
Note: The Embedded Transaction Container requires that these other finder methods on CMP entity
beans be implemented by subclassing an Embedded Transaction Container specific class,
BaseJBDCFinder.
Component interfaces: From an application programmer’s perspective, these interfaces provide access to
the application data or services on the EJB.
Transaction management: The J2EE EJB programming model provides for three forms of transaction
management:
1. Container managed
2. Bean managed
3. Programmatic managed.
The Embedded Transaction Container provides support 1 and 3. The Embedded Transaction Container
does not support bean managed transaction.
Container managed transactions: As the name implies, when using container managed transactions, the
container is responsible for the beginning, rolling back, and committing of transactions. The container
manages these transaction based on runtime transaction attributes that are defined for the beans. For
example, when calling a session bean method that has specifying “Required” as its runtime transaction
attribute, the container will check to ensure there already is a transactional context. If there is one, it will
continue on. If there isn’t one, a new transactional context will be established before continuing on.
The Embedded Transaction Container supports all the J2EE EJB 2.1 Runtime Transaction Attributes:
v NotSupported – suspends the transaction of the caller (if there is one) until the associated method
completes
v Supports – causes the bean to become part of the caller’s transaction (if there is one)
v Required – causes the bean to become part of the caller’s transaction (if there is one). If the caller is
not part of a transaction, a transaction is created for the life of this method call
v RequiresNew – when the caller is not part of a transaction, the behavior is the same as “Required”. If
the caller is part of a transaction, that transaction is suspended, and a new transaction is setup for the
life of the method call. After the new transaction’s scope ends, the old transaction is resumed.
v Mandatory - causes the bean to become part of the caller’s transaction (if there is one). If there isn’t
one, a TransactionRequiredException is thrown
v Never – if the caller is part of a transaction, a RemoteException is thrown. If the caller is not part of a
transaction, the method is called outside of a transactional scope.
Note: If no runtime transaction attribute is specified for a method, a default value of Required is used.
Programmatic transaction management: Letting the container manage transactions is much simpler than
dealing with the beginning, rollback and committing within the code itself. But, there are cases where the
programmatic approach is worthwhile. One case would be when method calls against several session
beans need to be treated as a transaction. While the Embedded Transaction Container does not provide a
J2EE-compliant Transaction Manager, it does provide a factory for creating user transactions, the
UTFactory. These user transactions provide a programmatic means of managing a transactional context.
DataSource/TxnDataSource: Typically connecting to a relational database is done via a JDBC Data
Source. These data sources are usually retrieved via JNDI.
The Embedded Transaction Container’s implementation of CMP beans uses a TxnDataSource, a subclass
of the JDBC Data Source. This class is defined in an internal package. While this TxnDataSource is
accessible by all via JNDI, it should only be used for CMP beans. Using a TxnDataSource in the
Developing applications 107
implementation of BMP beans or Session Beans could lead to a deadlock. So, when implementing BMP
beans or Session Beans that require relational persistence, a standard JBDC Data Source should be used.
Creating Embedded Transaction projects
This section details creating Embedded Transaction projects.
Using a Client Services Embedded Transaction project versus an EJB project
Embedded Transaction applications can be developed using either a Client Services Embedded
Transaction project or an EJB project. The choice of which to use depends on the application content and
its primary usage. In general, applications that primarily target the Lotus Expeditor platform or depend
on other OSGi services besides core Embedded Transaction support should be developed using a Client
Services project.
A Client Services Embedded Transaction project is an extension of the EJB project. Because of this, both
types of projects make use of the Rational Software Development EJB tools. In addition to this, a Client
Services Embedded Transaction project provides the following support for developing an application that
is targeting the Lotus Expeditor platform.
v The manifest file required by Lotus Expeditor application can be automatically managed by the tools.
v The project’s class path is maintained to match the class path environment that will exist in the Lotus
Expeditor runtime. This is useful for detecting class visibility problems at development time rather
than runtime.
An EJB project will not have the Lotus Expeditor specific tooling aids listed above, but can still be tested
and run on the Lotus Expeditor platform. This is accomplished by targeting the project’s server to the
Lotus Expeditor runtime through the project’s server properties. The tooling will automatically add the
proper manifest entries for Embedded Transaction support. However, if the application references other
OSGi services or bundles, the developer will have to manually add these dependencies to the manifest
file.
A Client Services Embedded Transaction project can also be tested and run on a platform other than
Lotus Expeditor by reassigning its targeted runtime through the project server properties. Refer to
“Debugging and testing applications” on page 323 for further general information. Refer to “Debugging
and testing Embedded Transaction applications” on page 120 for Embedded Transaction specific
debugging information.
Creating a Client Services Embedded Transaction project
Complete the following steps to create a new Client Services Embedded Transaction project:
1. Select File > New > Project. Under the Client Services folder, select Client Service Embedded
Transaction Project. The Embedded Transaction Project creation wizard displays.
2. On the first wizard page, specify the following options:
v Project name
v Project location (Default: current workspace)
Select either Finish or Next.
3. If you select Finish, the project will be created with the default project facets:
v EJB Module, 2.1
v Java, 5.0
v Embedded Transaction Bundle, 6.1
v The default Java Source Directory (ejbModule)
v The default Target Definition with Core OSGi, and Embedded Transaction Container service
selected.
108 Lotus Expeditor: Developing Applications for Lotus Expeditor
Note: The actual “service” definitions, which are now “features”, may change based on the new
“target platform” based definitions to be created by the runtime. The tools automatically select
the necessary “features” required for Embedded Transaction Container development.
4. If you selected Next, the Select Project Facets page displays. This page is for selecting facets and to
specify their versions. EJB Module, Java and Embedded Transaction Bundle facets are pre-selected,
and cannot be deselected. Select either Finish or Next.
5. If you selected Next, the EJB Module page displays. Use it to configure EJB module settings, such as
source folder information, and whether or not to create a client JAR. Select either Finish or Next.
6. If you selected Next, the Target Definition page displays. Use it to select target definitions and target
features. Core OSGI and Embedded Transaction Container services are pre-selected, and cannot be
deselected. You can add other Target Features if needed. Select Finish.
Note: Having a very large number of services selected can cause an IOException during deployment.
Do not use the Select All button.
Converting an EJB project to a Client Services Embedded Transaction project
You can convert an existing EJB project into a Client Services Transaction project by using the Convert
Project to Client Services project wizard.
1. Open the Convert Project to Client Services project wizard by selecting File > New > Other... Expand
Client Services, and select Convert Project to Client Services project.
2. The first wizard page contains a list of non-Client Services projects for conversion. Select the EJB
project to be converted. Only one project can be selected for conversion at a time.
3. If desired, select the Copy before creating option. When selected, the project will be copied before
being converted. You must specify the name of the new project copy. The new project copy will be
converted.
4. Selecting Finish assigns the project the default Target Definition with Core OSGi and Embedded
Transaction Container features.
5. If you select Next, the Target Platform page displays. Core OSGi and Embedded Transaction
Container features are pre-selected. If necessary, you may select additional features. Select either Next
or Finish.
6. Select Finish. a pre-conversion test is run, displaying any errors and warnings based on elements of
the existing EJB that may require modification when running against the Lotus Expeditor runtime. If
any errors or warning conditions are found, the user is prompted as to whether the conversion should
continue or be cancelled.
Embedded Transaction Container preferences
You can set Embedded Transaction Container preferences at the workspace level and on a per project
basis. For workspace preferences, select Window > Preferences... > Client Services > Embedded
Transaction. For project preferences, open the project’s pop-up menu and select Properties, then the
Embedded Transaction tab. In both cases, the Embedded Transaction properties page is displayed.
The following preferences are available:
Target database type
You can select either DB2e or Derby. Derby refers to Cloudscape v10 and higher.
ETC logging level
You can select the level of trace information logged to the console during deployment and
subsequent runtime testing. Levels include {fatal, error, warning, info, debug, trace}.
Modify the Embedded Transaction Container preferences and select OK to save, or select Cancel to exit
without saving your changes. The target database type preference can be set at both the workspace and
the project level.
The ETC logging level can be set at the workspace level.
Developing applications 109
When the “Use Default” option is selected for a project’s target database type, the value is retrieved from
the workspace’s target database type.
When the “Use Default” option is selected for a workspace’s target database type, “DB2e” is the default
value used.
When the “Use Default” option is selected for a workspace’s logging level, “error” is the default value
used.
Developing Embedded Transaction Container logic
The Embedded Transaction container is targeted for more constrained devices than typical J2EE EJB
servers/containers. While the programming models between the two are very similar, there are aspects
that are unique to Embedded Transaction Container.
The following tasks involve Embedded Transaction specific considerations:
v Implementing Finder Methods
v Configuring and Using Data Sources
v Locating EJBs
v Conserving JDBC Resources
v Working With User Managed Transactions
v Providing Custom Bundle Activation
There are also deployment related differences, such as the introduction of the eejb_deploy.xml file. These
differences are covered in “Packaging and deploying Embedded Transaction applications” on page 119.
Implementing finder methods
The Embedded Transaction Container uses the following approach to implementing custom finder
function. For each finder method declared on the Home interface (other than findByPrimaryKey), you
must provide the business logic required to build the corresponding collection. The tool requires that this
logic be packaged in an abstract finder helper class, which the tool extends as a concrete BaseJDBCFinder
helper class. Typically the business logic is encapsulated in a SQL SELECT statement.
The container provides a base BaseJDBCFinder class, com.ibm.pvc.txncontainer.BaseJDBCFinder, that
provides the bulk of the required finder functionality. By extending this class, you only need to supply
the actual finder logic. For every custom method, findXXX , defined on the Home interface, you must
code a corresponding ejbFindXXX method on the abstract finder helper class. BaseJDBCFinder provide the
methods getTableName() (returns String specifying the database table name) and
getPreparedStatement(String) (returns a PreparedStatement derived from the appropriate DataSource).
Finders can return single items or collections of items.
The following is a sample multi-result finder implementation, which returns a Collection:
public abstract class Customer20JDBCFinder extends BaseJDBCFinder
{
public Customer20JDBCFinder(DataSourceHome arg0, String arg1) {
super(arg0, arg1);
}
public Collection ejbFindByFirstName (String firstName) throws FinderException
{
final String selectSQL = "select * from customer where fname = ?";
PreparedStatement pstmt = null;
try {
pstmt = getPreparedStatement(selectSQL);
pstmt.setString(1, firstName);
110 Lotus Expeditor: Developing Applications for Lotus Expeditor
}
catch (SQLException e) {
throw new FinderException
("Problem executing Finder: "
+ selectSQL
+ ", Exception = "
+ e.toString());
}
return new BaseJDBCCollection(pstmt, this);
}
The following is a sample single-result finder implementation, which returns a single key:
public abstract class Customer20JDBCFinder extends BaseJDBCFinder
{
public Customer20JDBCFinder(DataSourceHome arg0, String arg1) {
super(arg0, arg1);
}
public Customer20Key ejbFindById (String Id) throws FinderException
{
final String selectSQL = "select id from customer where id = ?";
PreparedStatement pstmt = null;
try {
pstmt = getPreparedStatement(selectSQL);
pstmt.setString(1, Id);
}
catch (SQLException e) {
throw new FinderException
("Finder = "
+ selectSQL
+ ", Exception = "
+ e.toString());
}
return (Customer20Key) singleResultFinder(pstmt, true);
}
In addition to the SQL application logic, you should ensure the following requirements are met:
v Ensure that the finder helper is abstract.
v Ensure that the finder helper extends BaseJDBCFinder
v Code the appropriate two-parameter constructor, and invoke super()
v Collections must be used as the return type for mutli-result finders. An Enumeration return type is not
supported.
The Boolean parameter supplied to the singleResultFinder method determines whether only one object
can match the finder criteria. If true, the container will throw an exception if more than one object
matches; if false, one object will be returned from the result set, however, you will have no way of
determining which object is selected.
Suppose you have an entity bean to which you want to add additional searching/lookup capabilities. For
the purposes of these steps, assume you have an entity bean representing a customer, with the Customer
class representing the remote interface, CustomerBean the actual implementation, and CustomerHome as the
home interface. In order to add additional search methods, you need to do the following:
1. Update the CustomerHome class to define the new method (e.g. findByFirstName (String firstName).
2. Create a new class extending BaseJDBCFinder.
a. The constructor must call super( arg0, arg1 ).
b. You must implement a method ejb<findername>. For our example, therefore, you must implement
the method ejbFindByFirstName( String firstName ).
Developing applications 111
c. Use the method getPreparedStatement( sqlstring ) to obtain a statement to execute. The base
class provides the appropriate setup of requesting a connection, etc.
d. For a collection result, return a BaseJDBCCollection object.
e. For a single result finder, return the results of the singleResultFinder( ) method.
Configuring and using data sources
This section provides information on configuring and using data sources.
Creating and binding DataSource instances: Data base vendors provide implementation specific
DataSource classes for connecting to their databases. The Embedded Transaction Container requires a
specific DataSource, TxnDataSource, be used when connecting to the database for CMPs. This specific
DataSource is created via the TxnDataSourceFactory. The TxnDataSource is only used for CMP access to
the database. All other database access should be done using DataSource.
This “wrapping” of the vendor specific DataSource can be done before JNDI binding or after lookup.
Wrapping before JNDI binding is the best practice, since this is done once, as opposed to wrapping after
every lookup.
Note: For information on using JDBC DataSource to access JDBC data bases, refer to “Data access
application development best practices” on page 103.
Advanced topics: While the declarative JNDI means of wrapping and binding is the preferred method,
this can also be handled programmatically, as follows:
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import com.ibm.db2e.jdbc.DB2eDataSource;
import com.ibm.pvc.txncontainer.TxnDataSourceFactory;
private void createAndBindDataSource() throws NamingException {
// create DB2e specific Datasource object and set it’s jdbc url
DB2eDataSource db2eDS = new DB2eDataSource();
db2eDS.setUrl("jdbc:db2e:" + EJBDB_LOC);
// wrap and bind the vendor specific data source
DataSource ds = TxnDataSourceFactory.create(db2eDS);
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.ibm.pvc.jndi.provider.java.InitialContextFactory");
InitialContext context = new InitialContext(env);
context.bind(DATASOURCE_NAME, ds);
}
Where EJBDB_LOC is the physical location of the database, and DATASOURCE_NAME is the JNDI name of the
DataSource.
Locating and connecting to a DataSource: Session beans and client applications typically require a
database connection, and, according to the EJB specification, acquire the DataSource from JNDI in the
same way that it does when finding EJB homes (for more information, refer to “Finding EJB homes” on
page 113. The following sample shows how Session Beans and client applications can acquire a
Connection in the Embedded Transaction Container environment.
import java.sql.Connection;
import javax.sql.DataSource;
import javax.naming.InitialContext;
protected Connection getConnection() throws SQLException
{
DataSource ds = null;
try {
112 Lotus Expeditor: Developing Applications for Lotus Expeditor
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.ibm.pvc.jndi.provider.java.InitialContextFactory");
InitialContext context = new InitialContext(env);
ds = (DataSource) context.lookup(DATASOURCE_NAME);
}
catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException("Cannot lookup DataSource:" + e);
}
return ds.getConnection();
}
Where DATASOURCE_NAME is the JNDI name of the DataSource.
Locating EJBs
The EJB deployment descriptor includes information about the EJB’s JNDI binding. As with J2EE EJBs,
the JNDI related values can be set via the Deployment Descriptor editor.
The actual timing of EJB binding is handled by the Expeditor Server JNDI provider and Declarative JNDI.
Finding EJB homes: Client applications perform the following operation to access deployed EJBs:
// Import the JNDI InitialContext class
import javax.naming.InitialContext;
// Name of the Home
final String jndiName = "java:comp/env/EmployeeFromJDBC";
// Get the reference to the Home
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.ibm.pvc.jndi.provider.java.InitialContextFactory");
InitialContext context = new InitialContext(env);
final EmployeeHome localHome = (EmployeeHome)context.lookup(jndiName);
Conserving JDBC resources
EJBs provide abstractions that shield the programmer from the technical details of the underlying data
stores. These abstract layers need help managing the underlying data base resources to work within the
constraints of the embedded data base engines. For example, DB2e v8.2 supports a maximum of 20 open
statements. When working with Container Managed Persistence (CMP) Beans, statements may be opened
when a finder method creates a collection and when an iterator is used to walk the contents of that
collection. When an iterator has reached its end, the corresponding statement can be closed. While the
CMP abstraction layer will automatically close statements when it is certain they are no longer needed, it
is up to the application programmer to explicitly dispose of all Collections and Iterators to ensure that the
corresponding statements are closed prior to commit or garbage collection implicitly closing them. When
a program no longer needs a Collection or Iterator, if that object is an instance of IDisposable, the
dispose method should be called on it.
For example:
if (myCollection instanceof IDisposable) {
((IDisposable)myCollection).dispose();
}
Working with user managed transactions
The Embedded Transaction Container provides an implementation of a transaction manager. This
TransactionManager is used for automatically handling transaction management issues.
Note: Unlike a J2EE TranactionManager, the embedded transaction container’s transaction manager does
not provide an implementation of javax.transaction.TransactionManager, nor does it support two
phase commit.
Developing applications 113
Advanced topics: Applications generally do not work directly with the transaction manager. For the
advanced cases, the Embedded Transaction Container does provide a User Transaction Factory
(UTFactory) that enables applications to work with user transactions.
The UTFactory provides a static getUserTransaction() method that will return an implementation of the
javax.transaction.UserTransaction interface. This UserTransaction instance is used by developers for
handling typical transaction-related actions:
v begin() – creates a new transaction and associated it with the current thread
v commit() – completes the transaction associated with the current thread
v rollback() – rolls back the transaction associated with the current thread
v setRollbackOnly – after calling this method, a “rollback” is the only possible outcome of the
transaction associated with this thread
v getStatus() – obtains the status of the transaction associated with this thread
v setTransactionTimeout() – is not supported
Providing custom bundle activation
The Embedded Transaction Container tooling creates projects which reference the default bundle
activator, com.ibm.pvc.txncontainer.GenericActivator. This activator registers the EJB’s home interface.
The following information describes how to add a customized bundle activator to the project.
Advanced topics: A custom bundle activator would be required if the application needs to perform
OSGi specific operations. To create a custom bundle activator for an existing project, you can perform the
following steps:
1. Add the custom bundle activator class to the package in which the other EJB classes (home, interface,
implementation, finders) are located.
Note: The custom bundle activator class must extend the
com.ibm.pvc.txncontainer.GenericActivator class and call the start() and stop() methods of
GenericActivator inside its own start() and stop() methods.
2. Open the Bundle Manifest editor on the project’s META-INF/MANIFEST.MF file, and enter the custom
bundle activator class in the Class field of the Overview page. This will update the EJB manifest file
META-INF/MANIFEST.MF, by setting the Bundle-Activator property to the custom bundle activator class.
Custom bundle activator example:
import org.osgi.framework.BundleContext;
import com.ibm.pvc.txncontainer.GenericActivator;
/**
* Minimum bundle activator for EJBs
*/
public class MyBundleActivator extends GenericActivator {
public void start(BundleContext context) throws Exception{
// custom tasks here
super.start(context);
}
public void stop(BundleContext context){
// custom tasks here
super.stop(context);
}
}
Creating Session and Entity Beans
Typically, entity beans are used to model business Target Features. These services usually correspond to
“verbs” in the business domain. There are two types of Session Beans: Stateless and Stateful. The
difference between the two is whether the bean maintains a conversational state with a client. In stateless,
114 Lotus Expeditor: Developing Applications for Lotus Expeditor
every method call is independent. In stateful, information can be gathered and used for subsequent
method calls. In keeping with the goal of reducing the implementation size, the Embedded Transaction
Container only supports “Stateless”.
Typically, entity beans are used to model business objects that require persisting. These business objects
usually correspond to “nouns” in the business domain. Getters/Setters are used to access the state of
these objects. There are two types of Entity Beans: Bean Managed Persistence (BMP) Beans and Container
Managed Persistence (CMP) Beans. The difference between the two is whether or not the bean
programmer is responsible for implementing the actually storing of the bean’s data. For BMPs, the
programmer is responsible. For CMPs, the container is responsible.
Creating a Container Managed Persistence (CMP) bean: To create a Container Managed Persistence
bean, perform the following procedure:
1. Click File > New > Other > EJB > Enterprise Bean. The Create an Enterprise Bean wizard appears.
Note: Other EJB options, such as “XDoclet Enterprise JavaBean” should not be selected. The
“Enterprise Bean” option must be selected.
2. Select the Entity bean with container-managed persistence (CMP) fields radio button.
3. Select the EJB Component you want to add the bean to.
4. In the Bean name field, type the name you want to assign to the enterprise bean. By convention,
bean names should begin with an uppercase letter.
5. In the Source folder field, select the source folder for the new bean.
6. In the Default package field, enter the package name for the new bean.
7. If you are adding the bean to an EJB 2.0 or later project, in the CMP version drop-down list, select
the EJB specification level that you want to use for the new entity bean.
8. Optional: For EJB 2.x beans, you can select Generate an annotated bean class. If you select this
option, the wizard generates annotations at the beginning of the Java code for the bean class. The
annotations define the bean’s implementation as you specify in the wizard. The annotations are then
used by the EJB tools to generate the necessary bean classes, and to provide values that are inserted
in the EJB deployment descriptor (ejb-jar.xml). Select this option if you are familiar with
annotations and want to use the annotations to update the bean rather than using the deployment
descriptor.
9. Click Next.
10. In the Bean class field, enter the desired package and class name for the bean class. By default, the
wizard suggests a bean class based on the bean name and default package that you defined. A bean
class can be a new class that the wizard generates, or it can be an existing class in the project class
path. Click the Class button to open a dialog that lists the classes in the project that correspond to
the bean type. The name of the bean class appears blue for existing classes with source. The name of
the bean class appears red for existing binary classes.
11. Define the client views and interfaces. For EJB 2.0 or later beans, you can include a remote client
view, a local client view, or both. For EJB 1.1 beans, only a remote client view is supported. Every
session or entity bean must have at least one client view:
v Remote client view: Select this check box to include a remote client view for the session bean.
In the Remote home interface and Remote interface fields, enter the package and class names that
you want to use for the remote client view interfaces. The wizard uses the bean name and default
package to suggest values for the interface package and class names.
v Local client view: Select this check box to include a local client view for the session bean.
In the Local home interface and Local interface fields, enter the package and class names that you
want to use for the local client view interfaces. The wizard uses the bean name and default
package to suggest values for the interface package and class names.12. Optional: In the Key class field, enter the package and class name for the bean’s key class. By
default, the Use the single key attribute type for the key class check box is selected, and the type
Developing applications 115
of the key CMP attribute is used as the key class. Use this option if you have a single key attribute
whose type is a valid primary key field (for example, java.lang.Integer). If you include more than
one key attribute, a new compound key class is created. To specify a different key class, deselect the
Use the single key attribute type for the key class check box, and provide the name of the key
class.
13. Optional: Define the CMP attributes for the entity bean.
a. Click the Add button.
b. Specify the Name and Type for the attribute.
c. Optional: If the attribute is an array, select the Array check box. Specify the number of
Dimensions in the array.
d. Optional: Select Key field to make the attribute a key field for the entity bean.
e. Optional: Depending on the client views in your new bean, you can choose to promote the getter
and setter methods for the attribute to the remote and local interfaces.
f. Click Apply to create the attribute. Click Close when you are finished defining attributes.By default, the wizard specifies the following CMP attribute and declares it the key field:
v Name: id
v Type: java.lang.Integer
You can edit or remove this attribute.
Note: A key is required if the bean is not inherited. If a key is not defined, the bean will have
validation errors. After you create the bean, you can use the deployment descriptor editor to
add a key attribute or make an existing attribute the key.
14. Click Next.
15. Optional: In the Bean superclass field, type or select the desired class.
16. Optional: Define any interfaces that you want the remote or local client interfaces to extend:
v Click the Add button to open the Type Selection dialog box where you can select the interface that
you want to extend.
v If you added an interface that you no longer want to extend, select the interface in the list and
click Remove.17. Click Finish. The new entity bean is added to the specified EJB project.
Creating EJB CMP 1.1 beans: When a CMP 1.1 bean is created, its bean class contains code that is
specific to the WebSphere Application Server. This causes the following error in an Embedded Transaction
project: com.ibm.ivj cannot be resolved.
To fix this problem, perform the following editing changes to the CMP 1.1 bean’s Java source file:
v Delete all logic from method _removeLinks()
This will eliminate the project errors, and remove the WAS specific logic from the bean.
You can optionally remove the remaining generated associations logic from the bean as follows:
v Delete methods _initLinks(), _getLinks(), _removeLinks()
v Delete references to _initLinks()
v Delete contents of method ejbRemove()
Creating a stateless session bean: To create a stateless session bean, perform the following procedure:
1. Select File > New > Other > EJB > Enterprise Bean. The Create an Enterprise Bean wizard appears.
Note: Other EJB options, such as “XDoclet Enterprise JavaBean” should not be selected. The
“Enterprise Bean” option must be selected.
2. Select the Session Bean radio button.
116 Lotus Expeditor: Developing Applications for Lotus Expeditor
3. Select the EJB Component that you want to add the bean to.
4. In the Bean name field, type the name that you want to assign to the enterprise bean.
By convention, bean names should begin with an uppercase letter.
Note: You can use Unicode characters for the bean name, but Unicode characters are not supported
for enterprise bean packages and classes associated with enterprise beans.
5. In the Source folder field, select the source folder for the new bean.
6. In the Default package field, enter the package name for the new bean.
7. Optional: For EJB 2.x beans, you can select Generate an annotated bean class. If you select this
option, the wizard generates annotations at the beginning of the Java code for the bean class. The
annotations define the bean’s implementation as you specify in the wizard. The annotations are then
used by the EJB tools to generate the necessary bean classes, and to provide values that are inserted
in the EJB deployment descriptor (ejb-jar.xml). Select this option if you are familiar with
annotations and want to use the annotations to update the bean rather than using the deployment
descriptor.
8. Click Next.
9. Select Stateless as the session type for the new bean.
10. Select one of the following transaction types for the new bean:
v Container: Specifies that the transaction demarcation is performed by the container.
v Bean: Specifies that the transaction demarcation is performed by the bean.11. In the Bean class field, enter the desired package and class name for the bean class. By default, the
wizard suggests a bean class based on the bean name and default package that you defined. A bean
class can be a new class that the wizard generates, or it can be an existing class in the project class
path. Click the Class button to open a dialog that lists the classes in the project that correspond to
the bean type. The name of the bean class appears blue for existing classes with source. The name of
the bean class appears red for existing binary classes.
12. Define the client views and interfaces. For EJB 2.0 or later beans, you can include a remote client
view, a local client view, or both. For EJB 1.1 beans, only a remote client view is supported. Every
session or entity bean must have at least one client view:
v Remote client view: Select this check box to include a remote client view for the session bean. In
the Remote home interface and Remote interface fields, enter the package and class names that
you want to use for the remote client view interfaces. The wizard uses the bean name and default
package to suggest values for the interface package and class names.
v Local client view: Select this check box to include a local client view for the session bean. In the
Local home interface and Local interface fields, enter the package and class names that you want
to use for the local client view interfaces. The wizard uses the bean name and default package to
suggest values for the interface package and class names.13. Click Next.
14. Optional: In the Bean superclass field, type or select the desired class.
15. Optional: Define any interfaces that you want the remote or local client interfaces to extend:
v Click the Add button to open the Type Selection dialog box where you can select the interface that
you want to extend.
v If you added an interface that you no longer want to extend, select the interface in the list and
click Remove.16. Click Finish. The new session bean is added to the specified EJB project.
Creating a Bean Managed Persistence (BMP) bean: To create a Bean Managed Persistence bean,
perform the following procedure:
1. Click File > New > Other > EJB > Enterprise Bean. The Create an Enterprise Bean wizard appears.
Developing applications 117
Note: Other EJB options, such as “XDoclet Enterprise JavaBean” should not be selected. The
“Enterprise Bean” option must be selected.
2. Select the Entity bean with bean-managed persistence (BMP) fields radio button.
3. Select the EJB Component that you want to add the bean to.
4. In the Bean name field, type the name that you want to assign to the enterprise bean. By convention,
bean names should begin with an uppercase letter.
5. In the Source folder field, select the source folder for the new bean.
6. In the Default package field, enter the package name for the new bean.
7. Optional: For EJB 2.x beans, you can select Generate an annotated bean class. If you select this
option, the wizard generates annotations at the beginning of the Java code for the bean class. The
annotations define the bean’s implementation as you specify in the wizard. The annotations are then
used by the EJB tools to generate the necessary bean classes, and to provide values that are inserted
in the EJB deployment descriptor (ejb-jar.xml). Select this option if you are familiar with
annotations and want to use the annotations to update the bean rather than using the deployment
descriptor.
8. Click Next.
9. In the Bean class field, enter the desired package and class name for the bean class. By default, the
wizard suggests a bean class based on the bean name and default package that you defined. A bean
class can be a new class that the wizard generates, or it can be an existing class in the project class
path. Click the Class button to open a dialog that lists the classes in the project that correspond to
the bean type. The name of the bean class appears blue for existing classes with source. The name of
the bean class appears red for existing binary classes.
10. Define the client views and interfaces. For EJB 2.0 or later beans, you can include a remote client
view, a local client view, or both. For EJB 1.1 beans, only a remote client view is supported. Every
session or entity bean must have at least one client view:
v Remote client view: Select this check box to include a remote client view for the session bean. In
the Remote home interface and Remote interface fields, enter the package and class names that
you want to use for the remote client view interfaces. The wizard uses the bean name and default
package to suggest values for the interface package and class names.
v Local client view: Select this check box to include a local client view for the session bean. In the
Local home interface and Local interface fields, enter the package and class names that you want
to use for the local client view interfaces. The wizard uses the bean name and default package to
suggest values for the interface package and class names.11. In the Key class field, enter the desired package name and class for the entity bean’s key class. By
default, the wizard suggests a class name and package based on the bean name and default package
that you defined. A bean class can be a new class that the wizard generates, or it can be an existing
class in the project class path. Click the Class button to open a dialog that lists the classes in the
project that correspond to the bean type. The name of the bean class appears blue for existing classes
with source. The name of the bean class appears red for existing binary classes.
12. Click Next.
13. Optional: In the Bean superclass field, type or select the desired class.
14. Optional: Define any interfaces that you want the remote or local client interfaces to extend:
v Click the Add button to open the Type Selection dialog box where you can select the interface that
you want to extend.
v If you added an interface that you no longer want to extend, select the interface in the list and
click Remove.15. Click Finish. The new entity bean is added to the specified EJB project.
Customizing for target data base (DB2e and Derby)
Different code is generated based on what data base support is available on the platform to which the
EJB will be deployed.
118 Lotus Expeditor: Developing Applications for Lotus Expeditor
The project’s Embedded Transaction preferences can be used to specify the target data base to be used
when deploying. If the Use Default option is specified for the project, the workspace’s Embedded
Transaction preferences are used. Otherwise if Use Default is specified for the workspace, “DB2e” is the
defaulted value for the target data base.
Packaging and deploying Embedded Transaction applications
Embedded Transaction applications require a deployment step before they can be run. This is analogous
to the deployment step performed on an Enterprise Java Bean (EJB), and should not be confused with the
concept of deploying a bundle to the runtime (which involves methods of packaging and delivering the
bundle to the runtime). Embedded Transaction deployment involves specifying the proper deployment
information in deployment descriptors, and subsequently performing a deployment operation which
performs the necessary transformations on the project to enable it to be run by the Embedded Transaction
container.
Embedded Transaction applications require additional deployment information beyond that typically
provided when deploying an EJB. The standard EJB deployment descriptor, ejb-jar.xml , will still contain
proper deployment information. The Rational Software Development tools automatically manage this in
many cases, and provide an EJB Deployment Descriptor editor. For more information, refer to the EJB
Deployment Descriptor section of the Rational help. Refer to “Embedded Transaction Deployment
Descriptor” for the additional deployment information you must add for Embedded Transaction
applications, and “Embedded Transaction Deployment Editor” on page 120 for information on using the
editor to update this information.
Once the proper deployment information is specified, a deployment operation can be carried out to
enable the application to be run. “Invoking deployment”describes how and when the deployment
operation is carried out.
Invoking deployment
Deployment is automatically run whenever an Embedded Transaction project is selected to be run on a
Expeditor Server runtime that is launched through the tools. Refer to “Debugging and testing
applications” on page 323 for information on launching Lotus Expeditor runtimes through the tools.
Embedded Transaction Deployment Descriptor
The Embedded Transaction Container Tooling plug-in auto-generates the XML deployment file
(eejb_deploy.xml). This XML deployment file contains the custom deployment information required to
deploy the EJB. The information provided in the deployment file supplements the information already
included in the EJB deployment descriptor (ejb-jar.xml). To deploy the embedded EJB, you must supply
this information as an XML file that conforms to the schema file eejb-deployment.xsd, which is shipped
with the tooling. Information included in that file is as follows:
jndi-name
The name through which the deployed Home is accessed via the naming service (e.g., JNDI). It is
not assumed that the Home will be bound to a java:/comp/env/ejb Context, so the name
supplied in the deployment information is used exactly “as is”. Required for both session and
entity beans that use a (remote) home interface.
jndi-local-name
The name through which the deployed Home is accessed via the naming service (e.g., JNDI). It is
not assumed that the Local Home will be bound to a java:/comp/env/ejb Context, so the name
supplied in the deployment information is used exactly ″as is″. Required for both session and
entity beans that use a (local) home interface.
jdbc-bean
Supplies the information needed to deploy an entity bean to the Embedded Transaction Container
using a JDBC-based DataSource. In this element, the user specifies the name of the
Developing applications 119
abstract-finder-helper class that the user has supplied for the EJB, and also specifies the name
of the deployed finder-helper class that the tooling will generate. Required by container managed
persistence (CMP) entity beans only.
datasource-name
Specifies the name through which the TxnDataSource providing a connection to the data store
can be accessed from the naming service. Required by entity beans only.
table-name
Specifies the name of the relational database table that provides persistence for the
container-managed persistence (CMP) entity bean. This element is not required by bean-managed
persistence (BMP) entity beans.
deployed-class
Specifies the name of the deployed bean implementation class. Required for both session and
entity beans.
ejbivar
Specifies the name of the table column that provides persistence for the entity beans fields.
cmp-field
The name of the entity bean field to be persisted. The value of this field should match the value
specified in the EJB deployment descriptor, ejb-jar.xml.
Note: You must fill in the missing eejb_deploy.xml information for entity beans. No update to the
deployment file is necessary for stateless session beans.
Embedded Transaction Deployment Editor
Information in the “Embedded Transaction Deployment Descriptor” on page 119 can be managed through
the Embedded Transaction Deployment Editor. This editor can be opened as follows:
1. Locate the project’s Embedded Transaction Deployment descriptor file in the Explorer view. This is
file eejb_deploy.xml in the project’s source folder’s %SourceFolder%/META-INF folder.
2. Open the deployment descriptor by double clicking it, or right clicking and selecting Open. The
Embedded Transaction Deployment editor displays.
The editor will display all of the project’s defined beans in the Beans view. Select a bean from the Beans
view, and its associated Embedded Transaction deployment information will display on the right hand
side for editing.
Debugging and testing Embedded Transaction applications
This section includes Embedded Transaction Container specific debugging information.
For general debugging information, refer to “Debugging and testing applications” on page 323.
Enabling logging and tracing with the Embedded Transaction Container
Embedded Transaction Container logging enables a developer to associate logged messages with one of
six logging levels: fatal, error, warning, info, debug, and trace. These logging levels are ordered in
decreasing severity. Enabling a given log level implies that all log messages associated with that level, or
a higher log level, should be logged. Messages associated with a lower log level are ignored.
The value specified in the workspace’s Logging Level in the Embedded Transaction preferences is used
when deployment/testing is launched from the tooling. The logged messages are sent to the console.
The default logging configuration for the Embedded Transaction Container in the runtime is to log errors
using the OSGi LogService. To change the level of information being sent to that service, add the
following options to the VM arguments in the rcpinstall.properties file:
-Deejb.logging.priority.com=debug
-Deejb.logging.logwriters=com.ibm.pvc.txncontainer.internal.osgi.logger.OSGiLogWriter=debug
120 Lotus Expeditor: Developing Applications for Lotus Expeditor
This will turn on ″debug″ for all components of the Embedded Transaction Container.
Changing “debug” to “fatal”, “error”, “warning”, “info” or “trace” in the VM arguments will set the
logging level accordingly.
By default, the platform is configured to only persist error and warning messages. To change this level
for the Embedded Transaction Container in the runtime, add the following line to the
rcpinstall.properties file:
com.ibm.pvc.txncontainer.common.level=FINEST
Where FINEST would be the desired level.
For information on setting VM arguments when launching the runtime, refer to Configuring the platform
launcher.
For more information on platform logging, refer to Configuring platform logging and tracing
Run or debug Client Services Embedded Transaction Container projects using the
Client Services launcher
To run or debug a Client Services Embedded Transaction Container project from your workspace using
the Lotus Expeditor launcher, perform the following procedure:
1. Open the Run or Debug launcher by selecting Run > Run... (or Run > Debug...)
2. Select the Lotus Expeditor configuration type, and click New to create a new Lotus Expeditor launch
configuration.
3. Open to Plug-ins tab, and ensure the Embedded Transaction Container project to be tested is selected.
4. Select Run (or Debug).
5. Embedded Transaction Container deployment is performed on the Embedded Transaction Container
projects being run / debugged.
6. The Lotus Expeditor runtime is launched with the selected Client Services projects.
Run or debug a Client Services Embedded Transaction Container project on the
Lotus Expeditor runtime
To run or Debug Client Services Embedded Transaction Container projects from your workspace using a
“Run on Server” launch style on the Lotus Expeditor runtime, perform the following procedure:
1. Open the J2EE Perspective by selecting Window > Open Perspective > J2EE.
2. From the project Explorer view, select the project to be tested under Dynamic Web Projects folder
3. Right click the project to open the pop-up menu, and select Run As > Run on server...
4. Choose a Lotus Expeditor server if one exists, and click Next. If one does not exist, define a Lotus
Expeditor server:
a. Select IBM >Lotus Expeditor v6.1 as the server type. Click Next.
b. Choose a target definition and features, and click Next.5. Add or remove other projects that are configured on the server to test multiple projects.
6. Select the Finish button to launch.
7. Embedded Transaction Container deployment is performed on the Embedded Transaction Container
projects being run or debugged.
8. The Lotus Expeditor runtime is launched with the selected Client Services projects.
Run or debug a non-Client Services EJB project on the Lotus Expeditor runtime
To run or debug non-Client Services EJB projects from your workspace using a “Run on Server” launch
style on the Lotus Expeditor runtime, perform the following procedure:
1. You must update the project’s manifest file manually to add dependencies to Import-package or
Require-Bundle entries. Since the project is a non-Client Services project, it does not have the support
Developing applications 121
from Lotus Expeditor Toolkit to automatically manage the dependencies. Also, a default Embedded
Transaction deployment descriptor will be added to the project, if one does not exist.
2. Open the J2EE Perspective by selecting Window > Open Perspective > J2EE.
3. From the project Explorer view, select the (non-Client Services) EJB project to be tested.
4. Right click the project to open the pop-up menu, and select Run As > Run on server...
5. Choose a Lotus Expeditor server if one exists, and click Next. If one does not exist, define a Lotus
Expeditor server:
a. Select IBM > Lotus Expeditor v6.1 as the server type, and click Next.
b. Choose a target definition and features, and click Next. 6. Add or remove other projects that are configured on the server to test multiple projects.
7. Select the Finish button to launch.
8. EJB projects that are now targeted to the Lotus Expeditor runtime will be re-built. This will invoke
the Embedded Transaction Container validation logic, catching and tagging errors related to
unsupported function.
9. Embedded Transaction Container deployment is performed on the Embedded Transaction Container
projects being run or debugged.
10. The Lotus Expeditor runtime is launched with the selected Client Services projects.
Run or debug a Client Services Embedded Transaction Container project on a
non-Lotus Expeditor runtime
To run or debug Client Services Embedded Transaction Container projects from your workspace using a
“Run on Server” launch style on a non-Lotus Expeditor runtime, perform the following procedure:
1. Open the J2EE Perspective by selecting Window > Open Perspective > J2EE.
2. From the project Explorer view, select a project to be tested.
3. Right click the project to open the pop-up menu, and select Run As > Run on server...
4. Choose a non-Lotus Expeditor server if one exists, and click Next. If one does not exist, define a
non-Lotus Expeditor server:
a. Choose a server type, and click Next.
b. Specify the server settings depending on the server type selected. Potentially, there could be
multiple setup panels.5. Add or remove other projects that are configured on the server to test multiple projects.
6. Select the Finish button to launch.
Setting breakpoints on generated code
When setting breakpoints on generated code, you should open the corresponding class file from the
deployed-ejb.jar library. The editor will display the associated source, and you can set breakpoints.
Setting breakpoints on the Java files in the generated-source folder will not work, as this is not a project
source folder.
Developing management applications
This section provides information on management application development.
Developing applications to drive the Enterprise Management Agent
The Enterprise Management Agent enables a DM Server to manage a Lotus Expeditor client. It provides
the ability to perform software management (install, uninstall), update agent preferences, update
Configuration Admin information and retrieve hardware and software inventory. The Enterprise
Management Agent offers APIs for developers to drive the agent, access account information and update
agent preferences.
122 Lotus Expeditor: Developing Applications for Lotus Expeditor
Accessing the OSGiAgentService object
The OSGiAgentService class provides access to all the Enterprise agent related APIs.
com.ibm.pvc.osgiagent.core.OSGiAgentServiceFactory can be used to access the OSGiAgentService
Object.
The following sample code gets the OSGiAgentService object:
OSGiAgentService osgiAgentService = new com.ibm.pvc.osgiagent.core.
OSGiAgentServiceFactory().getAgentServiceObject();
The OSGiAgentService class provides APIs to:
v Add and modify SyncML Accounts
v Connect to the server
v Get the list of available software
v Modify polling properties
The following code creates a SampleAccount in the Agents managed SyncML tree:
OSGiAgentService OsgiAgentService = new com.ibm.pvc.osgiagent.core.
OSGiAgentServiceFactory().getAgentServiceObject();
Hashtable props = new Hashtable()
props.put(OSGiAgentConstants.keyAccountID, “SampleAccount”);
props.put(OSGiAgentConstants.keyUserName,”client1”);
props.put(OSGiAgentConstnats.keyClientPW,”Clientpw”);
props.put(OSGiAgentConstants.keyAddr,”http://sampleDMSServer/
dmserver/OMADMServletAuthRequired”);
OsgiAgentService.addAccount(“SampleAccount”,props);
The com.ibm.osg.service.osgiagent plug-in is required to access the OSGiAgentService.
Please refer to the Javadoc for com.ibm.pvc.osgiagent.core.OSGiAgentService and
com.ibm.pvc.osgiagent.core.OSGiAgentServiceFactory for more information.
Developing Enterprise Management Agent SyncML tree extensions
This section details developing Enterprise Management Agent SyncML tree extensions.
SyncML tree extensions overview
Applications can create, access and manage nodes within the SyncML tree which are managed by the
Enterprise Management Agent. An application can create SyncML Nodes so that it can be managed
remotely from a SyncML DM Server. Applications can request and access nodes by contributing
extensions to the tree within the application’s plug-ins. The Enterprise Management Agent gives the
ability to:
v Extend and manage a sub-node of the tree
v Register for a specific command for ./OSGi/Execute/exec SyncML leaf
Creating an Extension to the SyncML Tree
The com.ibm.osg.service.osgiagent plug-in provides the OSGiAgentTreeSyncmlNode extension point
which can be used to extend the SyncML tree and manage the new subtree. The extension must
implement the OSGiAgentSyncmlNodeExtension interface. The Enterprise Management Agent will read the
extension and create a node in the tree with the path provided in the nodeName attribute. It will then pass
the node back to the extension by instantiating the class provided by the attribute “class”. The plug-in
can create nodes and leaves within that node . For example, a plug-in that wants to extend the tree with
a new node called ./myplugin/newNode will define the following extension in its plugin.xml file:
<extension
point="com.ibm.osg.service.osgiagent.OSGiAgentTreeSyncmlNode">
<SyncmlSubtree
class="com.ibm.myplugin.MyNodesManager"
nodeName=" ./myApplication/permissions"
Developing applications 123
/>
</extension>
MyNodesManager:
public class MyNodesManager implements OSGiAgentSyncmlNodeExtension{
/* (non-Javadoc)
* @see com.ibm.pvc.osgiagent.syncml.extensions.OSGiAgentSyncmlNodeExtension#
manageSyncmlNode(com.ibm.syncml4j.dm.AbstractInterior)
*/
public void manageSyncmlNode(AbstractInterior subtree) {
AccessControlList acl = new AccessControlList();
acl.allowAll(AccessControlList.ADD);
acl.allowAll(AccessControlList.GET);
acl.allowAll(AccessControlList.REPLACE);
// This will create a new leaf ./myplugin/newNode/newLeaf
new Leaf((AbstractInterior)subtree,acl,null,"ModifyAppInfo" ,"Leaf which
indicates whether the client has authority to modify
the application
information",OSGiAgentConstants.textplain,
Meta.FORMAT_CHR,"yes",null);
}
}
From the above code, Enterprise agent creates the ./myplugin/newNode node, and MyNodesManager object
creates newLeaf leaf. If a different application registers the same node, then the class does not get
instantiated and the error CWPOA0039W is logged in the client logs.
Creating an Extension to register the command for Exec Leaf
The OSGiAgentTreeExecCmd extension point can be used for plug-ins that want to be called upon to
receive a particular command from the SyncML server. The main difference between
OSGiAgentTreeSyncmlNode and OSGiAgentTreeExecCmd is that in the OSGiAgentTreeSyncmlNode extension
point, the plug-in needs to know the SyncML classes and implement the set and get methods to get the
values of the sub-trees. On the other hand, the OSGiAgentTreeExecCmd extension point will get called
straight away if the server calls a particular command that the extension registers. The server calls the
command by setting the value of the command in the ./OSGi/Execute/exec leaf. The extension needs to
implement the OSGiAgentSyncmlCommand interface. For example, a plug-in that wants to be notified when a
dir command is sent from the server will define the following extension in its plugin.xml file:
<extension
point="com.ibm.osg.service.osgiagent.OSGiAgentTreeExecCmd">
<SyncmlCommand
CommandName="dir"
class="com.ibm.pvc.samples.osgiagent.syncmlcommand.exploiter.DirClass"/>
</extension>
DirClass:
public class DirClass implements OSGiAgentSyncmlCommand{
/* (non-Javadoc)
* @see com.ibm.pvc.osgiagent.syncml.extensions.OSGiAgentSyncmlCommand#
* commandCalled(java.lang.String[])
*/
public Hashtable commandCalled(String[] args) {
System.out.println("Dir got called from the server. The following
arguments were passed");
for (int i=0;i<args.length;i++)
{
System.out.println("Argument "+i+" = "+args[i]);
}
System.out.println("returning AOK as a standard out");
Hashtable h = new Hashtable();
124 Lotus Expeditor: Developing Applications for Lotus Expeditor
h.put(OSGiAgentSyncmlCommand.stdout, "file1 \n file2");
h.put(OSGiAgentSyncmlCommand.exitValue,"0");
return h;
}
The following plug-ins are required to access the Agent and Syncml Classes:
v com.ibm.osg.service.osgiagentc
v com.ibm.syncml4j
v com.ibm.syncml4j.dm
Please refer to com.ibm.pvc.osgiagent.syncml.extensions.OSGiAgentSyncmlCommand and
com.ibm.pvc.osgiagent.syncml.extensions.OSGiAgentSyncmlNodeExtension Javadoc and
com.ibm.osg.service.osgiagent.OSGiAgentTreeExecCmd and
com.ibm.osg.service.osgiagent.OSGiAgentTreeSyncmlNode extension point schema documentation for
more information.
For more information on SyncML, refer to “SyncML” on page 230.
Developing update manager applications
Lotus Expeditor for Devices provides the same application management API as Lotus Expeditor for
Desktop.
Developing messaging applications
Lotus Expeditor provides both enterprise class messaging through the Java Message Service (JMS), and
embedded messaging using WebSphere MQ Everyplace (MQe) and micro broker with the MQ Telemetry
Transport (MQTT) Java client APIs. MQe provides a point-to-point JMS provider, while the micro broker
with MQTT JMS provides a publish and subscribe JMS provider which enables Java developers to
leverage the JMS APIs to send and receive messages from the Lotus Expeditor runtime.
Understanding messaging applications
Messaging is intended to enable a wide variety of computers to exchange information. One of the main
benefits of messaging is to ’decouple’ a sending application from a receiving application. This decoupling
provides a very powerful abstraction, enabling the exchange of information to be independent of
manufacturer, application, operating system or connectivity reliability.
Traditionally, messaging is between large computers and a server. However, with the advent of Java
messaging implementations, now a new class of device interoperates with the messaging infrastructure.
This provides opportunities for an enterprise to broaden the reach of its networks.
Along with the transmission of simple messages, messaging is useful for transactional updates, or where
intermediate data updates or data ordering is required. Messages containing the complete update can be
sent to a server, where transaction managers can coordinate the update of multiple resources. Messaging
can also be paired with synchronization technology, such that transactions are sent by messages, and the
resulting database updates distributed back to the client through synchronization.
Messaging also can be used effectively in a disconnected environment (areas where connectivity is not
reliable), since a local queue manager is available to contain messages until the connection to the server
infrastructure is reestablished. After the connection is available, the queued messages are transferred to
the messaging server for further action.
Messaging, irrespective of the particular product or product group, is separated into two main categories:
v Point-to-point messaging
v Publish and Subscribe messaging
Developing applications 125
To take full advantage of the messaging capabilities of Lotus Expeditor, it is crucial to understand the
differences in these two messaging types.
Publish and subscribe messaging
Publish and subscribe is a style of messaging in which applications (subscribers) register interest in a
particular subject of interest (known as a topic) with an intermediary (a broker) that matches those
applications to messages sent by other applications (publishers) to the same subject of interest.
A single message sent by a publisher may be matched and sent to many subscribers. In publish and
subscribe messaging, one application can be both a publisher and subscriber to the broker. This
messaging style is of particular benefit when multiple components in a system need to receive
notification of a given event. For example, a temperature sensor may have many different systems
monitoring it for different reasons. The broker decouples the publisher from the subscribers and gives
flexibility for the adding or removal of publishers or subscribers without needing to perform application
specific integration between each component.
The following figure shows a simple publish and subscribe application that includes one publisher, one
broker, and three subscribers. A publication is sent from the publisher to the broker; a subscription is sent
from the subscriber to the broker; and the publication is then sent from the broker to the subscriber.
126 Lotus Expeditor: Developing Applications for Lotus Expeditor
A typical publish and subscribe application has more than one publisher, more than one subscriber, and
often more than one broker. An application can be both a publisher and a subscriber.
The publisher generates a message that it wants to publish and defines the topic of the message. This
message is sent to the broker that matches the message to the subscribers that have registered an interest
in the topic.
A subscriber registers a request for a publication by specifying the topic or topics that it is interested in.
Each message has a header and a body. The body contains the message content. The header, which is
similar to the subject field of an e-mail, describes the content of the message.
Related concepts
“Topics and hierarchical topic names” on page 128
“Publication and subscription messages” on page 130
Figure 4. A simple publish and subscribe application
Developing applications 127
Topics and hierarchical topic names: A topic is a character string that describes the data that is
published in a publish and subscribe system.
Topics are created in the broker when a client sends a request to subscribe or publish a message to a
given topic. For example, when a client subscribes to topic, that topic’s hierarchy is in place within the
broker because a client is subscribed to it and will receive any matching publications. It is not necessary
for a client to create topics as a specific step independently of these requests.
Topics are key to the successful delivery of messages in a publish and subscribe system. Instead of
including a specific destination address in each message, a publisher assigns a topic to the message. The
message broker matches the topic with a list of clients (subscribers) who have subscribed to that topic,
and delivers the message to each of those clients.
Note that a publisher can control which subscribers can receive a publication by choosing carefully the
topic that is specified in the message.
Only single byte (non-extended) characters are supported. However, there are three characters that have
special meanings. These characters (″/″, ″#″, and ″+″) are described in “Special topic characters” on page
129.
What is a topic tree
Although you can use any name for a topic, it is best to choose a name that fits into a hierarchical tree
structure. Thoughtfully designing topic names and topic trees facilitates these tasks:
v Subscribing to multiple topics
v Reacting automatically to messages about a specific topic, for example, by sending an alert to a
manager’s pager
Although you can construct a topic tree as a flat, linear structure, it is better to build a topic tree in a
hierarchical structure with one or more root topics. The following figure shows an example of a topic tree
with one root topic:
Each character string in the figure represents a node in the topic tree. A complete topic name is created
by concatenating the character strings from multiple levels of the tree. Use a forward slash (/) to separate
each part of a hierarchical name. For example, these are the topics from the tree shown in Figure 5:
Figure 5. Hierarchical topic tree
128 Lotus Expeditor: Developing Applications for Lotus Expeditor
finance
finance/stock
finance/stock/ibm
finance/stock/xyz
finance/stock/ibm/closingprice
finance/stock/ibm/currentprice
finance/stock/xyz/closingprice
finance/stock/xyz/currentprice
When you design a topic tree, remember that the broker cannot interpret or derive meaning from the
topic name. The broker uses the topic name to identify which messages to send (those that match the
subscription).
Special topic characters
A topic string can only include single byte (non-extended) characters. The topic level separator is used to
introduce structure into the topic, and can therefore be specified within the topic for that purpose. The
multi-level wildcard and single-level wildcard can be used for subscriptions, but they cannot be used
within a topic by the publisher of a message.
Topic level separator
The forward slash (/) is used to separate each level within a topic tree and provide a hierarchical
structure to the topic space. The use of the topic level separator is significant when the two
wildcard characters are encountered in topics specified by subscribers.
Multi-level wildcard
The number sign (#) is a wildcard character that matches any number of levels within a topic. For
example, if you subscribe to finance/stock/ibm/#, you receive messages on these topics:
finance/stock/ibm
finance/stock/ibm/closingprice
finance/stock/ibm/currentprice
The multi-level wildcard can represent zero or more levels. Therefore, finance/# can also match the
singular finance, where # represents zero levels. The topic level separator is meaningless in this
context, because there are no levels to separate.
The multi-level wildcard can be specified only on its own or next to the topic level separator
character. Therefore, # and finance/# are both valid, but finance# is not valid. The multi-level
wildcard must be the last character used within the topic tree. For example, finance/# is valid but
finance/#/closingprice is not valid.
Single-level wildcard
The plus sign (+) is a wildcard character that matches only one topic level. For example,
finance/stock/+ matches finance/stock/ibm and finance/stock/xyz, but not finance/stock/ibm/closingprice.
Also, because the single-level wildcard matches only a single level, finance/+ does not match
finance.
Use the single-level wildcard at any level in the topic tree, and in conjunction with the multilevel
wildcard. Specify the single-level wildcard next to the topic level separator, except when it is
specified on its own. Therefore, + and finance/+ are both valid, but finance+ is not valid. The
single-level wildcard can be used at the end of the topic tree or within the topic tree. For
example, finance/+ and finance/+/ibm are both valid.
Topic semantics and usage
When you build an application, the design of the topic tree should take into account the following
principles of topic name syntax and semantics:
v A topic must be at least one character long.
Developing applications 129
v Topic names are case sensitive. For example, ACCOUNTS and Accounts are two different topics.
v Topic names can include the space character. For example, Accounts payable is a valid topic.
v A leading ″/″ creates a distinct topic. For example, /finance is different from finance. /finance matches
″+/+″ and ″/+″, but not “+”.
v Do not include the null character (Unicode \x0000) in any topic.
v Only single-byte (non-extended) characters are supported in topic names.
The following principles apply to the construction and content of a topic tree:
v The length is limited to 64k but within that there are no limits to the number of levels in a topic tree.
v There can be any number of root nodes; that is, there can be any number of topic trees.
Publication and subscription messages: This section describes terms that you should be familiar with
for developing publish and subscribe applications. For more information about the role of the broker in
publish and subscribe messaging, see “Understanding the micro broker components” on page 135.
What is a publisher?
A publisher is a client application that creates a publication message, associates it with a topic, and sends
the message into the broker. The broker is responsible for distributing the message to all applications
subscribed to the topic.
What is a publication?
A publication is a message that is associated with a particular topic. A client application publishes a
publication to the broker. The broker then matches the publication against all applications that have
subscribed to the topic and distributes the publication to those applications.
The broker also distributes the publication to all connected and networked brokers that have subscribers
for the publication. The network can include any applications that can connect to the broker, such as
WebSphere MQ and WebSphere Message Broker.
Retained publications
In general, after sending a publication to subscribers, the broker deletes it. However, the publisher can
request that the broker keep a copy of the publication, which is then called a retained publication. Then the
broker remembers the last message that was published on the retained topic.
The advantage of retained publications is that applications subscribing to the topic will immediately
receive the retained publication and do not have to wait until a publishing application publishes the next
message. This allows the application to initialize itself immediately.
What is a subscriber?
A subscriber is a client application that requests to receive messages published on a specific set of topics.
The application sends a subscription message to the broker specifying the topics in which it is interested.
The broker then sends publication messages to the subscribing application whenever a publication
matching the set of client topics is processed. When the application no longer wants to receive
publications, it can unsubscribe from topics to which it previously subscribed.
A publish and subscribe messaging topology is a flexible topology that allows applications to be added
and removed without impacting other applications. For example, you can add new applications to
subscribe to particular topics. When you add a new subscribing application, you do not need to modify
the publishing application to know that an additional subscriber has been added. Similarly, you can add
new applications to publish on particular topics, without the need for subscribing applications to be
modified to be made aware of this. The topic name and the format of the data in the message have to be
130 Lotus Expeditor: Developing Applications for Lotus Expeditor
well defined, as these are the common pieces of information shared between a publishing and
subscribing application. Once you have done so, you can add or remove applications to publish or
subscribe as required.
Table 4. Example topologies
Topology type Description
One publisher to many subscribers One publishing application is generating publications
that are used by many subscribing applications. An
example is an application that publishes stock prices.
Many applications might subscribe for this information,
including brokers, Web sites, portfolios and many other
applications.
Many publishers to one subscriber The subscribing application is aggregating information
from different sources together to make a decision. An
example is a weather forecasting application which needs
to receive information from air pressure sensors, wind
speed sensors, humidity sensors, and temperature
sensors to make an accurate forecast.
Many publishers to many subscribers An example of this topology is an extension of the
weather forecasting example above. Here, multiple
forecasting simulations might be running in parallel.
Each simulation is a subscribing application aggregating
data from all the publishing applications.
When subscribing, either an absolute topic name or a topic name with wildcard characters can be used.
See “Topics and hierarchical topic names” on page 128 for more information about topic names.
When a publisher sends a message that has multiple subscribers, the broker sends a copy of the message
to each subscriber. Once a system has been deployed, new applications can be added at a later date
without having to change the applications already deployed. The new applications simply subscribe to
the existing topics set.
What is a subscription?
A subscription is created by the subscriber to define a set of publications that the subscriber should
receive from the broker. A subscription in its simplest form consists of one or more topics.
Some clients also allow additional filtering to be performed based on properties associated with the
publication. Publication property filtering is achieved using SQL syntax, so a subscriber would only
receive a matching publication if the subscription topic matched the publication topic and the SQL query
of the publication properties returned a true result. The SQL filter associated with a subscription is
referred to as the selector.
An application can create multiple subscriptions if required.
An example use of properties in subscriptions would be an application that is only interested when a
stock price rises above a certain value. Suppose the current stock price is published on topic
finance/stock/ibm/currentprice and the publication also contains a property called CURRENT_PRICE.
The subscribing application would subscribe to topic finance/stock/ibm/currentprice and specify a
selector of ’CURRENT_PRICE > 90’. This would ensure that while the stock price was below or $90, the
subscriber would not receive any publications.
Select from two types of subscriptions available to applications:
Non-durable subscriptions
A non-durable subscription is one whose lifetime is limited to the duration of the connection
Developing applications 131
between the subscribing application and the broker. A non-durable subscription is removed from
the broker when the application disconnects or when the application explicitly unsubscribes.
Durable subscriptions
A durable subscription is one whose lifetime is beyond the duration of the connection between
the subscribing application and the broker. A durable subscription is only removed from the
broker when the application explicitly unsubscribes. While the application is not connected,
matching publication messages are stored in the broker until the application reconnects.
Point-to-point – queues and queue managers
Queue managers handle queues that store messages. Applications communicate with a local queue
manager, and get or put messages to queues. If a message is put to a remote queue (a queue owned by
another queue manager), the message is transmitted over connections to the remote queue manager. In
this way, messages can hop through one or more intermediate queue managers before reaching their final
destination. You can configure queue managers with or without local queuing. All queue managers
support synchronous messaging operations. A queue manager with local queuing also supports
asynchronous message delivery.
The point-to-point messaging paradigm provides one-to-one messaging. In other words, messages are
consumed by only one receiver, unlike publish-and-subscribe where messages are consumed by multiple
receivers.
The following graphic shows the topology for point-to-point messaging:
Java Message Service
Java Message Service (JMS) is the standard Java API for messaging. It supports the two messaging
categories: point-to-point messaging and publish/subscribe messaging. JMS is defined as part of the Java
2 Enterprise Edition 1.3 and 1.4 definitions. It defines a package of Java interfaces, which allows for
provider-independence, but does not necessarily allow for provider interoperability.
The JMS APIs are provided with the Lotus Expeditor runtime. This runtime also includes a point to point
JMS provider based on MQe messaging and a publish and subscriber provider based on MQTT
messaging. The MQe classes for JMS are a set of Java classes that implement the JMS interfaces to enable
JMS programs to access MQe systems, while the MQTT client classes for JMS enable JMS applications to
access Lotus Expeditor micro brokers. Both MQe and micro broker support the bridging of messages to
enterprise messaging systems as well.
There are several benefits to using JMS as the API to write MQe messaging applications. Some
advantages are derived from JMS being an open standard with multiple implementations. Using an open
standard provides the following benefits:
v The protection of investment, both in skills and application code
v The availability of people skilled in JMS application programming
MessageMessage Sender Receiver
Message Server
MessageQueue
Figure 6. Point-to-point messaging
132 Lotus Expeditor: Developing Applications for Lotus Expeditor
v The ability to plug in different JMS implementations to fit different requirements
IBM has several implementations of JMS. Interoperability is provided between them.
More information about the benefits of the JMS API is available at: http://java.sun.com
The JMS application is written to use only references to the interfaces in the javax.jms package. All
vendor-specific information is encapsulated in implementations of the following JMS administered
objects:
v QueueConnectionFactory
v TopicConnectionFactory
v Queue
v Topic
Note: The Lotus Expeditor runtime provides both an MQe point-to-point JMS provider, which supports
the QueueConnectionFactory and Queue objects and an MQTT client JMS provider supporting
TopicConnectionFactory,ConnectionFactory and Topic objects.
These JMS administered objects are stored in a Naming Directory Interface (JNDI) namespace. A JMS
application can retrieve these objects from the namespace and use them without needing to know which
vendor provided the implementation.
WebSphere MQ Everyplace
WebSphere MQ Everyplace (MQe) is a member of the IBM WebSphere MQ family of business messaging
products. It exchanges messages with various applications, providing once and once-only assured
delivery leveraging the point to point message paradigm.
MQe provides an integrated set of security features enabling the protection of message data both when
held locally and when being transferred.
With synchronous message delivery, the application puts the message to MQe for delivery to the remote
queue. MQe simultaneously contacts the target queue and delivers the message. After delivery, MQe
returns immediately to the application. If the message cannot be delivered, the sending application
receives immediate notification. MQe does not assume responsibility for message delivery in the
synchronous case (non-assured message delivery).
With asynchronous message delivery, the application puts the message to MQe for delivery to a remote
queue. MQe immediately returns to the application. If the message can be delivered immediately, or
moved to a suitable staging post, it is sent. If not, it is stored locally. Asynchronous delivery provides
once and once-only assured delivery. After the message is provided to MQe, control is returned to the
application. MQe next takes responsibility for assured delivery of the message. Delivery occurs in the
background allowing the application to carry on its processing.
MQe also has the ability to exchange messages with WebSphere MQ host queue managers and brokers.
To do this, configure a MQe queue manager with bridge capabilities. Without the bridge, a queue
manager can communicate directly only with other MQe queue managers. However, it can communicate
indirectly through other queue managers in the network that have bridge capabilities.
As mentioned previously, a point-to-point JMS provider is included with MQe. Initially MQe must be
bootstrapped using the supplied connection factory. From then on, standard JMS APIs can be used.
Lotus Expeditor micro broker
This section introduces the Lotus Expeditor micro broker and describes the Lotus Expeditor micro broker
environment.
Developing applications 133
The Lotus Expeditor micro broker component is a small message broker that provides a messaging fabric
for integrating various parts of a solution such as applications, sensors, and actuators. A message broker
ensures that messages arrive at the correct destination and are transformed to the format required by
each destination. The micro broker is a Java implementation that runs on a wide range of standard
devices including PDAs, laptops, and desktops. In addition, micro broker runs on more specific devices,
such as programmable logic controllers (PLCs), smart home information hubs (for example, TV set-top
boxes), and automobile-mounted devices.
The micro broker is suitable for embedding in applications and solutions that have a need for messaging,
notification, and event services. The micro broker uses a publish and subscribe, one-to-many message
distribution pattern. It provides a messaging infrastructure that lightweight messaging clients use to
communicate with each other, within a single device, across a network, and when integrating with
enterprise brokers. This enables an end-to-end integration fabric, reaching from sensor and actuator
devices, to client applications and up to back-end applications. The micro broker provides an integration
capability near the edge of the network, as well as providing a gateway to the enterprise services bus
(ESB).
Before going into details of how the micro broker works, it is important to know some of the principles
and technologies that are used in the micro broker.
Related concepts
“Publish and subscribe messaging” on page 126Publish and subscribe is a style of messaging in which applications (subscribers) register interest in a
particular subject of interest (known as a topic) with an intermediary (a broker) that matches those
applications to messages sent by other applications (publishers) to the same subject of interest.
“Scenarios and applications”This section describes some use cases that use the micro broker.
“Understanding the micro broker components” on page 135This section describes the components of the micro broker.
Scenarios and applications:
This section describes some use cases that use the micro broker.
FloodNet: FloodNet is a UK project sponsored by the Department of Trade and Industry. Its mission is to
develop technologies to monitor flood levels through the use of pervasive computing technologies.
Technologies are initially being tested on a tidal estuary in the UK which is subject to daily tidal flooding.
The FloodNet system architecture uses a network based on the IEEE specification 802.11 for wireless LAN
technologies. Each node consists of an embedded computer mounted on a post in the estuary. The nodes
measure the water level at regular intervals and transmit the data over the network to a micro broker
running on a customized node called the gateway node. The data is consolidated and subsequently
transmitted over GPRS to a central WebSphere Message Broker where the sensor data is transformed and
delivered to any application that subscribes to the data.
The sensor data is used for various applications such as simulation models, GIS Visualization and
archiving. Using the publish and subscribe model, additional applications can be developed that make
use of the same data. Also, additional sensors could be added to the nodes, to measure say air pressure,
and the data flowed back through the brokers without disrupting existing applications. The intent is to
create a suite of applications that can accurately monitor, model, analyze and predict the state of rivers so
that agencies and the media can be notified on a timely basis when flooding is likely to occur.
RFID: Radio Frequency Identification (RFID) is a technology that allows items to be labelled with RFID
tags and the tags to be automatically read when passing within the vicinity of an RFID antenna and
reader. Tags can be read through a variety of substances such as paint, grime, and other visually and
134 Lotus Expeditor: Developing Applications for Lotus Expeditor
environmentally challenging conditions, where barcodes or other optically read technologies would be
useless. RFID tags can also be read in challenging circumstances at remarkable speeds, in most cases
responding in less than 100 milliseconds.
The micro broker has been used in a retail solution for tracking the location of pallets in shops,
warehouses, and distribution centers. The aim being to ensure the location of pallets is known at all times
and to ensure that pallets are delivered to the correct store in a timely manner.
The architecture is comprised of four tiers:
Devices
The edge of the network such as RFID readers, motion sensors, and light stacks.
Edge server
A small footprint computer that handles connectivity and manages the devices. Monitoring,
control and notifications together with business logic for implementing RFID use cases is handled
by the edge server.
Premises server
Manages a retail location, warehouse, or distribution center. Multiple edge servers can be
connected to a premises server.
Enterprise server
Manages global information from all premises servers.
A basic use case validates that a pallet that is being delivered is expected at the location. The pallet is
removed from the delivery vehicle and moved into the warehouse through the dock bay doors. A motion
sensor located at the dock bay door detects the pallet is moving into the warehouse. This results in the
edge server sending a control command to activate the RFID reader. Once the pallet has moved into to
the RFID field, the reader reads the tags and sends them to the edge server. A tag may be read multiple
times, producing multiple messages to the edge server. The edge server filters the tags, for instance,
removing duplicates. The filtered tags are then sent to the premises server for validation. The premises
server checks with a repository to confirm that the tag is expected in this retail location. (The premises
server may send a message to the enterprise server to do this validation). The result is sent back down to
the edge server. If the pallet (tag) is expected, then a green light is switched on and the pallet is accepted
into the warehouse. If not, a red light is switched on, and the pallet is returned to the delivery vehicle.
On the edge server, every device has an agent that contains an MQTT client. The agent receives output
from the device, creates well-defined messages that are sent to the micro broker, and accepts command
and control messages that are mapped to the device inputs.
Certain messages are routed to the premises server for processing. A good example is a tag is sent to the
premises server to validate if it is expected at this retail location. The micro broker accepts messages
destined for the premise servers and ensures the message is delivered to a micro broker on the premises
server. Typically, a bridging component in the micro broker is then used to route messages into enterprise
level servers for processing.
Understanding the micro broker components:
This section describes the components of the micro broker.
The micro broker logically consists of three parts: the broker, the bridge, and the clients.
Broker
The broker is the component to which client applications connect to perform publish and subscribe
messaging. It handles the matching of publications with subscriptions, the distribution of publications to
subscribing applications, and the persistence of messages to ensure message delivery at the quality of
Developing applications 135
service required. The broker acts as a hub for routing messages between clients, and with the aid of the
bridge, other messaging servers. The broker can store messages on behalf of a client that is not connected
and make them available to the client when it reconnects. In addition, the broker can store messages on
behalf of the bridge and make them available when the messaging servers that the bridge connects to are
available.
The broker supports different types of persistence for storing messages and subscriptions. The type of
persistence is selected when a broker is created and is dependent on how the broker is to be used. Types
of persistence supported include:
Memory only
Where data (messages and subscriptions) only ever reside in memory. In the event of a failure or
the broker being shutdown, the data will be lost.
Shutdown
Data resides in memory until the broker is shutdown, at which point the data is persisted. In the
event of a failure, the data will be lost.
Full persistence
Data is always written to a persistent store using transactions and will survive both a failure and
shutdown.
Bridge
The micro broker bridge is an extension of the micro broker that routes messages between the micro
broker and other messaging servers to form more sophisticated messaging topologies. The bridge allows
messages to be routed between the micro broker and the following messaging servers:
v Other micro brokers
v WebSphere MQ
v WebSphere Message Broker
v WebSphere Application Server
v Any JMS provider
The bridge can route messages between one or more messaging servers. If the bridge cannot connect to a
messaging server, messages destined for the messaging server can be stored by the broker. When the
messaging server becomes available, the bridge will connect to it and transfer the stored messages. In
addition, the bridge can transfer pending messages from the messaging server to the broker.
Typically, each type of messaging server supports its own messaging protocol and its own message
formats. The bridge plays the role of intermediary, routing messages across different protocols and
transforming messages to a format acceptable by each messaging server.
Client
Application programs interact with the broker using a client library. The client library enables the
application program to be executed in a number of ways:
v Within the same Java runtime process as the micro broker itself.
v On the same machine as the micro broker but within a different runtime process.
v On a different machine to the micro broker.
The broker is able to handle multiple clients concurrently exchanging messages.
Clients do not persist messages; they simply provide a means for an application to interact with other
clients (applications and services) using a broker. Before a client can interact with a broker, it must first
connect to the broker. Once connected, messages can be sent and received. A client does not need to be
continuously connected to a broker to receive messages. The broker can store messages on behalf of a
136 Lotus Expeditor: Developing Applications for Lotus Expeditor
client, allowing the client to receive stored messages when it reconnects. This provides an additional level
of decoupling between sending and receiving clients. Put another way, a client can send messages to
other clients that may not be running when the message is sent.
Two clients are provided for use with the micro broker:
v A JMS client that supports the publish and subscribe domain of the JMS specification. The JMS client is
the recommended client library to use when developing applications for the micro broker.
v A Java MQTT client. This client is used for more specialized situations such as devices with
constrained environments or where there is a specific requirement to use the MQTT protocol.
Java MQTT client: The API closely maps the MQTT protocol.
The Java client provides an event-driven model such that an application is informed when a message is
delivered to it.
As both WebSphere Message Broker 6 and WebSphere Event Broker 6 support the MQTT protocol, the
clients can also directly connect to them.
Micro broker topologies:
Combinations of the micro broker, clients, and bridges can be linked together in a number of ways. This
section describes some of the common patterns that are used when linking components together.
Stand-alone: The simplest combination of components is to have a single micro broker with one or more
clients connected to it.
MicroBroker
Client
Client
Client
Client
Client
Client
Developing applications 137
The micro broker acts as a message hub for clients connected to it. These clients can send messages to
each other. Clients can run in the same process (VM) as the micro broker, in a different process on the
same computer, or even a different computer providing a powerful messaging infrastructure.
It allows multiple applications running in their own processes to communicate with each other using
publish and subscribe messaging. The applications do not need to know about each other and more
importantly do not need to all be up and running at the same time.
It can act as a hub for multiple off board clients. For instance, a broker can act as a home message hub.
There are a wide range of devices that could be connected to the hub. On the whole, most devices have
their own interface. Adapters are used to map between the device interface and MQTT. In a home, you
can imagine creating many different types of adapter; a good example is an X10 adapter that can be used
for controlling X10-enabled devices such as lights, power sockets, and the heating system. Adapters can
be created for home weather stations, alarm systems, and central heating control. The broker then
provides a hub for monitoring and controlling of all the devices.
It can be used as a message hub in one process (VM). It may sound like a strange thing to use a message
broker to allow Java applications in one process to communicate with each other. But there are a number
of advantages that it brings:
v The applications are decoupled from each other.
v The programming model remains the same whether the application runs in the same process space as
the broker or whether it is off board.
v It is a simple matter to move an application out of the process the broker is running in and either run
it in a different process or different box. This requires either no change to the application or a simple
change to specify the network address that the broker is using.
v If an application is not running when a message is sent, the broker queues messages and delivers them
when the application starts.
Typically, a combination of the above connectivity patterns will be used. For instance, in the case of the
home automation example, clients that directly interact with devices will be off-board, yet the monitoring
and controlling application will be on-board with the micro broker.
Three tier: There are a limited number of examples particularly when talking about enterprise businesses
that run small stand-alone message hubs.
Typically an enterprise will have a main message hub that is fed by messaging clients, by smaller
message hubs in the enterprise, or by other businesses.
Prior to the advent of the micro broker, the device could directly connect to the enterprise brokers using
MQTT clients. The micro broker allows small message hubs to be created in locations where this was
previously not possible or not cost effective. For instance, a hub can be created in a gas station, an RFID
edge controller, an electricity sub station or pumping station. Clients connected to it then have a message
infrastructure that carries on running if there are problems with connectivity to the enterprise. It also
brings with it the benefit that messages can now flow more directly between devices and applications
without having to go all the way back to the enterprise.
The micro broker can communicate with the following enterprise servers:
WebSphere Event Broker and WebSphere Message Broker
The bridge maps the micro broker topic space to the enterprise broker topic space.
WebSphere MQ
The bridge maps the micro broker publish and subscribe topic space to the WebSphere MQ
point-to-point queue space or publish and subscribe topic space.
138 Lotus Expeditor: Developing Applications for Lotus Expeditor
JNDI-enabled JMS providers
The bridge maps the micro broker publish and subscribe topic space to the JMS provider’s
point-to-point queue space or publish and subscribe topic space.
These systems can also route messages on to other systems.
Peer to peer: Multiple micro broker components can also connect and send messages between each other.
For example, a retail store application uses multiple micro broker components. Each micro broker handles
messages from a set of RFID readers. A tag is read by the reader that it passes through as well as
additional readers; hence, the tag is passed to multiple micro broker components. By linking micro broker
components together, RFID tag messages are correlated across brokers.
Any combination: It is possible to link these patterns together to produce ever more sophisticated
networks of messaging components. For example, Figure 7 shows a set of micro broker components that
are linked together as peers, some of which are linked to the enterprise.
It is good practice to keep the messaging network as simple as possible. The more complex the system,
the harder it is to diagnose problems and to extend it in the future.
MQTT
The MQTT protocol is a messaging protocol designed to enable messaging from tiny devices such as
sensors and actuators.
MicroBroker
Client
Client
BridgeBridgeMicroBroker
Client
Bridge
MicroBroker
ClientClie
nt
Client
Client
Client
Bridge
EnterpriseEnterprise
Figure 7. A set of micro broker components linked together as peers
Developing applications 139
An example of a sensor is a thermometer and an actuator is a valve. Refer to WebSphere MQ Telemetry
Transport and http://www.mqtt.org for more information about the MQTT protocol specification.
The micro broker implements the server half of the MQTT protocol.
Quality of Service: MQTT delivers messages according to service levels defined in a Quality of Service
(QoS) parameter.
The levels are described below:
QoS 0 At most once delivery.
Messages are delivered according to the best efforts of the underlying TCP/IP network. No
response is expected. No retry semantics are defined in the protocol. Consequently, the message
will arrive at the destination broker either not at all or once. QoS 0 is also known as fire and
forget.
QoS 1 At least once delivery.
The arrival of a QoS 1 message at the broker, including its successful placement in a persistent
store is acknowledged. In the event of identifiable failure of the communications link, or of the
sending device, or after some period of time of non-receipt of the acknowledgement message, the
sender will resend a duplicate message. Consequently, the message is certain to arrive, but could
arrive more than once.
QoS 2 Exactly once delivery.
For QoS 2, additional protocol flows are employed above QoS 1 to ensure that duplicate
messages are not delivered to the receiving application. This is the highest level of service, and is
used when duplicate messages are undesirable. Of course, there is a price to be paid in terms of
network traffic, but often this is acceptable because of the importance of the message content.
The QoS can vary by message, allowing non-important messages to be delivered using QoS 0 and
important messages to be delivered using a QoS of 2.
Publishing and subscribing clients both specify a QoS to use. When they differ, the micro broker
negotiates the QoS by using the lower of the two specified values.
Assumptions for QoS levels 1 and 2
In any network, it is possible for devices or communication links to fail. If this happens, one end of the
link might not know what is happening at the other end. These are known as in doubt windows. In these
scenarios, assumptions have to be made about the reliability of the devices and networks involved in
message delivery.
MQTT assumes that the client and broker are generally reliable, and that the communications channel is
more likely to be unreliable. If the client device fails, it is typically a catastrophic failure, rather than a
transient one. The possibility of recovering data from the device is low.
Some devices have non-volatile storage, for example, flash ROM. The provision of more persistent storage
on the client device protects the most critical data from some modes of failure. Beyond the basic failure of
the communications link, the failure mode matrix becomes complex, resulting in more scenarios than the
specification for MQTT can handle.
The time delay (retry interval) before resending a message that has not been acknowledged is specific to
the application, and is not defined by the protocol specification.
Clean session: Clean session is a property that when set to true notifies the broker that it should remove
any state held in relation to the client on connect and remove any state after the client disconnects. State
140 Lotus Expeditor: Developing Applications for Lotus Expeditor
can be maintained by setting the clean session to false. If the client is disconnected, it can resume its
work with the server from the point of disconnection. State held in the broker includes subscriptions
registered by the client and any publications destined for the client. This state also includes information
about any in-flight message delivery at the point of disconnection. This means that if a QoS 1 or QoS 2
message flow is in progress and the client is disconnected, if clean session is set to false, the client and
broker will attempt to complete this flow when reconnected. Note that this does not mean that the client
can continue to send messages while disconnected from a broker, but that any in-flight message flows at
disconnect time may be resumed. Therefore, a consequence of setting clean session to true is that the
delivery of QoS 1 and QoS 2 messages can not be assured. This is because if your client disconnects half
way through sending a message, when the client reconnects the broker removes all the state related to the
client, including the message that should have been sent to it.
WebSphere MQ Everyplace and micro broker comparison
While MQe and micro broker products are similar in that they provide embedded messaging capabilities;
their specific set of features might make a better choice for certain client applications.
Table 5. MQ Everyplace and micro broker comparison
WebSphere MQ Everyplace Micro broker and MQTT
JMS provider included Yes. point-to-point provider. Yes; Publish and Subscribe
Messaging paradigm point-to-point (queue-based) Publish and Subscribe (topic-based)
Implementation type Java-based (platform-independent)
implementation
Java-based (platform-independent)
implementation
Bridging Supports bridge to MQe and
WebSphere Business Integration
Message and Event Brokers
Support bridge to WebSphere MQ,
and WebSphere Business Integration
(WBI) Message and Event Brokers
Wire protocol MQe- specific MQTT standards based
Separate small footprint client No Yes; Java and ‘c’
QOS for message delivery At least once and exactly once. Fire and forget, at least once, and
exactly once
Local and remote queues Yes No
Build in security Yes No
This is not an exhaustive comparison of the two products. See the product documentation for more
complete information about these products.
Enabling projects for messaging
This section provides information to enable projects for messaging.
Client Services target profile features
The Lotus Expeditor Toolkit provides Client Services target profile support. These target profiles simplify
the creation and configuration of messaging application projects, enabling you to select the
target-embedded messaging APIs, and provide automatic management of the requisite messaging
libraries. When developing a messaging application, you can select any of the Client Services target
profiles for your Client Services project. The following table provides a list of tasks and the appropriate
target feature for each profile in a Client Services project.
Table 6. Client Services project tasks
Task Target Feature
Embedded point to point messaging MQ Everyplace
JMS point-to-point messaging MQ Everyplace JMS Support and Java Message Service
(JMS) APIs
Developing applications 141
Table 6. Client Services project tasks (continued)
Task Target Feature
MQTT programming MQ Telemetry Transport Client
JMS publish and subscribe messaging MQ Telemetry Transport Client JMS Support and Java
Message Service (JMS) APIs
Embedded publish and subscribe messaging Lotus Expeditor micro broker
Bridging micro broker messages to WebSphere MQ Lotus Expeditor micro broker JMS Bridge(These target
features are only supported on J2SE 1.4 and Java 5
runtimes.)
Bridging micro broker messages to other JMS Providers
that support JNDI
Lotus Expeditor micro broker JNDI JMS Bridge1
For more information on Client Services projects, refer to “Lotus Expeditor Toolkit” on page 11.
Developing messaging application logic
This section provides information on messaging application logic development.
MQ Telemetry Transport
Documentation is available on the MQTT Web site at: http://mqtt.org.
Developing network aware applications
The network layer is a base network management layer in Lotus Expeditor that provides a framework to
applications and platform components for network invocations and network error handling. It provides
users with the capability to determine the current status of the client platform as well as their
connectivity to remote servers, and HTTP resources including Web Services. The application can choose
to be notified of a client status change or to check the client platform status by using the public APIs for
the offline manager. The application can also check the real network status by using the public APIs for
netstatus. This layer also includes the RCP version of the HTTP URL Handler which integrates with the
account API and offline manager.
Understanding network aware applications
The network framework provides a base framework to handle network errors and provides a network
monitoring service. It consists of the following plug-ins:
Table 7. Network Layer Plug-ins
Plug-in Contribution
com.ibm.rcp.net.faults(NetFaults) The main framework handling all kinds of network
Faults. It provides detector extension points for platform
applications to extend. It provides handler extension
points for platform and application components to
extend. It has been enhanced to take a context parameter
in the Fault object, the detectors and the handlers.
com.ibm.rcp.net.http Handles failures at the HTTP protocol level. New:
change the WMC 2.6 URLHandler implementation to
support Accounts integration and update NetFaults to
support multiple server status concept.
com.ibm.rcp.offline (OfflineManager) Manages the client state transitions and provide a service
for querying the state of the client. New: Manages the
multiple remote resources state transitions and provides
a service for querying of the state of the remote
resources.
142 Lotus Expeditor: Developing Applications for Lotus Expeditor
Table 7. Network Layer Plug-ins (continued)
Plug-in Contribution
com.ibm.rcp.net.status (NetStatus) Detects the network adapter status
com.ibm.rcp.net.status.linux Native implementation of NetStatus service on Linux
com.ibm.rcp.net.status.win32 Native implementation of NetStatus service on Win32
com.ibm.rcp.os.events (OS Events) Detects the OS power events like standby or hibernate
com.ibm.rcp.os.events.linux Native implementation of OS events on Linux
com.ibm.rcp.os.events.win32 Native implementation of OS events on Win32
com.ibm.rcp.os.powerawareness The interface layer on top of os.events and is to be used
by other plug-ins to query the system power status
com.ibm.net.faults.default (platform default
configuration)
The default configuration to order detectors and
handlers. It also provides some default implementation
of handlers and detectors.
When an application throws a network error and calls DetectAndHandle.detectAndHandle(), the
NetFaults component will walk through the detectors and handlers in the order specified by the default
configuration or its customized configuration. The platform provides all the detectors in a predefined
order. The application component can contribute its customized handlers.
The Http plug-in within the network layer replaces the default VM implementation of HTTP and HTTPS
URLStreamHandler’s to be based on the Apache HTTP client.
Enabling network aware applications
Lotus Expeditor client provides Client Services target definition support in the Rational tooling
environment. These target definitions simplify the creation and configuration of network aware
application projects, enabling you to select the target-embedded network layer APIs and libraries.
When developing a network aware application, you can choose the ″Network Components Feature″
available in the Default Target definition.
Developing network aware application logic
Any application component interested in the remote/local resource status and client status should
implement the INetworkAware interface or extend the NetworkAwareBase object. It also should call the
DetectAndHandle.detectAndHandle(e,contextKey) when an network error occurs.
The application components can create their customized handlers by using the NetFaults public API .
Lotus Expeditor platform components can also create customized Faults and detectors by using the
NetFaults internal API.
Applications can use the NetStatus public API to detect the real network status.
The Lotus Expeditor network framework replaces the default VM implementations of HTTP and HTTPS
UrlStreamHandler’s which are based on the Apache HTTP client. The UrlStreamHandler implementation
will utilize the Apache HTTP client.
The platform does not provide default implementations for remote resource monitors.
Component registration
Application components register to use the framework by implementing the INetworkAware interface.
INetworkAware is an interface that must be implemented by a network aware class, that is, one that
wishes to receive callback notifications for online/offline related events and network connectivity related
Developing applications 143
events. This interface replaces IOfflineService which is deprecated as of WCT2.6/IR4D1.01. Callers
should migrate to INetworkAware and need to implement the violent transition, disconnected() and
reconnected().
The following is an example of an implementation of this interface detailing the platform’s state change:
public class MyNetworkAwareImpl implements INetworkAware {
IOfflineServiceHelper _helper;
public INetworkAware init() {
_helper = IOfflineServiceHelperFactory.INSTANCE.create(this);
return this;
}
public IOfflineServiceHelper getOfflineServiceHelper() {
return _helper;
}
public void disconnected(){
//do things accordingly if the system state changed to offline. e.g if the
//network connectivity is lost.
//e.g if application calls IOfflineManager.disconnected(), IOfflineManager
//will fire a DISCONNECTED event, which directly call INetworkAware.disconnected()
// method here.
// no intermediate states.
}
public void reconnected(){
//do things accordingly if the system state changed to online.e.g. if the
//network connectivity is back.
//e.g if application calls IOfflineManager.reconnected(), IOfflineManager will
//fire an RECONNECTED event, which directly call INetworkAware.reconnected()
//method here.
//no intermediate states.
}
public void goOffline(){
//do things accordingly if the client state changed to offline.
//e.g if application calls IOfflineManager.goOffline(), IOfflineManager will
//fire an OFFLINE_REQUESTED event, which call INetworkAware.offlineRequested()
//method
//If INetworkAware.offlineRequested() return true, IOfflineManager will proceed
//and eventually fire an GO_OFFLINE event, which call the code here in
//INetworkAware.goOffline()
}
public void goOnline(){
//do things accordingly if the client state changed to online.
//e.g if application calls IOfflineManager.goOnline(), IOfflineManager will
// fire an ONLINE_REQUESTED event, which call INetworkAware.onlineRequested()
//method
//If INetworkAware.onlineRequested() return true, IOfflineManager will proceed and
// eventually fire an GO_ONLINE event, which call the code here in
//INetworkAware.goOnline()
}
...
}
Example of an implementation of this interface if you are interested in a remote server/resource state
change - whether it is up or down. Since the client can not change the remote server state (that is, call
IOfflineManager.goOnline() and IOfflineManager.goOffline()), there is no need to implement the
callback methods goOnline() or goOffline().
144 Lotus Expeditor: Developing Applications for Lotus Expeditor
public class MyNetworkAwareImpl implements INetworkAware {
IOfflineServiceHelper _helper;
public INetworkAware init(String context) {
_helper = IOfflineServiceHelperFactory.INSTANCE.create(this,context);
return this;
}
public IOfflineServiceHelper getOfflineServiceHelper() {
return _helper;
}
public void disconnected(){
//do things accordingly if the system state changed to offline. e.g if the remote
//server is down. e.g if application calls IOfflineManager.disconnected(),
//IOfflineManager will fire an DISCONNECTED event, which directly call
//INetworkAware.disconnected() method here.
// no intermediate states.
}
public void reconnected(){
//do things accordingly if the sytem state changed to online.e.g. if the remote
//server is back. e.g if application calls IOfflineManager.reconnected(),
//IOfflineManager will fire an RECONNECTED event, which directly call
//INetworkAware.reconnected() method here.
// no intermediate states.
}
...
}
The following, is an example of using the INetworkAware class if the INetworkAware object wishes to
receive callback notifications for client state change:
INetworkAware myImpl= new MyNetworkAwareImpl().init();
The following is an example of the INetworkAware object requesting to receive callback notifications for a
remote server:
String contextKey="http://www.yahoo.com"
getOfflineServiceHelper().closeService();
An example of using DetectAndHandle:
String contextKey = "http://www.yahoo.com";
try {
URL url = new URL(contextKey);
url.openConnection();
}catch(Exception e){
//Application code asserts to the platform that a network failure occurred.
DetectAndHandle.detectAndHandle(e,contextKey);
//still handle the Exception.
throw e;
}
Once you are finished with the network framework service, it must unregister its listener as follows:
getOfflineServiceHelper().closeService();
Creating and configuring handlers
An application can create its own handlers by extending the public handlers interface, and adding the
extensions in the plugin.xml file in the application plug-in.
The following is an example of the default plugin.xml file:
Developing applications 145
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin
id="com.ibm.rcp.net.defaults"
name="com.ibm.rcp.net.defaults_2.6.0"
version="2.6.0.4"
provider-name="IBM">
<requires>
<import plugin="com.ibm.rcp.net.faults"/>
<import plugin="com.ibm.rcp.net.http"/>
</requires>
<extension
point="com.ibm.rcp.net.faults.detector">
<detector
order="2"
detectorClass="com.ibm.rcp.net.http.faults.HttpFaultDetector"
id="com.ibm.workplace.faults.detector2"/>
<detector
order="3"
detectorClass="com.ibm.rcp.net.faults.internal.power.PowerMonitor"
id="com.ibm.workplace.faults.detector3"/>
<detector
order="5"
detectorClass="com.ibm.rcp.net.faults.internal.netstatus.
NetStatusFaultDetector"
id="com.ibm.workplace.faults.detector5"/>
<detector
order="6"
detectorClass="com.ibm.rcp.net.faults.internal.detectors.
TransientFaultDetector"
id="com.ibm.workplace.faults.detector6"/>
</extension>
<extension
id="com.ibm.workplace.faults.handler"
point="com.ibm.rcp.net.faults.handler">
<handler
order="1"
handlerClass="com.ibm.rcp.net.faults.internal.handlers.
AdvancedHandler"
id="com.ibm.workplace.faults.handler1"/>
</extension>
</plugin>
Adding and configuring customized handlers
You can add handlers and configure the platform to use its customized handlers. You must first create the
handler, then add the handler to the platform. The following is an example for creating a handler:
public class MyHandler implements Handler {
IOfflineManager _offlineManager = IOfflineManagerFactory.INSTANCE.create();
public MyHandler (){
}
public void handle(Fault[] faults) {
for(int i=0; i<faults.length; i++){
//check if there is a power fault.
Fault f = faults[i];
if( (f instanceof PowerFault)
||(f instanceof MultipleTransientThresholdFault)
||(f instanceof NetStatusFault)){
146 Lotus Expeditor: Developing Applications for Lotus Expeditor
disconnect();
break;
}
}
}
private void disconnect(){
_offlineManager.disconnected();
}
}
To add this extension to a plugin.xml:
<extension
id="com.ibm.workplace.faults.handler"
point="com.ibm.rcp.net.faults.handler">
<handler
handlerClass="com.ibm.rcp.handlers.MyHandler"
id="com.ibm.rcp.handlers1"/>
</extension>
Using the NetStatus API to detect the real network status
Applications can use the Netstatus API to detect if the network is on or off. The following is an example
of how to use the API (however, ensure you call the update() method first).
private static final NetStatusMonitor _monitor = new NetStatusMonitor();
_monitor.update();
if(!_monitor.hasAnyLinkConnection()){
//network is off
}
Note: If your system is connected using a device emulator, such as VMWare, or other connections that
share your LAN or wireless network connection, the netstatus API always returns true, even after
you disconnect the LAN cable and wireless. This is because of the existence of the virtual adaptor.
If you want your application to be notified of a true offline state (or plan to disconnect the system),
go to Start > My Network Places, right-click on Properties, and disable the shared connections.
The Netstatus API returns the correct status.
Using enhanced HttpClient
No special coding is required to use the replacement URLStreamHandlers/URLConnections for HTTP and
HTTPS. Simply create a URL object and then open the connection (with openConnection or openStream),
as such:
URL myURL = new URL(http://my.server.com/path/to/resource/);
HttpURLConnection myConn = (HttpURLConnection) myURL.openConnection();
InputStream in = myConn.getInputStream();
// read input stream
The HTTP URLConnection is a subclass of java.net.HttpURLConnection. The HTTPS URL Connection is a
subclass of javax.net.ssl.HttpsURLConnection but the methods introduced by
javax.net.ssl.HttpsURLConnection are not implemented. Otherwise, the HTTPS URLConnection subclass
operates normally.
The URLConnection subclasses will always consult the Accounts API to locate login credentials for the
subject URL. If credentials are present for the URL, then those credentials will be used to authenticate
access to the URL.
Developing applications 147
Monitoring remote resources
If an application wants to monitor the status of remote resources, it can create a customized handler and
kick off the monitor from the handler. It must also add the handler extension in the plugin.xml. The
following example illustrates adding a handler extension:
<extension
id="com.ibm.workplace.faults.handler"
point="com.ibm.rcp.net.faults.handler">
<handler
handlerClass="com.ibm.rcp.net.defaults.internal.handlers.RemoteResourceMonitor"
id="RemoteResourceMonitor"/>
</extension>
Monitors must call OfflineManager.reconnected() or OfflineManager.reconnected(contextKey) in order
for components to be notified of the reconnection once it is detected.
If taking advantage of the net faults framework and creating a handler to monitor local/remote resources,
monitoring should not be done within the handler itself. The handler may be used to initiate a monitor. It
is suggested that an Eclipse Job be used.
It is important that the application only monitors the connectivity to a remote resource when the state of
that resource is offline and the global state is online. Therefore, if a network disconnection occurs while a
monitor is active, the monitor should sleep until the client becomes reconnected.
Code example for an HTTP monitor:
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.core.runtime.jobs.Job;
import com.ibm.rcp.net.faults.ConnectFault;
import com.ibm.rcp.net.faults.Fault;
import com.ibm.rcp.net.faults.Handler;
import com.ibm.rcp.offline.api.manager.IOfflineManager;
import com.ibm.rcp.offline.api.manager.IOfflineManagerFactory;
public class RemoteResourceMonitor implements Handler {
String myContext = "http://www.myWebService.com";
HttpURLConnection webServiceConn = null;
URL webServiceURL = null;
IOfflineManager _offlineManager = IOfflineManagerFactory.INSTANCE.create();
public void handle(Fault[] f) {
//iterate through faults
for (int i = 0; i < f.length; i++) {
//if ConnectFault is found for given context
if (f[i] instanceof ConnectFault && ((String)f[i].getContext()).
equals(myContext)) {
Job monitorJob = new MonitorJob("webService monitor"){
protected IStatus run(IProgressMonitor arg0) {
while(isOffline) {
try {
webServiceURL = new URL(myContext);
148 Lotus Expeditor: Developing Applications for Lotus Expeditor
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
webServiceConn = (HttpURLConnection)webServiceURL.openConnection();
int webServiceResponse = webServiceConn.getResponseCode();
//if good response from webService, discontinue monitoring
if (webServiceResponse == HttpURLConnection.HTTP_ACCEPTED ||
webServiceResponse == HttpURLConnection.HTTP_CREATED ||
webServiceResponse == HttpURLConnection.HTTP_OK) {
//set offline state to false
isOffline = false;
//notify offline manager
_offlineManager.reconnected(myContext);
}
} catch (IOException e) {
e.printStackTrace();
}
}
//return status
return Status.OK_STATUS;
}
};
//schedule job
monitorJob.schedule();
break;
}
}
}
/**
* Internal class that extends the Eclipse Job framework. Contains a
* boolean housing the state of the remote resource for which the
* monitoring job was instantiated.
*/
private static abstract class MonitorJob extends Job {
boolean isOffline = true;
String id;
public MonitorJob(String id) {
super(id);
this.id = id;
}
}
}
Using the notification and check models
Lotus Expeditor supports both notification and check models as methods for checking client status.
Notification model: You should use the Notification model if you want your application to either react
to a state change on the client platform (for example, whether the platform goes online or offline), or
react after listening to a remote server.
The Notification model flows as such:
1. The application implements both the INetworkAware object and the callback method (such as,
disconnected() or reconnected()).
2. The application calls DetectAndHandle() when an error occurs.
3. DetectAndHandle() will walk through the detectors, which generate the Faults, and pass the Faults to
the handlers.
Developing applications 149
For example, if you have a networking error, the NetStatusFaultDetector will generate a
NetStatusFault by the NetStatusDetector. When you use the HTTP plug-in, if the server is down or
if there are networking errors, the exception type SocketException, ConnectException
,NoRouteToHostException will generate a ConnectFault by the httpDetector.
4. The handler calls IOfflineManager.
5. IOfflineManager generates the corresponding event (for example, EVENT_DISCONNECTED, which calls the
INetworkAware object’s disconnected() method.
Check model: If you just want to check the state of the client platform, you can call
IOfflineManager.isOnline().
IOfflineManager offmgr = IOfflineManagerFactory.INSTANCE.create();
If you want like to check the state of a remote server, you need to implement an INetworkAware object,
and use IOfflineHelper to check the state of a remote server.
Developing portlet applications
The Lotus Expeditor platform supports portlet applications that conform to the JSR 168 specification. The
portlet development tooling component of the Lotus Expeditor Toolkit allows users to create, develop,
launch/test and debug OSGi based Client Services portlet applications, as well as convert existing J2EE
server based portlet applications to run on the Lotus Expeditor platform. The portlet development tools
also assist in providing a Portal-like development experience by allowing users to wire and aggregate
portlet applications.
A portlet application bundle can be developed using many of the same portlet development tools
provided by the Rational Software Development platform. You should therefore refer to the Rational
online help section Developing Portlet applications as your initial portlet development tools reference.
The following table provides pointers to information on portlet development activities and information
on tasks that are unique to, or require special consideration when developing portlet applications for the
Lotus Expeditor platform.
Table 8. Portlet development activities
Task Reference
Understanding Client Services Portlet development. “Understanding Portlet applications”
Working with Client Services Portlet projects. “Creating Portlet projects” on page 154
Developing Client Services Portlet application logic. This
encompasses any special development considerations
when coding and constructing the Portlet application
logic.
“Developing Portlet application logic” on page 155
Importing Portlet application projects. “Importing Client Services portlet projects” on page 155
Debugging and testing Portlet applications. “Debugging and testing Client Services Portlet
applications” on page 157
“Client Services Server” on page 324
Deploying Client Services Portlet applications. “Deploying projects for local testing” on page 331
Using the command line WAB tool to convert a WAR to
a WAB.
“WAB Utility” on page 247
Understanding Portlet applications
Client Services portlet applications run on the Lotus Expeditor platform. A primary difference between a
Client Services portlet application and one that is deployed to run on a WAS or Portal runtime is that the
150 Lotus Expeditor: Developing Applications for Lotus Expeditor
Client Services portlet application must also be a valid OSGi bundle. Refer to “Working with OSGi
bundles” on page 366 for more information on bundles and the Lotus Expeditor platform.
The Lotus Expeditor Toolkit automatically handles many of these bundle specific details, which is why
developing the portlet application through a Client Services portlet project is the recommended
development path for portlet applications that are to be run on the Lotus Expeditor platform.
Nevertheless, it is also possible to develop the portlet application through a J2EE portlet project, and
subsequently test run it on the Lotus Expeditor platform. It is also possible to transform an existing
Portlet Web Application Archive (WAR) file into a Portlet Web Application Bundle (WAB) suitable for
running on the Lotus Expeditor platform through the use of the “WAB Utility” on page 247.
The following lists aspects of a Client Services portlet application that differ from a standard portlet
application.
v The Lotus Expeditor platform does not support deploying Enterprise Applications through an EAR.
The portlet application is directly deployed to the runtime.
v A Client Services portlet application has a manifest file, located in META-INF/MANIFEST.MF, that
contains bundle information including package and bundle dependencies. This is associated with the
bundle, and is separate from the manifest file found under the portlet application’s content folder.
v A Client Services portlet application contains additional deployment information in wab.properties.
This is located in the web content WEB-INF folder.
v JSP files are translated into their respective servlet classes before the web application is deployed to the
runtime as a WAB.
In most cases, these artifacts and differences are handled transparently by the Lotus Expeditor tools.
These differences do not affect the functionality of the portlet application. There are, however, some
development considerations you should take into account. These are described in Client Services Portlet
Application Development section.
A Client Services portlet application can be developed using many of the same portlet development tools
provided by the Rational Software Development platform. The primary differences are:
v Use the Client Services Portlet project wizard to create a Client Services portlet project, as described in
Creating a Client Services Portlet project.
v Since the Lotus Expeditor platform does not support EARs, EAR projects are ignored.
v When testing the project, target the Lotus Expeditor runtime when using the Run / Run on Server
action. Or, use the Lotus Expeditor launch configuration when using the Eclipse Run / Debug launch
feature. This is explained in Debugging and testing applications.
v When exporting the portlet application, use the Plug-in Development > Deployable plug-ins and
fragments wizard.
URL Addressability
The Portlet Container allows a user to call a portlet directly using an URL. Therefore a special URL
format is defined for portlets. This URL format allows portlets to be called just like servlets (by
context/portlet-name), but also provides the possibility to add additional portal context information, such
as the portlet mode or window state.
The portlet can be accessed by a URL mapping that has the following structure: http://host:port/context/portlet-name [/portletwindow[/ver [/action] [/mode] [/state] [rparam]]]
Any differing URL structure results in a com.ibm.wsspi.portletcontainer.InvalidURLException. Empty
Strings are not allowed as parameter value and throws an InvalidURLException.
http://host:port/context/portlet-name/portletwindow
The basic URL that is minimum to access a portlet. A default portletwindow called ‘default’ is
created.
Developing applications 151
/portletwindow
Mandatory parameter used to identify the portletwindow. This parameter must be set if choosing
to add more PortalContext information to the URL.
/ver=major.minor
Optional parameter to define the version of the portlet API that should be used. This parameter
must be set if choosing to add more PortalContext information to the URL. For now only the
version ‘1.0’ is allowed. Any differing version would throw an InvalidURLException.
/action
Parameter that needs to be set if the action method of the portlet should be called
The action parameter causes the action process of the portlet to be called. After the action has
been executed, a redirect is automatically issued to call render.
To control the subsequent render process, a document servlet filter can set a request attribute
with name com.ibm.websphere.portlet.action and value redirect to specify that the portlet
serving servlet directly returns after action without calling render.
/mode=view | edit | help | custom-mode
An optional parameter to define the portlet mode that should be used to render the portlet.
Default is the mode view. The value is not case-sensitive, e.g. View or VIEW result in the same
mode view.
/state=normal | maximized | minimized | custom-state
An optional parameter to define the window state that should be used to render the portlet.
Default is the state normal.
The value is not case-sensitive, e.g. Normal or NORMAL result in the same state normal.
* [ /rparam=name *[=value] ]
An optional parameter to specify render parameters for the portlet. Repeat this parameter chain
to provide more than one render parameter, e.g. /rparam=invitation/rparam=days=Monday=Tuesday
?name=value name2=value2 ...
Query parameters may follow optionally. They are not explicitly supported by the
portletcontainer, but they do not invalidate the URL format.
Client Services Portlet projects
This section describes Client Services Portlet projects.
Portlet API and Type support
The Portlet API and Type support in the Portlet Tooling component is entirely based on the Lotus
Expeditor Portlet runtime support.
v Portlet APIs supported - JSR 168
v Portlet Types supported - Empty, Basic, and Faces
Please note that there is no support for Struts Portlet in Lotus Expeditor. Additionally, only the Empty
portlet type will be supported in an AST integrated build. All three portlet types are supported on the
Rational Software Development Platform.
The following list illustrates which portlet type is created for each type option available:
Table 9. Portlet types
Portlet Type Description
Empty Portlet A portlet application that extends the GenericPortlet
class with minimum code in it. You are required to write
the code to complete the portlet.
152 Lotus Expeditor: Developing Applications for Lotus Expeditor
Table 9. Portlet types (continued)
Portlet Type Description
Basic Portlet A portlet application that extends the GenericPortlet
class defined in the Java Portlet Specification version 1.0
(JSR 168). You will be asked to select several options for
generating sample code.
Faces Portlet A portlet application that uses the JavaServer Faces
Portlet Framework (JSR 168).
For more JSR 168 information, refer to http://www.jcp.org/en/jsr/detail?id=168.
Unsupported features
The following feature is not supported by the Portlet Container:
v Displaying custom mode icons in portlet title bar - The Portlet Container does not provide a
mechanism whereby the portlet container branding can be updated to include custom mode icons in
the portlet title bar. Only the maximize, minimize, view, edit and help icons are supported and
displayed in the portlet title bar when the portlet is accessed. The same restriction applies to the portlet
aggregator page as well. To access the custom mode pages, developers should provide links within
their application to these pages.
The following optional feature is not supported by the Portlet Container:
v Expiration based caching - The JSR 168 Specification defines an expiration based caching mechanism.
The Portlet Container provides a caching mechanism to allow for the portlet content to be cached per
portlet per user client. This feature is optional.
The following optional service is not supported by the Portlet Container:
v User Information Service - The JSR 168 Specification allows portlets to define user attributes the
portlets might be interested in. The Portlet Container provides a mechanism to expose the available
user information to portlets. This feature is optional.
Using the Portlet Aggregation Tag Library
The Lotus Expeditor 6.1 runtime provides a Tag Library that can be used by developers to write JSPs that
aggregate multiple portlets on one page. This capability will not provide the developer with a full feature
portal aggregation implementation, but provides a very good migration scenario for developers who
already have aggregating servlets and JSPs and want to switch to portlets.
The biggest advantage for developers is that they can re-use portlets that currently run on the WebSphere
Portal Server on the Lotus Expeditor 6.1 client runtime. However, the Tag Library and the JSPs that use
this Tag Library can only be run on the WebSphere portlet container implementation (the protocol
between the tags and the portlet container is not standardized).
A brief description of the Aggregation Tags is provided below (for a more in-depth look at the
Aggregation Tags, please refer to “Aggregation tag library” on page 378):
v Init - This tag initializes the portlet framework and has to be used in the beginning of the JSP. All
other tags described in this section are only valid in the body of this tag, therefore the init tag usually
encloses the whole body of a JSP.
v State - This tag creates a URL pointing to the given portlet using the given state. This URL can either
be placed into a variable specified by the var attribute or can be written directly to the output stream.
v Insert - This tag calls the render method of the portlet and retrieves the content as well as title. The
content and title of the specified portlet can optionally be placed into variables using the contentVar
and titleVar attributes.
v urlParam – This tag adds a render parameter to the newly created URL.
Developing applications 153
v initBranding – This tag is to provide the branding theme information to the portlet aggregator page.
The theme includes - stylesheet, the title bar images, the title bar background color and the content
background color.
v initUrlPrefix – This tag is used to provide the URL prefix information for all the portlet URLs
including the render and action URLs. The URL prefix includes the server address and the port
number.
“Aggregating portlets to JSPs” on page 155 describes how to use the Lotus Expeditor 6.1 portlet tools to
write an aggregation JSP.
Note: If you manually create the aggregator JSP, then include the following tag library references in the
JSP:
<%@ taglib uri="http://ibm.com/portlet/aggregation" prefix="portlet"%>
<%@ taglib uri="http://ibm.com/portlet/aggregation/ext" prefix="portlet_ext"%>
Adding these tag library references will allow the Lotus Expeditor tools to automatically process
the tags during compilation of the aggregator JSP. The Lotus Expeditor portlet tools will
automatically add the above tag library references if you follow the procedure described in
“Aggregating portlets to JSPs” on page 155.
Creating Portlet projects
This section provides information on creating Portlet projects.
Creating a Client Services Portlet project
To create a new Client Services Portlet project that can be developed and deployed to a Lotus Expeditor
platform, perform the following procedure:
1. Start the RSDP or AST platform workbench with the Lotus Expeditor Toolkit installed.
2. Start the Portlet Project creation wizard by selecting File > New > Project > Client Services > Client
Services Portlet Project.
3. On the first page of the new Client Services Portlet project creation wizard, provide a unique Client
Services Portlet project name. Select the Client Services v6.1 target runtimes from the drop down list
if it is not already selected by default. The Portlet APIs selection should be auto-selected for JSR 168
portlet APIs. Finally, select the Portlet type from the drop-down list as per your needs.
Note: Please note that there is no support for Struts portlet projects in Lotus Expeditor 6.1. Refer to
“Portlet API and Type support” on page 152 for more details.
4. Click Next and go to the Portlet Settings page. Click Finish if you would like to accept the rest of the
default settings for your new portlet project.
5. To continue, click Next to either go to the Action and Preferences page or the Advanced Settings page,
based on the portlet type.
6. Click Next to open the Target Profile page to make target definition and target features and plug-ins
selections. The Target profile page displays the current default Target definition which is set on the
Client Services Preference page. The Core OSGi, Web Application and Portlet Application target
features are pre-selected and you may make additional choices if needed. You may select a different
target definition from the drop down list.
7. Select Finish to create the new Client Services Portlet project.
Converting a J2EE portlet project to a Client Services Portlet project
To convert an existing J2EE server (WebSphere Application/Portal server) based JSR 168 portlet project
into a Client Services Portlet bundle project that can be targeted to run on the Lotus Expeditor runtimes
platform, perform the following procedure:
1. Start the Rational Software Development Platform or AST with the Lotus Expeditor Toolkit installed.
154 Lotus Expeditor: Developing Applications for Lotus Expeditor
2. Start the Convert Project to Client Services Project wizard by selecting File > New > Other > Client
Services > Convert Project to Client Services Project.
3. The first page will display all the projects in the user’s workspace that are not already Client Services
projects. Select the project that you wish to convert and click Next. You can optionally choose to
create a copy of the project that is being converted to create a backup. To do so, run the wizard with
create a copy of the original project option checked before conversion and make all the
transformation changes to the new copy.
4. You can click Finish to perform the conversion and receive the default Target definition and features
selections.
5. Selecting Next displays the Target Profile selection page. Core OSGi, Portlet Application and Web
Application are pre-selected. Select additional target features if needed.
6. Click Next to open the next page, for providing options for automatically managing the project’s
manifest file. This is enabled by default, and set to prefer Import-Package or Require-Bundle
(depending on the workspace default settings) for resolving package dependencies.
7. Select Finish to perform the project conversion.
Refer to “Convert Project to Client Services Project Wizard” on page 375 for information on how to use
this wizard.
Importing Client Services portlet projects
To import Client Services Portlet projects that could be deployed to a Lotus Expeditor platform, perform
the following procedure. The import functionality is provided by the Eclipse Platform Development
Environment. Portlet Tooling provides no additional support for importing Client Services portlet
projects.
1. Start the Rational Software Development Platform workbench with Lotus Expeditor installed.
2. Select File > Import to invoke the import wizard.
3. Select the appropriate import type for importing Client Services portlet projects:
v Project Interchange
v Zip file4. Follow the wizard pages and select the Client Services portlet project that needs to be imported, then
click Finish.
The Client Services portlet project is imported into your workspace.
Adding a portlet to a Client Services Portlet project
To add portlets to a Client Services Portlet Project, perform the following procedure:
1. Select File > New > Other > Portal > Portlet to invoke the wizard to add portlets to an existing
Client Services portlet project.
2. Ensure that the project you wish to add the new portlet to is selected.
3. Enter the new portlet name and select the type from the drop down list.
4. Follow the wizard steps to select other settings for your new portlet.
Developing Portlet application logic
This section discusses Portlet application logic development.
Aggregating portlets to JSPs
Server based portal implementations (such as WebSphere Portal Server) dynamically generate portal
aggregator JSPs using portal configuration information provided by portal application developers. The
Lotus Expeditor platform does not have such dynamic support, and requires the portal layout
information to be specified in the aggregator JSP (using the HTML table element) with the packaged
application before deployment. The <HTML> table model allows application developers to arrange
Developing applications 155
portlets into rows and columns of cells. Using this model, multiple portlets can be rendered on a single
aggregator JSP page which can be viewed either in the Lotus Expeditor browser or an external platform
browser.
To create a JSP page that aggregates multiple Client Services Portlet applications, perform the following
procedure:
1. Select a Client Services Portlet or Web project where you wish to add the aggregated JSP.
2. Right click the Client Services Portlet or Web project and select New > Other > Web > JSP File to
create a new JSP file. Create the new JSP file in the Web Content directory of the project.
3. Open the JSP file in the JSP editor if it does not already open in the editor.
4. Ensure that the Palette view for the JSP is visible. If the Palette view is not available, then select
Window > Show View > Other > Basic > Palette to display the view.
5. Use the actions in the HTML category in the Palette to create the HTML content in the JSP. Add an
HTML table to the JSP page to create a framework for the layout of your perspective. Note that you
are expected to configure your HTML layout before and after adding the Portlet content into the JSP.
6. Select a table cell in the JSP editor and select the Add Portlet to JSP action in the Client Services
category from the Palette.
7. The Add Portlet to JSP action dialog is displayed to the user. The following table explains the various
options of the dialog:
Table 10. Add Portlet to JSP Dialog
Option Description Default Value
Servlet Mapping Select the servlet mapping that will
handle your aggregation JSP request
List of available servlet mappings
defined in the selected project’s Web
Deployment Descriptor
Select different project Allows the selection of a different
project to add Portlets
Provides a list of available Client
Services Portlet projects in the user’s
workspace
Portlet Select the portlet you wish to add (or
aggregate) to the JSP
List of available Portlets that are
defined in the selected project’s
Portlet Deployment Descriptor
Portlet Variables Portlet variables that are required to
generate JSP code that will aggregate
the portlet into the JSP
None
8. Click Finish to insert the code that will insert the portlet in the table cell of the JSP. Repeat the steps
to add additional portlets in other cells within the table.
9. The add portlet code will be compiled and built with the JSP and you can test and debug the
aggregated JSPs by right clicking on the JSP and selecting the Run on Server method.
Securing Portlet application resources
Though the J2EE specification does not specify the requirements for Portlet security with regards to
authentication and authorization, the Portlet Container will protect the portlet resources similar to the
way the J2EE servlet resources are protected when accessed through a URI. In declarative security the
application’s web descriptor specifies the application’s security policy (roles, access control, etc.) without
changing the applications code. As you can see the authentication and authorization aspects for Portlets
are similar to Servlet/JSPs.
Transport level security (HTTPS): The portlet specification requires that portlets which must be
accessed through HTTPS specify a security-constraint in the portlet.xml. The following is an example of
a security-constraint indicating that the portlet TestPortlet be allowed access only through HTTPS.
<security-constraint>
<display-name>Secure Portlets</display-name>
<portlet-collection>
156 Lotus Expeditor: Developing Applications for Lotus Expeditor
<portlet-name>TestPortlet</portlet-name>
</portlet-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
Programmatic security: The portlet specification also supports programmatic security using the
isUserInRole method, just like servlets do. The following is an example indicating that when the
isUserInRole(role1) is called within a portlet TestPortlet, it should return true if the user who is
accessing the portlet is in the Employee role.
<portlet>
<portlet-name>TestPortlet</portlet-name>
<portlet-class>portlet.TestPortlet</portlet-class>
<security-role-ref>
<role-name>role1</role-name>
<role-link>Employee</role-link>
</security-role-ref>
</portlet>
To configure a portlet application to use declarative / programmatic security on the Portlet Container, the
web descriptor must define a list of valid User Admin roles in the <role-name> tag. This list of roles can
include user and group roles. The above example uses the default User Admin role of Employee. The
Portlet Container assumes that all User Admin users store their passwords as a credential with the key
″password″. If no valid users are created with User Admin then the Portlet Container will not let anyone
access the portlet application resources that have been secured.
Debugging and testing Client Services Portlet applications
This section provides information for debugging and testing Client Services Portlet applications.
Debugging and testing Client Services Portlet projects on a test environment
To launch/debug a Client Services Portlet project on the packaged Lotus Expeditor test environment
using the Client Services launcher, perform the following procedure:
1. Using the J2EE, Web or Plug-in Development Environment perspective, bring up the Launch, Run or
Debug panel by clicking Run > Run... (or Run > Debug...).
2. Select the Lotus Expeditor configuration type and click New to create a new launch configuration.
3. Select the Client Services launcher as the target platform to launch on the Main tab.
4. Select the Plug-ins tab and ensure that the portlet applications you wish to test are selected, as well as
any other dependency plug-ins.
5. Click Run or Debug to launch.
Debugging and testing Client Services Portlet projects on non-Lotus Expeditor
runtimes
To debug and test Client Services portlet projects on a non-Lotus Expeditor runtime, perform the
following procedure:
1. Using the J2EE or Web perspective, select the portlet application project you wish to test or debug.
2. Right click on the project to bring up the context menu, and select Run As > Run on Server...
3. When the list of server configurations displays, select a non-Lotus Expeditor server (either WebSphere
Portal Server or WAS 6.1).
4. Add the Client Services Project that you wish to run/debug.
5. Click Finish to test or debug the application.
Developing Rich Client applications
This section provides information on Rich Client application development.
Developing applications 157
Creating a simple Rich Client Platform application
The Rich Client Platform is the minimal set of Eclipse SDK plug-ins needed to build an application with
a UI. However, rich client applications are free to use any API necessary for their feature set, and can
require any plug-ins beyond the bare minimum. The key differentiator of a rich client application from
the standard SDK workbench is that the application is responsible for defining which class should be run
as the main application. Your RCP application is an Eclipse plug-in. As such, you can use a plugin.xml
file to specify your application’s start-up class, which creates the workbench window.
In order to create a Java-UI application that can be contributed to the client platform, there is a minimal
set of objects that must be created:
v A plug-in for the application
v A view to display some data
v A perspective to contain the view
v An extension point declaration of the application for Lotus Expeditor
Note: The above objects are enough to run an application within the development environment. In order
to deploy and install the application, a feature and an update site are also required.
The following procedure illustrates how to create an application that can be contributed to the client
platform. In these steps, you will make use of the Plug-in Development Environment Plug-in Project, as it
provides a set of templates that will reduce the number of steps necessary to build the application.
1. Start Rational Software Development Platform.
2. Create a plug-in project to contain the application that you are providing. You will use a template that
provides a view in order to quickly build an application. Later, you can modify the code generated by
the template to meet your own requirements.
a. Select File > New > Other > Plug-in Development > Plug-in Project to create the initial project,
then select Next.
Note: If you do not see the Plug-in Development entry as one of the selections, select the Show
all wizards option to enable the display of all of the new project wizards. Selecting a new
Plug-in Project will cause a dialog to display inquiring whether you want to enable Eclipse
Plug-in Development. You will need to enable this capability to allow appropriate choices to
be populated on menus.
b. On the Plug-in Project panel, enter a name for your plug-in project, such as MyApplication. You
can use the defaults already entered on the rest of the panel. Select Next.
c. Use the default information on the Plug-in Content panel. Select Next.
d. On the Templates panel, check Create a plug-in using one of the templates. Then select Plug-in
with a view. Then select Finish.
e. A project called MyApplication will now be created in your workspace. Selected files will have
already been created within the project based on your selections to create a plug-in with a view.
The MyApplication and MyApplication.views packages will have been created in the src folder.3. Create the initial Java class for the perspective for the application. This class organizes the view
created by the template.
a. First, create a package to contain your perspective:
1) Highlight the src folder in your plug-in project.
2) Select File > New > Package. Enter MyApplication.perspectives as the Name, then select OK.b. Now, create the Java class for the perspective:
1) Select the MyApplication.perspectives package that you just created.
2) Select File > New > Class.
3) Enter a Name for the class, such as MyPerspective.
158 Lotus Expeditor: Developing Applications for Lotus Expeditor
4) The superclass will remain java.lang.Object.
5) Your class will implement the org.eclipse.ui.IPerspectiveFactory interface. Select the Add...
button next to Interfaces. Begin typing IPerspectiveFactory and the panel will complete the
name for you as you type. Select OK.
6) Select Finish to complete your class creation.c. You will need to add some code to the class that you just created in order to layout the view
created by the template:
1) Edit the MyPerspective.java file.
2) Locate the method createInitialLayout and replace the method with this code:
public void createInitialLayout(IPageLayout layout) {
String editorArea = layout.getEditorArea();
layout.addView( "MyApplication.views.SampleView",
IPageLayout.BOTTOM, 0.7f,editorArea);
layout.setEditorAreaVisible(false);
}
This code adds the view created during the project creation to this perspective.
3) Save and close the Java file.4. Now add an extension point to the plug-in to define the perspective that you just created:
a. Open the plugin.xml file contained in your project.
b. Select the Extensions tab within the editor. You should already see some extensions defined, such
as org.eclipse.ui.views and org.eclipse.ui.perspectiveExtensions.
c. Select Add..., then select org.eclipse.ui.perspectives, then OK.
d. Once org.eclipse.ui.perspectives has been added to the All extensions list, right click on the
entry, then select New > perspective.
e. The Extension Element Details information will display. Use the default information for the id and
name.
f. Select the Browse... button next to the class and select the
MyApplication.perspectives.MyPerspective class that you created in Step 3.5. Create the WctApplication extension point to enable contribution of the application to the client
platform
a. Select Add... again, uncheck the box for Show only extension points from the required plug-ins,
then select com.ibm.eswe.workbench.WctApplication, then OK.
b. Select the plugin.xml tab in the editor.
c. Replace the content:
<extension
point="com.ibm.eswe.workbench.WctApplication">
</extension>
with
<extension id="MyApplication"
point="com.ibm.eswe.workbench.WctApplication">
<DisplayName>MyPerspective</DisplayName>
<PerspectiveId>MyApplication.perspective1</PerspectiveId>
<Version>1.0.0</Version>
</extension>
d. Save and close the plugin.xml file.
Note: The LauncherSet extension point can also be used to contribute the application to the client
platform.
At this point, you can launch the platform from the workbench including this application. Clicking
Launch will display MyApplication as one of the selectable applications. Refer to “Debugging and testing
applications” on page 323 for more information on how to launch the client platform.
Developing applications 159
These are the basic steps towards creating the initial application. Once you have the basic application
created, you can continue to enhance your application, adding additional views, menus and menu items,
preference pages, dialogs, messages, and more. You can use the same plug-in that you created above, or
you can separate your user interface logic into additional plug-ins, depending upon your requirements.
In step 5, you implemented an extension point that defined the perspective ID to contribute to the Lotus
Expeditor workbench.
For more information on API specs and usage notes/best practices, refer to “UI toolkits” on page 82 and
“Using widgets on devices” on page 84.
Creating a simple Rich Client Application for devices
This section provides device specific information for creating Rich Client Applications.
Creating a Rich GUI application for devices
A Client Services Rich GUI application uses eSWT to draw its user interface. Similar to an Eclipse eRCP
workbench application, a Rich GUI application draws its user interface within a View. In eRCP, views are
displayed inside a workbench application. In Lotus Expeditor, when running on a desktop computer,
views are also displayed in a workbench, but on a device they are displayed as individual windows. No
matter how they are displayed, embedded Rich GUI applications are referred to as workbench
applications since they never create their own top level window.
Setup: Before creating a Rich GUI application for devices, you must first create a new workspace:
1. Select File > Switch Workspace.
2. Enter a folder name and click OK.
3. When asked if you want to set the Preferences to use the Lotus Expeditor Toolkit, click OK.
4. Select Go to the workbench.
Next, prepare the IDE to build applications for devices. For detailed information, refer to “Setup for
Device development” on page 13.
Creating the basic parts of a Rich GUI application: A Rich GUI application, contains the following
parts:
UIPlugin class: The application must have a class which
extends org.eclipse.ui.plugin.AbstractUIPlugin. This provides the base functionality for starting and
stopping the application within the OSGi framework. This class is created automatically when you create
a Client Services project.
To create a new application project:
1. Select File > New > Project.
2. Expand Plug-in Development and double-click Client Services Project.
3. Enter a project name and click Next.
4. Click Finish.
Add the necessary imports:
1. Resolve any error resulting from the AbstractUIPlugin import by switching to the Dependencies tab.
2. Click Add from the Imported Packages section.
3. Select org.eclipse.swt, org.eclipse.swt.widgets, org.eclipse.ui.plugin, org.eclipse.ui.part, then click
OK.
4. Click Save.
160 Lotus Expeditor: Developing Applications for Lotus Expeditor
View class(es): The application must have at least one class which extends
org.eclipse.ui.part.ViewPart. These classes provide the GUI implementation for the application. In
addition to the required “normal” view that is usually displayed on devices, Rich GUI applications may
also provide a “large” view that is more appropriate for desktop displays. A third “status” view may be
used to display minimal information on very small screens. The application launcher determines which
view is used.
To add a view class for your application:
1. In the Package Explorer, right click on your package.
2. Click New > Class.
3. Fill in a class name for your view class.
4. Change SuperClass to org.eclipse.ui.part.ViewPart.
5. Uncheck Inherit abstract methods and click Finish.
6. Edit the class to add the following methods.
A View class must implement the createPartControl() method. This is where the application initially
creates its GUI on an eSWT Composite widget.
public void createPartControl(Composite parent) {
Label label = new Label(parent, SWT.NONE);
label.setText("Hello World");
}
A View class must implement the setFocus() method in order to give focus to an appropriate control
when the View as a whole gains focus.
public void setFocus() {}
You must also need to add import statements for the SWT classes used.
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
7. Click Save.
Extensions: For an application to appear in the workbench or be launchable it must implement two
extensions. These are org.eclipse.ercp.eworkbench.applications and org.eclipse.ui.views.
For the org.eclipse.ercp.eworkbench.applications extension, the properties of “application” must be
set. An application ID is required and is typically the application package name. Name is a human
readable description. For the properties of “views”, at least the normal field must be set. This is the
reference ID of a specific view.
First, make your application a workbench application:
1. Switch to the manifest Extensions tab and click Add.
2. Uncheck Show only extension points from the required plug-ins.
3. Select org.eclipse.ercp.eworkbench.applications and click Finish.
4. Click No on the New Plug-in dependency popup.
5. Right click org.eclipse.ercp.eworkbench.applications in the All Extensions list.
6. Click New > Application.
For the org.eclipse.ercp.eworkbench.views extension, the properties of “view” must be set. A view ID is
usually defined as the combination of the view’s package name with a “.normal” postfix. Name is a
human readable description. Class specifies the particular View class (from above) that implements the
view. Category must be one of:
v org.eclipse.ercp.category.normal
v org.eclipse.ercp.category.large
v org.eclipse.ercp.category.status
Developing applications 161
Next, add a view for your application:
1. On the Extensions tab click Add.
2. Uncheck Show only extension points from the required plug-ins.
3. Select org.eclipse.ui.views and click Finish.
4. Right click org.eclipse.ui.views in the All Extensions list.
5. Select New > View.
6. Change the view name to something user friendly.
7. Change the class name to match the view class created above.
8. Set the category.
Connect your view to your application:
1. Right click the application element.
2. Click New > Views.
3. Add the view ID specified in step 6 above to the Normal field.
4. Click Save.
Running your application on the development runtime: You can run your application on the Lotus
Expeditor for Devices runtime by launching the workbench. The workbench will detect your application
and list it as an application you can start.
To run the workbench:
1. Select Run > Run...
2. Double-click Client Services.
3. Change the new configuration’s name.
4. For Runtime JRE, select jclDevice Win32 x86.
5. Switch to the Profile tab and select Default Device Target.
6. Press Apply and then Run.
Creating an eRCP workbench application from an existing project
To create an eRCP workbench application from an existing project, perform the following procedure:
1. Select File > New > Other.
2. Select CVS > Projects from CVS.
3. Select the Next button.
4. Enter the Repository Location Information:
a. Input “dev.eclipse.org” in the Host field.
b. Input “/cvsroot/dsdp” in the Repository path field.
c. Input “anonymous” in the User field.
d. Input your E-mail address in the Password field.
e. Select pserver in the Connection type list box.
f. Select the Next button.5. Select the Module:
a. Select Use an existing module.
b. Open org.eclipse.ercp.
c. Select org.eclipse.ercp.app.
d. Select the Finish button.6. Open the Plug-in Development perspective:
a. Select Window > Open Perspective > Other.
162 Lotus Expeditor: Developing Applications for Lotus Expeditor
b. Select Plug-in Development.
c. Select the OK button.7. Refactor/Rename the template project:
a. Select org.eclipse.ercp.app [dev.eclipse.org].
b. Select Refactor > Rename.
c. Enter “my.app”.
d. Select the OK button.8. Expand the template project and Refactor/Rename the source files:
a. Select src > org.eclipse.ercp.app.
b. Select Refactor > Rename.
c. Enter my.app.
d. Ensure you select the check box for Update textual occurrences in comments and strings (forces
preview).
e. Select preview.
f. Select OK.9. Modify the sample code:
a. Open my.app.
b. Modify the manifest to uniquely identify the plug-in:
1) Open META-INF.
2) Select MANIFEST.MF.
3) Change org.eclipse.ercp.app reference in Bundle-SymbolicName to my.app.c. Modify the extension IDs:
1) Select the Extensions tab.
2) Expand everything under org.eclipse.ercp.eWorkbench.applications.
3) Change application ID to my.app.
4) Change views normal to my.app.views.normal.
5) Change views large to my.app.views.large.
6) Expand the views and preferences extensions and similarly change all org.eclipse.ercp.app
references to my.app.
Note: Alternately, you can change the extension IDs by editing the plugin.xml directly from
the plugin.xml tab and replacing all org.eclipse.ercp.app occurrences to my.app.d. Modify the resource file to uniquely identify the plug-in in the user interface:
1) Select plugin.properties.
2) Modify appname to the following:
v My App3) Close the properties file.
Deploying an eRCP workbench application
To deploy an eRCP workbench application, perform the following procedure:
1. Create feature for deploying the plug-in:
a. Select File > New > Other.
b. Select Plug-in Development.
c. Select Feature Project.
d. Input My App as the Project name.
e. Select Next.
f. Select my.app and Finish.
Developing applications 163
2. Create an update site for the feature:
a. Select File > New > Other.
b. Select Plug-in Development.
c. Select Update Site Project.
d. Select Next.
e. Input My Site as the Project name.
f. Select Finish.
g. Select Add Feature in the Update Site Map.
h. Select My_App in the Feature Selection.
i. Select OK.3. Modify the update site’s site.xml file to be compatible with devices:
If you want the features on this site to only be deployable to devices, you can modify the update
site’s site.xml file to make it a device specific site (as in step b below). You may want to do this if
you have similarly named features which are targeted to desktops and do not want users to
accidentally install the device versions of these features. Generally, you will not want to do this
because device applications can also run on the desktop runtime and there is no need to restrict them
to devices.
a. Select the site.xml tab.
b. Replace <site> with <site type="org.eclipse.ercp.update.http">.
c. Select File > Save.4. Synchronize the update site:
a. Select Synchronize in Update Site Map.
b. Select Finish in Feature Properties Synchronization.5. Build the update site:
a. Select Build All in Update Site Map.6. Export the update site:
a. Select File > Export.
b. Select General > File System.
c. Select My Site.
d. Specify a location in the To directory field.
e. Select Finish.
f. Copy the exported directory onto the device.
Installing an eRCP workbench application
To install an eRCP workbench application, perform the following procedure:
1. On the device, select Start > Programs > Lotus Expeditor.
2. Select Application Manager.
3. Select Install New Application/Features.
4. Select Command.
5. Select Add Location.
6. Select Local and Next.
7. Select My Site on the device file system.
8. Select Finished.
9. Check the new location and select Next.
10. Expand the My Site feature, and check My App. Select Next.
11. Accept the license agreement and Next.
164 Lotus Expeditor: Developing Applications for Lotus Expeditor
Running an eRCP eWorkbench application
To run an eRCP eWorkbench application, perform the following procedure:
1. Select Start > Programs > Lotus Expeditor.
2. Select My App.
Managing an eRCP eWorkbench application
Once the application is installed on the device, it is necessary to manage the versions of the application
when making additional changes. There are several methods available for performing this task.
1. The standard development method is to use the Lotus Expeditor Application Manger to uninstall the
application and reinstall using the methods described above.
2. A shortcut method of updating an application is to export the application and copy the plug-in
directly to the device:
a. Select File > Export.
b. Select Plug-in Development > Deployable plug-in and fragments.
c. Select the plug-in.
d. Specify a location in the Directory field.
e. Close all Lotus Expeditor Applications on the device.
f. Copy the plug-in jar from the exported plug-ins directory to the device’s /eclipse/plugins
directory.3. A third way of performing the task is to update the version number of the plug-in and use the
methods described above to create an update site. Then use Application Manager to update the
existing plug-in. As long as the version of the plug-in greater then the previously installed version it
will install correctly. In the event that the version isn’t managed correctly, the application can be
uninstalled and reinstalled using the Application Manager.
Extending the capabilities of your application
There are common conventions that have been adopted in order to construct applications that are
intuitive and easy to use. By following the conventions and guidelines, you will enable your users to
become quickly productive, and will reduce the training required and the frustrations experienced in
using the application.
The subsections of this chapter focus on specific areas with suggestions and guidelines in how to build
your application to work in a pluggable, cooperative environment. In addition, since Lotus Expeditor is
based on Eclipse, you can also refer to the Eclipse User Interface Guidelines. To view the Eclipse User
Interface Guidelines, visit: http://www.eclipse.org/articles/Article-UI-Guidelines/Contents.html.
Customizing the user interface
Lotus Expeditor is a platform that enables multiple applications to run within a single workbench. It
therefore provides a mechanism for applying a common style to the user interface of the applications that
are contributed to it. It does so by supporting not only standard SWT controls, but also a set of custom
widgets for which you can extend the customization.
If you use these custom widgets to build the user interface of your application, you get the added
capability of reflecting the workbench style or theme in your application to ensure that it looks like a
member of the suite of application offerings. You can also apply a static style to the widgets to give your
application a look and feel that is different from the other application offerings, if that is what you want
to do.
Using themes:
The platform provides a set of widgets that are theme-aware. Being theme-aware means that the color,
font and background styles of the controls are managed by a centralized theme definition.
Developing applications 165
This theme definition is defined by a Cascading Style Sheet (CSS). CSS is a standard developed by the
World Wide Web Consortium (W3C) to provide a mechanism for adding style to Web documents. When
the platform starts, the StyleManager class is initialized with CSS content that was defined either through
administrator or end user preferences. If no CSS content is available, the user interface defaults to a
native look and feel. When the CSS content is parsed, the style manager is initialized with CSS content
and any widgets that have registered themselves with the style manager are updated with style
properties that reflect the new CSS content.
After you construct a widget, you can call the StyleManager to apply the proper styling to it. The
StyleManager initializes the paint delegate for the given SWidget internally and applies the style specified
by CSS to it.
The style manager is provided in the com.ibm.rcp.ui.css plug-in and depends on the W3C SAC (Simple
API for CSS) and the W3C Flute implementation of SAC. See http://www.w3.org/TR/SAC/ for more
details.
The default personality class provided in the com.ibm.rcp.personality plug-in manages the initialization
of the StyleManager and retrieves the CSS content.
To register a widget with the style manager:
1. Use or extend the default personality or if you are implementing your own personality, make sure it
calls one of the getInstance() methods of the StyleManager class and initialize the sytle manager with
CSS content by calling the parseURL() method.
For example:
URL cssURL = new URL(cssLocation);
StyleManager.getInstance().parse(cssURL);
2. Make a single call to the style() method for the widget.
For example, the following code registers an instance of SButton with the style manager:
SButton myButton = new SButton(parent, SWT.PUSH);
StyleManager.getInstance().style(myButton, false);
The parameters of the style() method let you specify whether you want to do the following:
v Apply the specified style to any child controls of a widget if the widget is a composite.
v Register the widget to be notified of any changes to the theme at runtime.3. In some cases, you may need to specify a CSS Class and set an ID on the widget to achieve the
proper styling. Use the setData(String key, Object value) method of SWidget to specify these
attributes.
For example:
// Create a button widget and style it
SButton myButton = new SButton(parent, SWT.PUSH);
StyleManager.getInstance().style(myButton);
...
// Create a composite widget of class MainWindow that
// contains a button and style it but do not style the button
Composite myComposite = new Composite(parent, SWT.NONE);
SButton myButton = new SButton(myComposite, SWT.PUSH);
myComposite.setData(StyleManager.CSS_CLASS, “MainWindow”);
StyleManager.getInstance().style(myButton, false);
...
// Create a button widget of class LaunchButton and style it
// but do not update the button if the styles change
166 Lotus Expeditor: Developing Applications for Lotus Expeditor
SButton myButton = new SButton(parent, SWT.PUSH);
myButton.setData(StyleManager.CSS_CLASS, “LaunchButton”);
StyleManager.getInstance().style(myButton, false, false);
Customizing the workbench:
Eclipse provides an SPI that allows clients to customize the look and feel of the Eclipse workbench. Lotus
Expeditor provides an implementation of this SPI called the Styled Presentation Factory, which is an
implementation of the Eclipse Presentation Factory SPI.
The styled presentation factory is theme-aware, which means that it leverages the style manager to drive
the look and feel. You can use the SPI to format the user interface framework by providing the style to
use for the trim for view parts, editor parts, the sidebar, toolbars, menu bars, and status bars. The styled
presentation factory uses many of the custom widgets in the SWTEX custom widget collection, including
the SViewForm, SViewStack, SToolBar and STabFolder widgets.
You cannot implement the Styled Presentation Factory using an API. Instead you contribute it to the
Eclipse platform using the org.eclipse.ui.presentationFactories extension point. This extension is provided
in the com.ibm.rcp.ui plug-in.
To customize the workbench, perform the following steps:
1. Initialize the Styled Presentation Factory by declaring the StyledPresentationFactory class in a factory
tag within the org.eclipse.ui.presentationFactories extension tag.
For example:
<extension point="org.eclipse.ui.presentationFactories">
<factory
class="com.ibm.rcp.ui.presentations.StyledPresentationFactory"
id="com.ibm.rcp.ui.presentations.StyledPresentationFactory"
name="Styled Presentation">
</factory>
</extension>
2. Enable the Styled Presentation factory using the Eclipse preference presentationFactoryId. Specify the
presentation factory class that you want to use with the following key:
org.eclipse.ui/presentationFactoryId=
com.ibm.rcp.ui.presentations.StyledPresentationFactory
Custom widgets:
The SWidgets are a collection of custom SWT widgets that are designed for use in Lotus Expeditor
applications.
The custom widget library embraces the SWT programming model and enables applications to mix and
match these widgets with standard SWT widgets. SWidgets extend the capabilities for customizing the
look and feel of controls provided by standard SWT widgets. SWidgets, like all standard custom widgets
based on SWT, subclass the Canvas class and register all the necessary event handlers. For SWidgets that
are similar to SWT controls, such as the SMenu and SMenuItem classes, the SWidget API is compatible
with the API of the equivalent SWT control. The advantage of using SWidgets is that the look and feel of
controls created using SWidgets is customizable. SWidgets are customizable because they use a paint
delegate pattern that enables all the rendering for the widgets to be done by a pluggable delegate object.
The paint delegate objects act like the skin of the control, and are referred to as skins. In fact, the S in
front of the widget class names stands for skinnable.
The library contains the following SWidgets:
v SButton – Represents a button. It is used, for example, as the control that a user clicks to activate the
launch menu. The SButton custom widget provides support for customizable rendering and some
alignment options that the standard SWT button does not support.
Developing applications 167
v SMenu – Represents the launch menu and other menus in the client. The SMenu custom widget
provides support for customizable rendering. It also provides functionality that the standard SWT
menu does not provide, such as support for context menus. The SCoolBar supports tearing out and
floating menu items or docking SMenu items in a SCoolBar in the form of a floating SToolBar.
v SToolbar and SCoolbar – Represent the main window toolbar. A SCoolbar is a container widget that
contains instances of SToolbars that may be repositioned or floated. The SToolbar widget also
represents the toolbar inside view parts and editor parts in the workbench. A toolbar that displays in a
view or editor is commonly referred to as an actionbar. The widgets provide support for a
customizable rendering and functionality that the standard SWT toolbar and coolbar do not. For
example, the SCoolBar supports tearing out and floating toolbars. The SToolBar and SCoolBar widgets
both have corresponding Jface contribution and wrapper classes. The API for these classes is very
similar to the API for the standard Jface manager and contribution classes.
v STabFolder – Represents a “grouped tab.” Grouped tabs allow for child tabs to be consolidated under a
parent tab and made accessible through a pop-up menu. The STabFolder is used in the main launcher
user interface and to represent tabs in many other places in the user interface.
v STable – Represents a table that structures the data that displays in views or editors. The custom table
widget provides support for a customizable rendering and functionality that the standard SWT table
does not provide.
v SViewForm and SViewStack – The SViewForm widget represents the area surrounding sidebar and
stand–alone views within a perspective. The SViewStack widget represents the container for a
collection of sidebar panels and enables you to resize and collapse the stack horizontally.
The custom widgets are provided in the com.ibm.rcp.swtex plug-in, which has dependencies on the
org.eclipse.swt plug-in. The library also contains a set of JFaceEX action classes.
Custom toolbar:
The Lotus Expeditor provides custom toolbar APIs that you can use to implement a toolbar and
contribute items to it in the user interface of a Lotus Expeditor application.
The custom toolbar provides the following features:
v Renders the toolbar skin, which means it formats the toolbar to make its style consistent with the style
defined for the application it displays in.
v Manages drag and drop of items within the toolbar.
v Saves and loads the last state of toolbar items using the Eclipse memento pattern, which uses the
saveState and restoreState methods to remember the position of each toolbar item when the user shuts
down the client so that the same toolbar items display when the user restarts it.
You can contribute Eclipse-based or custom controls to the main toolbar statically or dynamically. If you
contribute a toolbar item statically, when the user or administrator uninstalls the associated application
from the client, the toolbar item is not removed from the ActionSets extension point unless the user or
administrator removes it explicitly. The toolbar item will not be displayed because the view it is
associated with is no longer available, but it does add unnecessary clutter to the ActionSets extension
point. Contributing toolbar items dynamically allows for more flexibility. If a user or administrator
uninstalls an application, the dynamically-added menu item associated with it is automatically removed
from the extension point as well. Adding items dynamically takes advantage of the plug and play
capabilities of applications built for the Lotus Expeditor.
The com.ibm.rcp.swtex plug-in contains classes that are equivalent to those provided in the Eclipse
org.eclipse.swt plug-in, but that provide in addition support for rendering the skin of the control
appropriately. This plug-in provides the following APIs:
v com.ibm.rcp.swt.widgets.skins.SCoolBar
v com.ibm.rcp.swt.widgets.skins.SCoolItem
v com.ibm.rcp.swt.widgets.skins.SToolBar
168 Lotus Expeditor: Developing Applications for Lotus Expeditor
v com.ibm.rcp.swt.widgets.skins.SToolItem
The com.ibm.rcp.jfaceex plug-in contains classes that are equivalent to those provided in the Eclipse
org.eclipse.jface plug-in, but that provide in addition support for rendering the skin of the control
appropriately. This plug-in provides the following APIs:
v com.ibm.rcp.jface.action.SCoolBarManager
v com.ibm.rcp.jface.action.SToolBarManager
v com.ibm.rcp.jface.action.SToolBarContributionItem
v com.ibm.rcp.jface.action.SActionContributionItem
v com.ibm.rcp.jface.action.SControlContribution
Contributing Eclipse-based controls to the toolbar statically:
You can add pre-defined Eclipse buttons, such as push, radio, toggle or pulldown buttons to the main
toolbar statically by extending the standard Eclipse ActionSet or ActionSetPartAssocation extension
points.
You can also enable or disable the controls or toolbar items in the main toolbar and show or hide a group
of items in the main toolbar
To add an Eclipse-based control to the main toolbar, complete the following steps:
1. Contribute the control to the ActionSets extension point in the plugin.xml file for the application. For
example:
<extension point="org.eclipse.ui.actionSets">
<actionSet
label="Sample Action Set"
visible="true"
id="sample.toolbar.actionSet">
<action
label="Sample Action"
icon="icons/sample.gif"
class="sample.toolbar.actions.SampleToolBarAction"
toolbarPath="END_GROUP"
id="sample.toolbar.actions.SampleToolBarAction">
</action>
</actionSet>
</extension>
2. Associate the action set with a specific view by extending the actionSetPartAssociation extension
point. For example:
<extension point="org.eclipse.ui.actionSetPartAssociations">
<actionSetPartAssociation targetID="sample.toolbar.actionSet">
<part id="sample.ui.view"/>
<part id="sample.ui.view2"/>
</actionSetPartAssociation>
</extension>
When one of the associated views, the sample.ui.view or sample.ui.view2, is activated, the action set you
defined displays in the main toolbar.
Contributing custom controls to the main toolbar statically:
You can add any custom control that implements the com.ibm.rcp.jface.actions.ISContributionItem
interface to the main toolbar and enable or disable it statically by extending the ControlSet extension
point.
You can use the ControlSet extension point to add controls to the status bar as well.
Developing applications 169
To add a custom control to the main toolbar, complete the following steps:
1. Contribute to the ControlSet extension point in the plugin.xml file for the application. For example:
<extension point="com.ibm.rcp.platform.controlSets">
<controlSet visible="true" id="com.ibm.app.toolbar.controlset">
<control
toolBarPath="SampleGroup"
id="sample.FontFaceDropdownControlContribution"
class="sample.FontFaceDropdownControlContribution">
</control>
</controlSet>
</extension>
2. Create a corresponding class called sample.FontFaceDropdownControlContribution that extends the
com.ibm.rcp.jface.actions.SControlContribution class. The
com.ibm.rcp.jface.actions.SControlContribution class implements the
com.ibm.rcp.jface.actions.ISContributionItem interface. For example:
public class FontFaceDropdownControlContribution extends
com.ibm.rcp.jface.actions.SControlContribution {
/**
* the default constructor.
*/
public FontFaceDropdownControlContribution () {
super();
}
/* (non-Javadoc)
* @see com.ibm.rcp.jface.action.SControlContribution#createControl(
* org.eclipse.swt.widgets.Composite)
*/
protected Control createControl(Composite parent) {
// create a font face combo box in a tool item.
Combo combo = new Combo(parent, SWT.READ_ONLY);
for (int i = 0; i < faces.length; i++) {
combo.add(faces[i]);
}
return combo;
}
}
Contributing items to a toolbar dynamically:
You can add custom controls, such as coolitems or a coolbar to the main toolbar dynamically and enable
or disable them dynamically by using the Eclipse dynamic extension API.
Before completing the steps below, you must complete the steps defined in Completing custom controls to
the main toolbar statically.
To contribute toolbar items to the main toolbar dynamically, complete the following steps:
1. Call the addContribution method of the org.eclipse.core.runtime.IExtensionRegistry interface provided
by the Eclipse dynamic extension API in the org.eclipse.equinox.registry plug-in to add an extension
dynamically.
For example, the following code adds a custom fontface dropdown control to the main toolbar:
public void addFontFaceDropdownControl() {
StringBuffer sb = new StringBuffer();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
sb.append("<plugin>");
sb.append("<extension point=\"com.ibm.rcp.platform.controlSets\"");
sb.append(" id=\"com.ibm.app.toolbar.controlsets\">");
sb.append("<controlSet ");
sb.append(" visible=\"true\"");
170 Lotus Expeditor: Developing Applications for Lotus Expeditor
sb.append(" id=\"com.ibm.app.toolbar.controlset\">");
sb.append("<control");
sb.append(" id=\"sample.FontFaceDropdownControlContribution\"");
sb.append(" toolbarPath=\"ToolSetId/SampleToolGroup\"");
sb.append(" class=\"sample.FontFaceDropdownControlContribution\"/>");
sb.append("</controlSet>");
sb.append("</extension>");
sb.append("</plugin>");
try {
addExtension(sb.toString());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
void addExtension(String xmlsrc) throws UnsupportedEncodingException {
// use Eclipse Dynamic Extension API
IExtensionRegistry reg = RegistryFactory.getRegistry();
Object key = ((ExtensionRegistry)reg).getTemporaryUserToken();
Bundle bundle = Activator.getDefault().getBundle();
IContributor contributor =
ContributorFactoryOSGi.createContributor(bundle);
ByteArrayInputStream is =
new ByteArrayInputStream(xmlsrc.getBytes("UTF-8"));
reg.addContribution(is, contributor, false, null, null, key);
}
Note: See the Eclipse API javadoc for the org.eclipse.core.runtime.IExtensionRegistry#addContribution
for more information on the addContribution method.
2. Call the removeExtension method of the org.eclipse.core.runtime.IExtensionRegistry interface to
remove the custom control from the toolbar.
For example, the following methods remove the custom fontface dropdown control from the toolbar:
public void removeFontFaceDropdownControl()() {
// specify extension point and extension id
removeExtension("com.ibm.rcp.platform.controlSets",
"com.ibm.app.toolbar.controlsets");
}
void removeExtension(String extensionPointId, String extensionId) {
// use Eclipse Dynamic Extension API
IExtensionRegistry reg = RegistryFactory.getRegistry();
Object token = ((ExtensionRegistry)reg).getTemporaryUserToken();
IExtension extension =
reg.getExtension(extensionPointId, extensionId);
reg.removeExtension(extension, token);
}
Note: See the Eclipse API javadoc for the
org.eclipse.core.runtime.IExtensionRegistry#removeExtension for more information on the
removeExtension method.
Hiding or displaying items in the toolbar:
You can display or hide a group of items, both custom controls and standard buttons, in the main toolbar
using the standard Eclipse Activities API.
To hide or display a toolbar item in the toolbar, complete the following steps:
1. In the plugin.xml file, define a toolbar activity and associate the toolbar activity with a control set ID.
For example, the following code defines a toolbar activity called ″sample.toolbar″ and associates it
with the control set ID, ″com.ibm.rcp.platform.controlSet″:
Developing applications 171
<extension
point="org.eclipse.ui.activities">
<activity name="Sample ToolBar Activity"
id="sample.toolbar">
</activity>
<activityPatternBinding
activityId="sample.toolbar"
pattern=""sample\.dynamic\.toolbar\.plugin/
com\.ibm\.app\.toolbar\.controlset">
</activityPatternBinding>
</extension>
2. In the code, add a part listener to enable or disable the toolbar activity that displays the toolbar item.
For example:
IPartListener partListener = new IPartListener() {
public void partActivated(IWorkbenchPart part) {
if (part != null) {
IWorkbenchActivitySupport workbenchActivitySupport =
PlatformUI.getWorkbench().getActivitySupport();
IActivityManager activityManager =
workbenchActivitySupport.getActivityManager();
Set enabledActivityIds =
new HashSet(activityManager.getEnabledActivityIds());
if (part.getTitle().equals("Sample UI View") != -1) {
// add toolbar activity.
enabledActivityIds.add("sample.toolbar");
}
else {
// remove toolbar activity.
enabledActivityIds.remove("sample.toolbar");
}
workbenchActivitySupport.setEnabledActivityIds(enabledActivityIds);
}
}
public void partBroughtToTop(IWorkbenchPart part) {
}
public void partClosed(IWorkbenchPart part) {
}
public void partDeactivated(IWorkbenchPart part) {
}
public void partOpened(IWorkbenchPart part) {
}
};
public void setupControlSetActivities() {
/** create an UI thread to add the partListener to disable the
* toolbar activity.
*/
UIJob job = new UIJob("Setup controlSet Activities") {
public IStatus runInUIThread(IProgressMonitor monitor) {
IWorkbenchWindow window =
PlatformUI.getWorkbench().getActiveWorkbenchWindow();
window.getPartService().addPartListener(partListener);
return new Status(IStatus.OK,
Activator.PLUGIN_ID,
IStatus.OK,
"Setting up controlSet Activities complete",
null);
}
};
job.schedule();
}
Custom table control:
172 Lotus Expeditor: Developing Applications for Lotus Expeditor
The custom table control uses the Eclipse JFace model, view, controller (MVC) framework to present data
to users in a clearly structured format.
The custom table control supports generic SWT widgets. It provides interfaces and listeners to support an
asynchronous data loading mechanism. An asynchronous data loading mechanism enables the table to
load data only on an as-needed basis, improving the performance and reducing the memory cost
normally associated with retrieving data. The custom table control has the following features:
v Supports flat list and vertical layouts; supports a tree model.
v Provides a customizable user interface. It enables users to highlight text strings, edit cell content, add
alternating row background colors, color-code text, show, hide, or resize columns, and drag and drop
columns or data.
v Enables users to quick find data.
v Supports nested tables.
v Its widgets are skinnable, which means the color and font of the table, table body, and header match
the style of the application if the application is using a custom theme.
v Supports gutter selection (used in Notes) or Ctrl+ key selection (used in standard operating systems).
v Provides listeners to report user selections.
v Provides widgets that support the SWT accessibility APIs.
v Provides bidirectional support.
The Custom Table Control is comprised of the following parts:
v Content Provider
v Table Viewer
v Table widgets
Content provider:
The Content Provider is a set of interfaces that are implemented by applications to supply data to the
table control.
The table control has a paging mechanism that it uses to limit the amount of data it displays to one
subset of the available data at a time. It maintains a virtual mapping of the data divided into multiple
pages. You can specify the amount of data for the table to display per page by passing the following
parameters to the provider.
v count – Defines how many data items to load into memory at a time; it uses an integer value, such as
50.
v offset – Defines the first data element to load into memory using an index value, such as 0.
For example, a flat list table with a provider that is passed an offset parameter of 0 and a count
parameter of 50, would display data elements 1 through 49. If the entire data set included 75 items, items
50 through 75 would be displayed on a second page.
The following types of content providers are available:
v IPagedContent – Provides access to a content provider that returns one page of data at a time. The
getPage method returns the elements with a given offset and count. It also provides find methods that
enable users to search the data items for a specific data element.
v ITreePagedContent – Extends the IPagedContentProvider. It provides access to a content provider that
checks if the data item has children and preserves its relationship to those child objects and other
objects when it displays the data item.
Creating a hierarchical or categorized table:
Developing applications 173
You can create a table that has entries that can be categorized, or that display hierarchically, such as the
documents and responses that comprise a thread in a discussion view, by using a tree provider to define
the structure of the table control.
The tree label provider that you implement creates labels that can do either of the following:
v Span more than one column.
v Display a +/- sign to indicate that the item is expandable and has child documents that can be
displayed.
Adding listeners to expandable table rows enables you to track changes to rows, which can be expanded
in the following ways:
v Users can click the +/- sign displayed beside an entry that is hierarchical to expand or collapse the
row.
v Applications can programmatically expand or collapse a row.
To create a tree table:
1. Implement the ITreePagedContentProvider interface to indicate that you want the table to be
implemented as a tree control that has expandable columns. The ITreePagedContentProvider interface
inherits from the IPagedContentProvider interface.
2. Implement the IExpandTableLabelProvider.
For example:
public int columnSpan(Object element, int columnIndex) {
// TODO Auto-generated method stubif (arg0 instanceof GroupedMetadata){
if (element instanceof Categories){
switch(columnIndex){
case 0:
return _viewer.getTable().getColumnCount();
}
}
return 0;
}
public boolean isExpandable(Object element, int columnIndex) {
// TODO Auto-generated method stub
if (element instanceof Categories){
switch(columnIndex){
case 03:
return true;
}
}
return false;
}
3. Register a listener to the table that listens for the expansion or collapse of a table row.
For example:
Document doc = (Document)root;
//expand this level
this.expandItem(doc, doc.needExpanded());
//The application can monitor the event when a table item which has
//children is expanded or collapsed.
table.addTreeListener(new TreeListener(){
public void treeCollapsed(TreeEvent e) {
// TODO Auto-generated method stub
System.out.println("treeCollapsed(TreeEvent e) : " + e.toString());
TableItem item = (TableItem)e.item;
_viewer.refresh(item.getData(), true);
}
public void treeExpanded(TreeEvent e) {
174 Lotus Expeditor: Developing Applications for Lotus Expeditor
// TODO Auto-generated method stub
System.out.println("treeExpanded(TreeEvent e) : " + e.toString());
TableItem item = (TableItem)e.item;
item.setText(0, " 3 Messageing in threading " + item.getText(3));
// item.setImage(0, TestView.unread_descriptor);
}
});
Optimizing table performance:
You can decrease the time it takes to initially display data in a table by creating a table that pages and
trims the data it displays.
Paging data means that instead of showing all the rows of the table when a user first opens it, you
specify a number of rows to display per page and enable users to page through the table. Limiting the
number of rows per page enables the page to display more quickly because it has less data to retrieve
initially. It also makes the table easier for users to read and use. Trimming data means that the table does
not store any data in memory that does not need to be displayed immediately. For example, if an entry
has child documents, but the entry is collapsed, a trimmed table does not store the child entries in
memory, but instead retrieves them at the time that the row is expanded. By default, the table control
stores all data related to the currently displayed page in memory.
To optimize table performance:
1. Implement the IPagedContentProvider interface.
2. Use the void setItemTrimming(boolean enabled) method to enable automatic item trimming in virtual
tables.
Table viewer:
The Table viewer is a viewer that interacts with the table widgets.
It is responsible for associating data with the widgets that manipulate the data. It updates the widget
labels, adds or removes data from the table, and searches and sorts elements in the table.
The table viewer requests data from the provider and retrieves the data elements using an asynchronous
loading thread. The AsyncStructureViewer is the super class for the TableViewer.
Adding and removing table entries:
Use the methods in the Table Viewer to add and remove entries to the table.
The methods add or remove the table entries in-memory, but do not add or remove them from the data
source. You can register listeners to monitor adding and removing events, and then add or remove the
corresponding entries from the data store.
1. Create the data object you want to add to the table. For example:
Mail mail3 =new Mail();
mail3.setAuthor("New Data-3");
mail3.setSubject("Add Row form Menu");
mail3.setSendDate(new Date(System.currentTimeMillis()));
mail3.setSize(20);
mail3.setParent((Mail)DataUtil.getDatas().get(3));
2. Use the table viewer add() method to add the object to the table. For example:
TestView.getInstance().getViewer().add(mail3);
3. Use the table viewer remove() method to remove the object from the table. For example:
TestView.getInstance().getViewer().remove(mail3);
Developing applications 175
Implementing inline editing:
You can create cells that are editable by users by implementing the ICellModifier and CellEditor classes in
the TableViewer.
The ICellModifier interface indicates whether a cell can be changed, and if it can be, enables users to
provide a value for the cell. The interface has the following methods:
v public boolean canModify(Object arg0, String arg1) -- arg0 is the data of the row which contains the
cell, arg1 is the column ID of parent column of the cell. The returned value tells table if value of this
cell can be modified.
v public Object getValue(Object arg0, String arg1) -- arg0 and arg1 has same meaning as above method.
This method returns the value of specified cell.
v public void modify(Object arg0, String arg1, Object arg2) -- arg0 is the table item which contains the
cell, and arg1 is the column id of parent column of the cell, arg2 is the value to be changed to.
The CellEditor class is a JFace class that you must implement to keep the table JFace-compatible.
To implement inline editing, perform the following steps:
1. Implement the ICellModifier interface for the table viewer.
2. Call the setCellModifier method.
3. Use the setCellEditor() method to add an inline editor to the cell.
4. To disable an inline editor, pass a null parameter to the setCellEditor() method.
_viewer.setCellModifier(new ICellModifier(){
public boolean canModify(Object arg0, String arg1) {
return true;
}
public Object getValue(Object arg0, String arg1) {
if(arg0 instanceof Mail){
if(arg1.equalsIgnoreCase("ID_SUBJECT")){
return ((Mail)arg0).getSubject();
}
}
return null;
}
public void modify(Object arg0, String arg1, Object arg2) {
System.out.println("Please modify it");
}
});
CellEditor[] editors=new CellEditor[tableColCount];
for(int k=0;k<editors.length;k++){
editors[k]=null;
}
//disable the cell editor
editors[1]=new TextCellEditor(_viewer.getTable().getBody());
if(isEnableEditor)
_viewer.setCellEditors(editors);
else
_viewer.setCellEditors(null);
Adding listeners to tables:
You can pass a parameter called eventType to the addCellListener() method of the table control to register
a listener for an SWT event.
176 Lotus Expeditor: Developing Applications for Lotus Expeditor
Table 11. Adding listeners to table cells
Table widget How to add a listener to it
Cell Specify a col parameter for the SWT.Selection event. The col parameter
identifies the column in which the listener should listen for events.
Row (all cells in) Do not specify a col parameter.
Table (all cells in) Specify a col parameter with the value -1.
First column (all cells in) Specify a col parameter with the value 0.
You can use the element parameter to set up a condition under which the event should be added to the
cell. Use it to compare an object passed into the method with the object currently stored in the table cell.
If the object in the cell which gets the mouse event is equal to the object you pass into the method, the
corresponding cell listener is invoked. If the parameter is null, the table viewer ignores the condition and
adds the event to the corresponding cells.
You can use the interface named public boolean equals(Object obj) of the Object class to facilitate the
comparison of the objects. By default, the objects in the Object class are equal while the instances of those
objects are equal in memory.
1. Implement a table viewer.
private ITableViewer _viewer;
2. Implement the addCellListener() method.
public void addCellListener(int eventType, int col, Object element,
ICellListener listener)
The following example adds a listener to a column:
private ITableViewer _viewer;
/**
* Adds a listener to the first column and ignores the object parameter.
*/
_viewer.addCellListener(SWT.MouseHover, 0,null,new ICellListener(){
public void cellResponse(CellEvent e) {
switch(e.type){
case SWT.MouseHover:
System.out.println("the first cell detect: mouse hover on ");
break;
}
}
});
The following example adds a listener to a row:
private ITableViewer _viewer;
/**
* Adds a table row listener by setting the column value to -1.
* All the cells can be selected because cell selection is enabled.
*/
_viewer.addCellListener(SWT.Selection, -1,null,new ICellListener(){
public void cellResponse(CellEvent e) {
}
}
});
Developing applications 177
The following example tests whether the author of the mail message, which displays in the first column,
is Dave, and if it is, adds a listener to it:
/**
* Add the listener to monitor the first column cell but it’s author must
* be “Dave.â€
*/
Mail mail = new Mail();
mail.setAuthor("Dave");
_viewer.addCellListener(SWT.MouseUp, 0,mail,new ICellListener(){
public void cellResponse(CellEvent e) {
switch(e.type){
case SWT.MouseUp:
System.out.println("A user has clicked a cell with author equal to
Dave.");
}
}
});
…
/*In the Mail Object class*/
/**
* The method to compare the mail’s author with given object
*
*/
public boolean equals(Object obj){
if(obj instanceof Mail){
Mail mail=(Mail)obj;
return author.equals(mail.getAuthor());
}
return false;
}
// turn off the cell selection, mouse click will cause row to be selected.
table.disableCellSelection();
Adding controls to tables:
You can display controls, such as a Link, CheckBox, and ProgressBar control in the table cells of tables
built using the custom table control.
To add a control to a table cell, perform the following steps:
1. Specify a control layout for the control you want to add to the table cell so that it displays in the
correct position in the table. For example:
final ControlLayoutData layoutData = new ControlLayoutData();
layoutData.leftMargin =5;
layoutData.topMargin =5;
layoutData.rightMargin=8;
layoutData.bottomMargin=5;
final ControlLayoutData linkLayoutData =new ControlLayoutData();
linkLayoutData.bottomMargin =3;
2. Implement a Control provider, which implements the IControlProvider interface, to add a table
widget to a table cell. For example:
_viewer.setControlProvider(new IControlProvider(){
3. Depending on the element type of the cell contents, activate different controls for different columns in
the table row. For example, the following code checks to see if the cell contains a mail element. If it
178 Lotus Expeditor: Developing Applications for Lotus Expeditor
does, it then checks to see which column the current cell is in. If it’s in column 3, it adds a link
control to the cell. If it’s in column 2, it adds a button control to the cell. If it’s in column 5, it adds a
progress bar control to the cell.
public Control getCellControl(Object element, int column) {
if(element instanceof Mail){
Mail mail = (Mail)element;
if(column==3 && mail.getSubject().toLowerCase().indexOf("<a")
!= -1 && mail.getSubject().toLowerCase().indexOf("</a>") != -1){
final Link link = new Link(table.getBody(),
SWT.NO_FOCUS|SWT.NO_BACKGROUND);
link.setText(mail.getSubject());
link.setBackground(table.getBody().getBackground());
link.setLayoutData(linkLayoutData);
return link;
}
// the first cell
if(column ==2){
Button btn = new Button(table.getBody(),
SWT.CHECK|SWT.NO_FOCUS|SWT.NO_BACKGROUND);
btn.setBackground(table.getBody().getBackground());
btn.setLayoutData(layoutData);
return btn;
}
if(column == 5){
ProgressBar bar= new ProgressBar(table.getBody(),
SWT.INDETERMINATE|SWT.SMOOTH);
return bar;
}
}
return null;
}
});
Creating a table with a vertical layout:
A table with a vertical layout displays all the text of a row by forcing it to wrap and display in the
subsequent row.
A vertical layout is useful for displaying portrait-style documents within the Preview window, for
example. Use the setVerticalLayoutData() method of the STable class to set the table to use a vertical
layout. Using a vertical layout also enables you to apply different styles to different rows.
1. Construct a verticalLayoutData object that provides column layout information.
2. Call the setVerticalLayoutData() method of the STable class and pass to it the verticalLayoutData
object you constructed in Step 1.
3. Set the value of the setVerticalLayoutMode() method to true.
For example:
VerticalLayoutData layoutdata = new VerticalLayoutData();
TableColumn[] cols = table.getColumns();
//add these column into row 0
layoutdata.add(cols[0],0); //threading column
layoutdata.add(cols[3],0); //important
layoutdata.add(cols[4],0); //attachment
// next row, the column 1 align to column 3
layoutdata.setAlignColumn(cols[3]);
//add these column into row 1
layoutdata.add(cols[1], 1); //author
Developing applications 179
layoutdata.add(cols[2], 1); //date
cols[2].setAlignment(TableColumn.HORZ_ALIGNMENT_RIGHT); //right align
layoutdata.setWrapWidth(600); //width
table.setVerticalLayoutData(layoutdata); // set layout data
table.setVerticalLayoutMode(true); // turn on vertical layout
Another example:
VerticalLayoutData layoutdata = new VerticalLayoutData();
TableColumn[] cols = _table.getColumns();
layoutdata.add(cols[0],0); //add col 0 to the first row
layoutdata.add(cols[1], 0); //add col 1 to the first row
layoutdata.add(cols[2], 1); //add col 2 to the second row
Customizing tables:
You can customize a table by aligning the content of table columns and applying a style to the table
border.
To customize a table, complete the following steps:
1. Use the setAlignment() method on the table object to define how to align the content of the table
columns. Choose an alignment style from the following list of options:
v HORZ_ALIGNMENT_CENTER
v HORZ_ALIGNMENT_LEFT
v HORZ_ALIGNMENT_RIGHT
For example, the following code sets the alignment of the content of the columns to be centered:
tableCols[i].setAlignment(TableColumn.HORZ_ALIGNMENT_CENTER);
2. Use the set TableBorderStyle() method on the table object to apply a border style to the table. Choose
a table border style from the following list of options:
v TABLE_BORDER_NONE
v TABLE_BORDER_RECT
v TABLE_BORDER_ROUND
For example, the following code applies rounded borders to the table:
table.setTableBorderStyle(STable.TABLE_BORDER_ROUND);
Highlighting table text:
You can highlight specified strings in a table view built using the custom table control. Highlighted text
is useful in identifying instances of a text string in the documents returned by a search.
To implement text highlighting in a table, perform the following steps:
1. Specify the color or colors to use to highlight the text by calling the setHightlightingColor() method. If
you specify more than one color and the highlighted string contains more than one word, the first
word uses the first color specified and the subsequent words use the subsequent colors. For example:
table.setHightlightingColor(new Color[]{
Display.getCurrent().getSystemColor(SWT.COLOR_CYAN),
Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GREEN),
Display.getCurrent().getSystemColor(SWT.COLOR_DARK_YELLOW),
Display.getCurrent().getSystemColor(SWT.COLOR_DARK_RED)});
2. Enable highlighting for the table. For example:
table.setHighlighting(true);
3. Provide the following parameters to the highlighting method to specify a hashmap which contains a
group of words to highlight.
180 Lotus Expeditor: Developing Applications for Lotus Expeditor
v keys -- Contains a hashmap of strings to search for.
v isHiding -- Boolean value that determines whether or not to highlight words that do not match the
strings in the hashmap specified by the keys parameter. If true, words that do not match the strings
in the keys parameter are not highlighted.
v ignoreCase -- Boolean value that determines whether to highlight only words that have the same
capitalization as the strings specified by the keys parameter. If true, words that have the same
characters are returned even if they use different capitalization.
For example:
table.highlighting(keys, false, true);
Alternating the color of rows in the table:
You can add alternating background colors to the rows in a table view.
To alternate the background color of a table view, perform the following step:
Define the color of the alternating row using the setAlternateBackgroundColor() method. For example:
table.setAlternateBackgroundColor(
table.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND),
Item_Alter_bgColor, true);
Nesting tables:
You can display a table widget as the child of another table widget.
The nested table, the table that displays within the parent table, is associated with a table item. Its size is
set by the application and its location is adjusted by the parent table when a user scrolls through it. You
cannot create a nested table that is larger than the parent table. If you do, the size of the nested table is
automatically reduced to fit within the parent table.
Use the following constructor of the STable class to make a Table widget that displays within another
table.
public STable(STableItem parent, int style)
This constructor method establishes the relationship of a nested table, a parent table item, and the parent
table.
For example:
table = new STable(_parentItem,
SWT.TITLE|SWT.SHADOW_IN | SWT.MULTI | SWT.HIDE_SELECTION );
Adding a titlebar to a table:
The titlebar of a table contains title images and title text and displays at the top of a table control.
To add a titlebar to a table, perform the following step:
Do one of the following:
v To add a titlebar to a new table, when constructing the table widget, pass SWT.TITLE as the style
parameter.
table = new STable(_parentItem,
SWT.TITLE|SWT.SHADOW_IN | SWT.MULTI | SWT.HIDE_SELECTION );
v To add a titlebar to an existing table, programmatically construct the titlebar using the setTitle(),
setImage(), and setFont() methods of the table class.
Developing applications 181
table.getTitleBar().setTitle(title);
table.getTitleBar().setImage((ImageDescriptor.createFromFile(
TestView.class, "Minus.gif")).createImage());
table.getTitleBar().setFont(new Font(Display.getCurrent(),
"Arial", 12, SWT.BOLD));
final STable _table = table;
table.getTitleBar().addImageClickedListener(new Listener() {
public void handleEvent(Event event) {
System.out.println("child tableimage clicked");
_table.setVisible(false);
//_table.dispose();
_parentItem.setExpanded(false);
_parentItem.justify();
_parentTable.justify();
_parentTable.redraw();
}
});
Implementing search:
Applications can register quick find listeners on a table. A quick find listener is invoked when a user
types a string into the quick find field. The listener retrieves the user’s input and then invokes the search
method provided by the table viewer.
The table invokes the quick find listener when a user inputs the first character and passes that first
character to it. The application then reacts and displays the supporting quick find user interface, such as
a dialog box that contains a text field, into which users can finish entering the string they want to search
for. The listener must pass that first character to the user interface control that displays the full search
string.
1. Implement the IQuickFindListener interface.
table.addQuickFindListener(new IQuickFindListener(){
2. Retrieve the first character of the string that the user enters in the quick find field using the
FindStartWithListener listener. For example:
public void quickFind(String findString) {
FindStartsWithListener listener = new FindStartsWithListener() {
public void FindStartsWith(String inputText) {
if (inputText.length() > 0) {
3. To perform a search, set the FindOptions object to the current table viewer. For example:
TableViewer viewer = getViewer();
FindOptions findOp = new FindOptions();
viewer.setFindOptions(findOp);
4. In the FindOptions object, indicate the direction of the search. For example:
findOp.setSearchDirectionDown(false);
5. Use the quickFindInColumn() method to retrieve the first table item which contains the search string.
Identify the column in which to search using the columnId parameter. The findString parameter
identifies the string to search for. The filter parameter is optional and indicates whether or not to
display items that do not contain the string specified in the findString parameter. If filter is set to true,
the table items that do not contain the findString are hidden. If it is set to false, they are displayed.
For example:
TableItem item = null;
item = viewer.quickFindInColumn("ID_AUTHOR", inputText);
if (item != null) {
ISelection selection = new StructuredSelection(item.getData());
viewer.setSelection(selection, true);
}
182 Lotus Expeditor: Developing Applications for Lotus Expeditor
}
}
};
6. Call the FindStartsWithDialog() method to display the results. For example:
new FindStartsWithDialog(getSite().getShell(), findString, listener);
}
});
For example:
table.addQuickFindListener(new IQuickFindListener(){
public void quickFind(String findString) {
FindStartsWithListener listener = new FindStartsWithListener() {
public void FindStartsWith(String inputText) {
if (inputText.length() > 0) {
TableViewer viewer = getViewer();
FindOptions findOp = new FindOptions();
viewer.setFindOptions(findOp);
findOp.setSearchDirectionDown(false);
TableItem item = null;
item = viewer.quickFindInColumn("ID_AUTHOR", inputText);
if (item != null) {
ISelection selection = new StructuredSelection(item.getData());
viewer.setSelection(selection, true);
}
}
}
};
new FindStartsWithDialog(getSite().getShell(), findString, listener);
}
});
Launcher:
The launcher is a button control that displays in the workbench area below the main menu and is labeled
Launch. When a user clicks the Launch button, a hierarchical drop-down list of items displays. Users can
click an item in the list to start an application, open an application document, or open a bookmark.
The WorkbenchWindowAdvisor on the client does the work of instantiating the controls used to represent
the launcher in the user interface. The default personality provided in the
com.ibm.rcp.platform.personality plug-in implements the DefaultWorkbenchWindowAdvisor. The
DefaultWorkbenchWindowAdvisor creates a SButton widget to represent the Launch button. It
instantiates the LauncherManager and passes a reference for it to the button. It then schedules a Job to
build the LauncherRegistry. The LauncherRegistry builds a list of launcher contributions. Upon
completion of that job a UIJob is scheduled to fill the Launcher with the contributions resulting from the
LauncherRegistry build.
The following classes are a few of the classes provided by the package com.ibm.rcp.jface.launcher:
v LauncherManager - The class that is used to manage launcher contributions and the UI of those
contributions. It uses SMenuManager to render launcher contributions in a SMenu and uses a given
IBookmarkProvider for dynamically fetching a bookmark provider’s content. The LauncherManager is
a singleton enforced by making the constructors private and providing a static
getInstance(WorkbenchWindow) method for obtaining the singleton LauncherManager for a given
WorkbenchWindow.
v LauncherContributionItem – This class is the base class that all contributions that to the Launcher must
inherit from in order to be launched. The exceptions are a Separator or a GroupMarker.
Developing applications 183
v EnvironmentVar - Used by the NativeProgramLauncherContributionItem to define system environment
variable for configuring the environment to run a native program.
The following class is provided by the package com.ibm.rcp.jface.action:
v SMenuManager - A subclass of Eclipse’s MenuManager that has been enhanced to use SMenu rather
than Menu.
You can populate the launcher either dynamically through extensions or through direct calls into the
Launcher public APIs located in the platform personality bundle.
If you are using WebSphere Portal to create a Portal-based applications that you are pushing to the client
through the Composite Application Infrastructure, you can add the page to the launcher by specifying the
following page parameter: com.ibm.rcp.launcher=true.
Populating the launcher:
You can add items to the launcher dynamically or programmatically.
To contribute items to the launcher dynamically, you can implement the following extension points:
v launcherSet -- Contributes items to the extension point registry when the client starts and displays all
items in the launcher drop-down list.
v bookmarkProvider -- Contributes items to the launcher drop-down list lazily. The bookmarked items
are not loaded in memory until a user accesses the folder containing the items from the launcher
drop-down list. The list of bookmarks are updated when items are added or removed or when the
bookmark provider sends notification of some other change.
You can also contribute items to the launcher programmatically. The client provides the following classes
that are derived from the LauncherContributionItem class:
v NativeProgramLauncherContributionItem – Creates a link in the launcher to a native application.
v PerspectiveLauncherContributionItem – Creates a link in the launcher to a standard client application.
v UrlLauncherContributionItem – Creates a link in the launcher to a specified URL.
You can also create a custom implementation of a LauncherContributionItem-derived class.
If you are using WebSphere Portal to create a Portal-based applications that you are pushing to the client
through the Composite Application Infrastructure, you can add the page to the launcher by specifying the
following page parameter: com.ibm.rcp.launcher=true.
To populate the launcher, complete the following steps:
1. Use or extend the default personality or create your own personality which has a workbench window
advisor that initializes the LauncherManager class located in the com.ibm.rcp.jfaceex plug-in, and the
LauncherRegistry class located in the com.ibm.rcp.ui plug-in.
2. Do one of the following:
v Implement the launcherSet extension point and in it, define the type of contribution you are
providing. This method allows you to make a contribution without activating the contributing
plug-in. The plug-in’s compiled code is not loaded into memory, which helps the program to start
up quickly and requires fewer system resources. You can use one of the following elements to
implement a corresponding class, which are predefined LauncherContributionItem-derived classes
provided in the com.ibm.rcp.jfaceex plug-in
– nativeProgramLaunchItem -- Identifies the item as being a
NativeProgramLauncherContributionItem class, which is a contribution item that starts a native
program.
184 Lotus Expeditor: Developing Applications for Lotus Expeditor
– perspectiveLaunchItem -- Identifies the item as being a PerspectiveLauncherContributionItem
class, which is a contribution item that starts a standard client application by specifying an
Eclipse perspective.
– urlLaunchItem -- Identifies the item as being a UrlLauncherContributionItem class, which is a
contribution item that opens a URL.v Directly instantiate a LauncherContributionItem-derived class or create a custom one and add it
directly to the LauncherManager. If you use this method your plug-in must declare a dependency
on the com.ibm.rcp.jfaceex plug-in.
Creating custom launcher contribution items:
The client enables you to open standard items, such as an application or a URL from the launcher by
providing a collection of classes that extend the LauncherItemContribution class. You can implement your
own custom contribution item.
To create a custom launcher contribution item, perform the following steps:
1. Implement the launcherSet extensions point and define a launchItemType and launchItem for it in the
plugin.xml for your application.
2. Write the supporting launchContributionItem class that extends the LauncherContributionItem class
and name it with the name referenced in the launchItemType element definition.
For example:
package com.ibm.rcp.launcher.tests;
import org.eclipse.core.commands.Category;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.CommandManager;
import org.eclipse.core.commands.Parameterization;
import org.eclipse.jface.action.IContributionManager;
import com.ibm.rcp.jface.launcher.LauncherContributionItem;
import com.ibm.rcp.jface.launcher.LauncherItemParameter;
import com.ibm.rcp.jface.launcher.LauncherManager;
import com.ibm.rcp.jface.launcher.Param;
public class TestURILauncherContributionItem
extends LauncherContributionItem {
static final String TEST_URI_ITEM_COMMAND_ID =
"com.ibm.rcp.launcher.tests.testURICommand"; //$NON-NLS1$
public static final String TEST_URI_ITEM_ATTRIBUTE_URI = "uri";
private String uri;
public TestURILauncherContributionItem() {
super();
}
public Command getCommand() {
Command command = null;
IContributionManager contributionManager = getParent();
if (contributionManager != null &&
contributionManager instanceof LauncherManager) {
LauncherManager lm = (LauncherManager)contributionManager;
CommandManager commandManager = lm.getCommandManager();
if (commandManager != null) {
Category category =
commandManager.getCategory(LauncherManager.COMMAND_MANAGER_CATEGORY);
if (category != null) {
command = commandManager.getCommand(TEST_URI_ITEM_COMMAND_ID);
Developing applications 185
command.define("TestURICommand",
"Command for testing URI launching.", category, null);
command.setHandler(new TestURILauncherItemHandler());
}
}
}
return command;
}
3. Write the supporting Item class and name it with the name referenced in the launchItem element
definition. Use the Param class to define the item’s attributes.
For example:
public Parameterization[] getParameters() {
Parameterization[] parameters = null;
String uriString = getUri();
if (uriString != null) {
parameters = new Parameterization[1];
LauncherItemParameter parameter =
new LauncherItemParameter(TEST_URI_ITEM_ATTRIBUTE_URI,
"Launcher TestURI item uri attribute.");
parameters[0] = new Parameterization(parameter, uriString);
}
return parameters;
}
public void setUri(String uri) {
this.uri = uri;
}
public String getUri() {
return uri;
}
public void addAttribute(Param param) {
if (param == null) {
return;
}
String name = param.getName();
if (name.compareToIgnoreCase("uri") == 0) {
setUri(param.getValue());
}
}
}
Contributing bookmarks to the launcher:
Bookmarks are user-created references to applications, application documents or Web sites and are stored
within a folder in the launcher.
The DefaultWorkbenchWindowAdvisor provided in the com.ibm.rcp.platform.personality plug-in
schedules a Job to build the BookmarkProviderRegistry. Upon completion of that job it sets the bookmark
provider (IBookmarkProvider) to the LauncherManager. When a user expands a folder on the Launch
drop-down menu, the LaunchManager uses a working thread to query the bookmark provider to retrieve
any available bookmarks for the selected folder.
To contribute a bookmark to the launcher, complete the following steps:
1. Use or extend the default personality or create your own personality which has a workbench window
advisor that initializes the LauncherManager class located in the com.ibm.rcp.jfaceex plug-in, and the
186 Lotus Expeditor: Developing Applications for Lotus Expeditor
LauncherRegistry class located in the com.ibm.rcp.ui plug-in as well as uses the
BookmarkProviderRegistry located in the com.ibm.rcp.ui plug-in to retrieve the bookmark provider, if
any, and set it to the LauncherManager.
2. Do one of the following:
v Contribute bookmarks dynamically by implementing the bookmarkProvider extension point. This
method allows you to make a contribution without activating the contributing plug-in. The
plug-in’s compiled code is not loaded into memory, which provides for faster program startup and
requires fewer system resources. For example:
<extension point="com.ibm.rcp.ui.bookmarkprovider">
<provider
id="com.ibm.rcp.my.bookmark.provider"
class="com.ibm.rcp.my.boomark.provider"
name="My Bookmark"
ranking="20" />
</extension>
v Directly instantiate the bookmark by Implementing the IBookmarkProvider interface and
implementing the IBookmark interface to add the bookmark to the Launcher. If you use this
method, your plug-in must declare a dependency on the com.ibm.rcp.jfaceex plug-in. For example:
package com.ibm.rcp.launcher.bookmark.tests;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.themes.ITheme;
import com.ibm.rcp.jface.launcher.BookmarkEvent;
import com.ibm.rcp.jface.launcher.IBookmark;
import com.ibm.rcp.jface.launcher.IBookmarkProvider;
import com.ibm.rcp.jface.launcher.IBookmarkProviderListener;
public class TestBookmarkProvider implements IBookmarkProvider {
private static final String CLAZZ_NAME =
TestBookmarkProvider.class.getName();
private static final String PKG =
TestBookmarkProvider.class.getPackage().getName();
private static Logger logger = Logger.getLogger(PKG);
private ListenerList bookmarkListeners;
private boolean testAutoLaunch;
private ArrayList parentFolderItems;
static private HashMap testBookmarks;
static {
testBookmarks = new HashMap();
testBookmarks.put("Iris Office Notes",
"Notes://Ella/8525561F006EC144/2DE43156D1B5E6AB85256673004D9E25");
testBookmarks.put("Partner Forum 2006",
"Notes://CAMDB10/85257106005B472D/C742E0C537B8236F85256F90006C73D6");
testBookmarks.put("RCP Tasks Database",
"Notes://wrangle/85256E8B004CA57C/51D5AAF548E20AA685256E90004F3639");
testBookmarks.put("Clearcase Help Db",
"Notes://Everclear/8525675900776AA5/8178B1C14B1E9B6B8525624F0062FE9F");
Developing applications 187
}
static private HashMap testBookmarkFolders;
static {
testBookmarkFolders = new HashMap();
testBookmarkFolders.put("Build Dbs", "1");
testBookmarkFolders.put("RCP", "2");
testBookmarkFolders.put("Databases", "3");
}
static int[] folderIds = {IBookmark.FOLDER_ID_ROOT, 2, 1969, 1980};
private Runnable randomBookmarkUpdateGenerator = new Runnable() {
public void run() {
long folderCount = Math.round((Math.random() * folderIds.length));
if (folderCount > 0) {
int[] foldersToUpdate = new int[(int)folderCount];
for (int i = 0; i < folderCount; i++) {
long index = Math.round((Math.random() * (folderIds.length -1)));
foldersToUpdate[i] = folderIds[(int)index];
}
fireBookmarkFolderUpdate(foldersToUpdate);
}
/* Fire off again. */
long minutes =
Math.round((Math.random() * 4)) + 1; /* 1 to 5 minutes */
DateFormat df = DateFormat.getTimeInstance();
System.out.println("TFS -->> TestBookmarkProvider update interval =
" + Integer.toString((int)minutes) + " minutes at " +
df.format(new Date()) + ".");
Display.getDefault().timerExec((int)(minutes * 60000),
randomBookmarkUpdateGenerator);
}
};
public TestBookmarkProvider() {
/*
* Start random bookmark updating... can be newed from any thread.
* Make sure runs in ui thread.
*/
Display.getDefault().asyncExec(new Runnable() {
public void run() {
long minutes =
Math.round((Math.random() * 4)) + 1; /* 1 to 5 minutes */
DateFormat df = DateFormat.getTimeInstance();
System.out.println("TFS -->> TestBookmarkProvider update interval =
" + Integer.toString((int)minutes) + " minutes at " +
df.format(new Date()) + ".");
Display.getDefault().timerExec((int)(minutes * 60000),
randomBookmarkUpdateGenerator);
}
});
ITheme currentTheme =
PlatformUI.getWorkbench().getThemeManager().getCurrentTheme();
testAutoLaunch =
currentTheme.getBoolean("com.ibm.rcp.launcher.tests.TEST_AUTO_LAUNCH");
}
public List populate(int parentFolderId) {
if (logger.isLoggable(Level.FINER)) {
logger.entering(CLAZZ_NAME, "populate with parentFolderId =
" + Integer.toHexString(parentFolderId)); //$NON-NLS-1$
}
188 Lotus Expeditor: Developing Applications for Lotus Expeditor
List items = null;
if (parentFolderId == IBookmark.FOLDER_ID_ROOT) {
if (parentFolderItems == null) {
parentFolderItems = new ArrayList();
Set keySet = testBookmarks.keySet();
for (Iterator i = keySet.iterator(); i.hasNext(); ) {
IBookmark bm = createBookmark(IBookmark.FOLDER_ID_ROOT, false);
String key = (String)i.next();
bm.setLabel(key);
bm.setURL((String)testBookmarks.get(key));
parentFolderItems.add(bm);
}
keySet = testBookmarkFolders.keySet();
for (Iterator i = keySet.iterator(); i.hasNext(); ) {
String key = (String)i.next();
IBookmark bm = createBookmark(IBookmark.FOLDER_ID_ROOT, false);
if (bm instanceof TestBookmark) {
TestBookmark tbm = (TestBookmark) bm;
String val = (String)testBookmarkFolders.get(key);
tbm.setFolderId(Integer.parseInt(val));
}
bm.setLabel(key);
parentFolderItems.add(bm);
}
}
items = parentFolderItems;
} else if (parentFolderId == 2) {
items = new ArrayList();
IBookmark bm = createBookmark(2, false);
bm.setLabel("RCP sub bookmark 1 (Partner Forum url)");
bm.setURL("Notes://CAMDB10/85257106005B472D/
C742E0C537B8236F85256F90006C73D6");
items.add(bm);
bm = createBookmark(2, false);
bm.setLabel("RCP sub bookmark 2 (nothing is launched)");
if (bm instanceof TestBookmark) {
TestBookmark tbm = (TestBookmark) bm;
tbm.setFolderId(1969);
}
items.add(bm);
bm = createBookmark(2, false);
bm.setLabel("RCP sub bookmark 3 (Iris Office Notes url)");
bm.setURL("Notes://Ella/8525561F006EC144/
2DE43156D1B5E6AB85256673004D9E25");
items.add(bm);
} else if (parentFolderId == 1969) {
items = new ArrayList();
IBookmark bm = createBookmark(1969, false);
bm.setLabel("sub RCP sub bookmark 1 (nothing is launched)");
items.add(bm);
bm = createBookmark(1969, false);
bm.setLabel("sub RCP sub bookmark 2 (nothing is launched)");
items.add(bm);
bm = createBookmark(1969, false);
bm.setLabel("sub RCP sub bookmark 3 (nothing is launched)");
if (bm instanceof TestBookmark) {
TestBookmark tbm = (TestBookmark) bm;
tbm.setFolderId(1980);
}
items.add(bm);
} else if (parentFolderId == 1980) {
items = new ArrayList();
IBookmark bm = createBookmark(1980, false);
bm.setLabel("sub sub RCP sub bookmark 1 (nothing is launched)");
Developing applications 189
items.add(bm);
bm = createBookmark(1980, false);
bm.setLabel("sub sub RCP sub bookmark 2 (nothing is launched)");
items.add(bm);
bm = createBookmark(1980, false);
bm.setLabel("sub RCP sub bookmark 3 (nothing is launched)");
items.add(bm);
}
if (logger.isLoggable(Level.FINER)) {
logger.exiting(CLAZZ_NAME, "populate with parentFolderId =
" + Integer.toHexString(parentFolderId)); //$NON-NLS-1$
}
return items;
}
public void add(IBookmark newBookmark) {
// TODO Auto-generated method stub
}
public void remove(IBookmark bookmark) {
// TODO Auto-generated method stub
}
public void move(IBookmark bookmark, int folderId) {
// TODO Auto-generated method stub
}
public IBookmark getStartupFolder() {
if (logger.isLoggable(Level.FINER)) {
logger.entering(CLAZZ_NAME, "getStartupFolder"); //$NON-NLS-1$
}
IBookmark bm = null;
if (testAutoLaunch) {
bm = createBookmark(IBookmark.FOLDER_ID_ROOT, false);
bm.setLabel("RCP");
if (bm instanceof TestBookmark) {
TestBookmark tbm = (TestBookmark) bm;
tbm.setFolderId(2);
}
}
if (logger.isLoggable(Level.FINER)) {
logger.exiting(CLAZZ_NAME, "getStartupFolder"); //$NON-NLS-1$
}
return bm;
}
public IBookmark createBookmark(int parentFolderId, boolean isFolder) {
TestBookmark bm = new TestBookmark();
bm.setParentFolderId(parentFolderId);
if (isFolder) {
//TODO "Create" a new folder and set its id to the bookmark’s folder id.
// bm.setFolderId(folderId);
}
return bm;
}
public boolean isDirty(int folderId) {
return false;
}
190 Lotus Expeditor: Developing Applications for Lotus Expeditor
private void fireBookmarkFolderUpdate(int[] folderIds) {
if (bookmarkListeners != null) {
Object[] listeners = bookmarkListeners.getListeners();
if (listeners.length <= 0) {
return;
}
for (int i = 0; i < listeners.length; i++) {
IBookmarkProviderListener listener =
(IBookmarkProviderListener) listeners[i];
/* Handler can modify the array so just give each one their own copy */
int[] ids = new int[folderIds.length];
System.arraycopy(folderIds, 0, ids, 0, folderIds.length);
BookmarkEvent be =
new BookmarkEvent(BookmarkEvent.BOOKMARK_EVENT_FOLDER_MODIFIED,
ids);
listener.bookmarksUpdated(be);
}
}
}
}
3. Register the IBookmarkProviderListener on the LauncherManager to listen for bookmark updates or
changes on the launcher. When a bookmark changes, a BookmarkEvent object is sent to
IBookmarkProviderListeners.
For example:
public void addBookmarkProviderListener(
IBookmarkProviderListener listener) {
if (bookmarkListeners == null) {
bookmarkListeners = new ListenerList();
}
bookmarkListeners.add(listener);
}
public void removeBookmarkProviderListener(
IBookmarkProviderListener listener) {
if (bookmarkListeners != null) {
bookmarkListeners.remove(listener);
}
}
Use a working thread to read the launcherSet and bookmarkProvider registries and to fetch Bookmark
objects when users open folders in the launcher.
Sidebar:
The sidebar is a view that displays vertically at the side of the workbench window.
The sidebar can contain one or more panels. Panels contain views associated with applications. For
example, a user that has a mail Inbox view open in the main workbench window could start a chat with
a contact in the instant messaging buddy list that displays in one panel of the sidebar and check his
availability for a meeting in the personal calendar that displays in another panel. The sidebar is useful
because it enables users to conveniently view information and perform actions in more than one
application at a time.
Users can customize the sidebar by resizing the sidebar, resizing the panels in the sidebar, dragging and
dropping the panels to reorganize them, and adding or removing panels by selecting or deselecting the
panels from the View → Sidebar Panels menu.
Developing applications 191
The sidebar can be displayed in the following modes:
v Open -- Displays all available panels.
v Thin -- Only displays icons representing the applications contributing panels to the sidebar. Users can
click on an icon to expand the associated panels both vertically (if the panel is collapsed) and
horizontally. When the sidebar is open, users can click the double arrow icon to minimize the size of
sidebar or put it into ″thin″ mode. If the sidebar sash is dragged such that the width becomes small
enough, the sidebar goes into thin mode automatically.
v Hidden -- The sidebar does not display. Users can open the sidebar from the View menu.
The personality does the work of instantiating the user interface controls used to create the sidebar. The
WorkbenchWindowAdvisor provided in the default platform, in the com.ibm.rcp.platform.personality
plug-in, creates the sidebar by instantiating the ShelfPage class provided in the com.ibm.rcp.ui.shelf
plug-in. The ShelfPage creates a SViewStack widget to represent the sidebar and create SViewForm
widgets to represent each panel. Sidebars are populated with panels when the first application or
perspective starts and its content is subsequently updated when enabled Eclipse activities change. When
all applications or perspectives close and the workbench page closes, each panel reference and
SViewForm is disposed. Finally, when a user shuts down the client, the Shell is disposed, causing the
sidebar’s parent composite and any of its children (SViewStack, along with any SViewForms that have
not been disposed yet), to be disposed.
Creating a sidebar:
You can create a single sidebar that displays views for an application.
To create a single sidebar, perform the following step:
Instantiate a sidebar by passing a String ID, parent composite, and a configurer window to the ShelfPage
constructor.
private Composite myPageForm;
IWorkbenchWindow window = configurer.getWindow();
myShelfPage = new ShelfPage(EXPANDED, myPageForm, window);
Creating two sidebars:
You can create more than one sidebar for an application.
To create two sidebars, perform the following step:
Instantiate two ShelfPage objects. Provide different IDs, but the same parent composite in the ShelfPage
constructors for each sidebar.
For example, the following code from the
com.ibm.rcp.platform.personality.DefaultWorkbenchWindowAdvisor class creates sidebars that display on
the right and left side of the main window:
private MultiPageForm pageSwitcherForm;
IWorkbenchWindow window = configurer.getWindow();
shelfPage = new ShelfPage(RIGHT, pageSwitcherForm, window);
final Control shelfControl = shelfPage.getControl();
pageSwitcherForm.setShelfControl(shelfControl);
leftShelfPage = new ShelfPage(LEFT, pageSwitcherForm, window);
leftShelfPage.setOrientation(ShelfPage.LEFT);
final Control leftShelfControl = leftShelfPage.getControl();
pageSwitcherForm.setLeftControl(leftShelfControl);
192 Lotus Expeditor: Developing Applications for Lotus Expeditor
where pageSwitcherForm is the parent composite that defines where to position the two sidebar controls.
The setOrientation() method called on the leftShelfPage defines the direction for the sidebar to collapse.
Contributing a panel to a sidebar:
The sidebar can contain one or more panels. Panels contain views associated with applications.
To contribute a panel to a sidebar, perform the following steps:
1. Use the com.ibm.rcp.ui.shelfViews extension point to define a panel contribution. Define values for
the following attributes in the <shelfview> element.
v id -- Unique ID to distinguish each contributing view. If there are repeated IDs, only one of the
views will be displayed.
v view -- ID of the view to be displayed.
v region -- Positions the view within the stack of views set to be displayed in the sidebar. Options are
TOP, MIDDLE, or BOTTOM. BOTTOM is the default; if you do not specify a value, the panel is
added to the bottom of the stack.
v page -- Determines which sidebar the view should be displayed in. This value must match the
ShelfPage ID specified when the ShelfPage was instantiated. For example, if you are adding a panel
to the a sidebar for which you created two ShelfPage objects, with the IDs RIGHT and LEFT, the
options for the page value would be RIGHT and LEFT.
v showTitle -- Boolean value. Determines whether or not to show the title bar.
Note: Note: The com.ibm.rcp.platform.shelfViews extension point has been deprecated, but is being
maintained for backwards compatibility.
2. Create the sidebar.
3. Display the sidebar in the window. For example:
setShowShelf(true);
4. Call the update method of ShelfPage to display the panels. For example:
ShelfPage.update()
Hiding a sidebar:
You can hide a panel within the sidebar or the sidebar itself.
You can use activities to associate a sidebar panel with a specific application. Activities prevent toolbar
icons, custom menu options, sidebar panels, and control sets in one application from being displayed in
another application. When an activity is enabled in the platform, the user interface contributions
associated with that activity are shown. When an activity is disabled in the platform, its user interface
contributions are not shown. You can associate a panel with an activity to enable the panel to be
displayed or hidden based on whether the associated application is in use or is not. The patterns used by
the Workbench are composed of two parts. The first part uses the identifier of the plug-in that is
contributing the user interface extension. The second part is the ID used by the plug-in itself when
defining the contribution. The following format is used:
plug-in-identifier + "/" + local-identifier
where the identifiers include a backslash (\) before each period in their namespaces.
Do one of the following
v Set the value of the showShelfArea property in the theme to false. The theme is specified in the
plugin_customization.ini file of the branding plug-in.
v Use the org.eclipse.ui.activities extension point to filter out a sidebar view.
Developing applications 193
For example, in the following plugin.xml excerpt, the regional settings editor activity defined in the
com.ibm.myeditor.supereditor.baseview plug-in associates itself with the
com.ibm.supereditor.preferences.documenteditors.RegionalSettingsEditor contribution:
<extension point="org.eclipse.ui.activities">
<activity
name="RegionalSettingsEditor"
description="RegionalSettingsEditor Activities"
id="com.ibm.myeditor.RegionalSettingsEditor">
</activity>
<activityPatternBinding
activityId="com.ibm.myeditor.RegionalSettingsEditor"
pattern="com\.ibm\.myeditor\.supereditor\.baseview/com\.ibm\
.supereditor\.preferences\.documenteditors\.RegionalSettingsEditor">
</activityPatternBinding>
</extension>
Table widgets:
The table widgets are the parts of the graphical user interface that represent the table.
The widgets enable users to make changes to the data and customize the table. The following table
widgets are available:
v Body -- Paints the items to be rendered in the current window. An attribute of the body widget defines
the background color to use to fill the table body.
v Cell -- Part of an item widget. Occupies a single row and single column. Users can select cells. Cells
support listeners that listen for mouse and key events.
v Header -- Represents the text and image at the top of each column. Users can customize the table using
the header widget. They can reset the width of a column, hide a column, or reorder the columns in the
table.
v Item -- Represents an object in the table. Occupies a row in the table. Users can select and remove item
widgets from the table.
v Table -- The parent widget that manages all components in the table. It creates the header and body
and adds the items into the table. The table widget uses the tree nodes to arrange the items according
to the specified data model and preserves the appropriate relationships amongst the items in the table.
Sorting table entries:
The table control supports sorting entries in columns by reflecting the change in the sorting indicator in
the column header.
The actual reorganization of the data must be handled by the data model. The data model is also
responsible for refreshing the table after the data has been reorganized.
1. Retrieve the column’s sort indicator. For example:
private void SortBy(SelectionEvent e) {
STableColumn column = (STableColumn)_viewer.getTable().
getColumn(new Point(e.x, e.y));
SortIndicator indicator = column.getSortInidcator();
2. Set up logic to change the indicator based on the current setting of the sort order. For example:
if(indicator == SortIndicator.None) return;
if(indicator == SortIndicator.IsSortable) {
column.setSortIndicator(SortIndicator.Ascending);
}
else if (indicator == SortIndicator.Ascending) {
column.setSortIndicator(SortIndicator.Descending);
194 Lotus Expeditor: Developing Applications for Lotus Expeditor
}
else {
column.setSortIndicator(SortIndicator.IsSortable);
}
3. Retrieve the table data based on the new sort order. For example:
model.sortedBy(column.getSortInidcator());
_viewer.setInput(model);
}
4. Add a listener to listen for changes to the sort order. For example:
tableCols[i].addSelectionListener(new SelectionListener(){
public void widgetSelected(SelectionEvent e) {
SortBy(e);
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
Table color and font:
By default, the table uses the system default colors and fonts. You can specify different colors and fonts
using methods provided by the table control.
You can set the default color using a theme. See Using themes to customize the user interface.
The following list identifies the code you can use to set colors and fonts in a table. It lists the code
according to the order of priority they are given, the first one having the highest priority:
1. Cell paint modifier
2. Item paint modifier
3. Font only: TableCell methods and TableItem.setFont(int column, Font font) method
4. TableItem and TableHeader methods
5. Table methods
Color support
You can set a background and foreground color for the table, the table header, and individual rows. To
implement custom colors, use the setBackground() and setForeground() methods provided by the STable,
STableHeader, and STableItem classes. You can also use the IPaintModifier interfaces to set the color of
cells and items in the table.
Font support
You can set the font for the text that displays in a table, table header, and table rows using the setFont()
method of the STable, sTableHeader, and STableItem classes. You can also use the IPaintModifier
interfaces to set the font of cells and items in the table.
Specifying the color of table text:
You can use the IPaintModifier interface to apply a different color or font style to table items, including a
row, column, and a cell.
Developing applications 195
When painting a cell, the table uses the paint modifier added to the first column cell to get the color and
font settings. If no cell paint modifier is specified, the table checks whether a paint modifier is specified
for an item in the cell. A cell paint modifier has higher priority than an item paint modifier.
1. Implement the IPaintModifier interface using the setItemPaintModifier() method of the table class to
set the colors of a cell item. For example:
table.setItemPaintModifier(new IPaintModifier(){
public void dispose() {
}
2. Define the colors to use for the foreground and background of the item. For example:
public Color getForeground(Object element) {
if(element instanceof Mail){
Mail mail=(Mail)element;
boolean unread = mail.isUnread();
if (unread && isMarkedUnRead)
return table.getDisplay().getSystemColor(SWT.COLOR_RED);
}
return null;
}
public Color getBackground(Object element) {
if (!isMarkPeople) return null;
if(element instanceof Mail){
Mail mail=(Mail)element;
String author = mail.getAuthor();
if (author.equals("Jong Leng Zhang"))
return Item_bgColor;
if (author.equals("Lu Guang Zu"))
return Item_bgColor_1;
}
return null;//table.getDisplay().getSystemColor(SWT.COLOR_RED);
}
public int getStyle(Object element) {
return 0;
} });
tableCols[i].setCellPaintModifier(new IPaintModifier(){
...
});
3. Implement the IPaintModifier interface calling the setCellPaintModifier() method from a column in
the table to set the colors of the table cells in that column. For example:
tableCols[i].setCellPaintModifier(new IPaintModifier(){
...
});
Customizing the table header:
The table header control displays at the top of each table column and contains a title that describes the
information provided in that column.
You can use methods of the STable class to hide or show the table column header as well as set the font
and background color of the header.
To
1. Specify whether you want the header to display in the column using the setHeaderVisible() method of
the STable class. For example, if this represents the current table object, use the following code to
display the column header:
this.setHeaderVisible(true);
2. Define the font style for the text in the header using the setHeaderFont() method of the STable class.
For example:
196 Lotus Expeditor: Developing Applications for Lotus Expeditor
FontData fontdata = new FontData("Arial", 10, SWT.NORMAL);
Font font = new Font(parent.getDisplay(), fontdata);
this.setHeaderFont(font);
3. Define the background color for the header using the setBackground() method. For example:
this.getHeader().setBackground(new Color(null, new RGB(255,255,255)));
Custom JFace and SWT Widgets: The client UI provides a collection of custom SWT widgets, commonly
referred to as “SWidgets” that support the ability to customize the look and feel of the user interface.
This customization made possible through the StyledWidget interface. Each of the custom widgets
provided by the UI implement this interface. The UI also provides the respective Jface-like action and
model classes to simplify and provide a consistent programming model with Eclipse Rich Client
Platform. All of these custom widgets are located in the com.ibm.rcp.swtex plug-in.
This collection of custom widgets includes things like buttons, toolbars and tab folders. These widgets are
ubiquitous in the Lotus Expeditor client and are used by not only the Lotus Expeditor platform but by
applications and components that plug-in and extend the platform.
For example, the custom button widget (SButton) is used to represent the launch button that provides the
user with access to all of the applications that are installed on the Lotus Expeditor client. This same
custom button widget is also used by clients to build things like views and forms and because the
rendering of these widgets is extremely flexible, it takes on a different look and feel depending on its
context.
Using SWidgets
The SWidgets constructors take two parameters: the parent of the widget or NULL if the parent is the
Display and a style parameter.
To implement an SWidget, perform the following steps:
1. Create an instance of the widget.
For example:
// Create an instance of an SButton
SButton button = new SButton(parent, SWT.PUSH);
button.setText(“Launch”);
2. Add the button to the application user interface.
3. After the widget is used, dispose of it.
For example:
// Dispose of the SButton since it’s no longer needed
button.dipose();
Eclipse also provides several facilities to enable resource sharing among components. For example, if
several widgets must access the same Color, the Color can be added to, and referenced from, the JFace
color registry. This allows one Color instance to be shared by any number of widgets. Resources that are
shared should never be disposed. JFace provides these registries for fonts, images, and colors.
Managing contributions to the user interface
The JFaceEX plug-in provides action and model classes that simplify programming with SWTEX by
enabling you to manage contributions to the user interface.
The com.ibm.rcp.jfaceex plug-in provides a collection of JFace style action and contribution classes that
enable clients to implement shared behaviors between two or more widgets components such as toolbars
and menu bars. The API for these action and contribution classes are similar if not compatible with the
standard Eclipse action classes. The contribution API lends itself well to implementing extension
point-driven user interface components.
Developing applications 197
The JFaceEX action classes are provided in the com.ibm.rcp.jfaceex plug-in, which has dependencies on
the org.eclipse.jface plug-in.
To manage contributions using JFace actions, perform the following steps:
1. Create the action manager classes used to manage contributions to things like toolbars and menu bars
using one of the following constructors:
v A constructor that allows you to pass the toolbar or menu bar that may have been created
externally.
// create a toolbar manager with a toolbar created externally
SToolBar toolbar = new SToolBar(parent, SWT.NONE);
SToolBarManager toolbarManager = new SToolBarManager(toolbar);
v A constructor that takes no parameters or just a style parameter and the toolbar or menu bar is
later created internally.
// create a toolbar manager with a toolbar created internally
SToolBarManager toolbarManager = new SToolBarManager(SWT.NONE);
SToolBar toolbar = toolbarManager.createControl(parent);
2. Use a JFaceEX action to add the same action object to a toolbar and a menu bar. The action is
rendered as a tool item in a toolbar and a menu item in a menu bar.
For example:
Action exitAction = new ExitAction();
mySToolbarManager.add(exitAction);
mySMenuManager.add(exitAction);
3. When it is not longer needed, dispose of the JFaceEx action manager.
Note: The dispose method disposes of the toolbar or menu bar that it manages so clients must
remember not to reference those objects after the manager has been disposed.// dispose of the toolbar manager when it is no longer needed
toolbarManager.dipose();
Accessing SWidgets and JFaceEX objects
All widget and action classes must be accessed from the UI thread.
In SWT-based applications, the UI thread is the main thread. Accessing these classes from any other
thread results in an invalid thread access exception. If this exception is thrown while the client is starting,
it will fail to start.
To access a widget or JFaceEX object from a worker thread:
Do one of the following:
v Use the syncExec() or asyncExec() methods of Display.
// Update the text of my button from a worker thread
Display.getDefault().asyncExec( new Runnable() {
Public void run() {
myButton.setText(“My Button”);
}
});
v Use the org.eclipse.ui.progress.UIJob class. The UIJob class is a subclass of Job that is designed to
always run in the main thread. UIJob uses Display.asyncExec() internally.
Adding and contributing menus
The views and editors that are created and displayed will typically provide a visual representation of
data for the users. In order to control the application environment, such as opening or closing views, or
performing specific actions, such as syncing data, you will probably want to consider creating menu and
menu items to enable access to the actions.
198 Lotus Expeditor: Developing Applications for Lotus Expeditor
Menu contributions: You can make a feature you have created available to users by adding it to the
menu bar as a menu item. You can create and contribute menu items by implementing one of the
following contribution types:
v Global menu contributions -- These menu contributions persist across every application. These menu
items are universal and can be used from any context. These menu items are also ″retargetable,″ so
your application can write code to retarget these global menu items, such as Cut, Copy, and Paste, for
application-specific purposes.
v Part-associated action set contributions -- These menu contributions are specific to a single application
or view. By default, most menu items are local, meaning that if the view associated with those menu
items is not displayed, those menu items are not displayed. Contributing view-specific menu items
enables you to create discrete menu items for a page of your application that are not shared with other
products on the client.
Global menu contributions: You can add a menu item to one of the Lotus Expeditor global menus using
the org.eclipse.ui.actionSets extension point. You can also associate a new action set to a specific
perspective or view to ensure that the menu contributions defined in the action set appear in the user
interface only when the specified view is active using the org.eclipse.ui.actionSetPartAssociation
extension point. See the Platform Plug-in Developer’s Guide for more information on part associations.
For details on the ids used by the Lotus Expeditor Top Level Menus, refer to “Lotus Expeditor top level
menus” on page 365.
If actions are defined by the extension points within plug-ins, but are not associated with a particular
perspective, then the menu items/actions will be displayed at all times. By associating menu items and
actions with a particular perspective through perspectiveExtension extension points, the menu items and
actions will be displayed only when the application’s perspective is displayed.
The File menu provided by the workbench contains entries for Preferences and Exit. Usability guidelines
suggest that Exit always be the last menu item. The Preferences action is provided by the workbench, and
launches a dialog that aggregates all provided preference pages.
Part-associated action set menu contributions: In addition to having the global menus, each product
contributes its own menu items on the existing menus. A product can also contribute entire menus to the
menu system.
Pop-up menus: A pop-up menu is the menu that is displayed when a user right-clicks the background of
a user interface component, such as a view or editor. Pop-up menus should repeat pertinent menu items
that are available on the menu bar or pertinent actions on a dialog box triggered from a menu item. The
contents of the pop-up menu must change to be appropriate to the object that has focus or the object that
is selected.
A pop-up menu item should not be the only way a user accesses a piece of functionality. All pop-up
menu items must have access keys for accessibility. Indicate the access key by underlining the key text in
the menu item label. If possible, use the same access key as the one used for the menu item in the menu
bar.
You should give context menus to the following objects:
v Objects in a navigator view, such as folders or document libraries
v Items selected in a list.
Icons in menus: If a menu item is represented by a toolbar button with an icon, include that icon on the
menu. If your application does not have a toolbar or the toolbar does not include icons, do not include an
icon on the menu. Include icons to the left of the menu items.
Developing applications 199
Creating a top-level menu: You can write code that performs a task and enable users to trigger that task
from a corresponding menu item in the user interface by creating a menu item contribution and adding it
to the application menu. The File, Application, View and Help menus appear at the top level. You can
contribute a menu item that displays at the top-level as well.
The following steps make use of the templates provided with the Plug-in Development Environment to
show you how to create an action, an actionSet, and a top-level menu.
1. Start your Rational Software Development Platform.
2. Create a plug-in project to contain the menu and action that you are providing. You will use a
template that creates a Java class for the action, and adds all of the appropriate extension points.
Later, you can modify the code generated by the template to meet your own requirements.
a. Select File > New > Other > Plug-in Development > Plug-in Project to create the initial project,
then select Next.
b. On the Plug-in Project panel, enter a name for your plug-in project, such as MyAction. You can use
the defaults already entered on the rest of the panel. Select Next.
c. Use the defaults on the Plug-in Content panel. Select Next.
d. On the Templates panel, check Create a plug-in using one of the templates. Select Hello, World.
Then select Finish.
e. A project called MyAction will now be created in your workspace. Selected files will have already
been created within the project based on your selections to create a plug-in with a view. The
MyAction and MyAction.actions packages will have been created in the src folder.
At this point, you can launch the platform from the workbench including this plug-in (for more
information, refer to “Debugging and testing applications” on page 323). A new top level menu, Sample
Menu, will be displayed along with File, Application, View and Help. This menu will contain an action
labeled Sample Action.
As you launch the platform, you will notice that regardless of the application that you open, the same
Sample Menu always appears. If you want to change this menu so that it appears only with a particular
application, you will need to create a perspectiveExtension. To illustrate these steps, the following steps
assume that the Simple Rich Client Platform application was created in “Creating a simple Rich Client
Platform application” on page 158.
1. Open the plugin.xml file for the MyAction project you just created
2. Now Add an extension point to the plug-in to define the perspective Extension that you want to
create.
a. Open the plugin.xml file contained in your MyAction project.
b. Select the Extensions tab within the editor. You should already see the org.eclipse.ui.actionSets
defined.
c. Select Add..., then select org.eclipse.ui.perspectiveExtension, then select OK.
d. Once org.eclipse.ui.perspectiveExtensions has been added to the All extensions list, right click
on the entry, then select New > perspectiveExtension.
e. The Extension Element Details information will display. Enter MyApplication.perspective1 as the
target id. (This is the perspective created in “Creating a simple Rich Client Platform application”
on page 158).
f. Now select the MyApplication.perspective1 perspectiveExtension. Right click and select New,
then select actionSet.
g. The Extension Element Details information will display. Enter MyAction.actionSet as the target id.
h. Expand the org.eclipse.ui.actionSets, and select the Sample Action Set.
i. The Extension Element Details information will display. Change the value of visible from true to
false.
j. Save and close the plugin.xml file.
200 Lotus Expeditor: Developing Applications for Lotus Expeditor
As you launch the platform, you will notice that the Sample Menu displays only when your application
is visible.
You can further limit or associate a menu with a particular view. By using the
org.eclipse.ui.actionSetPartAssociation extension point, you can assign a particular action set to a
view or editor such that the action set displays only when the view or editor is active. To modify the
MyAction plug-in to associate with a particular view instead of just a perspective, you would need to
make the following changes:
1. Open the plugin.xml file contained in your MyAction project.
2. Select the Extensions tab within the editor. You should already see the org.eclipse.ui.actionSets
defined.
3. Remove the org.eclipse.ui.perspectiveExtensions extension point, as it is no longer necessary for
the actionSet to be associated to the perspective.
4. Select Add....
5. Select org.eclipse.ui.actionSetPartAssociations, then OK.
6. Once org.eclipse.ui.actionSetPartAssociations has been added to the All extensions list, right click
on the entry, then select New > actionSetPartAssocation.
7. The Extension Element Details information will display. Enter MyAction.actionSet as the target id.
This is the action set you want to associate with a view.
8. Select the MyAction.actionSet actionSetPartAssociation. Right click and select New, then select part.
9. The Extension Element Details information will display. Enter MyApplication.views.SampleView as the
part.
When you launch, the SampleMenu will now only display when the Sample View is active. Since
MyApplication only has the single view available, you won’t really notice an immediate difference.
However, if you were to modify MyApplication to add another view, then you would notice that Sample
Menu only displays when Sample View is active.
Creating views
A perspective can show one or more views simultaneously. Depending upon the application
requirements, the number of views that are simultaneously displayed views may change in response to
events occurring within the application. Application developers may force views to maintain a specific
size or location, or to remain open, when displayed in the perspective. Alternatively, application
developers may allow users to close views. While the Lotus Expeditor workbench application enables the
ability to “reset” a perspective to its initial configuration, you may want to also provide menu options
that enable users to open specific views if they are inadvertently closed.
Applying Capitalization and punctuation guidelines
Use appropriate punctuation, such as a periods, exclamation points, or question marks at the end of
complete sentences. Add a colon at the end of labels for controls in forms and dialog boxes.
For more information, see the Eclipse User Interface guidelines.
The following table describes when to use headline-style capitalization and when to use sentence-style
capitalization:
Developing applications 201
Table 12. Capitalization and punctuation
Capitalization style Guideline Use for
Headline Capitalize the first letter of each
word, except the following:
v Articles such as: a, an, and, the
v Short prepositions that are between
words such as: for, in, of, on, and
to
v Command buttons (push buttons)
v Dialog box title bars
v Menu items
v Menu titles
v Section headers (For example, the
header to a section on a form.
Section headers should also be
bold.)
v Tabs
v Title bars
v ToolTips (When the toolTip is for a
toolbar item; all other tooltips use
sentence-style capitalization.)
v Window titles
Sentence Capitalize the first letter of the first
word and any proper nouns, such as
Workplace.
v Check box labels
v Dialog box labels
v Group box or group bar titles
v Radio buttons
v Text field labels
Creating helpful messages
Whenever possible use the standard message dialog boxes provided in the MessageDialog class as part of
the org.eclipse.jface.dialogs package. The error message can be modeless, which means it allows the
user to continue to interact with the application or modal, which means it requires that the user respond
to the error dialog box before continuing to use the application. The following sections describe the
available Eclipse message types.
Critical: Informs users of a serious problem that prevents them from continuing their work. Critical
error messages are always modal.
Example code:
MessageDialog.openError
(parent.getShell(),
"Error Title",
"Error Message");
Warning: Alerts users to a condition that requires a decision before proceeding. In many cases you must
add buttons to support the Yes, No, and Yes, No, Cancel conditions. Warning messages are usually
modal; that is, users must respond before continuing to interact with the application.
Example code:
MessageDialog.openWarning
(parent.getShell(),
"Warning Title",
"Warning Message");
Customizing existing applications
This section provides information on customizing existing applications.
202 Lotus Expeditor: Developing Applications for Lotus Expeditor
Activities
An activity is a logical grouping of function that is centered on a certain kind of task. For example,
developing Java software is an activity commonly performed by users of the Eclipse SDK platform, and
the Java Development Tooling defines many UI contributions (views, editors, perspectives, preferences,
etc.) that are only useful when performing this activity. Before we look at the mechanics for defining an
activity, let’s look at how they are used to help ″declutter″ the UI.
The concept of an activity is exposed to the user, although perhaps not apparent to a new user. When an
activity is enabled in the platform, the UI contributions associated with that activity are shown. When an
activity is disabled in the platform, its UI contributions are not shown.
Activities are defined using the org.eclipse.ui.activities extension point. Refer to the Platform Plug-in
Developer’s Guide for more information on activities and how to define them.
Activities can be used in a static manner to “hide” UI contributions that other plug-ins may have
provided.
Lotus Expeditor supports the use of activities.
Using activities: Activities are associated with UI contributions using activity pattern bindings, patterns
that are matched against the id of the UI contributions made by plug-ins. The process to “filter out” UI
contributions is a two-step process described below:
Step 1: Defining an activity: Activities are defined using the org.eclipse.ui.activities extension point.
Activities are assigned a name and description that provide information about an activity. The id of the
activity is used when defining pattern bindings or other relationships between activities.
An example activity definition:
<extension
point="org.eclipse.ui.activities">
<activity
name="Lotus Expeditor Specific"
description="Filters out Lotus Expeditor UI contributions"
id="Lotus Expeditor Activities">
</activity>
Step 2: Binding activities to UI contributions: Activities are associated with UI contributions using pattern
matching. The pattern matching used in activity pattern bindings follows the rules described in the
java.util.regex package for regular expressions. The patterns used by the workbench are composed of
two parts. The first part uses the identifier of the plug-in that is contributing the UI extension (the
plug-in namespace). The second part is the id used by plug-in itself when defining the contribution
(which may or may not also include the plug-in id as part of the identifier). The following format is used:
plug-in-identifier + "/" + local-identifier
For example, the following activity pattern binding states that all UI contributions from the Lotus
Expeditor workbench plug-in (com.ibm.eswe.workbench) are associated with the Lotus Expeditor Specific
activity. When this activity is enabled or disabled, the state of the UI contributions within the workbench
are affected.
<activityPatternBinding
activityId="Lotus Expeditor Specific"
pattern="com\.ibm\.eswe\.workbench/.*" />
</activityPatternBinding>
The next binding is more specific. It states that the contribution named install defined in the
InstallUpdate Launcher plug-in (com.ibm.eswe.installupdate.launcher) is associated with the Lotus
Expeditor Specific activity. As this activity is enabled or disabled, the specific contribution will be
included
Developing applications 203
<activityPatternBinding
activityId="Lotus Expeditor Specific"
pattern="com\.ibm\.eswe\.installupdate\.launcher/install" />
</activityPatternBinding>
Lotus Expeditor does not define any activities for use by applications. However, applications may define
activities to group Lotus Expeditor UI contributions. Refer to “Lotus Expeditor top level menus” on page
365 for specific details about the menu identifiers defined by the client platform.
Integrating existing RCP applications into Lotus Expeditor
Lotus Expeditor is based upon the Eclipse Rich Client Platform, but has added a set of Eclipse plug-ins
above and beyond the set normally part of RCP. In addition, Lotus Expeditor has provided a workbench
advisor and an application that can be used for many situations.
Application developers may have constructed applications based solely upon the Eclipse Rich Client
Platform (or other Eclipse-based platforms), without necessarily targeting Lotus Expeditor.
Generally, you can run applications which are written for generic RCP on Lotus Expeditor. However,
there may be some differences between the generic Eclipse RCP or SDK and Lotus Expeditor:
v Because the workbench advisor and application are already provided, applications that include a
workbench advisor or RCP application may no longer need to use those capabilities. However, some
application function may have been included in the advisor or application. You may be able to re-use
the plug-ins without necessarily running the application
v Actions that may have been defined by the advisor class could be defined as actionSets in a new or
existing plug-in.
v Lotus Expeditor has provided a set of menus as identified in “Lotus Expeditor top level menus” on
page 365. Note that applications that provide action sets may expect certain other menus to be present.
If those menus are not present, then actionSets that do not include a menu option will not display
their actions.
v To enable applications written as perspectives to contribute to Lotus Expeditor, you will need to update
the application plug-in that defines the perspective to also define the Lotus Expeditor WctApplication
extension point.
v Lotus Expeditor may not include all of the plug-ins typically provided in the Eclipse SDK. If the
Eclipse SDK was the target for application development, then attempting to run the application on
Lotus Expeditor may not be successful. In general, plug-ins that exist in the Eclipse SDK could be
added to Lotus Expeditor along with the application plug-ins. Note that IBM will not provide support
for these plug-ins if added to the platform.
Developing with user interface widgets
This section provides information on developing with user interface widgets.
Adding spell checking to applications
Applications can perform spell checking with the SpellChecker interface methods. In order to spell check,
a series of dictionaries must be set as a comparing library. Applications can use the setLocale() method
in SpellChecker interface to set the dictionaries.
There are four ways to performing spell checking:
v Checking misspelled words using the dictionaries supported by given locale
v Checking misspelled words using the dictionaries supported by the platform default locale
v Checking misspelled words using given dictionaries
v Checking misspelled words using given dictionaries and a customized user dictionary
Using dictionaries supported by a given locale:
204 Lotus Expeditor: Developing Applications for Lotus Expeditor
SpellChecker isc = TextAnalyzerFactory.getSpellCheckerInstance();
isc.setLocale(TextAnalyzerConstant.LANGUAGE_ID_EN_US);
MisspelledWord[] misWords = isc.checkSpelling(txtToCheck,5,true);
The core spell checking methods have been defined in the SpellChecker interface. An application needs
to get an instance of SpellChecker to perform the spell check. The application can acquire the instance
through the TextAnalyzerFactory’s getSpellCheckerInstance() method. In this case, applications need
to set the locale name for which you wish to check misspelled words in. checkSpelling(String
theInputText, int numSuggestions, boolean autoCorrect) is the concrete method to do spell check
using the input string. It returns an array of MisspelledWord. The suggestion words are stored in
theMisspelledWord.suggestions field. You can also get one misspelled word‘s suggestions using the
getSuggestions(String word, int numSuggestions, int suggestionType) method.
Using dictionaries supported by the platform default locale:
SpellChecker isc = TextAnalyzerFactory.getSpellCheckerInstance();
MisspelledWord[] misWords = isc.checkSpelling(txtToCheck,5,false);
If an application wishes to do spell checking with the platform default locale, it can invoke the
SpellChecker’s checkSpelling(String theInputText, int numSuggestions, boolean autoCorrect)
method directly after getting the instance of SpellChecker. It does not need to call the SpellChecker’s
setLocale() method.
Using given dictionaries:
SpellChecker isc = TextAnalyzerFactory.getSpellCheckerInstance();
String locale = TextAnalyzerConstant.LANGUAGE_ID_EN_US;
//get all the main dictionaries
DictionaryManager dm = TextAnalyzerFactory.getDictionaryManager();
DictionaryInfo[] mainDic = dm.getDictionaryInfo(locale,
DictionaryInfo.DICT_TYPE_SPELL_MAIN);
isc.setLocale(locale, mainDic, null);
MisspelledWord[] misWords = isc.checkSpelling(txtToCheck,5,true);
If you wish an application to do a spell check in appointed dictionaries, and know the location
information for those dictionaries, you can set them directly using the setLocale(String locale,
DictionaryInfo[] mainDictArray, UserDictionary[] userDictArray) method. Ensure you set the
userDictArray parameter to null.
Using given dictionaries and a customized user dictionary:
SpellChecker isc = TextAnalyzerFactory.getSpellCheckerInstance();
String locale = TextAnalyzerConstant.LANGUAGE_ID_EN_US;
//get all the main dictionaries
DictionaryManager dm = TextAnalyzerFactory.getDictionaryManager();
DictionaryInfo[] mainDic = dm.getDictionaryInfo(locale,
DictionaryInfo.DICT_TYPE_SPELL_MAIN);
//get all the user dictionaries
UserDictionaryManager udm = TextAnalyzerFactory.getUserDictionaryManager();
UserDictionary[] uDict = udm.getUserDictionaries(locale);
isc.setLocale(locale, mainDic, uDict);
MisspelledWord[] misWords = isc.checkSpelling(txtToCheck,5,true);
To check misspelled words with a user dictionary, the application must put the user dictionaries info into
the third parameter of the setLocale(String locale, DictionaryInfo[] mainDictArray,
UserDictionary[] userDictArray) method. The application can get the user dictionaries info from the
UserDictionaryManager interface.
Using the getSuggestions string:
SpellChecker isc = TextAnalyzerFactory.getSpellCheckerInstance();
isc.setLocale(TextAnalyzerConstant.LANGUAGE_ID_EN_US);
MisspelledWord[] misWords = isc.checkSpelling(txtToCheck,5,true);
// ......
Developing applications 205
String[] suggestions = misWords[i].suggestions; // one way
//......
Stirng misspeltWord = misWords[i].word;
// another way
String[] suggestions = isc.getSuggestions(misspeltWord,5,
TextAnalyzerConstant.SUGGESTION_TYPE_SPELL_AID);
An option to get one misspelled word’s suggestions, is with the getSuggestions(String word, int
numSuggestions, int suggestionType) method. There are two types of applications that can use the
suggestionType parameter:
v TextAnalyzerConstant.SUGGESTION_TYPE_SPELL_AID
Standard spell suggestion
v TextAnalyzerConstant.SUGGESTION_TYPE_WILDCARD
Get suggestions based on wildcards in the input word
User dictionary manager: The user dictionary manager of TextAnalyzer is used to manage the user
dictionary data and maintain the consistency of concrete binary dictionaries between different engines of
the same locale. The manage user dictionary functions include:
v Create/delete a user dictionary
v Add/remove words in a user dictionary
v Get all user dictionaries
v Get words in a user dictionary
Create/delete user dictionary:
UserDictionaryManager udm =
TextAnalyzerFactory.getUserDictionaryManager();
String locale = TextAnalyzerConstant.LANGUAGE_ID_EN_US;
UserDictionary ud= udm.createUserDictionary(locale, “common_1”);
// ......
udm.deleteUserDictionary(ud);
In this example, all user dictionary manage methods have been declared in the UserDictionaryManager
interface. An application needs to get an UserDictionaryManager instance with the TextAnalyzerFactory’s
getUserDictionaryManager () method to manage user dictionaries. The application can invoke
UserDictionaryManager’s createUserDictionary(String locale, String suggestionName) to create a
user dictionary. It returns a UserDictionary object. The user dictionary’s name can not be the same in the
current locale, but can be the same in a different locale. The application invokes
deleteUserDictionary(UserDictionary targetDict) to delete a user dictionary. The targetDic parameter
value can be acquired from the return value of createUserDictionary(), getUserDictionary(String
locale, String dictName), or getUserDictionaries(String locale) method in UserDictionaryManager.
Get user dictionary:
UserDictionaryManager udm =
TextAnalyzerFactory.getUserDictionaryManager();
String locale = TextAnalyzerConstant.LANGUAGE_ID_EN_US;
// Get a user dictionary with locale and dictionary name
UserDictionary ud = udm.getUserDictionary (locale,”common_1”);
// Get all user dictionaries with the given
UserDictionary[] uds = udm.getUserDictionary (locale);
In the above code, the application first acquires an instance of the UserDictionaryManager interface, then
invokes the UserDictionaryManager’s getUserDictionary(String locale, String dictName) method to
get the specified user dictionary. The returned value is a UserDictionary object. The application can also
acquire the UserDictionary object through the UserDictionaryManager’s getUserDictionaries(String
locale) method. Please note that the returned value is an array of the UserDictionary object.
Add/remove user word:
206 Lotus Expeditor: Developing Applications for Lotus Expeditor
UserDictionaryManager udm =
TextAnalyzerFactory.getUserDictionaryManager();
String locale = TextAnalyzerConstant.LANGUAGE_ID_EN_US;
UserDictionary ud= udm.createUserDictionary(locale, ”common_1”);
ud.addUserWord(”tst”); // Add a use word
UserWord word = new UserWord();
word.userWord = ”Hello”;
word.type = UserWord.TYPE_EXCEPTION_WORD;
ud.addUserWord(word); // Add an exception word
UserWord uWord = new UserWord();
uWord.userWord = ”Pls”;
uWord.replaceWord = ”Please”;
uWord.type = UserWord.TYPE_EXCEPTION_WORD;
ud.addUserWord(uWord); // Add an auto correct word
ud.removeUserWord(”tst”);
ud.removeUserWord(word);
ud.removeUserWord(uWord);
In the above code, the application first acquires an instance of the UserDictionaryManager interface, and
creates a user dictionary to get an instance of UserDictionary. Of course, the application can also use
other methods in UserDictionaryManager to get an instance of UserDictionary. Next, the application can
invoke the addUserWord(String word) or addUserWord(UserWord userWord) methods in UserDictionary to
add a word into the user dictionary. The same word cannot be added into a user dictionary. The words
stored in the user dictionary are UserWord objects. The UserWord has two types: TYPE_USER_WORD(1) and
TYPE_EXCEPTION_WORD(2). The number in parenthesis is the integer value for each of the types. There are
thee kinds of words made by the two types:
v common user word - a UserWord object without a replaceWord value, whose type is TYPE_USER_WORD.
This kind of word is commonly a misspelled word, and treated as a correct word when spell checking
is performed.
v exception word - a UserWord object without a replaceWord value, whose type is TYPE_EXCEPTION_WORD.
This kind of word commonly is a correct word, and treated as a misspelled word when spell checking
is performed.
v auto-correction word - a UserWord object with a replaceWord value, whose type is
TYPE_EXCEPTION_WORD. This kind of word can be misspelled or correct, and treated as a misspelled
word. It will set a value into MisspelledWord’s autoCorrectReplacement field, for auto-correction by
the application when spell checking is performed.
When an application creates a UserWord object, the default type is TYPE_USER_WORD. If the application
wants to create an exception word or an auto-correction word, it must set the word’s type manually.
Next, the application invokes removeUserWord( String word) or removeUserWord(UserWord userWord) to
delete a user word from the user dictionary.
Get user words in a user dictionary:
UserDictionaryManager udm =
TextAnalyzerFactory.getUserDictionaryManager();
String locale = TextAnalyzerConstant.LANGUAGE_ID_EN_US;
UserDictionary ud= udm.createUserDictionary(locale, ”common_1”);
ud.addUserWord(”tst”); // Add a use word
UserWord uword = new UserWord();
uword.userWord = ”Hello”;
uword.type = UserWord.TYPE_EXCEPTION_WORD;
ud.addUserWord(uword); // Add an exception word
UserWord[] words = ud.getUserWords();
In the above sample code, the application first acquires an instance of the UserDictionaryManager
interface, and creates a user dictionary to get an instance of UserDictionary. Of course, the application
can also use other methods in UserDictionaryManager to get an instance of UserDictionary. For example,
the getUserDictionary(String locale, String dictName) method. The application can acquire the words
which the user stored in a user dictionary. The return value is an array of UserWord objects.
Developing applications 207
Contributing custom spell checking services: The TextAnalyzer framework defines two extension
points: com.ibm.rcp.textanalyzer.Engines and com.ibm.rcp.textanalyzer.Dictionaries. These extension
points for applications are used to contribute custom spell check engines and custom dictionaries. By
default, the TextAnalyzer framework provides two spell check engines: POE (IBM LanguageWare 2.7
engine), jFrost (IBM LanguageWare 5 engine) and 29 dictionaries which are the main dictionaries used for
spell checking within various languages. Applications can use them directly. You can contribute a custom
spell check engine or a custom dictionary into the framework as well.
Contributing a custom spell checking engine: You can contribute a custom engine to spell check the existing
29 dictionaries or a custom engine to spell check your custom dictionaries.
To contribute a custom spell checking engine, perform the following procedure:
1. Create a plug-in project to extend the com.ibm.rcp.textanalyzer.Engines extension point.
2. Add com.ibm.rcp.textanalyzer as its required bundle.
3. Create a class to implement the com.ibm.rcp.textanalyzer.spellchecker.SpellCheckerEngine
interface.
4. Create a class to implement the com.ibm.rcp.textanalyzer.spellcheck.UserDictionaryListener
interface to share the user dictionaries between different applications.
5. Complete the custom spell check engine extension in the plugin.xml file.
6. Build the project.
7. Package the project in a feature.
The following is an example of contributing a simple engine which can check format dictionaries (such
as, misspelt word(suggestion 1, suggestion 2,...., into the TextAnalyzer) is:
ss(as,sos,sis)
tst(test,tat,tot)
examplee(example,examples,exampled)
First create a plug-in project named com.ibm.contributed.custom.engine. Then open its META-INF or
MANIFEST.MF file. In the Dependencies tab, add the com.ibm.rcp.textanalyzer plug-in as its require
bundle. The MANIFEST.MF file will look like:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Engine Plug-in
Bundle-SymbolicName: com.ibm.contributed.custom.engine;singleton:=true
Bundle-Version: 1.0.0
Bundle-Vendor: IBM
Bundle-Localization: plugin
Require-Bundle: com.ibm.rcp.textanalyzer
Next, create a com.ibm.contributed.custom.engine package in its src directory. In the same directory,
create a class named CustomEngine, which implements the
com.ibm.rcp.textanalyzer.spellchecker.SpellCheckerEngine interface. In the CustomEngine class, an
inner class of UserDictionaryHandler which implements the
com.ibm.rcp.textanalyzer.spellchecker.UserDictionaryListener interface is created to handle
DataChangedEvent and UserDictionaryEvent for sharing the user dictionary data between applications.
SpellCheckerEngine.java file:
package com.ibm.contributed.custom.engine;
import java.util.ArrayList;
import com.ibm.rcp.textanalyzer.TextAnalyzerConstant;
import com.ibm.rcp.textanalyzer.TextAnalyzerFactory;
import com.ibm.rcp.textanalyzer.dictionarymanager.DictionaryInfo;
import com.ibm.rcp.textanalyzer.spellchecker.MisspelledWord;
import com.ibm.rcp.textanalyzer.spellchecker.SpellCheckerEngine;
import com.ibm.rcp.textanalyzer.spellchecker.udm.DataChangedEvent;
208 Lotus Expeditor: Developing Applications for Lotus Expeditor
import com.ibm.rcp.textanalyzer.spellchecker.udm.UserDictionaryEvent;
import com.ibm.rcp.textanalyzer.spellchecker.udm.UserDictionaryListener;
import com.ibm.rcp.textanalyzer.spellchecker.udm.UserDictionaryManager;
public class CustomEngine implements SpellCheckerEngine
{
// The locale for this instance
private String locale;
// The engine name for this instance
private String engineName="CustomEngine";
// The activated dictionaries for this instance
private ArrayList masters = new ArrayList();
// User dictionaries for this instance
private ArrayList userDics = new ArrayList();
// User dictionary handler for this instance
private UserDictionaryHandler udHandler=new UserDictionaryHandler(this.locale,
this.engineName, this.masters);
public boolean addIgnoreWord(String word) {
return SimpleUtils.ignoreWord(word);
}
public MisspelledWord[] checkSpelling(String texttoCheck, int numSuggestions,
boolean autoCorrect) {
if(texttoCheck.equalsIgnoreCase(""))
return new MisspelledWord[0];
return SimpleUtils.checkWordsInDictionary(texttoCheck, numSuggestions,
autoCorrect);
}
public MisspelledWord[] checkSpelling(String texttoCheck, int begin, int end,
int numSuggestions, boolean autoCorrect) {
String checkText=texttoCheck.substring(begin,end);
return checkSpelling(checkText, numSuggestions, autoCorrect);
}
public String[] getSuggestions(String misSpelledWord, int numSuggestions,
int suggestionType) {
if (suggestionType != TextAnalyzerConstant.SUGGESTION_TYPE_SPELL_AID){
return null;
}
if(numSuggestions==0)
return new String[0];
return SimpleUtils.getSuggestions(misSpelledWord, numSuggestions);
}
public boolean closeSession() {
closeAllDictionaries();
// Remove from listener list
UserDictionaryManager udm = TextAnalyzerFactory.getUserDictionaryManager();
udm.removeUserDictionaryListener(udHandler);
return true;
}
public boolean openSession(String locale, DictionaryInfo[] dicInfo) {
// Open the given dictionaries
if(locale==null)
return false;
if(dicInfo==null)
return false;
this.locale=locale;
for(int i=0; i<dicInfo.length; i++)
{
DictionaryInfo dict=dicInfo[i];
if(dict.getDictType()==DictionaryInfo.DICT_TYPE_SPELL_USER)
{
Developing applications 209
// Open the user dictionary
this.masters.add(0,dict);
this.userDics.add(dict);
}
else
{
// Open the main dictioary
this.masters.add(dict);
}
}
openDictionaries();
// Register with the user dictionary manager. Set event handlers
UserDictionaryManager udm = TextAnalyzerFactory.
getUserDictionaryManager();
udm.addUserDictionaryListener(udHandler);
return true;
}
public boolean setBooleanPreference(int prefID, boolean prefValue) {
return SimpleUtils.setBooleanPreference(prefID, prefValue);
}
// Create an inner class to handle the UserDictioanry event
private class UserDictionaryHandler implements UserDictionaryListener
{
private String locale;
private String engineName;
private ArrayList masters;
public UserDictionaryHandler(String locale, String engineName,
ArrayList masters)
{
this.locale=locale;
this.engineName=engineName;
this.masters=masters;
}
public void dataChanged(DataChangedEvent event) {
// Get the target dictionary to which to add or remove words
DictionaryInfo dicInfo = event.getTargetDictionary();
UserDictionaryManager udm = TextAnalyzerFactory.getUserDictionaryManager();
// Check event is for this engine and this locale
while (dicInfo.getLocale().equalsIgnoreCase(this.locale)
&& dicInfo.getEngineName().equalsIgnoreCase(this.engineName)) {
// Get the target dictionary
SimpleDictionary dict=SimpleUtils.getDictionaryContent
(dicInfo.getDictionaryName());
if (event.getType() == DataChangedEvent.EVENT_ADD_WORD) {
// Add the word to the target dictionary
SimpleUtils.addWordsToDictionary(dict, event.getData());
// Update the Eclipse xml file
udm.updateBinaryUserDictionaryStatus(dicInfo,
UserDictionaryManager.DICTIONARY_INFO_UPDATE);
break;
}
if (event.getType() == DataChangedEvent.EVENT_REMOVE_WORD) {
// Remove the word from the target dictionary
SimpleUtils.removeWordsToDictionary(dict, event.getData());
// Update the Eclipse xml file
udm.updateBinaryUserDictionaryStatus(dicInfo,
UserDictionaryManager.DICTIONARY_INFO_UPDATE);
break;
}
210 Lotus Expeditor: Developing Applications for Lotus Expeditor
break;
} // end of while
}
public void userDictionaryChanged(UserDictionaryEvent event) {
DictionaryInfo dicInfo = event.getTargetDictionary();
UserDictionaryManager udm = TextAnalyzerFactory.getUserDictionaryManager();
// Check event is for this engine and this locale
while (dicInfo.getLocale().equalsIgnoreCase(this.locale)
&& dicInfo.getEngineName().equalsIgnoreCase(this.engineName)) {
if (event.getType() == UserDictionaryEvent.EVENT_CREATE_USER_DICTIONARY) {
// Create new user dictionary and add to masters array
SimpleDictionary dict=SimpleUtils.createPhysicalDictionaryFile
(dicInfo.getDictionaryName(), dicInfo.getFilePath());
// Add new dictionary to the head of the masters ArrayList
this.masters.add(0, dict);
// Reopen the dictionaries
openDictionaries();
// Update the Eclipse xml file
udm.updateBinaryUserDictionaryStatus(dicInfo,
UserDictionaryManager.DICTIONARY_INFO_UPDATE);
break;
}
if (event.getType() == UserDictionaryEvent.EVENT_DELETE_USER_DICTIONARY) {
// Find the targetDictionary within the masters ArrayList
SimpleDictionary dict=getDictionaryFromMasters(dicInfo.getDictionaryName());
// Remove it from materdic list and delete the physical file
if (dict != null) {
this.masters.remove(dict);
SimpleUtils.deletePhysicalDictionaryFile(dicInfo.getDictionaryName(),
dicInfo.getFilePath());
// Reopen the dictionaries
openDictionaries();
// Update the Eclipse xml file
udm.updateBinaryUserDictionaryStatus(dicInfo,
UserDictionaryManager.DICTIONARY_INFO_DELETE);
break;
}
} // end of if delete event
break;
} // end of while locale applies loop
} // end of try block
}
private void openDictionaries()
{
SimpleUtils.openDictionaries(this.masters);
}
private void closeAllDictionaries()
{
SimpleUtils.closeDictionaries(this.masters);
}
Developing applications 211
private SimpleDictionary getDictionaryFromMasters(String dictName)
{
return SimpleUtils.findDictionaryInList(dictName, this.masters);
}
}
SimpleDictionary.java file:
package com.ibm.contributed.custom.engine;
import java.util.HashMap;
public class SimpleDictionary {
public String dictName;
public String filePath;
public HashMap words;
}
SimpleUtils.java file:
package com.ibm.contributed.custom.engine;
import java.util.HashMap;
import java.util.List;
import com.ibm.rcp.textanalyzer.spellchecker.MisspelledWord;
import com.ibm.rcp.textanalyzer.spellchecker.SpellCheckerPreference;
import com.ibm.rcp.textanalyzer.spellchecker.udm.UserWord;
public class SimpleUtils
{
private static HashMap allWords = new HashMap();
private static int ignorePrefs = 0;
private String token =" ";
public static SimpleDictionary createPhysicalDictionaryFile
(String dicName, String dicFilePath)
{
// TODO Create a dictionary file.
return null;
}
public static boolean deletePhysicalDictionaryFile(String dicName,
String dicFilePath)
{
//TODO Delete the dictionary file
return false;
}
public static SimpleDictionary getDictionaryContent(String dicName)
{
//TODO Read the physical dictionary file
return null;
}
public static void setDictionaryContent(String dicName)
{
//TODO Write the physical dictionary file
}
public static void addWordsToDictionary(SimpleDictionary dict,
UserWord[] words)
{
//TODO Append the words to the end of the dictionary
}
public static void removeWordsToDictionary(SimpleDictionary dict,
UserWord[] words)
{
212 Lotus Expeditor: Developing Applications for Lotus Expeditor
//TODO Append the words to the end of the dictionary
}
public static boolean openDictionaries(List dictionaries)
{
//TODO Open all the dictionaries and collect all misspelt words into a map
return false;
}
public static boolean closeDictionaries(List dictionaries)
{
//TODO Close all the dictionaries and dispose the data collect in
//openDictionaries() method
return false;
}
public static SimpleDictionary findDictionaryInList(String dicName,
List dictionaries)
{
//TODO Find the dictionary in the dictionaries list
return null;
}
public static boolean ignoreWord(String word)
{
if(allWords.containsKey(word))
allWords.remove(word);
return true;
}
public static String[] getSuggestions(String misSpelledWord,
int numSuggestions)
{
String strSuggestion;
if(allWords.containsKey(misSpelledWord))
strSuggestion = (String)allWords.get(misSpelledWord);
else
strSuggestion = "";
String[] aSuggestion=strSuggestion.split(",");
int len=aSuggestion.length;
if(len<numSuggestions)
return aSuggestion;
else
{
String[] retV=new String[numSuggestions];
for(int i=0;i<numSuggestions; i++)
retV[i]=aSuggestion[i];
return retV;
}
}
public static boolean setBooleanPreference(int prefID, boolean prefValue) {
if (prefValue) {
// Turn on the flag
ignorePrefs |= prefID;
}
else {
// Turn off the flag
ignorePrefs &= ~prefID;
}
return true;
}
private static boolean canIgnore(String word, int ignorePrefs) {
if ((ignorePrefs & SpellCheckerPreference.PREF_IGNORE_WORD_FILE) ==
SpellCheckerPreference.PREF_IGNORE_WORD_FILE) {
Developing applications 213
return false;
}
if ((ignorePrefs & SpellCheckerPreference.PREF_IGNORE_WORD_URLS) ==
SpellCheckerPreference.PREF_IGNORE_WORD_URLS) {
return false;
}
if ((ignorePrefs & SpellCheckerPreference.PREF_IGNORE_WORD_IN_UPPERCASE) ==
SpellCheckerPreference.PREF_IGNORE_WORD_IN_UPPERCASE) {
int len = word.length();
for(; len > 0; len--){
if (Character.isLowerCase(word.charAt(len-1)))
break;
}
if (len == 0) return true;
}
if ((ignorePrefs & SpellCheckerPreference.PREF_IGNORE_WORD_WITH_NUMBER) ==
SpellCheckerPreference.PREF_IGNORE_WORD_WITH_NUMBER) {
int len = word.length();
for(; len > 0; len--){
if (Character.isDigit(word.charAt(len-1)))
return true;
}
}
return false;
}
public static MisspelledWord[] checkWordsInDictionary(String texttoCheck,
int numSuggestions, boolean autoCorrect)
{
//TODO
return null;
}
}
Next, complete the engine extension in plugin.xml file. The finished plugin.xml file looks like:
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<plugin>
<extension
point="com.ibm.rcp.textanalyzer.Engines">
<engine
class="com.ibm.contributed.custom.engine.CustomEngine"
interface="com.ibm.rcp.textanalyzer.spellchecker.SpellCheckerEngine"
locales="en-US,zh-CN"
name="CustomEngine"
platform="All"
provider="IBM"
thirdParty="true"/>
</extension>
</plugin>
Next, build the project, if you are using Eclipse PDE tools, the build.properties file looks like:
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
plugin.xml
After completing the above steps, the project structure looks like:
com.ibm.contributed.custom.engine/
src/
com.ibm.contributed.custom.engine/
214 Lotus Expeditor: Developing Applications for Lotus Expeditor
CustomEngine.java
SimpleDictionary.java
SimpleUtils.java
META-INF/
MANIFEST.MF
build.properties
plugin.xml
Finally, you should include the plug-in in your feature project:
<plugin
id="com.ibm.contributed.custom.engine"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
The feature/plug-in containing the custom engine can be added into the TextAnalyzer framework after
installation. In this sample, after installation this plug-ins will be packaged into a jar file:
com.ibm.contributed.custom.engine_1.0.0.jar.
Contributing custom dictionaries: You can contribute your existing custom dictionary files into the
TextAnalyzer framework. The framework supports two engines:
v jFrost - IBM LanguageWare 5 engine
v POE - IBM LanguageWare 2.7 engine
LanguageWare provides the underlying functionality required to enable all types of applications to
process and understand natural language text. The IBM LanguageWare provides the LanguageWare
Resource Workbench (LRW) to create custom dictionaries based on the jFrost engine. If you wish to use
the default provided jFrost or POE engine as your dictionary’s check engine, your dictionary should be
jFrost or POE format, otherwise it will not be checked successfully.
To contribute a custom dictionary, perform the following procedure:
1. Create a plug-in project to extend the com.ibm.rcp.textanalyzer.Dictionaries extension point.
2. Put the custom dictionary .dic file into a directory in the plug-in project.
3. Complete the custom dictionary extension in plugin.xml file.
4. Build the project.
5. Package the project in a feature.
The following is an example of contributing an English (United States) medical dictionary that can be
checked by the jFrost engine into the TextAnalyzer framework.
First, create a plug-in project named com.ibm.sample.dic.en_US_Medical.
Then, create a directory named dictionaries in the project, and put the custom dictionary file
en_US_medical.dic into that directory;
Next, complete the dictionary extension in the plugin.xml file. The finished
plugin.xml file should look like:
<plugin>
<extension
point="com.ibm.rcp.textanalyzer.Dictionaries">
<dictionary
description="English medical dictionary"
dict_name="en_US_medical.dic"
engine="jFrost"
filePath="./dictionaries/en_US_medical.dic"
language="en-US"
locale="en-US"
provider="IBM"
Developing applications 215
version_info="1.0.0"
type="spell"/>
</extension>
</plugin>
Next, build the project. If you are using Eclipse PDE tools, make sure that the dictionaries directory is
built into the binary. The build.properties file should look like:
bin.includes = plugin.xml,\
META-INF/,\
dictionaries/
After completing the above steps, the project structure should look like:
com.ibm.sample.dic.en_US_Medical/
dictionaries/
en_US_medical.dic
META-INF/
MANIFEST.MF
build.properties
plugin.xml
At last, you should include this plug-in in your feature project, make sure this plug-in will unpack after
installation. To do this: either check Unpack the plug-in archive after the installation in the feature
Plug-ins and Fragments view. Or, add the following lines to you feature.xml file:
<plugin
id="com.ibm.sample.dic.en_US_Medical"
version="1.0.0"
unpack="true"/>
The feature/plug-in containing the custom medical dictionary can now be added into the TextAnalyzer
framework after installation. In this sample, the file structure after installation should look like:
com.ibm.sample.dic.en_US_Medical_1.0.0/
dictionaries/
en_US_medical.dic
META-INF/
MANIFEST.MF
plugin.xml
Implementing an embedded Web browser
Lotus Expeditor Client supports running a Web browser that is embedded in the client window.
The embedded browser is a configurable and manageable browser that you can embed in a client
application. The com.ibm.rcp.ui.browser plug-in provides APIs that you can use to implement a web
browser view.
The embedded browser plug-in does the following:
v Provides default user interface controls, such as a toolbar, status bar, and history bar.
v Is based on the DOM Browser developed by IBM. The DOM Browser is based on the Eclipse Standard
Widget Toolkit (SWT) Browser, and also leverages APIs that are native to the supported browsers.
v Supports Internet Explorer on Windows and Mozilla on Linux.
v Supports displaying pages in HTML/DHTML(CSS/Script), and XML format. Supports displaying
plug-ins, applets, activeX, flash, and PDF content.
v Provides APIs that you can use to customize it and configure its security.
v Supports the com.ibm.rcp.ui.browser.portal-feature feature in a managed client environment, which you
can install to add the Managed Browser Administration Portlet to WebSphere Portal. Administrators
can use this portlet to further customize the browser view.
To implement an embedded browser, perform the following steps:
216 Lotus Expeditor: Developing Applications for Lotus Expeditor
1. Create an embedded browser view.
2. Enhance the embedded browser.
Creating an embedded browser:
You can use the APIs provided with the client to create an embedded browser.
To create an embedded browser, use one of the following APIs:
v BrowserFactory.launch API – Returns a full instance of an EmbeddedBrowser object that the application
can continue to interact with by registering to be notified of browser events and reacting to them. For
example, you could implement the addTitleListener to listen for events that make it suitable for the
application to change the browser title and code the application to do so.
v IWorkbenchPage.showView API – Opens a URL in a browser view. Use this method if you do not want
to subsequently perform advanced interactions with the browser.
To instantiate an embedded browser, perform the following steps:
1. Define the embedded browser configuration ID.
String id = browserAppId + ".browserview." + Integer.toString(++counter);
2. Construct an embedded browser configuration hash map and pass in the ID you defined in the
previous Step.
Map configMap = new HashMap();
configMap.put(BrowserPreference.ID, id);
configMap.put(BrowserPreference.INITIAL_URL, "http://www.ibm.com");
...
3. Retrieve a reference to the current page using the getActivePage() method of the IWorkbenchPage
interface.
IWorkbenchPage activePage=
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
4. Do one of the following:
v Use the launch() method of the BrowserFactory interface to instantiate the embedded browser.
For example:
EmbeddedBrowser browser = BrowserFactory.launch(activePage, configMap, id);
v Use the IWorkbenchPage interface to start the embedded browser, by providing the configuration
map to the setBrowserConfigMap() method. Then use the showView() method of the
IWorkbenchPage interface to create the view, and finally, the getBrowser() method to display it.
For example:
BrowserFactory.setBrowserConfigMap(id, configMap);
activePage.showView(BrowserPreference.BROWSER_VIEW_ID, id,
IWorkbenchPage.VIEW_ACTIVATE);
EmbeddedBrowser browser = BrowserFactory.getBrowser(id);
Enhancing an embedded browser view:
Use the EmbeddedBrowser interface to enhance the view by adding listeners to handle events and
implementing navigation.
To enhance an embedded browser view, you can perform the following steps:
1. Add listeners to handle any of the following events:
v Close window event:
Developing applications 217
myEmbeddedBrowser.addCloseWindowListener(new
EmbeddedBrowserCloseWindowListener(){
Public void close(){
//dispose of my widget
}
});
v Completed document event:
myEmbeddedBrowser.addDocumentCompleteListener(new
EmddedBrowserDocumentCompleteListener(){
Public void changed(EmbeddedBrowserDocumentCompleteEvent event){
//get the HTML document
}
});
v URL change event:
myEmbeddedBrowser.addLocationListener(new
EmbeddedBrowserLocationListener(){
Public void changed(EmbeddedBrowserLocationEvent event){
//get the url.
}
Public void changing(EmbeddedBrowserLocationEvent event){
//get the changing url, and set doit.
}
});
v Open window event:
myEmbeddedBrowser.addOpenWindowListener(new
EmbeddedBrowserOpenWindowListener(){
public void onAfterOpenWindow(EmbeddedBrowserWindowEvent event) {
//do some task after a new window opens.
}
public void onBeforeOpenWindow(EmbeddedBrowserOpenWindowEvent event) {
//do some task before a new window opens.
}
});
v Title change event:
myEmbeddedBrowser.addTitleListener(new EmddedBrowserTitleListener(){
Public void changed(EmbeddedBrowserTitleEvent event){
//get the changed title and set it as the new title
}
});
2. You can implement navigation using one of the following methods:
v Get the current URL
String url= myEmbeddedBrowser.getUrl();
v Set the URL
myEmbeddedBrowser.setUrl(“http://www.ibm.com”);
v Stop the retrieval of a URL
myEmbeddedBrowser.stop();
3. You can define a cookie and pass it to a Web address.
String url=http://www.ibm.com;
//set a cookie before navigating to the Web address.
String cookie=”aaa=bbb”;//cookie string
myEmbeddedBrowser.setCookie(url,cookie);
myEmbeddedBrowser.setUrl(“http://www.ibm.com”);
218 Lotus Expeditor: Developing Applications for Lotus Expeditor
Setting browser preferences: You can define default preference settings for the embedded browser in
the following ways:
v Programmatically using the fields defined in the BrowserPreferences class.
v By defining Eclipse preferences.
Setting browser preferences programmatically:
Use a configuration map to programmatically define preferences for the embedded browser.
You can define values for the following embedded browser preferences:
Field name Description
static java.lang.String ALLOW_DOWNLOAD_OVERWRITE Determines whether to overwrite the local file
static java.lang.String ALLOW_SCRIPTS Determines whether Javascript and ActiveX are
enabled.
static java.lang.String BROWSER_EDITOR_ID Defines the embedded browser editor id.
static java.lang.String BROWSER_VIEW_ID Defines the embedded browser view id.
static java.lang.String CONFIG_ID Specifies the configuration id of the embedded
browser configuration.
static java.lang.String CONFIG_TITLE Specifies the title of the embedded browser
configuration.
static java.lang.String
CONTROLLED_DOWNLOAD_DOMAIN_LIST
List of domains from which a user cannot
download unsolicited files.
static java.lang.String CONTROLLED_DOWNLOAD_PATH Local path to which to save a downloaded file.
static java.lang.String DOMAINS_DENY If set to true, specifies that navigation to the
domains in the DOMAINS_LIST field is prohibited.
If set to false, specifies that navigation to the
domains in the domainsList preference is permitted.
The default is to ″deny nothing.″ When a user
navigates to a restricted domain, an error restricted,
a message is displayed for the user.
static java.lang.String DOMAINS_LIST List of domains for restricting navigation.
static java.lang.String DOWNLOAD_DOMAINS_DENY If set to true, the list in the
DOWLOAD_DOMAINS_LIST field specifies
domains from which downloading files is
prohibited. If set to false, it specifies a list of
domains from which downloading files is
permitted. The default is to ″deny nothing.″
static java.lang.String DOWNLOAD_DOMAINS_LIST List of domains for restricting unsolicited file
download.
static java.lang.String ENABLE_APPLET Determines whether to support Java2 Applets.
Applies to Internet Explorer browsers only.
static java.lang.String ENABLE_BOOKMARKS Determines whether Bookmark functions are
enabled.
static java.lang.String ENABLE_CONTEXT_MENU Determines whether a right click displays a context
menu. Applies only to Internet Explorer.
static java.lang.String ENABLE_HELP Determines whether the F1 key displays an
associated Help topic.
static java.lang.String FILE_PERMIT Determines whether a browser can access local files
and certain Mozilla ″about:″ pages.
Developing applications 219
Field name Description
static java.lang.String HOME_URL The Web address that displays when a user clicks
the Home button. If useBrowserHome = ″true″, the
browser’s home page defaults to the home page
address defined by the native browser and ignores
the value you define here. If useBrowserHome =
″false″, the browser’s home page uses this value.
The default value for this preference is the value of
the initialURL preference.
static java.lang.String ID Specifies the ID of the embedded browser
configuration.
static java.lang.String INITIAL_URL Default Web address for the browser. This is the
address that displays when the browser first opens.
static java.lang.String LAUNCH_TYPE Specifies the launch type for the browser instance.
Options are ″editor″ or ″view.″
static java.lang.String LOCK_TOOL_BAR Determines whether toolbar items can be dragged
and dropped by users. Not applicable if
showToolbar is set to false.
static java.lang.String LTPA_TOKEN_MANAGER Provided by the launcher, this object implements
the ILTPATokenManager interface.
static java.lang.String POPUP_DOMAINS_DENY If set to true, the list in the
POPUP_DOMAINS_LIST field specifies a list of
domains from which popups are prohibited. If set
to false, it specifies a list of domains from which
popus are permitted. The default is to ″deny
nothing.″
static java.lang.String POPUP_DOMAINS_LIST List of domains for restricting unsolicited popups.
static java.lang.String POPUP_STYLE Defines the style of the popup window. The options
are:
default – If the browser window does not have a
status bar or a tool bar, a new window opens.
Otherwise, the window opens in a tab view.
popup – opens a new window.
embedded – opens a window in the active tab
view.
static java.lang.String PROXY_AUTO_CONFIG_URL Specifies the automatic proxy configuration URL. If
this field is set, other proxy setting fields are
ignored.
static java.lang.String PROXY_BY_PASS Web addresses that do not use the proxy server. For
Mozilla, use a comma as a separator and for
Internet Explorer, use a semicolon.
static java.lang.String PROXY_FTP_PORT Defines the proxy port for the FTP protocol.
static java.lang.String PROXY_FTP_SERVER Proxy server for the FTP protocol.
static java.lang.String PROXY_GOPHER_PORT Defines the proxy port for the Gopher protocol.
static java.lang.String PROXY_GOPHER_SERVER Proxy server for the Gopher protocol.
static java.lang.String PROXY_HTTP_PORT Defines the proxy port for the HTTP protocol.
static java.lang.String PROXY_HTTP_SERVER Proxy server for the HTTP protocol.
static java.lang.String PROXY_PORT Unified proxy port. Defines the port number for the
proxy server. If set, this setting is used for all
network protocols.
220 Lotus Expeditor: Developing Applications for Lotus Expeditor
Field name Description
static java.lang.String PROXY_SERVER Unified proxy server. If this field is set, proxies for
separate network protocols, such as HTTP, Gopher,
and FTP are ignored. Proxies for each network
protocol are set to this proxy.
static java.lang.String PROXY_SOCKS_PORT Defines the proxy port for SOCKS.
static java.lang.String PROXY_SOCKS_SERVER Proxy server that supports SOCKS packages.
static java.lang.String PROXY_SSL_PORT Defines the proxy port for the SSL protocol.
static java.lang.String PROXY_SSL_SERVER Proxy server for the SSL protocol.
static java.lang.String SHOW_BOOKMARK Determines whether the Bookmark button is
enabled.
static java.lang.String SHOW_HISTORY Determines whether the Back and Forward buttons
are enabled.
static java.lang.String SHOW_HOME Determines whether the Home button is enabled.
static java.lang.String SHOW_PAGE_CONTROL Determines whether the Stop and Refresh buttons
are enabled.
static java.lang.String SHOW_PRINT Determines whether the Print button is enabled.
static java.lang.String SHOW_TOOL_BAR Determines whether the toolbar is displayed. If
false, showHistory, showPageCtrl showHome,
showPrint, and showBookmark are all forced false.
static java.lang.String SHOW_URL_ENTRY Determines whether the URL entry field is
displayed. If false, the user cannot type in URLs.
static java.lang.String TITLE Embedded Browser preference name: title String
specifying the browser title
static java.lang.String USE_BROWSER_HOME If set to false, the Home button opens the page
defined in the homeURL field or in the initialUrl
field if no homeURL value is defined. If set to true,
the Home button opens the home page defined for
the native browser. The home page for a native
Internet Explorer browser is specified using the
Internet Options menu. The home page for a native
Mozilla browser is specified using a private profile
preference, called ″browser.startup.homepage.″ On
Windows, this configuration item only applies to
Internet Explorer.
static java.lang.String USE_BROWSER_ICON Determines which view icon to display based on
the embedded browser type, Internet Explorer or
Mozilla.
static java.lang.String USE_BROWSER_STATUS Determines whether status text is obtained from
Browser StatusText events or from DOM mouseover
events.
static java.lang.String USE_BROWSER_TITLE Determines whether view title is derived from
BrowserTitle events (typically title of a displayed
webpage) or from the TITLE field.
static java.lang.String USER_PREFERENCE_NAMES Array of embedded browser preferences for an end
user.
static java.lang.String WEB_BROWSER Internal field.
To set embedded browser preferences programmatically, perform the following steps:
1. Define the embedded browser configuration ID.
Developing applications 221
String id =
browserAppId + ".browserview." + Integer.toString(++counter);
2. Construct an embedded browser configuration map and pass values for the preferences into it.
Map configMap = new HashMap();
configMap.put(BrowserPreference.browser_field, value);
configMap.put(BrowserPreference.browser_field, value);
...
where the browser_field variables are preferences you want to set for the browser.
3. Create the embedded browser.
Setting browser preferences as Eclipse preferences:
Use Eclipse preferences to define default preferences for the embedded browser.
You can use Eclipse preferences to define values for the following subset of embedded browser
preferences:
Key Value options Default value Description
embeddedBrowser ″platform″
″MSIE″
″Mozilla″
″platform″ ″platform″ – Defaults to
″MSIE″ on Windows and to
″Mozilla″ on Linux.
″MSIE″ – Microsoft Internet
Explorer for the Windows
platform. If specified on the
Linux platform, ″Mozilla″ is
used instead and a warning
is logged.
″Mozilla″ – Browser for the
Linux platform.
enableApplet Boolean string false Determines whether to
support Java2 Applets.
Applies to Internet Explorer
browsers only.
enableBookmarks Boolean string true Determines whether
Bookmark functions are
enabled.
homeURL String null The Web address that
displays when a user clicks
the Home button. If
useBrowserHome = ″true″,
the browser’s home page
defaults to the home page
address defined by the
native browser and ignores
the value you define here.
If useBrowserHome =
″false″, the browser’s home
page uses this value. The
default value for this
preference is the value of
the initialURL preference.
222 Lotus Expeditor: Developing Applications for Lotus Expeditor
Key Value options Default value Description
initialURL String ″about:blank″ Default Web address for the
browser. This is the address
that displays when the
browser first opens. The
URL may contain
%USERID% and
%PASSWORD% tokens that
are replaced by the values
you define for the
%USERID% and
%PASSWORD%
preferences.
popupStyle String null string or ″default″ Defines the style of the
popup window. The
options are:
default – If the browser
window does not have a
status bar or a tool bar, a
new window opens.
Otherwise, the window
opens in a tab view.
popup – opens a new
window.
embedded – opens a
window in the active tab
view.
proxyByPass String null Web addresses that do not
use the proxy server. For
Mozilla, use a comma as a
separator and for Internet
Explorer, use a semicolon.
proxyFtpPort Integer 0 Defines the proxy port for
the FTP protocol.
proxyFtpServer String null Proxy server for the FTP
protocol.
proxyGopherPort Integer 0 Defines the proxy port for
the Gopher protocol.
proxyGopherServer String null Proxy server for the Gopher
protocol.
proxyHttpPort Integer 0 Defines the proxy port for
the HTTP protocol.
proxyHttpServer String null Proxy server for the HTTP
protocol.
proxyPort Integer 0 Unified proxy port. Defines
the port number for the
proxy server. If set, this
setting is used for all
network protocols.
Developing applications 223
Key Value options Default value Description
proxyServer String null Unified proxy server. If this
item is set, proxies for
separate network protocols,
such as HTTP, Gopher, and
FTP are ignored. Proxies for
each network protocol are
set to this proxy.
proxySocksPort Integer 0 Defines the proxy port for
SOCKS.
proxySocksServer String null Proxy server that supports
SOCKS packages.
proxySslPort Integer 0 Defines the proxy port for
the SSL protocol.
proxySslServer String null Proxy server for the SSL
protocol.
showBookmark Boolean string false Determines whether the
Bookmark button is shown.
showHistory Boolean string true Determines whether the
Back and Forward buttons
are shown and enabled.
showHome Boolean string true Determines whether the
Home button is shown and
enabled.
showPageCtrl Boolean string true Determines whether the
Stop and Refresh buttons
are shown enabled.
showPrint Boolean string true Determines whether the
Print button is shown and
enabled.
showToolbar Boolean string true Determines whether the
toolbar is displayed. If
false, showHistory,
showPageCtrl, showHome,
showPrint, and
showBookmark are all
forced false. This
configuration item can be
deprecated since it can be
determined from those
other configuration items.
showURLEntry Boolean string true Determines whether the
URL entry field is
displayed. If false, the user
cannot type in URLs.
title String ″title″ Title that displays in the
embedded browser title bar.
1. Modify the plugin_customization.ini file in the com.ibm.rcp.platform.personality.branding plug-in to
provide values for the keys you want to set. Use the following syntax:
com.ibm.rcp.ui.browser/key=value
For example, to specify that bookmarks should be enabled in all browser views, add the following
key and value:
224 Lotus Expeditor: Developing Applications for Lotus Expeditor
com.ibm.rcp.ui.browser/showBookmark=true
com.ibm.rcp.ui.browser/enableBookmarks=true
2. Save and close the plugin_customization.ini file. These default settings will be in effect for any users
that provision the com.ibm.rcp.platform.personality.branding plug-in to their client machines.
Using the Rich Text Editor
The Rich Text Editor is a text editor that provides a full set APIs to control text elements and wrapper. It
is based on the DOM browser widget, which itself is based on the SWT browser widget.
The Rich Text Editor has the advantage of being completely configurable, manageable, and easily
modified. It is based on DOM Browser, can be embedded in a Java application, and provide a default UI
(such as a tool bar). It provides APIs for application development, and can extend an application’s
functions, such as handling events and contents.
Creating a custom Rich Text Editor: To create a custom Rich Text Editor, use RichTextEditorFactory
class to get a RichTextEditor instance:
1. Add com.ibm.rcp.rte to the Require Bundles item in the MANIFEST.MF file.
2. Import com.ibm.rcp.rte.RichTextEditor and com.ibm.rcp.rte.RichTextEditorFactory into your
code.
3. Create RichTextEditor using the RichTextEditorFactory class:
RichTextEditorFactory .createRichTextEditorInstance(Composite parent,
int compositeStyle, int toolBarStyle, int rteStyle)
v The argument parent is the container composite for the Rich Text Editor
v The argument compositeStyle is the SWT style of the Rich Text Editor
v The argument toolBarStyle is used to configure the toolbar
v The argument rteStyle is used to configure rte, for example, setting it as a Read-Only property
The Rich Text Editor uses about:blank as the default editing page. You can specify a page or set the
string content after the Rich Text Editor builds.
Using and controlling the custom Rich Text Editor: After you create a RichTextEditor instance, you
can use it as a composite. It provides its own UI interface for configuration. You can either click the
toolbar to execute the function, or use the API to implement your requirement.
Using APIs to control Rich Text Editor documents: APIs are provided for controlling Rich Text Editor
documents. You can use them to modify the document. For example, rte.bold is to set the bold or
unbold status of selected elements.
Adding custom listeners to Rich Text Editor documents: The Rich Text Editor provides add/remove listener
methods. You can use them to add custom listeners to documents, implement new functions, or attach
new components to the Rich Text Editor.
For example:
rte.addDocumentEventListener("contextmenu",new EventListener() {
public void handleEvent(org.w3c.dom.events.Event e) {
....
}
});
Sample code:
package com.ibm.rcp.samples.rte;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.layout.GridLayout;
Developing applications 225
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
import org.w3c.dom.events.EventListener;
import com.ibm.rcp.rte.RichTextEditor;
import com.ibm.rcp.rte.RichTextEditorFactory;
public class RTEView extends ViewPart {
public static final String ID = "RTEWrapper.view";
private Composite parent;
private Composite realParent;
public void createPartControl(Composite parent) {
realParent = parent;
this.parent = new Composite(parent, SWT.NONE);
RichTextEditor rte=
RichTextEditorFactory.createRichTextEditorInstance(
this.parent,
SWT.BORDER | SWT.FLAT,
RichTextEditor.TOOLBAR_STYLE_ALL,
RichTextEditor.RTE_STYLE_DEFAULT
);
// rte.setUrl("file:///xxxx.html."); This line can set a
// local file as default editing page.
// rte.setUrl("http://www.ibm.com"); This line can set a
// website page as default editing page.
rte.setSourceContent(
"<html>" +
"<head>" +
"<title>setSourceContent</title>" +
</head>" +
"<body>" +
"test" +
"<a href=\"http://www.ibm.com\">link</a>" +
"</body>" +
"</html>"
);
rte.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent key) {
System.out.println(" keyPressed");
}
public void keyReleased(KeyEvent key) {
System.out.println(" keyReleased");
}
});
rte.addDocumentEventListener("contextmenu",new EventListener() {
public void handleEvent(org.w3c.dom.events.Event e) {
System.out.println("contextmenu");
}
});
rte.addDocumentEventListener("focusin",new EventListener() {
public void handleEvent(org.w3c.dom.events.Event e) {
System.out.println("focusin");
}
});
GridLayout layout = new GridLayout();
realParent.layout();
this.parent.setLayout(layout);
this.parent.layout();
}
public void setFocus() {
// TODO Auto-generated method stub
}
}
226 Lotus Expeditor: Developing Applications for Lotus Expeditor
Accessing a Web address with the integrated browser application
Lotus Expeditor has an integrated browser application within the workbench. You can use the
com.ibm.rcp.ui.browser.launcher code provided in Lotus Expeditor to implement a hypertext link in
your application that, when clicked, opens the Web address in a browser view. You can set the browser
view to be an embedded browser in the application tabs or an external browser, which is set as default.
To access a web address in your application by launching an integrated browser application, perform the
following procedure:
1. Set the Required Bundle as: Require-Bundle: com.ibm.rcp.ui.browser.launcher.
2. In the class that implements the API, import the following class:
import com.ibm.rcp.ui.browser.launcher.BrowserLauncher;
3. Get the BrowserLauncher instance using the BrowserLauncher class.
BrowserLauncher launcher=BrowserLauncher.getLauncher();
4. Launch the given URL with the integrated browser application through the default method.
launcher.launchURLasDefault("http://www.ibm.com");
This will launch a browser view with web address http://www.ibm.com. The default behavior is set
by the user through the Web Browser preference page. The user can select the embedded web browser
in the client or the external browser set as the default to launch this web address.
5. Launch the given URL with the embedded browser view.
final String secondaryId = BROWSER_VIEW_ID + Integer.toString(++counter);
Map configMap = new HashMap();
configMap.put(BrowserPreference.ID, secondaryId);
BrowserLauncher bLauncher=BrowserLauncher.getLauncher();
bLauncher.launchURLasEmbedded("http://www.ibm.com",null, configMap);
This will force the launch of an embedded browser in the application tab of the workbench. You can
have advanced control of the browser view by configuring the configMap object. All configurations are
described in “Setting browser preferences” on page 219.
Note: This sample code requires the bundle com.ibm.rcp.ui.browser, and should import
com.ibm.rcp.ui.browser.launcher.BrowserLauncher in the class.
6. Launch the given URL with an external browser.
launcher. launchURLasExternal ("http://www.ibm.com");
This will force the launch of an external browser with a given URL.
The following is sample code illustrates the urlLauncher.java.
package com.ibm.rcp.samples.ui.browser.launcher;
import java.util.HashMap;
import java.util.Map;
import com.ibm.rcp.ui.browser.BrowserPreference;
import com.ibm.rcp.ui.browser.launcher.BrowserLauncher;
public class urlLauncher{
private static final String BROWSER_VIEW_ID =
"com.ibm.rcp.ui.samples.browsertab"; //$NON-NLS-1$
private static int counter;
public boolean launchURL(String url){
BrowserLauncher launcher=BrowserLauncher.getLauncher();
return launcher.launchURLasDefault(url);
}
public boolean launchURLasEmbed(String url){
final String secondaryId = BROWSER_VIEW_ID + Integer.toString(++counter);
Map configMap = new HashMap();
configMap.put(BrowserPreference.ID, secondaryId);
BrowserLauncher bLauncher=BrowserLauncher.getLauncher();
Developing applications 227
return bLauncher.launchURLasEmbedded(url,null, configMap);
}
public boolean launchURLasExternal(String url){
BrowserLauncher launcher=BrowserLauncher.getLauncher();
return launcher.launchURLasExternal(url);
}
}
Customizing the integrated browser application with Eclipse preferences: Application developers can
customize the behavior of the integrated browser application by editing the plug-in customization file
(plugin_customization.ini). The default settings control the behavior of the user’s client after
provisioning.
Table 13. Configurable items
Preferences Values Default Descriptions
showPreferencePage True
False
True Toggle the Web Browser preference
page
enableBrowserSSO True
False
True Toggle support for pre-authentication
allowUserDefinedHomePage True
False
True Toggle home page setting UI. This
depends on the setting of
showPreferencePage.
adminHomePage String value about:blank Configures the admin default home
page
For example, if an application developer wants to set the admin home page with his company’s web site,
he can modify its plug-in customization file by adding the following lines:
#Set home page for browser application
com.ibm.rcp.ui.browser.launcher/adminHomePage=http://www.ibm.com
Using the Portlet Viewer
The Portlet Viewer is an Eclipse view wrapper for a JSR 168 portlet. It consists of a SWT browser instance
whose URL points to a JSR 168 portlet deployed onto the Portlet Container. The main benefit of the
Portlet Viewer is that it provides a unified rich client component for the composite application
framework, for portlet presentation and interaction.
A Portlet Viewer instance can be instantiated and contributed to the Lotus Expeditor platform in two
ways:
v In the Portal-managed environment, portlet information will be stored in the Composite Application
(CA) XML file and passed down to the Lotus Expeditor platform through the Composite Application
Infrastructure. The portlet information from the CA XML file will be translated to a Portlet Viewer
instance by the Topology Handler. Its important to note that the portlets are deployed as part of a
composite application and that the Portlet Viewer instances are merely views within the application
perspective.
v In the non Portal managed environment, the Portlet Viewer instances can be defined and contributed
using the Portlet Viewer extension point - com.ibm.rcp.portletviewer.portlets.
To declaratively define and contribute a Portlet Viewer instance to the Lotus Expeditor platform, perform
the following procedure:
1. Select the Client Services Portlet project you want to wrap with the Portlet Viewer instance.
2. Right click the plugin.xml descriptor file and select Open With > Plug-in Manifest Editor.
3. Select the Extensions tab.
228 Lotus Expeditor: Developing Applications for Lotus Expeditor
4. Select Add.
5. Locate the com.ibm.rcp.portletviewer.portlets extension point and select Finish.
6. Update the <portletData> element attributes.
7. Save the plugin.xml file.
Portlet Viewer extension examples: Sample definition for a JSR 168 portlet
<extension point="com.ibm.rcp.portletviewer.portlets">
<portletData
entityId="com.bank.portletviewer.helloworldportlet"
portlettype="JSR168"
portletname="HelloWorldPortlet"
portletwindowid="HelloWorldPortletWindow1"
contextroot="/HelloWorld ">
</portletData>
</extension>
Using the Portlet Viewer with WSRP portlets: The Portlet Viewer can also be used to view a remote
WSRP portlet, through the WSRP feature in Lotus Expeditor. Just like the Portlet viewer for a local JSR
168 portlet, you have two choices to view a WSRP portlet:
v In the Portal-managed environment, you can deploy a WSRP rich client enablement portlet as part of a
composite application, which will be configured to store the WSRP related meta-data of a WSRP
provider portlet. The WSRP rich client enablement portlet will export the WSRP meta-data into the
Composite Application (CA) XML file and pass it down to the Lotus Expeditor platform through the
Composite Application Infrastructure. The meta-data will be used by the portlet viewer to start the
WSRP facility in Lotus Expeditor and present the WSRP provider portlet.
v In the non Portal managed environment, the Portlet Viewer instances can be defined and contributed
using the Portlet Viewer extension point - com.ibm.rcp.portletviewer.WsrpPortlets.
To declaratively define and contribute a Portlet Viewer instance to the Lotus Expeditor platform, perform
the following procedure:
1. Select the Client Services Portlet project you want to wrap with the Portlet Viewer instance.
2. Right click the plugin.xml descriptor file and select Open With > Plug-in Manifest Editor.
3. Select the Extensions tab.
4. Select Add.
5. Locate the com.ibm.rcp.portletviewer.WsrpPortlets extension point and select Finish.
6. Update the <wsrpData> element attributes.
7. Save the plugin.xml file.
Extension examples: Below is a sample definition for a WSRP portlet:
<extension point="com.ibm.rcp.portletviewer.WsrpPortlets">
<wsrpData
entityId="com.bank.portletviewer.helloworldWSRPPortlet"
wsrp_wsdl="http://host:port/wps/wsdl/wsrp_service.wsdl"
handle="111222333444"
need_client_clone="true"
isSecured="true"
tokenType="LTPA">
</wsrpData>
</extension>
Note: If you configure the Portlet Viewer in your development environment, always set
need_client_clone as true, and do not use the pop_handle in the extension. To get the wsrp_wsdl,
handle, and security information, you need to refer to your portal product’s documentation. If you
use IBM WebSphere Portal, you can use the XMLAccess tool to retrieve the portlet handle.
Developing applications 229
Widgets for devices
On certain devices, the TextExtension widget does not function as documented. When the device
language is set to an Asian language and the TextExtension widget style is UPPERCASE, lowercase
letters can still be input.
Creating help for the application
If you are creating a set of Help information for your application, and you intend on using the built-in
Lotus Expeditor help plug-ins, you should use the Eclipse PDE to create a help plug-in. The Help Plug-in
provides for XML configuration of the Table of Contents, and content specified as HTML.
For more information on creating a help plug-in, refer to the section Plug-in Help in the Platform Plug-in
Developer’s Guide located in the Eclipse Help system.
Developing synchronization applications
This section provides information on synchronization application development.
SyncML
This section provides information on SyncML.
Understanding SyncML development
As desktop computers, laptops, personal digital assistants (PDAs) and advanced phones have become
part of our business and personal life, the need to access current, consistent data in multiple locations has
become a pressing need. The term synchronization, often abbreviated to sync, is broadly used to address
this requirement. This section discusses synchronization, along with a description of SyncML4J, an IBM
offering that enables ISVs and developers to implement SyncML based applications.
Resources can either be standalone items on the file system, such a word processor document, or items
managed within an application, such as a calendar within a Personal Information Manager (PIM)
application. If a single user accesses the resources that are required from a single location, such as when
using a standalone desktop computer, there is no synchronization issue; the user is always working with
the single, and thus current, version. In a local area network, where multiple users access resources on a
shared file system, there is no inherent synchronization facility. If two users open and edit the same file,
the last person to save the file overwrites the content input by the other user. As applications have
become more sophisticated, they often provide support for multi-user access to the resources they
manage; however this usually assumes continuously connected devices on a network.
Contacts, calendars, and memos are three common resources that a user might want access to on a
variety of devices, beyond the desktop. The most significant problem with situation is that these devices
often operate disconnected from the desktop computer for significant periods and it is possible to edit the
data on these devices, as well as on the desktop. To merge the edits from both locations, the data must be
synchronized between these two devices.
Previously, these resources were synchronized between a desktop machine and single PDA using the
synchronization software that was provided with the PDA. Often this meant data was synchronized to a
desktop application provided by the device manufacturer; this might not have been the default
application used on the desktop, particularly in a corporate scenario. Facilities were often provided to
import data to the PDA desktop counterpart application, but this was usually a one-off activity, with no
facility to update or merge ongoing edits between the applications.
The situation became worse as the facilities of mobile phones improved. Now there was a third device,
usually from a different manufacturer, to synchronize. One possible solution was to ensure that all three
devices ran software from one manufacturer and expect manufacturers to ensure compatibility, but this is
not the way the market evolved.
230 Lotus Expeditor: Developing Applications for Lotus Expeditor
Within this context a consortium of companies began the SyncML Initiative to develop an open
synchronization standard appropriate to server, desktop, and handheld devices. The organization
developed data synchronization (DS), then device management (DM) specifications and regularly held
SyncFests, where software and device manufacturers were able to test interoperability between various
servers and devices. In November 2002, the SyncML Initiative was integrated into the Open Mobile
Alliance (OMA), with the following mission:
“The mission of the Open Mobile Alliance is to facilitate global user adoption of mobile data services by
specifying market driven mobile service enablers that ensure service interoperability across devices,
geographies, service providers, operators, and networks, while allowing businesses to compete through
innovation and differentiation.”
Technology overview: This section provides an introduction to the SyncML4J toolkit available from IBM
for the development of sync clients based on the OMA DS and DM standards.
The DS and DM standards needed to take into account the differing device and the network
characteristics. To achieve the widest adoption, the protocol had to be suitable for implementation on
resource-constrained devices. As a ‘wire’ protocol, it does not specify either an implementation language
or application programming interface (API); rather, the protocol is a sequence of XML packages
exchanged between client and server during a sync session. Some key protocol features defined in the
specifications include support for:
v Multiple data types, including binary
v XML and WBXML encodings
v Multiple transports, including HTTP, HTTPS, OBEX, IrDA
v Client and server authentication and message integrity
The specifications are available for download on the OMA Web site. The adoption of the specifications is
progressing; some manufacturers are shipping devices that are DS enabled, several software vendors have
toolkits available, and there are open source, C and Java toolkits available.
The latest IBM offering for DS and DM is called SyncML4J, and is part of Lotus Expeditor v6.1.
SyncML4J enables the creation of DS and DM clients for the Java 2 Platform. SyncML4J is pure Java,
delivered as an Eclipse feature. Eclipse is an award-winning open source platform for the construction of
powerful software development tools and rich desktop applications. SyncML4J comprises plug-ins for the
runtime libraries necessary for creating data synchronization, applications, and device management client
applications.
SyncML4J common: In the same way that the DS and DM specifications are based on a common
representation and protocol, SyncML4J is built on common components for protocol handling and
transport. All mandatory wire commands are supported, as are Basic and MD5 authentication and
HMAC message integrity.
The DM device tree represents all manageable settings on the device. The DM specification defines how
the tree is used to maintain account information for the DM agent. SyncML4J uses a similar approach to
maintain account information for the DS agent; it also uses the tree to maintain a list of data sources
capable of interacting with the DS agent. In this way, the developer has the option to manage the client.
The applications are loosely coupled to the agents, so there is no dependency on a particular user
interface (UI) library within the base framework. A variety of UIs can be used to build an application,
sharing the framework sync code.
SyncML4J data synchronization: SyncML4J provides support for all the mandatory DS 1.1.2 client wire
commands. As a framework, SyncML4J supports user-defined data sources (or databases). These can
range from simple opaque resources, such as memos and images, to complex schema-aware data types
such as relational databases or PIM databases. The framework enables the data sources and their
Developing applications 231
capabilities to be modeled by implementing the SyncSource and SyncSourceCap interfaces respectively.
The implementation is then registered into the device tree as a DSSource node. A new (or existing) DSAcc
node models the account information, such as server address and credentials and the set of local
databases that can be synchronized by this account. Within the DSAcc, for each DSSource node there is a
corresponding DSTarget node, recording the corresponding remote database URI, and credential and
anchor information. No support is provided in the framework to assist with conflict resolution or
duplicate detection; these are implicit responsibilities of implementers of the SyncSource interface.
SyncML4J device management: SyncML4J provides support for all the mandatory DM 1.1.2 client wire
commands, together with an API for manipulating the device tree locally. Custom nodes are created by
subclassing and implementing the abstract methods in AbstractInterior, then adding instances of the
class into the device tree.
As previously noted, the device tree represents all manageable settings on the device, including in
volatile or non-volatile memory and file or I/O system. Custom nodes enable resources that are external
to the framework to be manipulated. For example, you can implement a custom node to set the time and
declare it into the device tree as ./device/time. Subsequent commands to get and replace the value of
that node could then trigger JNI code to get and set the actual OS system time.
You can save memory, by virtualizing sub-trees using custom nodes, rather than by providing a
one-to-one mapping between persistent device tree nodes and resources. A reference to a URI that is a
logical child to the custom node dynamically instantiates an appropriate node, enabling it to be
manipulated by the wire commands. For example, to make the files of a computer disk drive manageable
using the device tree, rather than populate the tree with hundreds of interior nodes and thousands of leaf
nodes, you can implement and add a single custom node to the device tree as ./device/driveC,
referencing the drive root. In this example, as wire commands to manipulate files on the drive are
received, the custom node dynamically creates nodes to model the addressed file, to which the wire
commands are forwarded.
Enabling projects for SyncML development
This section provides information on enabling projects for SyncML development.
Client Services target definition components: Lotus Expeditor Toolkit provides Client Services target
definition support. These target definitions simplify the creation and configuration of synchronization
application projects, enabling you to select the SyncML components and provide automatic management
of the requisite SyncML libraries. When developing a SyncML application any of the Client Services
target definitions can be selected for your Client Services project, but be sure to select the SyncML4J
target features on the Target Profile page. For more information on Client Services projects please refer to
“Lotus Expeditor Toolkit” on page 11.
SyncManager
This section provides information on the SyncManager.
Understanding the SyncManager
The Lotus Expeditor Synchronization UI provides a synchronization page and a schedules page for
viewing and managing all synchronization applications, as well as the menus for launching
synchronization and changing options.
The Synchronization page provides users the ability to quickly view all types of synchronizable
applications with their synchronization status. Each synchronizable application is displayed in a row with
its name, enabled/disabled, priority, last run time, scope, summary, server, and status. Offline Composite
Applications are displayed on this page. This page also provides users the ability to start or stop
synchronization, as well as a quick way to set synchronization schedules and change options.
232 Lotus Expeditor: Developing Applications for Lotus Expeditor
The Synchronization schedules page is part of the Lotus Expeditor preferences dialog. It provides users
the ability to set two sets of schedules for normal and high priority applications respectively. It also
provides the synchronization triggers settings applied to all the synchronizable applications.
The Synchronization UI is built on the SyncManager. The SyncManager is a framework that provides a
consistent interface to basic synchronization functionality (e.g., start sync) for heterogeneous applications
and services. It also enables synchronization application or service specific extensions. The SyncManager
provides a public API which supports development of a synchonization UI, as well as the basic
SyncManager functionality.
Enabling projects for the SyncManager
Besides the SyncManager APIs mentioned in the previous section, the SyncManager defines three
extension points:
v SyncService
v TypeService
v SchedulerService.
By implementing extensions to the first two extension points, developers can have their own application
specific entities appear in the Synchronization UI and participate in scheduled sync. By implementing an
extension to the third extension point, developers can define their own scheduler.
SyncService extension point: The first step to having your project’s objects appear in the
Synchronization UI is to define a SyncService extension. This extension provides functionality, such as
defining and persisting your project’s object instances, and starting sync for one or more of these
instances. The com.ibm.rcp.sync.syncServices is the SyncService extension point.
The SyncService implementation defines a SyncService specific SyncUnit interface. This service’s primary
responsibilities are:
v The management of the persistence of instances of this interface
v The synchronization of the data associated with its sync unit instances
v Sending events related to its sync units, such as the status of the synchronization of a sync unit’s data
(for example, sync starting, sync progress, sync completed or sync aborted).
Please refer to the SyncManager Javadoc for more information on the interface the SyncService must
implement and the events it must send.
TypeService extension point: The next step to having your project’s objects appear in the
Synchronization UI is to define a TypeService extension. This extension provides the “typing” of the
objects that appear in the Synchronization UI. It is this typing that associates different icons and different
preference pages with the different types of objects that appear in the UI. The
com.ibm.rcp.sync.typeServices is the TypeService extension point.
Since there must be at least one TypeService defined for each SyncService, the SyncService and the first
TypeService are typically packaged together in the same project. The following example details a
plugin.xml for this setup:
Developing applications 233
To implement the specific Options Dialog for your synchronizable application (sync unit), perform the
following procedure. By doing so, the existing Synchronization UI will allow you to open your Options
Dialog from the main synchronization page.
1. The TypeService implementer must implement a specific SyncUnit class, which implements the
org.eclipse.core.runtime.IAdaptable interface. And this class should be different from all other
SyncUnits provided by this or other type services. For example, different type services should not
create the SyncUnit instances of the same class, if they want to show different Options pages for each
type.
2. Contribute to Eclipse’s org.eclipse.ui.propertyPages extension point to implement the
PropertyPages for each specific SyncUnit class. The objectClass attribute defined in this extension
point must be the full qualified name of your sync unit class. For example:
<extension point="org.eclipse.ui.propertyPages">
<page
objectClass="com.ibm.rcp.sync.services.syncml.typeservice.
SyncmlSubscription"
name="SyncML Subscription Base Properties"
class="com.ibm.rcp.syncui.syncml.propertypages.
SyncmlBasePropertyPage"
id="com.ibm.rcp.syncui.syncml.propertypages.
SyncmlBasePropertyPage">
</page>
<page
objectClass="com.ibm.rcp.sync.services.syncml.typeservice.
SyncmlSubscription"
name="SyncML Subscription Extended Properties"
class="com.ibm.rcp.syncui.syncml.propertypages.
SyncmlExtendedPropertyPage"
id="com.ibm.rcp.syncui.syncml.propertypages.
SyncmlExtendedPropertyPage">
</page>
</extension>
3. If a common Options page is for more than one types of applications (sync units), you must declare
extension for each, though they are associated to the same property page class. For example,
SyncmlSubscription implements SyncUnit and SyncmlHierarchySubscription extends
SyncmlSubscription.
<extension point="org.eclipse.ui.propertyPages">
<page
objectClass="com.ibm.rcp.sync.services.syncml.typeservice.
SyncmlSubscription"
name="SyncML Subscription Properties"
class="com.ibm.rcp.syncui.syncml.propertypages.
SyncmlPropertyPage"
id="com.ibm.rcp.syncui.syncml.propertypages.
SyncmlPropertyPage ">
</page>
<page
objectClass="com.ibm.rcp.sync.services.syncml.typeservice.
SyncmlHierarchySubscription"
name="SyncML Subscription Properties"
class="com.ibm.rcp.syncui.syncml.propertypages.
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin>
<extension point="com.ibm.rcp.sync.syncServices">
<syncService factoryClass="com.ibm.rcp.sync.test.syncservice1.smextension.SyncServiceFactoryImpl"
type="com.ibm.rcp.sync.test.syncservice1">
</syncService>
</extension>
<extension point="com.ibm.rcp.sync.typeServices">
<typeService factoryClass="com.ibm.rcp.sync.test.syncservice1.smextension.TypeServiceFactoryImpl">
</typeService>
</extension>
</plugin>
234 Lotus Expeditor: Developing Applications for Lotus Expeditor
SyncmlPropertyPage "
id="com.ibm.rcp.syncui.syncml.propertypages.
SyncmlPropertyPage">
</page>
</extension>
SchedulerService extension point: The com.ibm.rcp.sync.schedulerServices is the SchedulerService
extension point.
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin>
<extension point="com.ibm.rcp.sync.schedulerServices">
<schedulerService class = " com.ibm.rcp.sync.test.schedulerservice.smextension.TestSchedulerService"
id = "com.ibm.rcp.sync.test.TestSchedulerService">
</schedulerService>
</extension>
</plugin>
Lotus Expeditor provides an extension for this SchedulerService extension point and an associated
preference page. So, there is no need to provide one. This extension point can be used by developers to
replace the default scheduler service that is provided. To do this, you must:
1. Remove the com.ibm.rcp.syncui.schedule plug-in from Lotus Expeditor.
2. Create your own schedule plug-in.
3. Implement your own SyncScheduler, SyncSchedulerService, and SchedulerEventListener, making
sure you have a scheduler associated with SyncManager.NORMAL_PRIORITY_FILTERNAME and
SyncManager.HIGH_PRIORITY_FILTERNAME respectively.
4. Implement your schedule preference page. You should contribute to org.eclipse.ui.preferencePages
extension point.
5. Install your schedule plug-in to Lotus Expeditor.
Developing SyncManager application logic
The SyncManager provides public APIs that enable developers to call SyncManager functionality from their
applications, or to write their own Synchronization UI if they desire.
A factory is provided to gain access to the SyncManager instance.
SyncManager sm = SyncManagerFactory.getSyncManager();
The SyncManager sends events to listeners about changes in its state and synchronization job progress.
Once you get the SyncManager instance, you will register a listener.
private EventListener listener = new EventListener();
sm.addSyncEventListener(listener);
Here is an example EventListener that simply prints out info on the events received.
public class EventListener implements SyncEventListener {
public EventListener() {}
public void receive(SyncEvent event) {
Object context = event.getEventContext();
StringBuffer sb = new StringBuffer();
sb.append("\nEvent received: " + event.getEventType());
/*
if (syncUnit != null) {
sb.append("\n ");
sb.append(syncUnit);
sb.append(" listener");
} else {
Developing applications 235
sb.append("\n SyncManager listener");
}
*/
if (context instanceof SyncUnitContext) {
SyncUnitContext suContext = (SyncUnitContext) context;
sb.append("\n syncUnitEvent: ");
sb.append(suContext.getSyncUnitUri());
sb.append(", ");
sb.append(suContext.getSyncUnitType());
} else if (context instanceof JobContext) {
JobContext jContext = (JobContext) context;
sb.append("\n jobEvent: ");
sb.append(context.getJobId().getId());
} else {
sb.append("\n " + context);
}
if (context instanceof ProgressContext) {
ProgressContext pContext = (ProgressContext) context;
sb.append("\n totals :");
sb.append(“pContext.getCompletedWorkUnits());
sb.append("/");
sb.append(pContext.getTotalWorkUnits());
}
System.out.println(sb.toString());
}
}
Once you have the SyncManager instance, there are various actions that can be performed with it. These
actions include tasks related to the creating/updating/deleting of the items that appear in the
Synchronization Page and also synchronization related things like starting sync for a subset of the
defined items.
The defined items will be instances of various classes that are provided by the TypeServices. Each item
has a URI associated with it, and it is the responsibility of the SyncService implementation to guarantee
the uniqueness of their objects’ URIs across all SyncServices in the system.
Each of these instances will implement the SyncUnit interface that is defined by the SyncManager. This
SyncUnit interface includes the attributes that would generally be common to all items that appear on the
Synchronization Page.
These instances are value objects with getters/setters for read/write attributes and getters for read-only
attributes. The most commonly used SyncUnit attributes are:
v Uri – this read-only attribute uniquely identifies an instance within the system
v Type – this read-only attribute is the name provide by the TypeService for a specific kind of SyncUnit
v ServerName – the server name that this item will use for synchronization, this will be null if there is
no such server name.
v Session Scope – during a specific sync session, a different type of sync may be desirable. For example,
the user may choose to only send changed data up to the server, as opposed to having all the updated
data on both the client and server synchronized. This attribute defines this session configuration info.
v DisplayName – provides a means of associating a user-friendly name with a SyncUnit, rather than
relying on the URI.
v Enabled/Disabled flag – this attributes is used to prevent a SyncUnit from synchronizing.
v Priority – this attribute enables different SyncUnits to synchronize more often than others. The
SyncScheduler determines when to synchronize SyncUnits based on their priority.
v IconUrl – the attribute is used by the Synchronization page when displaying icons.
Other SyncUnit attributes are described in the SyncManager Javadoc.
236 Lotus Expeditor: Developing Applications for Lotus Expeditor
Like the SyncManager, SyncUnits also sends events to its listeners. The events sent are specific to the
SyncUnit instance that sends them. Events include information about on-going synchronization of the
SyncUnit, and information about the SyncUnit state, such as that it has been updated. The SyncManager is
responsible for receiving the events sent by the SyncService related to a specific SyncUnit and redirecting
them so that they will be sent out from the SyncUnit itself.
One important thing to notice about the SyncUnit interface is that the Uri attribute is read-only. An
implication of this is the SyncUnit interface alone can not be used to create a new SyncUnit. A
TypeService/SyncService defined interface that extends the SyncUnit interface would be used in the
creation of a SyncUnit using the SyncManager APIs. This class would provide its own service specific
methods for getting/setting attributes that would be used to derive the SyncUnit Uri. For example:
public interface SyncService1SyncUnit extends SyncUnit {
public String getSourceUri();
public void setSourceUri(String sourceUri);
public String getTargetUri();
public void setTargetUri(String targetUri);
}
In this case, when a sync unit is added, the SyncService uses the sourceUri and/or the targetUri to
derive the SyncUnit’s Uri. The TypeService extends the SyncService defined SyncUnit interface providing
a name for the type. Since the first typed sync unit is typically provided along with the SyncService,
there is usually no need to add any additional methods when extending the SyncService defined
interface.
public interface TypeService1SyncUnit extends SyncService1SyncUnit {
public static String TYPE = TypeService1SyncUnit.class.getName();
}
The following steps add a SyncUnit of a given type through the SyncManager APIs. First, an instance of
the proper class for the type is created. This is done via the createSyncUnit API. This API takes the type
as a parameter. Based on the input type, the SyncManager will return an instance of the class that
corresponds to the type that was retrieved from the associated TypeService. The returned instance will
have default values assigned to all of its attributes. Next, the returned instance is cast to the appropriate
TypeService defined interface, and the attribute values are set. Lastly, this instance is added to the
SyncManager via the addSyncUnit call. This call will return a new instance that has the Uri set.
Note: The original instance can then be re-used. There is no need to call createSyncUnit multiple times
when adding multiple SyncUnits. This original instance continues to have no Uri set. So, it can not
be used as a parameter to other SyncManager methods, such as sync or update. The SyncUnit
instance returned from the createSyncUnit call is to be used for those other SyncManager method
calls.
The following code demonstrates how to add to SyncUnits using a single createSyncUnit call:
TypeService1SyncUnit tempSyncUnit = (TypeService1SyncUnit) sm.createSyncUnit(TypeService1SyncUnit.TYPE);
tempSyncUnit.setSourceUri("MySourceUri1");
tempSyncUnit.setTargetUri("MyTargetUri1");
TypeService1SyncUnit syncUnit1 = (TypeService1SyncUnit) sm.add(tempSyncUnit);
tempSyncUnit.setSourceUri("MySourceUri2");
tempSyncUnit.setTargetUri("MyTargetUri2");
TypeService1SyncUnit syncUnit2 = (TypeService1SyncUnit) sm.add(tempSyncUnit);
Similarly, to update a SyncUnit, when you do not have the instance, you would issues a getSyncUnit, cast
it if necessary, make the updates to the instance and then call updateSyncUnit.
Developing applications 237
Removing a SyncUnit can be done with or without a SyncUnit instance. There is no need to get the
instance to remove it from SyncManager. It can be removed using just the SyncUnit’s Uri.
The values, such as SessionScope, that are stored with the SyncUnit are the values the scheduler uses
when kicking off synchronization of that sync unit. Besides supporting scheduled sync, the SyncManager
provides methods for performing synchronization on demand. These sync methods allow for the
synchronization of subsets of SyncUnits. To sync using the stored SyncUnit values, use the methods that
take the Uri(s) of the SyncUnit(s) to sync. Sync methods are also provided that take SyncUnit instances.
Use these methods when it is desirable to change the configuration for a single sync session. For
example, a slow sync is not something that is usually repeated over and over. To perform a one time
slow sync, a SyncUnit would be retrieved using the get SyncUnit API. The sessionScope would be set to
slow sync, and then that sync unit would be passed in to the sm.sync(SyncUnit) method. The values
used for that sync session will not be persisted.
Developing Web applications
The Lotus Expeditor platform supports Servlet 2.4 and JSP 2.0 web applications as well as Servlet 2.3 and
JSP 1.2 web applications. Web applications targeting the Lotus Expeditor platform are called Client
Services web applications. Since components in the Lotus Expeditor platform are called bundles, a web
application targeting this platform is also referred to as a Web Application Bundle or WAB.
A WAB can be developed using many of the same web development tools provided by the Web Tools
Platform and Rational Software Development platform. You should therefore refer to the online help
section Web Application Development User Guide as your initial web development tools reference. The
following topics discuss the additional development considerations and tool usage required when
targeting a web application for the Lotus Expeditor platform.
The following table provides pointers to information on web development activities and information on
tasks that are unique to, or require special consideration when developing web applications for the Lotus
Expeditor platform.
Table 14. Web development activities
Task Reference
Understanding Client Services web application concepts. “Understanding Web Applications” on page 239
Working with Client Services Web projects versus
Dynamic Web projects, and when to use one versus the
other.
“Creating Web Application projects” on page 239
Developing Client Services web application logic. This
encompasses any special development considerations
when coding and constructing the web application logic.
“Accessing resources” on page 242
“Using JSP Standard Tag Libraries” on page 243
“Java Server Faces (JSF) development” on page 243
“Struts development” on page 243
Exporting web application bundles. “Exporting Web Application bundles” on page 247
Securing the web application through user authentication
and authorization.
“Securing Web Application resources” on page 244
Debugging and testing the web application. “Debugging and testing applications” on page 323
Deploying the web application to a runtime. “Deploying projects for local testing” on page 331
Using the command line WAB tool to convert a WAR to
a WAB.
“WAB Utility” on page 247
Configuring the web container. Refer to the Web Container configuration information in
the documentation Assembling and Deploying Lotus
Expeditor Applications
238 Lotus Expeditor: Developing Applications for Lotus Expeditor
Table 14. Web development activities (continued)
Task Reference
Web container logging. “Web Container Logging” on page 249
Understanding Web Applications
Client Services web applications run on the Lotus Expeditor platform. A primary difference between a
Client Services web application and one that is deployed to run on a WAS or Tomcat runtime is that the
Client Services web application must also be a valid OSGi bundle. Refer to “Working with OSGi bundles”
on page 366 for more information on bundles and the Lotus Expeditor platform. The Lotus Expeditor
Toolkit automatically handles many of these bundle specific details, which is why developing the web
application through a Client Services web project is the recommended development path for web
applications that are to be run on the Lotus Expeditor platform. Nevertheless, it is also possible to
develop the web application through a Dynamic Web project, and subsequently test run it on the Lotus
Expeditor platform. Refer to “Using a Client Services Web project versus a Dynamic Web project” on
page 240 for more details. It is also possible to transform an existing Web Application Archive (WAR) file
into a Web Application Bundle (WAB) suitable for running on the Lotus Expeditor platform through the
use of the WAB Utility.
The following lists aspects of a Client Services web application that differ from a standard web
application.
v The Lotus Expeditor platform does not support deploying Enterprise Applications through an EAR.
The web application is directly deployed to the runtime.
v A Client Services web application has a manifest file, located in META-INF/MANIFEST.MF, that contains
bundle information including package and bundle dependencies. This is associated with the bundle,
and is separate from the manifest file found under the web application’s content folder.
v A Client Services web application has a plugin.xml file that contains extension point contributions to
lazily start the web application.
v A Client Services web application contains additional deployment information in wab.properties. This
is located in the web content WEB-INF folder.
v JSP files are translated into their respective servlet classes before the web application is deployed to the
runtime as a WAB.
In most cases, these artifacts and differences are handled transparently by the Lotus Expeditor Toolkit.
These differences do not affect the functionality of the web application. There are, however, some
development considerations you should take into account depending on the web technologies you will be
using. These are described in “Developing Web Application logic” on page 242.
A Client Services web application can be developed using many of the same web development tools
provided by the Web Tools Platform and Rational Software Development platform. The primary
differences are:
v Use the Client Services Web project wizard to create a Client Services web project, as described in
“Creating a Client Services Web project” on page 240.
v Since the Lotus Expeditor platform does not support EARs, EAR projects are ignored.
v When testing the project, target the Client Service runtime when using the Run As/Run on Server
action. Or, use the Client Service launch configuration when using the Eclipse Run / Debug launch
feature.
v When exporting the web application, use the Plug-in Development > Deployable plug-ins and
fragments wizard, as described in “Exporting Web Application bundles” on page 247.
Creating Web Application projects
This section provides information on Web Application project creation.
Developing applications 239
Using a Client Services Web project versus a Dynamic Web project
Web applications can be developed using either a Client Services web project or a Dynamic Web Project.
The choice of which to use depends on the application content and its primary usage. In general, web
applications that primarily target the Lotus Expeditor platform or depend on other OSGi services besides
core servlet and JSP support should be developed using a Client Services web project.
A Client Services web project is an extension of the dynamic web project. Because of this, both types of
projects make use of the dynamic web project tools. In addition to this, a Client Services web project
provides the following support for developing a web application that is targeting the Lotus Expeditor
platform.
v The OSGi manifest file required by Lotus Expeditor applications can be automatically managed by the
tools.
v The project’s class path is maintained to match the class path environment that will exist in the Lotus
Expeditor runtime. This is useful for detecting class visibility problems at development time rather
than runtime.
A Dynamic web project will not have the Lotus Expeditor specific tooling aids listed above, but can still
be tested and run on the Lotus Expeditor platform. This is accomplished by targeting the project’s
runtime to the Client Service runtime through the project’s Targeted Runtimes properties. The tooling will
automatically add the proper OSGi manifest entries for Servlet and JSP support. However, if the
application references other OSGi services or bundles, the developer will have to manually add these
dependencies to the manifest file.
Client Services web project can also be tested and run on a platform other than Lotus Expeditor by
reassigning its targeted runtime through the project Targeted Runtimes properties. Refer to “Debugging
and testing applications” on page 323 for further information.
Creating a Client Services Web project
Perform the following procedure to create a new Client Services Web project:
1. Select File > New > Project. The new project wizard displays.
2. Expand the Client Services folder. This lists the Client Services project wizards. Choose Client
Services Web Project, then select Next. The Client Services Web Project panel displays.
3. Specify a project name in the Project name field. This is the only field you are required to fill in. Select
Finish to create a project with default settings.
The additional settings that can be configured through this wizard are described in the following tables,
along with their default values. Access the additional wizard panels through the Next and Back buttons.
Selecting Finish on any of the wizard pages will create the project with the settings you have specified
up to that point.
Client Services Web Project panel
Table 15. Client Services Web Project panel
Option Description Default value
Project name Enter a name for the new Client
Services Web Project.
None
Project location You may click Browse to select a file
system location for the new project.
The default location creates the
project in your current workspace.
Project Facets panel
240 Lotus Expeditor: Developing Applications for Lotus Expeditor
Allows you to set and configure the facets that should be enabled for the project.
Table 16. Project Facets panel
Option Description Default value
Configurations Select the project facet configuration. <custom>
Project Facet Select project facets associated with
this project, and their versions.
Web Bundle (version 6.1)
Dynamic Web Module (version 2.4)
Java (version 5.0)
The above facets are pre-selected, and
cannot be de-selected. You can
modify the versions.
Web Module panel
Allows you to configure the web module settings.
Table 17. Web Module panel
Option Description Default value
Context Root Specify the project’s context root. Project name
Content Directory Specify the project’s web content
directory.
WebContent
Java Source Directory Specify the project’s Java source
directory.
src
Target Profile page
Allows the selection of a target definition and associated features and/or plug-ins to be selected for the
project. By default, the necessary features for supporting web projects will be selected.
Table 18. Target Profile page
Option Description Default value
Target Definition Select from the list the Target
Definition this Client Services project
will target. You can change your
selection later in the Client Services
property page.
Default Target
Developing applications 241
Table 18. Target Profile page (continued)
Option Description Default value
Target Features Check the Target Features that your
Client Services project will require.
You can change your selection later
in the Client Services property page.
Grey entries are required by the
Target Definition and cannot be
deselected.
The ″Core OSGi Interfaces″ Target
Feature is required by all Target
Definitions.
The following features (if they exist
in the Target Definition) are
automatically selected (and cannot be
de-selected) by Web Application
Tools:
v IBM JavaServer Faces (JSF)
Extensions
v JavaServer Pages (JSP) API
v JSP Standard Tag Library (JSTL)
v Reliability, Availability,
Serviceability (RAS)
v Servlet API
v Web Application Compatibility
v Web Target Feature
v Web Container
v Web Container - HTTP Service
v Apache MyFaces
Converting a Dynamic Web project to a Client Services Web project
You can convert an existing Dynamic Web project into a Client Services Web project by using the Convert
Project to Client Services project wizard. Refer to “Convert Project to Client Services Project Wizard” on
page 375 for information on how to use this wizard.
This will retain the existing web application logic of the project, and will add Client Services tooling
support.
Note: There is no wizard to convert a Client Services Web project back to a Dynamic Web Project. If you
wish to retain the original Dynamic Web Project, you must copy the project before converting it.
This can be done by selecting the Copy project before conversion check box in the wizard.
Developing Web Application logic
This section provides information on Web Application logic development.
Accessing resources
The Servlet and JSP specifications do not guarantee that a web application’s resources will be represented
as files in the host machine’s file system. Many web container implementations do expand web
applications into the file system, and some existing web applications take advantage of this
implementation detail to reference resources as Java Files. However, web applications that target a Lotus
Expeditor runtime are represented as jar bundles that do not have to be expanded in the file system to
run. Because of this, your web application should use the ServletContext.getResourceAsStream() API
when accessing web application resources. It should not assume these resources will be available as files.
The API ServletContext.getRealPath() should also not be used as it is implementation dependant. For
the Lotus Expeditor runtime, it will return null since the web application resources are not expanded in
the file system. Again, such resources can be accessed as IO streams through
ServletContext.getResourceAsStream().
242 Lotus Expeditor: Developing Applications for Lotus Expeditor
Using JSP Standard Tag Libraries
The Lotus Expeditor platform includes the JSP Standard Tag Libraries (JSTL) version 1.1 as part of the
runtime. These libraries will support both Servlet 2.4 / JSP 2.0 compliant applications as well as Servlet
2.3 / JSP 1.2 applications. If your application makes use of JSTL tags, you do not need to include copies
of the JSTL libraries in your web application’s WEB-INF/lib directory.
Java Server Faces (JSF) development
JavaServer Faces is a technology that helps you build user interfaces for dynamic Web applications that
run on the server. The JavaServer Faces framework manages UI states across server requests and offers a
simple model for the development of server-side events that are activated by the client. JavaServer Faces
is consistent and easy to use. For additional information on JavaServer Faces, refer to the Developing
Web sites and applications and Web Tools Features section in the Help Contents of the Rational
Software Development Platform.
The client platform supports the use of JavaServer Faces based web applications. The required JAR files
are provided as part of the client platform runtime.
By default, the project will be configured to use the Apache MyFaces JSF implementation. Refer to “Using
the Sun JSF Reference Implementation” for instructions on how to use the Sun Reference Implementation.
When creating a new Client Services web project, select Base Faces and JSTL facets from the Project
Facets page in the wizard.
For an existing Client Services web project, select the Base Faces and JSTL facets by performing the
following procedure:
1. Right click your project in the Project Explorer view, and select Properties.
2. Select Project Facets > Add/Remove Project Facets...
3. From the Project Facets window, select the Base Faces and JSTL facets, then click Finish.
You should now be able to continue development of your JSF based web application.
Using the Sun JSF Reference Implementation: The Sun JSF Reference Implementation is part of the
Device Runtime Environment (DRE). You must first install the DRE into the toolkit using the installation
instructions for the DRE.
Configuring a project: Perform the following steps to configure a project to use the Sun JSF Reference
Implementation:
1. Create a Client Services project with JSF support. See instructions in “Java Server Faces (JSF)
development.” The project will be configured to use Apache JSF by default.
2. Open the Manifest editor for the project, and select the Dependencies tab.
a. If org.apache.myfaces is listed under Required Plug-ins, remove it.
b. Add com.ibm.pvc.servlet.jsf to the Required Plug-ins.3. Edit the project’s Client Services properties.
a. Right click the project and select Properties. Select the Client Services page.
b. Deselect the Apache MyFaces feature from the project’s Target features, and click OK.
When running the project, you must manually select the proper plug-in to include it in the runtime. On
the launch configuration Plug-ins tab, ensure that com.ibm.pvc.servlet.jsf is selected.
Note: When using Run on Server, you can get to this tab through the Advanced... button.
Struts development
Struts is a framework of open-source software that can help you build Web applications quickly and
easily. It relies on standard technologies such as Java beans, Java servlets, JavaServer Pages (JSP), and
Developing applications 243
XML. Struts encourages application architectures based on the Model 2 approach, which is basically the
same as the model-view-controller (MVC) design pattern. For additional information on Struts, refer to
the Developing Web sites and applications and Web Tools Features sections in the Help Contents of the
Rational Software Development Platform.
The client platform supports the use of Struts-based web applications. All web applications that intend to
use Struts must include the Struts jars within the web application.
The Web tools in the Rational Software Development Platform enable development of Struts-based
applications by adding libraries to the WebContent\WEB-INF\lib directory of each web application.
When creating a new Client Services web project, select Struts facet from the Project Facets page in the
wizard.
For an existing Client Services web project, select Struts facet by performing the following steps:
1. Right click on your project in the Project Explorer view, and select Properties.
2. Select Project Facets > Add/Remove Project Facets...
3. From the Project Facets window, select the Struts facet, then Finish.
Securing Web Application resources
This section provides information on securing Web Application resources.
Configuring a Web Application: The Web Container supports the declarative J2EE security model. In
declarative security the application’s web descriptor specifies the application’s security policy (roles,
access control etc.) without changing the applications code. The following is an example code snippet
from a web descriptor that shows the declarative security syntax. This example secures web application
resources with url-pattern=/secure/* -
<security-constraint>
<display-name>myLoginTest</display-name>
<web-resource-collection>
<web-resource-name>LoginTest</web-resource-name>
<url-pattern>/secure/*</url-pattern>
<http-method>GET</http-method>
<http-method>PUT</http-method>
<http-method>HEAD</http-method>
<http-method>TRACE</http-method>
<http-method>POST</http-method>
<http-method>DELETE</http-method>
<http-method>OPTIONS</http-method>
</web-resource-collection>
<auth-constraint>
<description>Any user</description>
<role-name>user.anyone</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/error.jsp</form-error-page>
</form-login-config>
</login-config>
<security-role>
<role-name>user.anyone</role-name>
</security-role>
To configure a web application to use declarative security on the Web Container, the web descriptor must
define a list of valid User Admin roles in the <role-name> tag. This list of roles can include user and
group roles. The above example uses the default User Admin role of user.anyone. This means any valid
user can be used to log into this web application. The Web Container assumes that all User Admin users
244 Lotus Expeditor: Developing Applications for Lotus Expeditor
store their passwords as a credential with the key ″password″. If no valid users are created with User
Admin then the Web Container will not let anyone access the web application resources that have been
secured.
Note: Developers may also use programmatic security to control access to a web application. For more
information on the Web descriptor, declarative and programmatic security models refer to the
Servlet 2.3 specification.
Supported authentication mechanisms: As a prerequisite for gaining access to any web application
resources which are protected by an authorization constraint, a user must be authenticated using one of
the supported authentication mechanisms. The Web Container supports the following authentication
mechanisms:
v Basic Authentication (BASIC)
v Form-based Authentication (FORM)
DIGEST and CLIENT-CERT authentication mechanisms are not supported.
Using the User Admin Service to create users and roles: The Web Container uses the User Admin
service to authenticate and authorize requests for secured web application resources. You can use the
User Admin API to add, modify, or delete properties and credentials for existing users. The following
snippets of code show how you can add a user and delete a user:
Add a user
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.useradmin.UserAdmin;
public class MyWebApplication {
/**
* Used to store reference to UserAdmin service
*/
private static UserAdmin userAdmin = null;
/**
* Plug-in bundle context
*/
private BundleContext context;
/**
* Plug-in start method
*/
public void start(BundleContext bc) throws Exception {
String username = “Joe”;
this.context = bc;
ServiceReference ref = bc.getServiceReference(“org.osgi.service.useradmin”);
userAdmin = (UserAdmin) bc.getService(ref);
useradmin.createRole(username, Role.User);
}
/**
* Plug-in stop method
*/
public void stop(BundleContext bc) throws Exception {
}
}
Delete a user
Developing applications 245
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.useradmin.UserAdmin;
public class MyWebApplication {
/**
* Used to store reference to UserAdmin service
*/
private static UserAdmin userAdmin = null;
/**
* Plug-in bundle context
*/
private BundleContext context;
/**
* Plug-in start method
*/
public void start(BundleContext bc) throws Exception {
String username = “Joe”;
this.context = bc;
ServiceReference ref = bc.getServiceReference(“org.osgi.service.useradmin”);
userAdmin = (UserAdmin) bc.getService(ref);
useradmin.removeRole(username);
}
/**
* Plug-in stop method
*/
public void stop(BundleContext bc) throws Exception {
}
}
For more information on the User Admin service, please refer to the OSGi Release 3 specification and the
OSGi Javadoc.
Using the Admin Utility for OSGi to create users and roles: With the Admin Utility for OSGi, you can
use the User Admin Service to manipulate user definitions. You can add, modify, or delete properties and
credentials for existing users.
To add a user:
1. Launch the Lotus Expeditor platform.
2. Install the Admin Utility for OSGi feature.
3. Select Application Open > Admin Utility for OSGi to run the application
4. Once the application is started, select the User Admin label in the left-side frame.
5. Select Create New User. The Create New User input fields display.
6. Enter a user name and select Create User.
To delete a user:
1. Launch the Lotus Expeditor platform.
2. Install the Admin Utility for OSGi feature.
3. Select Application Open > Admin Utility for OSGi to run the application
4. Once the application is started, select the User Admin label in the left-side frame.
5. Check the check box for the user to be deleted and select Delete. You will need to refresh the data
before removing the user from the form
246 Lotus Expeditor: Developing Applications for Lotus Expeditor
To create a group:
1. Launch the Lotus Expeditor platform.
2. Install the Admin Utility for OSGi feature.
3. Select Application Open > Admin Utility for OSGi to run the application
4. Once the application is started, select the User Admin label in the left-side frame.
5. Select Create New Group. The Create New Group form displays.
6. Add members to the group by selecting a member in the Available Roles box and choosing either
Basic or Required to move the member to the New Member Roles box.
7. Select Create Group to finish creating the group.
Exporting Web Application bundles
A Client Services Web project can be exported as a bundle by performing the following procedure:
1. Select File > Export...
2. From the Plug-in development folder, select Deployable plug-ins and fragments, and click Next.
3. Select the Client Services web project to be exported under Available plug-ins and fragments, then
define the Export options and Destination.
4. Select Finish.
WAB Utility
The WAB utility is a command line utility for transforming Web Application Archive (WAR) files into
Web Application Bundle (WAB) files that are suitable for running in the Lotus Expeditor platform. Note
that the web development tools provide a wizard for exporting both Client Services web projects as
WABs. See “Exporting Web Application bundles” and “Using Ant tasks to build a deployable bundle” on
page 331. These tools should be used when you are dealing with web projects under the Lotus Expeditor
Toolkit. The WAB utility is a standalone utility that can be used to transform existing WAR files
independent of any projects managed by the Lotus Expeditor Toolkit.
WAB Utility installation: Perform the following procedure to install the WAB utility:
1. Install a J2SE 5.0 JRE on your development machine, and set the environment variable JAVA_HOME to
the location of this JDK.
2. The WAB utility is delivered as one of the plug-ins installed with the Lotus Expeditor Toolkit. For the
WTP environment, it is located in the following directory (where WTP_HOME is the directory that your
Web Tool Platform has been installed to):
WTP_HOME/common/plugins/com.ibm.pvc.tools.web.translator_6.1.0
For the WTP environment, it is located in the following directory (where WTP_HOME is the directory
that your Rational Software Development platform has been installed to):
WTP_HOME/eclipse/plugins/com.ibm.pvc.tools.web.translator_6.0.0
You can copy this directory elsewhere on your development machine, if desired, or use it in place. For
convenience, you should add the WAB utility directory to your system’s PATH environment variable.
This will enable you to invoke the WAB utility scripts without specifying their full path.
3. The invocation scripts for the WAB utility are within the WAB utility directory described in step 2.
v wab.bat - Windows script for invoking the WAB utility
v wabc - Linux script for invoking the WAB utility
Refer to “WAB Utility usage and parameters” for details on how to use the WAB utility.
WAB Utility usage and parameters: The WAB utility is invoked through a script. On Windows systems,
this script is wab.bat. On Linux systems this script is wabc. The following will use the Windows wab.bat
script in examples, and assumes that the WAB utility directory has been added to the system’s PATH
environment.
The WAB utility performs the following transformations on your web application:
Developing applications 247
v All JSP files are translated to their underlying servlet classes. In addition to the standard J2SE libraries,
the utility automatically adds required javax.servlet* class libraries to the class path during
translation. Any other classes referenced by the application’s JSPs, that are not part of the application
itself (through WEB-INF/classes or WEB-INF/lib), must be specified through the utility’s –classpath
parameter.
v If an OSGi compliant manifest file does not exist in the web application, one will be added. It will have
the necessary package dependency statements for supporting web applications. If your application is
referencing additional external packages, you will need to include your own custom
META-INF/MANIFEST.MF manifest file that includes these package dependencies through either the
Import-Package or Require-Bundle fields, or use the -requirebundle parameter to add them to the
Require-Bundle field. Note that the utility will augment an existing manifest file to contain any missing
dependencies, and will not overwrite any preexisting entries.
v A plugin.xml file will be created by default to contain the extension point contribution to lazily start
the web application. If you use the WAB tool with -nolazystart option, the plugin.xml file will not be
created.
WAB Utility examples: The simplest use of the WAB script only specifies the war file to be translated:
wab myweb.war
The above will create a myweb.jar file in the directory from which the tool was invoked.
You can specify the target name and location for the WAB JAR using the -o parameter:
wab myweb.war -o /myruntime/eclipse/plugins/myweb.jar
You can add additional libraries to the translation class path using the -classpath parameter:
wab myweb.war -classpath myLib1.jar;myLib2.jar
You can use the -g option to specify compilation with debug information.
WAB Utility parameters: WAB utility invocation has the following form:
wab <war file> [ Options ]
The following table describes the options parameters available to the WAB utility.
Table 19. WAB Utility options parameters
Option Description
<war file> File name of the war file to be transformed into a WAB
file. This must be a Servlet 2.3 or 2.4 compliant war file.
-contextpath <path> Specify the context path for the web application. By
default, the base name of the output file is used for the
context path. For example, the default context path for
inventory.jar is /inventory.
-classpath <classpath> Augment the class path to be used for the JSP file
compilation. The WAB utility automatically includes the
javax.servlet.* packages on the class path.
-o <output file> File name of the resulting WAB file. The default name is
the base <war file> name with .JAR file extension, placed
in the same directory as <war file>.
-includesource When specified, the WAB file will include the original
JSP source files. By default, JSP files are removed from
the WAB, since they are translated to their underlying
servlet classes.
-g When specified, JSP files are compiled with debug
information.
248 Lotus Expeditor: Developing Applications for Lotus Expeditor
Table 19. WAB Utility options parameters (continued)
Option Description
-id <name> Specify the bundle symbolic name. By default, this is the
base name of the output file.
-portlet Option for translating web archives that are also portlets.
-jsf Option for translating web archives that leverage
JavaServer Faces technology.
-jstl Option for translating web archives that leverage JSP
Standard Tag Library.
-nolazystart Option for not adding extension point contribution to
lazily start the web project at deployment time.
-javalevel <Java level> Java compliance level. Valid values are ″14″ and ″15″.
Default is ″14″.
-requirebundle <requirebundle> A comma separated list of bundle symbolic names to be
added to the Require-Bundle header of the manifest file.
Using Lotus Expeditor servers
The concept of Server Runtimes describes run time environments by containing information such as the
run time install directory and JRE. Web projects can have a target run time associated with them. This
allows the tools to modify the project’s build characteristics based on the targeted run time type. This
mechanism also allows the tools to run non-Client Services web projects on a Client Services run time.
Client Services web projects have Client Services 6.1 run times as the targeted run times.
A Client Services run time type is a custom runtime type for the Client Services run time that supports
Web modules for J2EE level 1.3 and 1.4 and EJB modules for J2EE level 1.2 and 1.3. A Client Services
Server is associated with the Client Services run time type. Servers will contain launch specific
information that corresponds to the set of information that can be configured when running a run time
workbench.
There are multiple Client Services run time instances of the Client Services run time type. Each instance
has a Target Definition ID that it corresponds to, in order to calculate which JRE is associated with this
run time. There is at least one instance for each Target Definition, defined automatically by the system as
needed. You are able to create and edit new instances.
Servicing Web Applications
This section provides information on servicing Web Applications.
Web Container Logging
The Web Container will log all messages using the JDK logging. The Lotus Expeditor logger plug-in will
redirect these messages to <USER_HOME>\IBM\RCP\<INSTALL_ID>\<USER_NAME>\logs\rcp.log.*.
The following table shows the mapping between the Web Container log levels and the levels used by
JDK:
Table 20. Web Container log level mapping
Web Container Log Level OSGi Log Level java_util.Level
ERROR ERROR SEVERE
WARNING WARNING WARNING
INFO INFO INFO
DEBUG DEBUG FINE
EVENT DEBUG FINER
Developing applications 249
Table 20. Web Container log level mapping (continued)
Web Container Log Level OSGi Log Level java_util.Level
ENTRY DEBUG FINER
EXIT DEBUG FINER
Configuring the Web Container Logging: Since the Web Container leverages JDK logging to log all
messages, no additional configuration of the Web Container is necessary. Refer to the Logging chapter in
the documentation Assembling and Deploying Lotus Expeditor Applications for more information on how to
configure the runtime logging framework.
Debugging and testing Web Applications
This section provides information on debugging and testing Web Applications.
Running and debugging using Client Services launcher
To run or debug a Client Services Web Project using Client Services launcher, perform the following
procedure:
1. Bring up the Run or Debug launcher by selecting either Run > Run... or Run > Debug...
2. Select the Client Services configuration type, and click New to create a new Client Services launch
configuration.
3. Select the Plug-ins tab, and ensure the web projects to be tested are selected.
4. Select either Run or Debug to launch.
Running and debugging using ″Run on Server″ style
This section provides information on running and debugging using ″Run on Server″ style.
Running or debugging Client Services Web Projects on the Client Services runtime: To run or debug
a Client Services Web Project on the Client Services runtime, perform the following procedure:
1. Select Window > Open Perspective > Other > J2EE to switch to the J2EE Perspective.
2. From the project Explorer view, select the Client Services web project to be tested.
3. Right click the project, and select Run As > Run on server... (or Debug As > Debug on Server...)
4. Choose a Client Services 6.1 server if one exists and click Next. If one does not exist, define a Client
Services 6.1 server by:
a. Select IBM > Client Services v6.1 as the server type, and click Next.
b. Choose a target definition and/or features/plug-ins, and click Next.5. To test multiple projects, add or remove other projects that are configured on the server.
6. Select Finish to launch.
Run or debug a non-Client Services Web Project on the Client Services runtime: To run or debug a
non-Client Services Web Project on the Client Services runtime, perform the following procedure:
1. Change the project’s runtime to the Client Services runtime from the project’s Properties > Targeted
Runtimes. If the Client Services run time is not displayed or disabled, remove the project facets that
are not supported by the Client Services run time, as noted in this page.
2. Update the project’s manifest file manually to add dependencies to Import-package or Require-Bundle
entries, if the project has dependencies on OSGi bundles or services other than servlet and JSP. Since
the project is not a Client Services Web Project, it does not have the support from Lotus Expeditor
Toolkit to automatically manage the dependencies.
3. Select Window > Open Perspective > Other > J2EE to switch to the J2EE Perspective.
4. From the project Explorer view, select the web project to be tested.
5. Right click the project, and select Run As > Run on server... (or Debug As > Debug on Server...)
250 Lotus Expeditor: Developing Applications for Lotus Expeditor
6. Choose a Client Services 6.1 server if one exists, and click Next. If one does not exist, define a Client
Services 6.1 server by:
a. Select IBM > Client Services v6.1 as the server type, and click Next.
b. Choose a target definition and/or features/plug-ins, and click Next.7. To test multiple projects, add or remove other projects that are configured on the server.
8. Select Finish to launch.
Running or debugging a Client Services Web Project on a non-Client Services runtime: To run or
debug a Client Services Web Project on a non-Client Services runtime, perform the following procedure:
1. Select Window > Open Perspective > Other > J2EE to switch to the J2EE Perspective.
2. From the project Explorer view, select the web project to be tested.
3. Right click the project and select Run As > Run on server...
4. Choose a non-Client Services server if one exists and click Next. If one does not exist, define a
non-Client Services server by:
a. Choose a server type, and click Next.
b. Specify the server settings depending on the server type selected. This process might involve
multiple setup panels.5. To test multiple projects, add or remove other projects that are configured on the server.
6. Select Finish to launch.
Developing Web Services
The Lotus Expeditor Toolkit extends the Rational Software Development Platform through plug-ins that
enable you to build applications targeting the Lotus Expeditor runtime platform. The Lotus Expeditor
Toolkit Web Services plug-in suite enables you to develop applications that consume and are exposed as
Web Services targeting the OSGi-based Lotus Expeditor runtime platform. For more information on the
Rational Software Development Platform, visit http://www.ibm.com/pvc.
Understanding Web Services
The Lotus Expeditor Toolkit Web Services runtime plug-in suite provides functionality similar to libraries
that implement the Java 2 Micro Edition Web Services Specification (JSR-172). In some cases, very
complex Web services that comply with JAX-RPC may not be fully consumable by JSR-172 Web services.
For cases where the client needs to consume JAX-RPC based services, the Apache Axis 1.3 Web Services
tools provided with Lotus Expeditor Toolkit should be used instead.
To enable you to develop Web Services applications, the Web Services Tools allows you to generate client
code that consumes Web Services as well as exposes OSGi services as Web Services providers.
Note: The Lotus Expeditor Client for Devices does not support exposing OSGi services as Web Services
providers. Lotus Expeditor Client for Devices only supports JSR-172, without dynamic Web
Services clients.
An application that will consume a Web Service needs to identify the service end-point, typically a URL
to a Web Services Description Language (WSDL) document and use the interface to invoke the Web
Services provider. An application that will be exposed as a Web Services provider must implement a Java
interface that defines the Web Service calls.
Technologies
This section provides information on Web Services technologies.
Web Services Description Language (WSDL): A WSDL document provides the description of the Web
Services interface. In Lotus Expeditor Toolkit Web Services terminology, A top-down approach is used to
generate code from a WSDL (typically used for developing Web services clients), whereas a bottom-up
Developing applications 251
approach is used to generate a WSDL from code (typically used for developing Web Services
providers).The Lotus Expeditor Toolkit Web Services v 6.1 plug-in supports both the top-down approach
and bottom-up approach.
For more information about WSDL, please visit http://www.w3.org/TR/wsdl.
Simple Object Access Protocol (SOAP): SOAP is the message format of the transaction that takes place
when a Web Services client that communicates with a Web Services provider. The WSDL defines the
restrictions on the format of these messages.
For more information about SOAP please see http://www.w3.org/TR/soap.
JAX-RPC: The Java API for XML-Based Remote Procedure Call (JAX-RPC) enables developers to build
Web Services using XML-based RPC functionality according to the SOAP 1.1 specification.
We have included Apache Axis 1.3 in the runtime to allow clients to consume the JAX-RPC based
services.
Note: We do not support JAX-RPC based providers in the runtime. Also, Lotus Expeditor Toolkit extends
the RAD’s Web Services wizards to allow the generate the JAX-RPC based client that are compliant
with the Lotus Expeditor Runtime.
For more information about JAX-RPC, please visit http://java.sun.com/xml/jaxrpc.
The Web Services Client Programming Model: Similar to the programming model specified in the Web
Services for J2ME specification (JSR-172), the Lotus Expeditor Toolkit provides the following capabilities:
1. A generated stub from the Web Services Description Language (WSDL) description of the service
operation.
The Mobile Web Services Client wizard generates a static client stub class using the WSDL that is
exported from the Web Services provider as its input. The stub is then used to invoke the Web
Services provider.
In addition to the static stub, the Web Services Gateway proxy library (com.ibm.pvcws.osgi), a
component of Lotus Expeditor can be used to generate a dynamic client stub on-the-fly. This dynamic
client stub may be used in place of the static client stub, which hard-codes the SOAP message
definitions and method calls, in order to build Web Services clients dynamically. Other functionality
provided by this proxy library is the ability to provide custom marshallers (serializers) for types that
are incompatible with JSR-172.
Note: Lotus Expeditor Client for Devices supports a JSR-172 implementation only and does not
provide com.ibm.pvcws.osgi.
2. WSDL-defined API.
The WSDL document defines an application programming interface (API) that makes up the complete
Web Services client application. This API must be present on both the server and client side to allow
the endpoints to communicate properly.
3. Instantiation of the stub
The client application uses an instance of the static or dynamic stub to indirectly access the Web
service defined by a given WSDL.
It is imperative that the WSDL definition reflects the actual interface to the Web service at runtime.
The JAX-RPC subset does not perform any version control. Any differences between the defined
WSDL and the instance of the Web Service may produce unpredictable results.
4. Invocation of stub methods that correspond to the implementation of service endpoint operations.
The Web Services client application can use an instance of the stub to set stub properties, including
the service endpoint. The methods generated in the stub are used to call service endpoint operations.
252 Lotus Expeditor: Developing Applications for Lotus Expeditor
5. Packaging the stub with the client application.
The generated stub is provided in source form. It is used during application development.
Tools
This section provides information on Web Services tools.
Tools for Mobile Web Services development: The Lotus Expeditor Toolkit provides tools for creating
Mobile Web Services client code, as well as code for exposing an OSGi service as a Web services provider.
The tools provided include:
v A Web Services client wizard that includes a wizard to configure security
v A Web Services client security wizard to configure client security
v A Web Services provider wizard to expose OSGi services as Web Services providers
v A Web Services provider security wizard to configure security
v Editors to modify WS-Security configurations
v A Web Services wizard for creating JAX-RPC based clients
Web Services Resource Framework
This section contains information regarding the Web Services Resource Framework.
Lotus Expeditor for Devices does not support WSRF.
Understanding WSRF applications
Web Services Resource Framework (WSRF) defines a resource as a logical entity that is identifiable, and
has zero or more properties, which are expressible in XML infoset. A resource may also have a lifecycle.
A Web Service Resource (WS-Resource) is the combination of a resource and a Web service through which
the resource can be accessed.
WSRF defines a set of specifications for accessing resources using Web services in a stateful manner. For
more information about WS-Resource, please visit http://www.osgi.org.
Technologies: Web Services Resource Framework Specification
The Web Services Resource Framework (WSRF) is a family of specifications introduced in January 2004,
with the intention to provide a way to access stateful resources using a standard set of message exchange
patterns, fronted by web services. The WSRF family specifications include the following:
v WS-Resource
v WS-ResourceProperties
v WS-ResourceLifetime
v WS-BaseFaults
The key concept in WSRF is the WS-Resource, which is composed of a Web service and a stateful resource.
A stateful resource can be the files in a file-system or rows in a relational database, or an encapsulated
object in an OSGi Service. The consumer of the WS-Resource can access this stateful resource using
standard set of message exchange pattern described as operations in the above set of specification. For
example, WS-ResourceProperties specifications define a standard set of message exchanges that allow a
requestor to query or update the resource property values. Similarly, WS-ResourceLifetime specification
standardizes the means by which a WS-Resource can be destroyed; it defines the means by which a
resource may be destroyed after a period of time. WS-BaseFaults provides a standard way of reporting
faults from WS-Resources to requestor.
Developing applications 253
WS-Resource: A WS-Resource is the combination of a resource and a Web service through which the
resource can be accessed. WSRF specification defines the pattern by which resources are accessed through
Web services, and the means by which WS-Resources are referenced. Some of the important characteristics
of WS-Resource are as follows:
v A reference to a WS-Resource is represented by an endpoint reference (EPR), or more precisely an XML
element whose type is, or is derived (by extension), from the complexType named
EndpointReferenceType defined by the [WS-Addressing] specification. Such EPRs must reference exactly
one WS-Resource.
v The set of properties of the resource must be expressed using an XML Infoset described by XML
schema. The WS-Resource must support accessing resource properties through message exchanges
defined by the WS-Resource Properties specification [WS-ResourceProperties].
v A WS-Resource may support the message exchanges defined by the WS-Resource Lifetime specification
[WS-ResourceLifetime].
The following figure shows a simple scenario in which three different resources (A, B and C) being
exposed as WS-Resource using a Web service.
1. EndpointReference2. Webservice3. EndpointReference (same as 1)4. wsa:Address referring to the Webservice5. The resourceID referring to Resource "C"
1
4
2
C
A
B5
3
<wsa:EndpointReference><wsa:Address>
http://someOrg.com/aWebService</wsa:Address><wsa:ReferenceProperties>
<tns:resourceID> C </tns:resourceID></wsa:ReferenceProperties></wsa:EndpointReference>
ServiceRequestor
The EndpointReference for the resource “C” is shown below:
<wsa:EndpointReference>
<wsa:Address>http://someOrg.com/aWebService</wsa:Address>
<wsa:ReferenceParameter>
<tns:resourceID> C </tns:resourceID>
</wsa:ReferenceParameter>
</wsa:EndpointReference>
In complex scenarios, there may be additional information in EndpointReference, provided as
ReferenceParameters or MetaData that may be required to clearly identify the associated resource.
WSRF Runtime: WSRF Plug-ins are an implementation of the following set of specifications in the OSGi
environment:
254 Lotus Expeditor: Developing Applications for Lotus Expeditor
Table 21. Specification versions
Specification Number Specification Version
1 WS-Resource http://docs.oasis-open.org/wsrf/wsrf-ws_resource-1.2-spec-cs-01.pdf
2 WS-ResourceLifeTime http://docs.oasis-open.org/wsrf/wsrf-ws_resource_lifetime-1.2-spec-cs-01.pdf
3 WS-ResourceProperties http://docs.oasis-open.org/wsrf/wsrf-ws_resource_properties-1.2-spec-cs-01.pdf
4 WS-BaseFault http://docs.oasis-open.org/wsrf/wsrf-ws_base_faults-1.2-spec-cs-01.pdf
5 WS-Addressing http://www.w3.org/TR/2005/CR-ws-addr-core-20050817
The WSRF implementation provides an environment to host WS-Resources in an OSGi environment.
These WS-Resources by definition can be accessed through web services in a stateful manner. The
programming model supported by the WSRF implementation allows for exposing varied constructs, like
an OSGi Service, a Java Bean, a physical file system, or a database as a WS-Resource.
The WSRF implementation also provides a client runtime environment, where WS-Resource clients and
applications can run and access WS-Resources.
Note: Running Lotus Expeditor with the osgi.resolveMode=strict option, may lead to unpredictable
behavior of the Web Services Resource Framework (WSRF) components.
WSRF tools: The Lotus Expeditor Toolkit provides tools for creating WS-Resource and client code to
access the same. The tools provided include:
v Wizard to expose OSGi services as WS-Resource providers
v Wizard to create client code for accessing WS-Resource hosted on Lotus Expeditor
Note: Running Lotus Expeditor Toolkit with osgi.resolveMode=strict option may lead to unpredictable
behavior of the Web Services Resource Framework (WSRF) tool components.
Creating WS-Resource projects
The WSRF implementation realizes the WS-Resource modeling in OSGi environment with the help of the
following constructs:
v Resource
v Adapter
v Web service
Resource: An OSGi bundle that encapsulates one or more stateful service objects that must be exposed as
a WS-Resource. This service object in the Resource Bundle may be developed to encapsulate some
business logic or be a software façade for a hardware resource. Some examples of a Resource include: a
row of a database, a file system, or hardware components.
Web service: An OSGi bundle that acts as a Web Service facade for the WS-Resource. This Web service
exposes WSRF standard port-types and custom port-types defined by the Resource developer. This
bundle also encapsulates the Message-level authentication/authorization logic.
Adapter: An OSGi bundle, acts as a bridge between the Web Services bundle and the Resource bundle. It
routes the incoming Web Service messages (both the WSRF standard port-types and the custom
Developing applications 255
port-types) to the Resource instance addressed by the requestor. Any responses from the resource
(including exceptions) are routed back through the Adapter and the Web Service bundle to the requestor
as normal response or faults. The Adapter bundle is partially generated by the WSRF tool wizards, with
placeholders for the developer to provide implementation to interact with the Resource instances in the
Resource bundle.
Sample WS-Resource on Lotus Expeditor
The following figure illustrates the various components of WS-Resource in an OSGi environment and the
corresponding client components. The client environment need not necessarily be an OSGi environment.
WS-Resource clientapplication
WS-Resource Client Environment WS-Resource Provider Environment
Communication -Application specificproduct
Database -Physical resource
Web service client stub
WSRF4OSGi ClientComponents
WS-Addressing
Web service Clientruntime
Resource Service(OSGi Service)
WS-Resource Adapter
WS-ResourceWeb Service
WSRF4OSGi ClientComponents
WS-Addressing
Web Services ServerRuntime (SOAP Server)
Communication-SOAP over HTTP
OS
GI/N
on
-OS
iE
nviro
nm
en
t
OS
Gi
En
viro
nm
en
t
In the above scenario, the physical resource (a database) is exposed as an OSGi service, and this OSGi
service is in turn exposed as a WS-Resource.
Developing WS-Resource providers: Developing a WS-Resource involves creating the basic OSGi service
that exposes a physical resource as an OSGi service, and creating the Adapter to the resource and the
corresponding Web service through which the resource can be accessed. Some part of the WS-Resource
can be developed using the tools provided by Lotus Expeditor Toolkit. For other development needs, the
developers are required to provide the implementation.
Developing the resource bundle: This step involves development of an OSGi service that exposes a physical
resource such as a database, Java bean or a file system. The developer has to define a service interface
and provide an implementation for this interface (if necessary). For the case of simplicity, it is assumed
that the developer provides an implementation to the service interface. Developers can use the standard
plug-in development wizard to create an OSGi plug-in to expose this interface as an OSGi service.
In the next step, create the Web Service bundle and the Adapter bundle to expose the OSGi service
interface (Resource) as a WS-Resource.
Developing the Web Services bundle and adapter bundle: Lotus Expeditor Toolkit provides wizards to
generate the WS-Resource Adapter and Web service components. The WS-Resource Web service
256 Lotus Expeditor: Developing Applications for Lotus Expeditor
component is completely generated by the tool, whereas the WS-Resource Adapter component generated
by the tool requires you to provide enhancements/modifications based on specific scenarios.
The WS-Resource Provider Wizard of the Lotus Expeditor Client Lotus Expeditor Toolkit uses the
bottom-up approach to generate the WSDL (and the related Provider/Client code skeletons) from a Java
Interface/code. Perform the following steps to create the Web service OSGi bundle and the related
Adapter bundle for the given Resource bundle:
1. Select File > New > Other.
2. Select Client Services > Mobile Web Services > WS-Resource Provider.
3. From the WS-Resource details page:
v Provide the WS-Resource name (for example, WS-Resource).
v Select the WS-Resource service interface.
v Add the service interface selected in step 2 and the dependent classes to the Resource Classpath
list.
v Select the Web Service Resource Framework (WSRF) specifications that should be implemented:
– By default, WS-Resource property specification is selected. Implementation of this specification is
mandatory for any WS-Resource.v Select Expose service methods to be able to expose the other methods of the OSGi Service Interface
as custom port-type of the WS-Resource, in addition to the standard WSRF port-types.
v To secure the WS-Resource, select Enable Security. Enabling security requires users to update the
generated Java classes and configuration files. Refer to the section “Securing a WS-Resource” on
page 273 for detailed steps.
v Click Next.4. From the WS-Resource properties selection page:
v Select the properties of the WS-Resource that should be exposed.
v You can also define additional properties to the WS-Resource, by selecting the Add property...
button.
– Specify the name of the property.
– Select the property type.
– If the property is an array, select the Array type check box.
– Click OK.v Click Next.
5. The Web service methods selection page is displayed if you chose to expose Web service methods in
Step 3.
v Select the methods that should be exposed as Web service methods.
v Click Next.6. From the project details page.
v Provide the Name and Location for the creation of new projects for the WS-Resource Web Service
and WS-Resource Adapter.
v Click Next.7. From the plug-in details page, provide the following information
v Plug-in name
v Plug-in ID
v Plug-in Version8. Click Finish.
After clicking Finish, the Lotus Expeditor Toolkit creates two projects in the workspace, one each for
WS-Resource adapter and WS-Resource web service.
Developing applications 257
Note: WS-Resource Provider wizard works on a bottom-up approach for generating a WSDL description
(such as, given the Java Interface a WSDL description is generated). The generated WSDL
description follows non-wrapped style. Hence, the custom port-types (service methods) in a
WS-Resource will not accept Java class/interface with the following constructs, while generating
WS-Resource provider bundles:
v Java interface with methods having more than one parameter
v Java interface with overloaded methods
Note: Currently, the WS-Resource Provider wizard does not provide a facility to specify the multiplicity
of the resource properties. However, the wizard defaults the multiplicity and nillable attributes as
follows:
Table 22.
Data Types minOccurs maxOccurs nillable Description
Primitive types (e.g.,
int)
1 1 false The property can not
be ’null’.
Wrapper types (e.g.,
Integer)
1 1 true The property can be
’null’.
Primitive array types
(e.g., int[])
0 unbounded false The property can be
’null’. If the property
is not ’null’, the
individual array
elements can not be
’null’.
Wrapper array types
(e.g., Integer[])
0 unbounded true The property can be
’null’. If the property
is not ’null’, the
individual array
elements can be
’null’.
Complex types (e.g.,
URI)
1 1 true The property can be
’null’.
Complex array types
(e.g., URI[])
0 unbounded true The property can be
’null’. If the property
is not ’null’, the
individual array
elements can be
’null’.
Similar conventions are followed for the parameters and return types of custom port-types (service
methods) in Web Services.
Note: The generated WS-Resource Provider exposes all the port types of WS-ResourceProperties
specification. The wizard does not provide a facility to deselect portTypes designated as ″optional″
by the specification. Similarly, when you choose to implement the WS-ResourceLifeTime interface,
all portTypes of the WS-ResourceLifeTime specification will be exposed.
Note: The wizard does not support non-English text for property names, resource class/interface name,
project name, package prefix, plug-in name, plug-in ID, plug-in provider name. Entering
non-English text in the wizard fields may result in undesirable behavior.
Creating a WS-Resource client: To generate the client stub, the WS-Resource should have been
deployed and started on the Lotus Expeditor runtime.
258 Lotus Expeditor: Developing Applications for Lotus Expeditor
To create a Web Service Resource client from the WSDL description of WS-Resource, perform the
following steps:
1. Select File > New > Other.
2. Select Client Services > Mobile Web Services > Create WS-Resource Client.
3. From the WS-Resource details page:
v Enter the location of the WSDL description published by the Web Services provider. You can
provide an HTTP URL.
v To secure access to the WS-Resource, select Enable Security. Enabling security requires users to
update the generated Java classes and configuration files. Refer to the section “Securing a
WS-Resource” on page 273 for detailed steps.
v Click Next.4. From the project details page.
v Provide the Name and Location for the creation of a new project for the WS-Resource client.
v Click Next.5. From the plug-in details page, provide the following information:
v Plug-in name
v ID
v Version6. Click Finish.
After clicking Finish, the Lotus Expeditor Toolkit creates the project in the workspace, for the
WS-Resource client.
Note: To run the WS-Resource Client wizard successfully, the computer that runs the wizard should be
connected to the internet, since the wizard accesses external schemas while generating the client
Java code.
Note: The WSDL description for WSRF Web services follows a non-wrapped style and the WS-Resource
client wizard is customized to generate Java code for WSRF Web services only. Also, using a
different client generation tool (other than the WS-Resource Client wizard) to generate Java code
for WSRF Web services may not generate correct code.
Note: The SOAP messages exchanged between the WS-Resource provider and the WS-resource client
follow a non-wrapped style. Currently, the WS-Addressing implementation is customized to handle
non-wrapped style SOAP messages.
Note: Currently, the wizard does not support non-English text for the project name, package prefix,
plug-in name, plug-in ID, plug-in provider name. Entering non-English text in the wizard fields
may result in undesirable behavior.
Note: You should not modify the generated package or class names of the WS-Resource client bundle.
Supported data types: The current version of the WSRF implementation supports the following data
types as WS-Resource properties, parameters and return types in service methods:
Primitive/Primitive array int, long, float, double, byte, short, boolean
Wrapper/Wrapper array Integer, Float, Long, Double, Byte, Short, Boolean
Developing applications 259
Complex types/Complex array java.lang.String
java.net.URI
java.util.Calendar
java.util.Date
javax.xml.namespace.QName
org.w3c.dom.Element
com.ibm.wsaddressing.wsspi.EndpointReference
com.ibm.wsrf.common.duration.Duration
Any Java bean that wraps one or more of the above
types as instance variables
Note: Currently, custom marshaller support is not provided for WSRF Web services.
Note: char, Character, char[], Character[] types are not supported. The org.w3c.dom.Element[] type
is not supported in custom port types (service methods) as parameters and return types. Java
beans containing the org.w3c.dom.Element[] type as a field is not supported. Usage of data types
other than those mentioned above in the table, may lead to unexpected behavior.
Note: EndpointReference (com.ibm.wsaddressing.wsspi.EndpointReference) type requires special
handling at the application level. The WS-Resource Client EndpointReference is handled as
com.ibm.wsrf.common.addressing.EndpointReferenceType. For example, if a custom port type takes
EndpointReference type as a parameter or return type, the corresponding method signature in the
generated WS-Resource Client SOAP stub will have EndpointReferenceType as a parameter or
return type. Similarly, in case of Resource properties of type EndpointReference, the WS-Resource
Client SOAP stub will have EndpointReferenceType as a type (QType for the property) description.
You should send and receive objects of type EndpointReferenceType while invoking methods on
the generated client SOAP stub. You can use the following methods provided by the
com.ibm.wsrf.common.util.EndPointReferenceHelper class to convert objects of one type to another
and vice-versa:
v EndPointReferenceHelper.getEndPointReferenceObject (EndpointReferenceType eprType) –
Returns object of type EndPointReference
v EndPointReferenceHelper. getEndPointReferenceTypeObject(EndpointReference epr) – Returns
object of type EndPointReferenceType
Developing WSRF application logic
This section contains information on developing logic for WSRF applications.
WS-Resource provider application: WS-Resource Web service generated by the toolkit does not require
any changes. On the other hand, WS-Resource Adapter generated by Lotus Expeditor Toolkit requires
you to provide implementation of the business logic in the generated classes.
The tool will generate the following sub-components in the adapter plug-in:
Table 23.
Sub-Component Name Description
<ResourceName>AdapterActivator Bundle activator of WS-Resource Adapter OSGi bundle.
<ResourceName>AdapterImpl Client requests received by WS-Resource Web service are
delegated to this class.
260 Lotus Expeditor: Developing Applications for Lotus Expeditor
Table 23. (continued)
Sub-Component Name Description
<ResourceName>LifeTimeManager This class contains the empty implementations for
managing the lifetime of the WS-resource. Developers
should provide implementation to the methods based on
the scenario.
<ResourceName>PropertyManagerBase This is the base class for <ResourceName>PropertyManager
class bridging the WSRF implementation and
WS-Resource Adapter.
<ResourceName>PropertyManager This class contains empty implementations for managing
the WS-Resource properties. Developers should provide
implementation to the methods based on the scenario.
<ResourceName>ServiceManager This class contains default implementation for the Service
methods.
<ResourceName>AdapterIfc This Interface contains the Service Methods that the
adapter is exposing on behalf of the actual resource.
For example, if the resource name is SampleResource the names of the generated classes would be
SampleResourceLifeTimeManager, SampleResourcePropertyManager, and so on.
You should provide implementation to methods of the following classes:
v <ResourceName>LifeTimeManager
v <ResourceName>PropertyManager
For explaining the steps in making the Resource, Web service and Adapter to work together as
WS-Resource, a simple resource called PrinterResource is considered.
PrinterResource: This resource is developed as an OSGi bundle and has the following service interface
and implementation:
public interface IPrinterService {
String getJobID();
void setJobID(String argJobID);
}
public class PrinterService implements IPrinterService {
private String jobID = null;
public String getJobID() {
return jobID;
}
public void setJobID(String argJobID) {
jobID = argJobID;
}
}
The PrinterService is registered to the OSGi environment through the bundle activator class of
PrinterResource OSGi bundle.
PrinterResourceAdapter: The Adapter is generated using the WS-Resource Provider wizard for the above
resource. You should provide implementation for the methods in the following classes generated by the
wizard:
Developing applications 261
PrinterResourceLifeTimeManager
The following code sample shows a sample implementation of getResource, createResource, and
destroyResource methods.
public class PrinterResourceLifeTimeManager {
private BundleContext bundleContext = null;
private IPrinterService printerService = null;
public PrinterResourceLifeTimeManager(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
public Object getResource(ResourceConfiguration resourceConfiguration)
throws ResourceUnavailableFault, Exception {
ServiceReference ref = bundleContext
.getServiceReference(IPrinterService.class.getName());
printerService = (IPrinterService) bundleContext.getService(ref);
return printerService;
}
public ResourceConfiguration createResource(Element configInfo)
throws ResourceUnavailableFault, ResourceUnknownFault, Exception {
ResourceConfiguration retValue = new ResourceConfiguration();
return retValue;
}
public void destroyResource(ResourceConfiguration resourceConfiguration)
throws ResourceUnavailableFault, ResourceNotDestroyedFault,
Exception {
// Scenario specific logic for destroying resource
}
}
The implementation of createResource is specific to the WS-Resource developed. The
ResourceConfiguration object returned by the method will contain information about the created
resource for the consumption of the client application. The implementation of getResource
returns an instance of the OSGi service. Similarly, the implementation of destroyResource is also
specific to the WS-Resource developed.
PrinterResourcePropertyManager
The following code sample shows a sample implementation of the methods in the
PrinterResourcePropertyManager class:
public class PrinterResourcePropertyManager extends PrinterResourcePropertyManagerBase{
private org.osgi.framework.BundleContext bundleContext;
public PrinterResourcePropertyManager(org.osgi.framework.BundleContext bundleContext) {
super(bundleContext);
this.bundleContext = bundleContext;
}
public void updateJobID(Object resource, Object value)
throws ResourceUnavailableFault,
UnableToModifyResourcePropertyFault,
UpdateResourcePropertiesRequestFailedFault,
InvalidModificationFault,
BaseFault, Exception {
printerresource.IPrinterService resourceRef = (printerresource.IPrinterService) resource;
java.lang.String inValue = (java.lang.String)value;
resourceRef.setJobID(inValue);
}
public Object getJobID(Object resource)
262 Lotus Expeditor: Developing Applications for Lotus Expeditor
throws ResourceUnavailableFault, Exception {
printerresource.IPrinterService resourceRef = (printerresource.IPrinterService) resource;
return resourceRef.getJobID();
}
public void insertJobID(Object resource, Object value)
throws ResourceUnavailableFault,
UnableToModifyResourcePropertyFault,
InsertResourcePropertiesRequestFailedFault,
InvalidModificationFault,
BaseFault, Exception {
printerresource.IPrinterService resourceRef = (printerresource.IPrinterService) resource;
java.lang.String inValue = (java.lang.String)value;
resourceRef.setJobID(inValue);
}
public void deleteJobID(Object resource)
throws ResourceUnavailableFault,
DeleteResourcePropertiesRequestFailedFault, Exception {
printerresource.IPrinterService resourceRef = (printerresource.IPrinterService) resource;
// Scenario specific logic for deleting resource property
}
}
If the exposed property is of the array type, special handling may be required. For example, if
the jobID is of type String array, the implementation for insertJobID and updateJobID will be as
below:
public void updateJobID(Object resource, Object value)
throws ResourceUnavailableFault,
UnableToModifyResourcePropertyFault,
UpdateResourcePropertiesRequestFailedFault,
InvalidModificationFault,
BaseFault, Exception {
printerresource.IPrinterService resourceRef = (printerresource.IPrinterService) resource;
String[] inValue;
if(value == null){
inValue = null;
}else{
inValue = new String[((Object[])value).length];
System.arraycopy(value, 0, inValue, 0, ((Object[])value).length);
}
resourceRef.setJobID(inValue);
}
public void insertJobID(Object resource, Object value)
throws ResourceUnavailableFault,
UnableToModifyResourcePropertyFault,
InsertResourcePropertiesRequestFailedFault,
InvalidModificationFault,
BaseFault, Exception {
printerresource.IPrinterService resourceRef = (printerresource.IPrinterService) resource;
String[] inValue;
if(value == null){
inValue = null;
}else{
inValue = new String[((Object[])value).length];
System.arraycopy(value, 0, inValue, 0, ((Object[])value).length);
}
}
Developing applications 263
In cases where the exposed property is array of primitives such as the int array, they will be
received in the adapter as the corresponding wrapper array (for example, int array will be
received as the Integer array). This wrapper array has to be iterated and the corresponding
primitive array should be constructed.
The following samples explain how the primitive and wrapper arrays are handled.
Consider an example of a resource property that is of the type primitive array, for example int
[]. In the WS-Resource Adapter (in the PropertyManager class), the object received in the
Update/Insert method would be of type Integer[] instead of int[]. This should be handled as
follows:
public void insertIntArray(Object resource, Object value)
throws Exception {
//cast the resource object to proper type
Object[] temp = (Object[])value;
//this would be Object[] and not int[]
int[] arr = new int[temp.length];
for (int i = 0; i < temp.length; i++) {
arr[i] = ((Integer)temp[i]).intValue();
}
//invoke the method on the resource object
}
Similarly, in the case of the GetResourceProperty operation, int[] is handled as follows:
throws Exception {
//cast the resource object to proper type
//int[] arr = get the int[] object from resource object
Integer[] integers = new Integer[arr.length];
for (int i = 0; i < integers.length; i++) {
integers[i] = new Integer(arr[i]);
}
return integers;
}
For handling an array of Java Wrapper array types, such as Boolean[] must be handled as
follows:
public void updateWBooleanArray(Object resource, Object value)
throws Exception {
//cast the resource object to proper type
Boolean[] newVal = new Boolean[((Object[])value).length];
System.arraycopy(value, 0, newVal, 0, ((Object[])value).length);
//invoke the method on the resource object
}
WS-Resource client applications: WS-Resource Client SOAP stubs generated by the toolkit do not
require any modifications. You will have to create the application that delegates the requests to the
generated client SOAP stub to consume the services offered by WS-Resource.
WS-Resource creation operations: Clients can create an instance of WS-Resource by using the
createWSResource() method provided in the WS-Resource. The code sample below shows how clients can
create an instance of a WS-Resource:
264 Lotus Expeditor: Developing Applications for Lotus Expeditor
Note: Clients must save WS-Resource Endpoint Reference for any further communication with this
instance of the WS-Resource.
configInfo: The configInfo is a Node array of Text/Element objects that contains any information that
may be required to instantiate the WS-Resource. For example, in the case of a Database, it might be the
User ID and password information. In the case that WS-Resource does not require any such information,
then configInfo may be null.
WS-Resource LifeTime operations: WS-Resource LifeTime provides the following operations for
managing the lifetime of a WS-Resource:
Setting the termination time of a WS-Resource instance: Client applications can set the TerminationTime for
the WS-Resource by passing a Calendar or Duration Object. WSRF server side runtime framework
destroys the resource at a scheduled time. The termination time can be set by providing a future time, or
by passing a Duration Object (containing the duration of the remaining Time). The code example below
shows how to mark the termination time for the WS-Resource by passing a future time:
String uriString = //Endpoint address (URL string) of the Webservice
EndpointReference addressingEPR = null;
AddressSoap_Stub stubObj = null; //Webservice client stub object, AddressSoap_Stub
is the sample stub obj.
WSAddressingService addressingService = null;
//Create an End Point Rreference for the Webservice
try {
addressingService = new WSAddressingServiceImpl();
addressingEPR = addressingService.createEndpointReference(uriString);
} catch (Exception e) {
//handle exception
}
Node[] configInfo = //Text/Element Nodes representing the configuration Info
Calendar initTermTime = //the termination time client want to set for the WS-Resource
EndpointReference fromEPR = //the from EPR field. This will be null in the case of a non Webservice client.
HandlerList handlerList =//the handlerList Object containing the Handler objects to be used in this request.
//Create an instance of Web service client SOAP stub
stubObj = new AddressSoap_Stub();
//Create Operation Context for this operation
OperationContext operationContext = new OperationContext();
operationContext.setFromEPR(fromEPR); //optional, it need to be set when there is a Resource to
Resource communication
operationContext.setToEPR(addressingEPR);
operationContext.setHandlerList(handlerList); //setting handler list is optional. Required only
if custom messsage handlers are required.
OperationContext.setCurrentThreadsContext (operationContext);
//Invoke create Operation on the SOAP Stub
EndpointReference wsresourceEPR = null;
try {
wsresourceEPR = stubObj.createWSResource(configInfo, initTermTime);
} catch (Exception e1) {
//handle exception
}
Calendar newTerminationTime = //new termination time to be set for this instance of WS-Resource
EndpointReference fromEPR = the from EPR field. This will be null in the case of a non Webservice client
EndpointReference wsresourceEPR = //Resource Qualified EndpointReference returned by WS-Resource as
a result of createWSResource operation
AddressSoap_Stub stubObj = new AddressSoap_stub(); //Sample Webservice Client Stub object
int termTime = 0//future time
Calendar newTerminationTime = Calendar.getInstance();
newTerminationTime.set(Calendar.HOUR, termTime);
try{
opContextObj = OperationContext.getCurrentThreadsContext();
opContextObj.setToEPR(wsresourceEPR);
stubObj.setTerminationTime(newTerminationTime);
}catch(Exception e){
// handle exceptions
}
Developing applications 265
//use the below code if you want to setTerminationTime using Duration.
String pattern= "P2Y5M4D";
Duration durationInfo = Duration.parse(pattern));
try{
opContextObj = OperationContext.getCurrentThreadsContext();
opContextObj.setToEPR(wsresourceEPR);
stubObj.setTerminationTime(durationInfo);
}catch(Exception e){
// handle exceptions
}
Destroying the WS-Resource instance: Client applications can explicitly destroy the WS-Resource. On
receiving such a request, the WSRF server side framework removes references to the associated
WS-Resource. The code example below shows how you can explicitly destroy the WS-Resource:
EndpointReference wsresourceEPR = //Resource Qualified EndpointReference returned by
WS-Resource as a result of createWSResource operation
AddressSoap_Stub stub = new AddressSoap_stub(); //Sample Webservice Client Stub object
try{
opContextObj = OperationContext.getCurrentThreadsContext();
opContextObj.setToEPR(wsresourceEPR);
stubObj.destroy();
}catch(Exception e){
// handle exceptions
}
WS-Resource properties operations: This section contains information regarding WS-Resource
properties operations.
Acquiring the value of a resource property: It is assumed that the client has saved the EndpointReference
that was returned to the client as a result of a createWSResource operation. The code example below
shows how you can get the value of a resource property from WS-Resource:
QName propertyQName = // QName of the resource property whose value client is looking for
EndpointReference wsresourceEPR = //Resource Qualified EndpointReference returned
by WS-Resource as a result of createWSResource operation
propertyQName = //value for QName can be populate from generated RPD java file i.e
Address_ResourcePropertyDocument.java
AddressSoap_Stub stubObj = new AddressSoap_stub();
//create an Operation Context for this operation
try{
OperationContext operationContext = OperationContext.getCurrentThreadsContext();
opContextObj.setToEPR(wsresourceEPR);
OperationContext.setCurrentThreadsContext(opContextObj);
Object[] responseObj = stubObj.getResourceProperty(propertyQName);
}catch(Exception e){
// handle appropriate Exceptions
}
Fetching values of multiple properties: It is assumed that the client has saved the EndpointReference that
was returned to the client as a result of the createWSResource operation. The code example below shows
how you can get the value of multiple resource properties from the WS-Resource using the
GetMultipleResourceProperties operation:
QName []propertyQNames = // QName array of the resource properties whose value client
is looking for
EndpointReference wsresourceEPR = //Resource Qualified EndpointReference returned by
WS-Resource as a result of createWSResource operation
AddressSoap_Stub stubObj = new AddressSoap_stub();
//create an Operation Context for this operation
266 Lotus Expeditor: Developing Applications for Lotus Expeditor
try{
OperationContext operationContext = OperationContext.getCurrentThreadsContext();
opContextObj.setToEPR(wsresourceEPR);
OperationContext.setCurrentThreadsContext(opContextObj);
Hashtable ht = stubObj.getMultipleResourceProperties(propertyQNames);
Set enu = ht.keySet();
Iterator itr = enu.iterator();
while(itr.hasNext()){
QName key = (QName)itr.next();
String displayValue = new String();
Object value = ht.get(key);
displayValue = value.toString();
}
}catch(Exception e){
// handle appropriate Exceptions
}
Querying for resource properties information: The WSRF implementation allows clients to query values of
the Resource properties exposed by the WS-Resource. For doing a query, users should provide XPath 1.0
based query expression information. For example, SampleWSResource_ResourcePropertyDocument/CurrentTime. The root of RPD will always be <PID>_ResourcePropertyDocument.
Below is a sample code explaining how to query a WS-Resource:
String expression = //the query expression [e.g //emailed to query emailed Property]
EndpointReference wsresourceEPR =
//Resource Qualified EndpointReference returned by WS-Resource as a
result of createWSResource operation
AddressSoap_Stub stubObj = new AddressSoap_Stub();
try{
opContextObj.setToEPR(wsresourceEPR);
OperationContext.setCurrentThreadsContext(opContextObj);
Node [] response = stubObj.queryResourceProperties(expression);
} catch(Exception e){
//handle exceptions
}
Deleting the value of a resource property: It is assumed that the client has saved the EndpointReference,
which was returned to the client as a result of the createWSResource operation. In the WSRF
environment, deletion of a resource property does not imply that the values associated with that resource
property are deleted from the actual resource instance. The property will get removed from the Resource
Property Document for that WS-Resource Instance. The responsiblity of deleting the value of the resource
property lies with the adapter Property manager class. It must be noted that the WS-Resource may or
may not allow deletion of a resource property.
Once the property is deleted, the user may no longer be able to get/query the value of that resource
property. To interact again with that property the user must use the insert operation.
It must be noted that delete should not be used for TerminationTime and CurrentTime resource properties.
The code example below illustrates how to delete a resource property in the WS-Resource:
QName deleteQName = // QName of the resource property whose
value has to be deleted, this can be find from generated RPD class
i.e Address_ResourcePropertyDocument
EndpointReference wsresourceEPR = //Resource Qualified EndpointReference
returned by WS-Resource as a result of create WS-Resource operation
AddressSoap_Stub stubObj = new AddressSoap_stub(); // Sample Client Stub Object
try{
Developing applications 267
operationContext.getCurrentThreadsContext();
operationContext.setToEPR(wsresourceEPR);
OperationContext.setCurrentThreadsContext(opContextObj);
stubObj.deleteResourceProperties(deleteQName);
} catch (Exception e){
// handle exceptions
}
Inserting a resource property value: It is assumed that the client has saved the EndpointReference, which
was returned to the client as a result of the createWSResource operation. Inserting a resource property
implies that the property gets inserted in the Resource Property Document of that WS-Resource instance.
If the property already exists in the Resource Property Document, then the insert operation for that
property will fail.
It must be noted that insert should not be used for TerminationTime and CurrentTime resource properties.
The code example below illustrates how to insert a resource property in the WS-Resource:
QName insertQName = // QName of the resource property whose value has to be inserted
Object insertValue = //new value for resource property represented by above QName
EndpointReference wsresourceEPR = //Resource Qualified EndpointReference returned
by WS-Resource as a result of createWSResource operation
AddressSoap_Stub stubObj = new AddressSoap_stub(); // Sample Client Stub Object
InsertRequestType insertRP = new nsertRequestType(insertQName , insertValue);
try{
opContextObj.setToEPR(resourceEPR);
OperationContext.setCurrentThreadsContext(opContextObj);
stubObj.insertResourceProperties(insertRP);
}catch(Exception ex){
//handle exceptions
}
Note: The InsertResourceProperty operation cannot be invoked without setting a default value for the
property. If you want to insert a property without any value, set the property value to null in the
case of non-primitive type properties. In the case of primitive type properties, set a default value.
Updating a resource property value: It is assumed that the client has saved the EndpointReference, which
was returned to the client as a result of createWSResource operation. Note that the WS-Resource may or
may not allow updates on a resource property.
It must be noted that update should not be used for TerminationTime and CurrentTime resource
properties.
The code example below shows how one can update the value of a resource property in the
WS-Resource:
QName updateQName = // QName of the resource property whose value has to be updated
Object updateValue = //new value for resource property represented by above QName
EndpointReference wsresourceEPR = //Resource Qualified EndpointReference
returned by WS-Resource as a result of createWSResource operation
AddressSoap_Stub stubObj = new AddressSoap_stub(); // Sample Client Stub Object
UpdateRequestType updateRP = new UpdateRequestType (updateQName , updateValue);
try{
opContextObj.setToEPR(resourceEPR);
OperationContext.setCurrentThreadsContext(opContextObj);
268 Lotus Expeditor: Developing Applications for Lotus Expeditor
stubObj.updateResourceProperties(updateRP);
}catch(Exception ex){
//handle exceptions
}
Using Set Operation to simultaneously insert update delete resource properties: The SetResourceProperties
operation allows multiple changes to the resource property or properties in the resource property
document through a single request. It is assumed that the client has saved the EndpointReference, which
was returned to the client as a result of createWSResource operation. The Set request will maintain the
order of insert/update/delete provided by the client.
It must be noted that set should not be used for TerminationTime and CurrentTime resource properties.
The code example below shows how you can perform multiple operations through
SetResourceProperties on a resource property:
QName insertQName = // QName of the resource property whose value has to be inserted
Object insertValue = //new value for resource property represented by above QName
QName updateQName = // QName of the resource property whose value has to be updated
Object updateValue = //new value for resource property represented by above QName
QName deleteQName = // QName of the resource property whose value has to be deleted
SetRequestType insertRP = new InsertRequestType(insertQName, insertValue);
SetRequestType updattRP = new UpdateRequestType(updateQName, updateValue);
SetRequestType deleteRP = new DeleteRequestType(deleteQName);
ArrayList aList = new ArrayList();
aList.add(insertRP);
aList.add(updateRP);
aList.add(deleteRP);
EndpointReference wsresourceEPR = //Resource Qualified EndpointReference
returned by WS-Resource as a result of createWSResource operation
AddressSoap_Stub stubObj = new AddressSoap_stub(); // Sample Client Stub Object
try{
SetResourceProperties srp = new SetResourceProperties();
SetRequestType [] srt = new SetRequestType[aList.size()];
for(int i=0; i<aList.size(); i++){
srt[i] = (SetRequestType)aList.get(i);
}
opContextObj.setToEPR(resourceEPR);
OperationContext.setCurrentThreadsContext(opContextObj);
srp.setSetRequestTypes(srt);
stubObj.setResourceProperties(srp);
}catch(Exception ex){
//handle exceptions
}
Fetching the ResourcePropertyDocument: Users can get the ResourcePropertyDocument for the WS-Resource
instance. Below, is a sample code explaining how you can get the Resource Property Document for the
WS-Resource instance:
EndpointReference wsresourceEPR = //Resource Qualified EndpointReference
returned by WS-Resource as a result of createWSResource operation
AddressSoap_Stub stubObj = new AddressSoap_stub(); // Sample Client Stub Object
try{
opContextObj.setToEPR(wsresourceEPR);
OperationContext.setCurrentThreadsContext(opContextObj);
Object[] obj = stubObj.getResourcePropertyDocument();
} catch(Exception e){
//handle exceptions
}
Developing applications 269
Replacing the ResourcePropertyDocument: The PutResourcePropertyDocument operation helps the
applications to replace the existing resource property document of a WS-Resource. It is assumed that the
client has saved the EndpointReference, which was returned to the client as a result of the
createWSResource operation.
It must be noted that put should not be used for TerminationTime and CurrentTime resource properties.
The code example below shows how one can use the putResourcePropertyDocument functionality:
QName propertyQName1 = // QName of the resource property whose value has to be updated
Object propertyValue1 = //new value for resource property represented by above QName
QName propertyQName2 = // QName of the resource property whose value has to be updated
Object propertyValue2 = //new value for resource property represented by above QName
EndpointReference wsresourceEPR = //Resource Qualified EndpointReference returned
by WS-Resource as a result of createWSResource operation
Address_PropertyMap map = new Address_PropertyMap // sample Property Map Object,
this will be generated by the tool.
map.addPropertyToMap(propertyQName1, propertyValue1);
map.addPropertyToMap(propertyQName2, propertyValue2);
AddressSoap_Stub stubObj = new AddressSoap_stub(); // Sample Client Stub Object
try{
opContextObj.setToEPR(resourceEPR);
OperationContext.setCurrentThreadsContext(opContextObj);
stubObj.putResourcePropertyDocument(map);
} catch(Exception e){
//handle exceptions
}
WS-Resource service method operations: This is a special type of interaction with WS-Resource. In
addition to exposing methods related to various WSRF specifications, the WSRF implementation allows
WS-Resources to also expose service methods. The code example below shows how clients can invoke a
service method echo(int i1) on a WS-Resource:
EndpointReference wsresourceEPR = //Resource Qualified EndpointReference
returned by WS-Resource as a result of createWSResource operation
AddressSoap_Stub stubObj = new AddressSoap_stub();
try{
operationContext.setToEPR(wsresourceEPR);
stubObj.setCurrentOperationContext(operationContext);
int i1 = 100;
int i2 = stubObj.echo(i1);
} catch (Exception e){
// handle exceptions if any
}
Note: The server side exceptions will be received as BaseFault or JAXRPCException at the client
application. To retrieve the exception trace, the following methods of the WSBFUtils class can be
used:
v WSBFUtils.getFaultTrace(Throwable argThrowable)
v WSBFUtils.getFaultTrace(BaseFault argBaseFault)
For example, the exception trace from JAXRPCException can be retrieved as follows:
catch (JAXRPCException e)
{
Throwable throwable = e.getLinkedCause();
String traceStr = WSBFUtils.getFaultTrace(throwable);
..................
}
270 Lotus Expeditor: Developing Applications for Lotus Expeditor
The exception trace from BaseFault can be retrieved as follows:
catch (BaseFault e)
{
Throwable throwable = e.getLinkedCause();
String traceStr = WSBFUtils.getFaultTrace(throwable);
..................
}
For a detailed explanation on developing WS-Resource and client applications, refer to the IBM
developerWorks article published at http://www.ibm.com/developerworks/lotus/library/expeditor-wsrf.
Packaging and deploying WS-Resource bundles
Web services applications are deployed as OSGi bundles/Eclipse plug-ins. They run in the Lotus
Expeditor runtime platform and require no extra special handling.
Deploying a WS-Resource provider: The WS-Resource provider is comprised of a Resource bundle,
Adapter bundle and the Web service bundle. In order to deploy the WS-Resource provider, launch an
instance of the Lotus Expeditor runtime with the Resource bundle, Adapter bundle and the Web service
bundle installed.
To deploy a Web services provider from the IDE, perform the following procedure:
1. Select Run > Run....
2. Create a new Lotus Expeditor runtime instance if necessary.
3. From the Plug-ins tab, ensure that your Web services provider bundles are checked.
4. Select Run.
5. At the osgi> prompt in the console view, type ss to display a list of all registered bundles and their
associated IDs.
6. From the list, find the bundle ID for your Resource, Adapter and Web service bundles.
7. At the osgi> prompt in the console view, type start <bundle ID>, where <bundle ID> is the bundle
ID for your plug-ins. Please start these three bundles in following order:
a. Resource bundle
b. Adapter bundle
c. Web service bundle8. Once the WS-Resource provider bundles are started, the WSDL for the WS-Resource Web service will
be immediately available for client use.
9. Verify that the Web services provider has started successfully. From a browser, enter the following
URL:
http://<machine>:<port>/ws/pid/<servicepid>?wsdl
Where <machine> is the name of the machine hosting the Web services provider, <port> is the port
used by the Web container, and <servicepid> is the name used to register the Web services provider.
You may look in the WS-Resource Web service Bundle Activator class to know the service PID for the
Web service Component of the WS-Resource.
For example, if the machine hosting the Web service is the local host, the Web container is listening on
port 1477, and the Java interface used to expose the Web service provider is MyWebServiceImpl, the
WSDL URL will be:
http://localhost:1477/ws/pid/MyWebServiceImpl?wsdl
Note: In cases where the WS-Resource Provider is security enabled, you should export Resource, Adapter
and Web Service as Folder type bundles instead of JAR type bundles.
Developing applications 271
Note: By default, the Web container port is dynamically selected by the Lotus Expeditor runtime. Refer
to the Web Container Configuration section in the Assembling and Deploying Lotus Expeditor
Applications in order to bind the Web container to a static port instead.
Deploying a WS-Resource client: In order to deploy the WS-Resource client, launch an instance of the
Lotus Expeditor runtime with the WS-Resource client bundle installed.
To deploy a Web services provider from the IDE, perform the following procedure:
1. Select Run > Run...
2. Create a new Lotus Expeditor runtime instance if necessary.
3. From the Plug-ins tab, ensure that your WS-Resource client bundle is checked.
4. Select Run.
5. At the osgi> prompt in the console view, type ss to display a list of all registered bundles and their
associated IDs.
6. From the list, find the bundle ID for your WS-Resource client bundle.
7. At the osgi> prompt in the console view, type start <bundle ID>, where <bundle ID> is the bundle
ID for your WS-Resource client bundle.
Note: It is not recommended to deploy a WS-Resource provider and its corresponding WS-Resource
client in the same instance of a Lotus Expeditor Client runtime. To use the WS-Resource client in
any application bundle in order to access a WS-Resource, export WS-Resource client as a Java
library (JAR) and package it along with the bundle, using the following procedure:
1. Export the WS-Resource Client Stub as JAR.
2. Copy the JAR file into the project folder of the bundle (the bundle with which WS-Resource
client should be packaged).
3. If the WS-Resource client is security enabled:
a. Add the following bundle to the Require-Bundle list of the bundle Manifest:
com.ibm.pvcws.wss.4. Edit the bundle Manifest to include the WS-Resource client JAR in Bundle-ClassPath
5. Edit the build configuration of the bundle to include the WS-Resource client JAR while
building the bundle
6. Export the bundle and deploy it on the Lotus Expeditor runtime
Accessing a WS-Resource from Java applications: A WS-Resource deployed on the Lotus Expeditor
runtime can also be accessed through a Java application.
To access a WS-Resource from a Java application, perform the following procedure:
1. Export the WS-Resource Client Stub as JAR and set it to the application class path
2. Set the Java libraries (JARs) corresponding to the following bundles to application classpath.
v com.ibm.pvcws
v com.ibm.pvcws.osgi
v com.ibm.wsaddressing
v com.ibm.wsbf
v com.ibm.wsrf.client
v com.ibm.wsrf.common
v org.eclipse.osgi
v com.ibm.icu
3. If the WS-Resource client is security enabled, additionally set the following in the classpath:
com.ibm.pvcws.wss.
272 Lotus Expeditor: Developing Applications for Lotus Expeditor
4. If the WS-Resource client is security enabled, copy all the generated folders containing security related
templates/files to the location where Java client application is run.
5. Run the application as a stand-alone Java application.
Securing a WS-Resource
The WSRF implementation currently supports security enablement of a WS-Resource at two levels:
v Enabling security, while generating a WS-Resource: This is achieved by selecting the Enable Security
checkbox while generating providerand client code for the WS-Resource.
v Enabling security, while running a pre-generated secured WS-Resource: Once the provider and client
components have been generated with the security code, you can enable the security at the runtime
with following security configurations:
– sign only
– encrypt only
– sign and encrypt
– authentication only
WSRF does not support the following security configurations in this release:
v signing and authentication
v encryption and authentication
v signing, encryption and authentication
To enable the security, it is necessary that both provider (sever-side) and client (client-side) be enabled
with the same security configuration.
Enabling security for WS-Resource providers: To enable security at server side, you must create the
WS-Resource provider component using the WSRF Provider tool with the Enable security option
checked.
With the exception of the ″authentication only″ security configuration, security configurations do not
require any modification in the generated code. If you want to enable the security configuration as
″authentication only″, you need to write the Custom Authenticator class. Please refer to “Developing
custom authenticators” on page 274 for more details.
To enable the security in the WS-Resource Provider at the runtime, you are required to:
v Create a Configuration Property (VM system property) in the rcpinstall.properties file that has the
format <WS-Resource_Webservice_Bundle_Symbolic_Name>.wsrf.security.type.
v The property will be set to a specific value depending on the required security configuration. The table
below shows the values to be set for the different security configuration.
Value Description
authenticator For security as ‘authentication only’
sign For Security as ‘sign only’
encrypt For security as ‘encrypt only’
sign_encrypt For security as ‘sign and encrypt’
For example, to enable security as ‘authentication only’ in the PrinterResource, if the Web service
component’s bundle symbolic name is printerresource.webservice, then set the Configuration Property
as:
- Dprinterresource.webservice.wsrf.security.type=authenticator
Note: The Configuration Property can be added into the rcpinstall.properties file, located at:
Developing applications 273
For Windows:
<home drive>\<home path>\IBM\rcp\<rcp.install.id>\<username>
For Linux:
<home>/IBM/rcp/<rcp.install.id>/<user>
To turn off security, delete the configuration property created for that particular WS-Resource.
Enabling security for WS-Resource clients: Create a WS-Resource’s client using the WS-Resource Client
tool with the Enable security option checked.
Once the client component is generated, it can be enabled with the appropriate security configuration
(same as provider’s security enablement option chosen).
To enable a particular security option, you are required to:
v Create a Configuration Property (VM system property) that has the format <PID>.wsrf.security.type.
v The property will be set to a specific value depending on the required security configuration. The table
below shows the values to be set for the different security configuration.
Value Description
authenticator For security as ‘authentication only’
sign For Security as ‘sign only’
encrypt For security as ‘encrypt only’
sign_encrypt For security as ‘sign and encrypt’
For example, for a WS-Resource configured with security as ‘authentication only’ and PID
‘PrinterResource’, the system property variable should be set as
PrinterResource.wsrf.security.type=authenticator.
For ‘authentication only‘ security configuration, the user ID and password should be provided by the
user as below:
1. When the client code is generated with the ‘Enable Security’ check box selected, a file named
ibm-webservicesclient-bnd.xmi is generated. This can be located in the package
*.security.authenticator.
2. This file will be edited and to provide the user id and password as below:
<basicAuth userid="user1" password="password1"/>
For turning off the security, delete the configuration property created for that particular WS-Resource.
Developing custom authenticators: In cases, when the WS-Resource Provider is generated with the
Enable Security check box selected, the tool generates the custom authenticator with empty
implementation in the WS-Resource Web service bundle.
This authentication can be identified by the name <ResourceName>Authenticator.java, and is available in
<PackagePrefix>.authenticator. For example, if the resource name is PrinterResource and the package
prefix is com.myorg, the authenticator generated can be identified as
com.myorg.authenticator.PrinterResourceAuthenticator.
You will have to provide implementations for the following methods of this class, to authenticate users
and to authorize users to access WS-Resource, Resource properties and the custom portTypes. This is
required if you have chosen the security configuration as ″authentication only″.
274 Lotus Expeditor: Developing Applications for Lotus Expeditor
public void doAuthenticate(WSRFMessageAutheticatorContext authMessageContext)
throws WSSException {
}
public void doAuthorize(WSRFMessageAutheticatorContext authMessageContext)
throws WSSException {
}
A typical authentication implementation (implementation of the doAuthenticate() method) will involve
getting the user name from the context and authenticating the user against a repository (file, database,
LDAP etc). The implementation should throw a new WSSException if the authentication fails.
A typical authorization implementation (implementation of the doAuthorize() method) will involve
writing authorization code by using WSRF SOAP request’s specific details - such as the operation name,
resource Properties and resource ID . The SOAP request details can be taken from the private methods
present within the <ResourceName>Authenticator.java class. The provider generates these private
methods within the <ResourceName>Authenticator.java class automatically and developers are just
required to consume these methods for the case specific implementation of doAuthorize() method.
Mobile Web Services
This section provides information regarding Mobile Web Services.
Creating Mobile Web Services
This section provides information on Mobile Web Services creation.
Creating Mobile Web Services providers: Any OSGi service can be exposed on the Lotus Expeditor as a
Web Services provider using the Lotus Expeditor Toolkit, provided that the service implements a Java
interface.
To create a Web Services provider from scratch, perform the following procedure:
1. Create a new Client Services project named MyWebServicesProvider:
a. Select File > New > Project.
b. Select Client Services > Client Services Project.
c. Select Next.
d. Type a name for the new project (for example, MyWebServicesProvider).
e. Select Next.
f. Select Next.
g. Select Finish.2. Create the code that will be exposed as a Web services provider:
a. Create a simple Java interface with a method declaration.
b. Create a Java bean class that implements the interface, and then select it in the workspace.
c. Select File > New > Other.
d. Select Client Services > Mobile Web Services > Mobile Web Services Provider.
e. Select the Java interface implemented by the selected file.
f. Select the classes requiring custom marshalling (if any).
g. If you checked Configure Security on the first page, select Next and refer to “Securing Mobile
Web Services” on page 289 for information on how to configure Web services security. Otherwise,
select Finish.
After selecting Finish, the Lotus Expeditor Toolkit will modify the Bundle Activator of the targeted
project with the following changes:
Developing applications 275
v Modifies BundleActivator to implement ServiceTrackerCustomizer methods -
addingService(),modifiedService() and removedService() to perform life cycle management in OSGi
runtime
v Adds the exposeService() method
The following is an example of a provider:
/* method called from addingService(ServiceReference arg0)
* exposeService(WebServiceProvider provider) created after exposing as web service
*/
private void exposeService(WebServiceProvider provider) {
Hashtable props = new Hashtable();
props.put(org.osgi.framework.Constants.SERVICE_PID, "MyWebService");
props.put("com.ibm.pvcws.wsdl", "");
context.registerService(complexprovider.ComplexInterface.class
.getName(), new MyWebService(), props);
provider.exportPid("MyWebService");
}
/*(non-Javadoc)
*@see org.osgi.util.tracker.ServiceTrackerCustomizer#addingService(org.osgi.framework.ServiceReference)
*/
public Object addingService(ServiceReference arg0) {
WebServiceProvider provider = (WebServiceProvider) context.getService(arg0);
registerCustomSerializers();
if (provider != null)
exposeService(provider);
return provider;
}
/*(non-Javadoc)
*@see org.osgi.util.tracker.ServiceTrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference)
*/
public void modifiedService(ServiceReference arg0, Object arg1) {
//DO NOTHING
}
/*(non-Javadoc)
*@see org.osgi.util.tracker.ServiceTrackerCustomizer#removedService(org.osgi.framework.ServiceReference)
*/
public void removedService(ServiceReference arg0, Object arg1) {
//DO NOTHING
}
Also, the start() method of the Bundle Activator will be modified to open the serviceTracker for OSGi,
so that the provider bundle waits until the web services runtime starts. In addition, the
registerCustomSerializers () method will be generated inside the Bundle Activator, if custom
marshalling types have been selected from the wizard. This method registers all the custom marshaller
factory classes as an OSGi service.
Both the exposeService() and registerCustomSerializers() needs to be get called from inside of
addingService() in order to have proper life cycle management. In addition, the Servicetracker must be
stopped and closed inside of the BundleActivator stop() method.
The Mobile Web Services Provider wizard also auto generates the full implementation code for custom
marshallers if the Web Services provider needs to handle java.util.Calendar or java.util.HashMap or
java.util.Hashtable. If the Web Services provider needs to handle non-bean classes or types that are
incompatible with JSR-172 (other than the one’s mentioned above), custom marshallers need to be
implemented. Please refer to the section “Custom serialization (marshalling)” on page 283 for more
information.
276 Lotus Expeditor: Developing Applications for Lotus Expeditor
Lotus Expeditor for Devices does not support running Web Services providers.
Creating Mobile Web Services clients: The Lotus Expeditor Toolkit can be used to create Web Services
client code that calls Web Services providers via a static or dynamic stub. The Lotus Expeditor Toolkit
supports the creation of Web Services clients using the WSDL of the Web Services provider. If the Web
Services client will target a Lotus Expeditor Client Web Services provider, it is best to create and deploy
the Web Services provider first, prior to creating the Web Services client so that its WSDL is available.
Following are the steps to create a Web services client from scratch:
1. Create a new bundle project named MyWebServicesClient:
a. Select File > New > Project.
b. Select Client Services > Client Services Project.
c. Select Next.
d. Type a name for the new project (for example, MyWebServicesClient).
e. Select Next.
f. Select Next.
g. Select Finish.2. Create a new client interface and optional stub:
a. Select File > New > Other.
b. Select Client Services > Mobile Web Services > Mobile Web Services clients.
c. Select Browse to select the source folder or project for your client application.
d. Optionally, enter the package you wish to use for the static stubs or leave it blank to use a default
package instead. Please note that this option will not be enabled if you select the Create Dynamic
Stub check box, as the Web Services runtime component requires that the package name match the
WSDL namespace.
The Lotus Expeditor Client for Devices does not support exposing OSGi services as Web Services
providers. Lotus Expeditor Client for Devices only supports JSR-172. without dynamic Web
Services clients.
Note: If you intend to deploy a Web service client and a Web service provider in the same
runtime, place the Web service client stub in a package that is different from the package of
the Web service provider to prevent a runtime conflict.
e. Enter the URL of the WSDL exposed by the Web Services provider (if known) or use the Browse
button to load the WSDL from any project in the current workspace.
Please note that if the URL is secured with SSL (e.g. HTTPS), you will need to start the Rational
Software Development Platform with the appropriate VM arguments to provide a valid client
certificate. For example, the following VM arguments can be used:
-Djavax.net.ssl.keyStore=<path_to_keystore_file>
-Djavax.net.ssl.keyStoreType=<keystore_type>
-Djavax.net.ssl.keyStorePassword=<keystore_password>
-Djavax.net.ssl.trustStore=<path_to_truststore_file>
-Djavax.net.ssl.trustStoreType=<truststore_type>
-Djavax.net.ssl.trustStorePassword=<truststore_password>
Optionally, select the Create Dynamic Stub check box to use dynamic stubs rather than create
static stubs.
A dynamic stub allows the Web Services client to create and use custom marshallers for WSDL
types that are non-bean classes or are incompatible with JSR-172.
f. Optionally, select the Configure Security check box to configure Web Services Security for the
client.
g. Select Next.
Developing applications 277
h. If there are any types in the WSDL that may require custom marshalling, you will be presented
with the option of generating custom marshaller stubs for each type. This will only be true if the
Create Dynamic Stub check box is checked.
For each type selected in the list, two classes will be generated; MarshalFactory and Marshaller.
These classes are implementations of the corresponding classes in the com.ibm.pvcws.osgi plug-in.
Please refer to the section “Custom serialization (marshalling)” on page 283 for information on
how to complete the implementation of custom marshallers.
i. If Configure Security was checked on the first page, select Next and refer to the section “Securing
Mobile Web Services” on page 289 for information on how to configure Web services security.
Otherwise, select Finish.
Note: If you receive an exception with the message “Parsing of the specified WSDL failed”,
followed by an explanation, ensure that the WSDL is accessible through a browser. This
exception could either be the result of a firewall message in HTML-form requiring
authentication, or could be due to an invalid WSDL. Please consult with your administrator.
Lotus Expeditor for Devices does not support dynamic stubs and Web Services security. As a result, any
Web Services clients using dynamic stubs or Web Services security will not work on Lotus Expeditor for
Devices.
Static Mobile Web Services clients: If you generated Web Services client code to use a static stub (i.e., you
did not check the Create Dynamic Stub box), you will see that a *Soap_Stub class was generated. To
invoke the Web services provider, you simply need to instantiate a *Soap_Stub object and then call the
Web services provider methods you wish to exercise. For example:
A method consumeService() will be generated inside the Bundle Activator.
private void consumeService(BundleContext context)throws Exception {
MyWebServiceSoap_Stub stub=new MyWebServiceSoap_Stub();
//TODO: invoke any of the stub methods to consume the service
Invoke your stub methods as follows:
System.out.println("Name=" + stub.getName());
}
Please note that this release of the Lotus Expeditor Toolkit does not support custom marshalling with
static Web Services clients.
Note: The generated stub will contain the URL of the WSDL specified in the tools. However, this is only
ideal for consuming Web services at fixed locations. If you intend to host a Web services client in
the same Lotus Expeditor runtime of the Web services provider, and the Web container is selecting
a port dynamically, you must register the Web services client BundleActivator as an
HttpSettingListener (package com.ibm.pvc.webcontainer.listeners.HttpSettingListener) in
order to obtain the port that the Web container is listening on at startup. Then set the endpoint
address accordingly. Following is a listing of the changes that need to be made on the project
MANIFEST.MF file and the BundleActivator class, including a sample implementation of the
HttpSettingListener interface:
MANIFEST.MF:
Require-Bundle: ..., com.ibm.pvc.sharedbundle
import com.ibm.pvc.webcontainer.listeners.HttpSettingListener;
public class MyBundleActivator
extends implements BundleActivator, HttpSettingListener
{
String endpoint = null;
278 Lotus Expeditor: Developing Applications for Lotus Expeditor
public void start(BundleContext context) throws Exception
{
/* *********************************************************** */
/* when running a web service client on the same host as a web */
/* service provider, we must change the endpoint so that it */
/* matches the port!! */
/* *********************************************************** */
context.registerService(HttpSettingListener.class.getName(),
this, null);
/* *********************************************************** */
/* create the endpoint URL; please note that you will need to */
/* make some changes to prevent a race condition with the set- */
/* ting of httpPort as the registerService call from above is */
/* asynchronous so it may not be properly set by the time we */
/* execute the line below. */
/* *********************************************************** */
endpoint = "http://localhost:" + httpPort + "/ws/pid/echoSvc";
/* *********************************************************** */
/* if using a static client, use the following code */
/* *********************************************************** */
// change the endpoint to use the dynamic port which is set below
EchoServiceSoap_Stub stub = new EchoServiceSoap_Stub();
stub._setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY,
endpoint);
/* *********************************************************** */
/* if using a dynamic client, use the following code */
/* *********************************************************** */
String service = "com.ibm.pvcws.osgi.proxy.WSProxyService";
ServiceReference ref = bundleContext.getServiceReference(service);
if (ref == null)
{
System.err.println("Error: WSProxyService does not “ +
“exist.");
return;
}
WSProxyService wsManImpl =
(WSProxyService)context.getService(ref);
try
{
/* register the wsdl at the new endpoint */
wsManImpl.register(endpoint + "?wsdl");
}
catch (Exception e)
{
e.printStackTrace();
return;
}
}
/**
* Implement the Web Container Listener interfaces */
*/
private static boolean validSetting = false;
private static int httpPort = 0, httpsPort = 0;
private static final String PROPERTY_HTTP_PORT = "http.port";
private static final String PROPERTY_HTTPS_PORT = "https.port";
private static Map settingsMap = new HashMap();
public void settingsAdded(String pid, Dictionary properties)
{
Developing applications 279
Map settings = new HashMap();
Enumeration enum = properties.keys();
while (enum.hasMoreElements())
{
String key = (String)enum.nextElement();
settings.put( key, properties.get( key ) );
}
settingsMap.put( pid, settings );
if (!validSetting)
{
scanSettings();
}
}
public void settingsModified(String pid, Dictionary properties)
{
Map settings = (Map)settingsMap.get( pid );
if (settings == null)
{
settings = new HashMap();
}
Enumeration enum = properties.keys();
while (enum.hasMoreElements())
{
String key = (String)enum.nextElement();
settings.put( key, properties.get( key ) );
}
settingsMap.put( pid, settings );
scanSettings();
}
public void settingsRemoved(String pid)
{
settingsMap.remove( pid );
scanSettings();
}
private void scanSettings()
{
boolean found = false;
Iterator iter = settingsMap.keySet().iterator();
while (!found && iter.hasNext())
{
Map settings = (Map)settingsMap.get( (String)iter.next() );
Integer httpPort_ = (Integer)settings.get(
PROPERTY_HTTP_PORT );
if (httpPort_ != null)
{
httpPort = httpPort_.intValue();
validSetting = true;
found = true;
}
Integer httpsPort_ = (Integer)settings.get(
PROPERTY_HTTPS_PORT );
if (httpsPort_ != null)
280 Lotus Expeditor: Developing Applications for Lotus Expeditor
{
httpsPort = httpsPort_.intValue();
validSetting = true;
found = true;
}
}
if (!found)
{
validSetting = false;
httpPort = 0;
}
}
}
Lotus Expeditor for Devices does not support the use of Web Services providers. As a result, you cannot
host a Web services client in the same Lotus Expeditor for Devices runtime of a Web services provider.
Dynamic Mobile Web Services clients: If you generated Web Services client code to use a dynamic stub (i.e.,
you checked Create Dynamic Stub) you will need to register the WSDL of the Web Services provider
using the Gateway plug-in. In order to do this, use the WSProxyServiceFactory class to get a new instance
of WSProxyService and invoke one of the following methods at runtime:
boolean register(String url);
boolean register(String url, Dictionary properties);
Both methods take the URL of a WSDL resource that describes the Web Services provider. The WSDL will
be retrieved and parsed to determine the end point of the Web Services provider, the names of the
interface to register, and the data structures and methods used by the interface. An automatically
generated ″virtual″ OSGi service will then be registered by the Web Services Gateway-using the class
name of the interface derived from the WSDL-defined operation.
After the OSGi service is registered, it can be retrieved from the OSGi service registry and used just as
any other local OSGi service. The OSGi service will stay registered until the virtual bundle for that
service is stopped or uninstalled. If the unregister() method of WSProxyService is used, the virtual
bundle registering the service will be stopped.
Note: When running a dynamic Web services client and a Web services provider in the same runtime,
there will be two bundles providing the Web service interface (the client and the provider). As a
result, the call to retrieve the OSGi service will return one reference to each. There are two
solutions to this problem. First, you may set a property on the Web services provider
exposeService method in order to differentiate between the two. Second, You may directly access
the dynamic proxy by invoking the runtime APIs.
Following is as an example on how to set this property on the Web services provider side:
private void exposeService(BundleContext context)
{
Hashtable props = new Hashtable();
props.put(org.osgi.framework.Constants.SERVICE_PID,
“MyWebServiceImpl");
props.put("com.ibm.pvcws.wsdl", "");
props.put(“NAME”, “PROVIDER”);
context.registerService(MyWebService.class.getName(),
new MyWebServiceImpl(), props);
WebServiceProvider provider = getProvider(context);
provider.exportPid("MyWebServiceImpl");
}
The Bundle Activator code must implement the ServiceTrackerCustomizer in order to handle the life
cycle management for OSGi. Hence the methods addingService(),modifiedService() and
removedService() must be added to the Bundle Activator code.
Developing applications 281
public Object addingService(ServiceReference arg0) {
WSProxyService proxy = (WSProxyService) context.getService(arg0);
//do this if custom marshalling is needed for the clients
registerCustomSerializers();
try{
consumeService();
} catch (Exception e) {}
return proxy;
}
public void modifiedService(ServiceReference arg0, Object arg1) {
//DO NOTHING IN THIS METHOD
}
public void removedService(ServiceReference arg0, Object arg1) {
//DO NOTHING IN THIS METHOD
}
start() must create a service tracker for the WSProxyService and open it.
private String proxyService = WSProxyService.class.getName();
public void start(BundleContext context) throws Exception {
super.start(context);
this.context = context;
proxyTracker = new ServiceTracker(context,proxyService, this);
proxyTracker.open();
}
The registering of the WSDL and dynamic web service invocation code goes into consumeService() or at
the end of addingService() method.
public void consumeService(BundleContext context) throws Exception
{
WSProxyService wsManImpl = WSProxyServiceFactory.newInstance(context, null);
try
{
// NOTE: since by default the web container listens on a random port,
// refer to the section Static Web Services clients for information on
// how to obtain the chosen port programmatically and use that
// information to form the WSDL URL.
wsManImpl.register("http://localhost:8777/ws/pid/MyWebServicesImpl?wsdl);
ServiceTracker tracker = new ServiceTracker(context,
MyWebService.class.getName(), null);
tracker.open();
ServiceReference refs[] = tracker.getServiceReferences();
for (int i=0; i<refs.length; i++)
{
String name = refs[i].getProperty("NAME");
MyWebService stub = (MyWebService)context.getService(refs[i]);
if ((name != null) && (name.equals("PROVIDER")))
{
/* this is a direct call into the service and is more
efficient as it would not execute the web services
stubs
*/
282 Lotus Expeditor: Developing Applications for Lotus Expeditor
System.out.println("NAME= + stub.getName());
}
else
{
/* this is an indirect call into the service which is
less efficient as it would execute the web services
stubs
*/
System.out.println("NAME= + stub.getName());
}
}
tracker.close();
}
catch (Exception e)
{
e.printStackTrace();
}
The ServiceTracker must be disposed prior to exiting the bundle activator.
public void stop(BundleContext context) throws Exception {
plugin = null;
super.stop(context);
if (tracker != null)
tracker.close();
if (proxyTracker != null)
proxyTracker.close();
}
If the Web Services client needs to handle non-bean classes or types that are incompatible with JSR-172
style Web Services, custom marshallers need to be implemented. Please refer to the section “Custom
serialization (marshalling)” for more information.
Note: Following is an example on how to invoke the runtime API to access the dynamic proxy. This
approach is less complex than the first one. All you are required to do is the following inside the
consumeService method. No Service tracking logic is required.
if (!wsManImpl.register(MyWebService.class,wsdlURL, props)) {
throw new Exception("Unable to consume service " + wsdlURL);
}
MyWebService stub = (MyWebService)wsManImpl.getProxy();
Developing Mobile Web Services logic
This section provides information on Mobile Web Services logic development.
Custom serialization (marshalling): Custom marshalling is required for handling non-bean classes as
well as types that are incompatible with JSR-172. Custom marshalling can be implemented for both Web
Services providers and Web Services clients. The Lotus Expeditor Toolkit v 6.1 does automatically
generate custom marshaller code for Web Services providers and for Web Services clients.
When custom marshalling is required, the developer is responsible for completing the code within the
generated MarshalFactory and Marshaller classes on the Web services client and Web services provider.
However, the Lotus Expeditor Toolkit Web Services will provide complete custom marshalling code
implementation for three non JSR-172 types - Calendar, HashMap and Hashtable.
This section describes the Application Program Interfaces (API) used in the serialization of data structures
that cannot be automatically serialized by the Web Services engine for OSGi.
Developing applications 283
The current Web Services engine uses Java reflection and conventions similar to the functionality
described in JSR 101 to automatically generate a WSDL to describe services and to serialize data
structures for wire transmission. However, in order for the conventions to work, the data structures must
have a bean-like structure. Unfortunately, not all data structures have this structure and require specific
logic to be serialized and described.
The serialization API has the following features:
v The programmer does not deal with XML processing
v Only the structures that need special processing need custom marshallers
Using the Lotus Expeditor Toolkit, it is easy to write classes that can automatically be serialized.
However, when working with legacy classes and classes that contain logic, complications arise. Very few
of the standard Java classes lend themselves to automatic serialization. For example, consider the
java.util.Properties class and the java.util.Calendar class. Neither of these two classes follows the
Lotus Expeditor Web Services conventions. Furthermore, there is a standard mapping of the Calendar
class into the XML Schema namespace, so even the namespace mapping conventions do not apply. The
serialization API allows data structures to be introspected.
It has three parts:
v Class name to QName mapping
v Enumeration of the members that make up a class
v Extractions of members from an instance of a class
As it is an introspection API, the programmer does not need to be involved in the generation of XML for
the WSDL or the SOAP messages. In fact, the actual encoding can be changed since none of the encoding
constructs manifest themselves in the API.
Another design feature of the serialization API is that it is only needed for those classes that cannot be
processed using the standard conventions. For example, when the marshaller of the Properties class is
written, it is represented as an array of PropertyEntry objects where PropertyEntry is a class that has two
public members, a key and a value, both of type String. Because PropertyEntry follows the normal
convention for writing serializable data structures it is not necessary to write a custom marshaller for it.
Custom marshallers implement the ClassDescriberMarshalFactory interface and are registered in the
OSGi service registry. The Web services engine dynamically looks up the marshallers and invokes them
when needed. This allows for easy deployment of new marshallers and non-disruptive removal of
unnecessary marshallers.
Lotus Expeditor for Devices does not support custom marshalling.
MarshalFactory: When the Web Services engine needs to map a class to a QName or get a Marshal for a
QName, it relies on the MarshalFactory class for help. The MarshalFactory class consists of two methods:
public interface MarshalFactory {
Class getClassForQName(ClassLoader cl, QName qtype);
Marshal getMarshaller(QName qtype);
}
Marshal: The Marshal interface has only one method and should not be implemented directly. Either a
SimpleMarshal or ComplexMarshal should be implemented instead. Each time a new object needs to be
serialized, the Web Services engine will call MarshalFactory.getMarshaller(). When servicing the call,
the MarshalFactory can create a Marshal object to handle the specific object, or it can reuse an already
created object. The Web Services engine interacts with the two Marshal types in different ways. The
engine selects the appropriate behavior by checking the class of the Marshal returned from
getMarshaller().
284 Lotus Expeditor: Developing Applications for Lotus Expeditor
ClassDescriberMarshalFactory: The ClassDescriberMarshalFactory is an interface that extends both
MarshalFactory and CustomClassDescriber. It does not add any new methods. Bundles must register
services using the ClassDescriberMarshalFactory in the service registry to provide custom serializers.
Serializers that only register services under the MarshalFactory and CustomClassDescriber interfaces will
be ignored by the web service engine. Examination of the information provided by the
CustomClassDescriber and the MarshalFactory shows that there is some overlap between the information
provided by the two interfaces. It is the responsibility of the implementer to ensure that the information
is consistent.
Examples: This section contains two examples of custom marshallers. The first example shows a
SimpleMarshal for Calendar, and highlights the use of QName mapping to map Calendar to
xsd:dateTime, a mapping defined in JAX-RPC. The second example shows a ComplexMarshal for
Properties, and also illustrates the use of an intermediate class and the use of QName mapping to return
a non imported class.
Calendar example: The serialization of Calendar is illustrated in two parts: the implementation of
CalendarMarshalFactory, and the implementation of CalendarMarshal.
CalendarMarshalFactory
You must register a service of type ClassDescriberMarshalFactory, so that it is the interface that the
CalendarMarshalFactory implements. First, you must setup static member variables to aid in your
processing:
static QName calendarType = new QName(NamespaceConstants.NSURI_SCHEMA_XSD,
"dateTime");
static ClassDescriptor cdCalendar = new ClassDescriptor(calendarType, 1, 1, true);
static ClassDescriptor cdCalendarArray = new ClassDescriptor(calendarType, 1,
Integer.MAX_VALUE, true);
static PartsDescriptor parts = new PartsDescriptor
(new QName[] { new QName("entries") ],
new Class[] { PropertyEntry[].class });
Now you can write the methods that correspond to ClassDescriber:
public boolean canDescribe(Class c) {
if (c.isArray()) c =
c.getComponentType();
return c.equals(Calendar.class);
}
In the canDescribe() method you can describe both Calendar and Calendar[]. Ensure you check for both.
public ClassDescriptor getQType(Class c) {
boolean isArray = false;
if (c.isArray()) {
c = c.getComponentType();
isArray = true;
}
if (c.equals(Calendar.class)) {
return isArray ? cdCalendar :
cdCalendarArray;
}
return null;
}
This checks to see if the class in question is Calendar and returns the precomputed ClassDescriptors if
appropriate. Observe that the only difference between cdCalendar and cdCalendarArray is that the
maxOccurs value is 1 in the non-array case, and MAX_VALUE in the array case.
Developing applications 285
public PartsDescriptor getParts(Class c) {
return null;
}
getParts() always returns null since even if c == Calendar.class. The Calendar class is a simple class
and therefore has no parts.
public Class getClassForQName(ClassLoader cl, QName qtype)
{
return calendarType.equals(qtype) ?
Calendar.class : null;
}
This method handles QName mapping. It simply returns the Calendar class if the qtype matches
xsd:dateTime.
static SimpleMarshal calMarshal = new CalendarMarshal();
Marshal getMarshaller(QName qType)
{
return calendarType.equals(qType) ?
calMarshal : null;
}
This is the final method to be implemented. In this method, the system returns a precomputed
SimpleMarshal for Calendar if the qType is xsd:dateTime. The CalendarMarshal has no reusable state, even
if it is used concurrently.
CalendarMarshal
When Calendar is encoded as xsd:dateTime it has no members, so you must use a SimpleMarshal.
public Object deserialize(String value) {
try {
Date date = new SimpleDateFormat().parse(value);
Calendar cal = new GregorianCalendar();
cal.setTime(date);
return cal;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
To deserialize a Calendar object, take the given string and use SimpleDateFormat to create a Calendar.
Note that even though the return value is Object, we must return a Calendar since that is the type this
Marshal handles.
public String serialize(Object o)
{
Calendar cal = (Calendar)o;
String date = new SimpleDateFormat().format(cal.getTime());
return date;
}
In the serialize() method we know that o is of type Calendar since that is the type this Marshal
handles. You must convert the string to use SimpleDateFormat() and return the result.
public Object newArray(int size)
{
return new Calendar[size];
}
The final method returns an array of the correct size. The Calendar serializer is ready to register in the
service registry.
286 Lotus Expeditor: Developing Applications for Lotus Expeditor
Properties example: The serialization of Properties is illustrated in two parts: the implementation of
PropertiesMarshalFactory, and then of PropertyMarshal.
This example covers the serializer for Properties. As explained, the Properties class is serialized using an
intermediate class called PropertyEntry:
public class PropertyEntry {
public String key;
public String value;
}
Because PropertyEntry is a class that is easily serialized, you don’t need to write a serializer for it.
However, since it is an intermediate class, you must return the PropertyEntry.class in the QName
mapping. In addition, since the conventional class name is used for QName mapping, you don’t need a
mapping for Properties. It is easy to get confused because the PropertiesMarshalFactory maps
PropertyEntry, a class it is only using internally, but not Properties, the class it is actually serializing.
Remember from the QName mapping discussion that mapping is used when the QName doesn’t
correspond to the convention or the class isn’t a class that will normally be imported by the virtual
bundle or the service. Properties does not meet either case, but PropertyEntry meets the second case. The
getClassForQName is defined as follows:
static QName propertyEntryQType = DefaultMarshalFactory.defaultGetClassQName
(PropertyEntry.class.getName());
public Class getClassForQName(ClassLoader cl, QName qtype)
{
return qtype.equals
(propertyEntryQType) ?
PropertyEntry.class:null;
}
Because the PropertyMarshalFactory is almost exactly the same as CalendarMarshalFactory, only the
getParts() method is shown. It differs substantially:
static PartsDescriptor parts = new PartsDescriptor(new QName[]
{ new QName("entries") },
new Class[] { PropertyEntry[].class });
public PartsDescriptor getParts(Class c) {
if (c.isArray()) c = c.getComponentType();
if (c.equals(Properties.class)) {
return parts;
}
return null;
}
The Properties class is encoded as a complex type with one member (entries) that is an array of
PropertyEntrys. As a result, the PartsDescriptor for Properties is made up of two arrays of one entry.
The first array has the name of the members, and the second the class of the members. The
PropertiesMarshal implements ComplexMarshal and has the following members:
public Object newArray(int length)
{
return new Properties[length];
}
As with Calendar, this is a simple method that returns an array of the specified size.
public Object newHandle() {
return new Properties();
}
Since Properties has a default constructor and has methods to add to it, it can return an instance of
Properties.
Developing applications 287
{
PropertyEntry entries[] = (PropertyEntry[])value;
for(int i = 0; i < entries.length; i++) {
((Properties)handle).put(entries[i].key, entries[i].value);
}
}
As there is only one member for Properties, there is no need to check the index. You must only put the
received entries into the Properties object.
public Object newInstance(Object handle) {
return handle;
}
There is nothing to do in this routine since handle is already a Properties object.
public Object getMember(Object obj, int index) {
Properties props = (Properties)obj;
Vector entries = new Vector();
Enumeration en = props.keys();
while(en.hasMoreElements()) {
PropertyEntry entry = new PropertyEntry();
entry.key = (String)en.nextElement();
entry.value = props.getProperty(entry.key);
entries.add(entry);
}
return entries.toArray(new PropertyEntry[0]);
}
As there is only one member, the index does not need to be checked. Since you are generating the entries
member, you must build a PropertyEntry[] and return it.
public int getMemberCount() {
return 1;
}
There is only one member.
QPart entriesPart = new QPart(new QName("entries"),
DefaultMarshalFactory.defaultGetClassQName(PropertyEntry.class.getName()), 0,
Integer.MAX_VALUE, true, true);
public QPart getPart(int index) {
return entriesPart;
}
Return the QPart that describes the entries member. The first argument of the Qpart constructor is the
standard mapping of the QName. The second says that the array can be an empty array. The third says
that it is an unbounded array. The fourth says it can be null. The fifth indicates whether the QPart needs
to be qualified (always set it to true).
Final steps: Now that the serializers are prepared, you must register them in the service registry so that
they can be used. This is done by registering the serializers in the clients’ and providers’ respective
activator classes. This method can be called either at the beginning of the start or the addingService
methods.
public void registerSerializers {
bc.registerService(
ClassDescriberMarshalFactory.class.getName(),
new PropertyMarshalFactory(),
null);
bc.registerService(
288 Lotus Expeditor: Developing Applications for Lotus Expeditor
ClassDescriberMarshalFactory.class.getName(),
new CalendarMarshalFactory(),
null);
}
To register the serializers, construct the two factories and register them as ClassDescriberMarshalFactory
services.
Securing Mobile Web Services
This section provides information on securing Mobile Web Services.
Lotus Expeditor for Devices does not support Web Services security. Lotus Expeditor for Desktop does
not support XML encryption feature on the jclDesktop profile because of limitation of crypto library on
the jclDesktop profile. We are planning to add support for encryption on the jclDesktop profile in future
release.
Creating secure Mobile Web Services providers: In order to secure Web Services providers, you must
first create the Web Service provider by referring to “Creating Mobile Web Services providers” on page
275. During the creation process, ensure you check Configure Security. Perform the following procedure
to configure Web services security to secure Web Services providers:
1. Select the Web services name and port from the drop down menu.
2. Configure Web Services Security:
a. Select the Web Services name and port from the drop down menu.
b. Under ’How to create Web Services Security configuration’, select the appropriate choice. For a test
case, select Template configuration, then select the Server type and the Security template from the
drop down menus.
v Use other Lotus Expeditor 6.x configuration
If you browse a folder including the existing WS-Security configurations for the Lotus Expeditor
under the other existing project, you can import the configurations into the working project.
v Import WebSphere Application Server 6.x configuration
If you browse a folder including the existing WS-Security configurations for the WAS 6.0 client,
you can import the configurations into the working project.
v Use template configuration
If you select one client type and one security template, you can use the predefined
configurations in the working project.
The following is a list of client types:
– Lotus Expeditor 6.x
– WebSphere Application Server 6.x
– WebSphere Application Server 5.x
The following is a list of security templates:
– Signing and encryption
– Signing only
– Encryption only
– Basic authentication only
– Signing and basic authentication
– Encryption and basic authentication
– Signing, encryption, and basic authenticationc. Select Finish.
After completing the above procedure, the WS-Security Provider Editor appears. Some files may be
created or modified, as follows:
Developing applications 289
v WS-Security-related code is inserted in the file BundleActivator.java under the working project
v The files ibm-webservices-ext.xmi, ibm-webservices-bnd.xmi, serverSample.jks, and wssecurity.xml
are generated in the export directory of the package containing the file BundleActivator.java.
CAUTION: The serverSample.jks is designed for testing only. You should use your own key store file
instead of serverSample.jks for commercial use to avoid security exposure. Anyone who can
read this document can access the serverSample.jks.
CAUTION: The jclDesktop profile cannot read the file serverSample.jks that is created using other
profiles such as J2SE due to the limitation of the jclDesktop profile’s ability to read a
keystore and vice versa. We are planning to remove this limitation in a future release.
At this point, you may re-deploy your Web Services provider with security enabled, but if you want to
change the WS-Security configurations in the working project, you can edit them with the WS-Security
Provider Editor. To understand more details of how to edit the WS-Security configurations, please refer to
“Editing the Mobile Web Services security configuration” on page 293.
Securing pre-existing Mobile Web Services providers: To secure a Web Services provider that already
exists, perform the following procedure:
1. Launch the Web Services provider security configuration wizard:
a. Select File > New > Other. Select Lotus Expeditor Mobile Web Services > Lotus Expeditor Mobile
Web Services Provider Security Configuration.
b. Click Next.
c. Enter the source folder containing the Java source code for the Web Services provider application.
d. Enter the URL of the WSDL exposed by the Web Services provider.
e. Click Next.2. Configure Web Services Security:
a. Select the Web Services name and port from the drop down menu.
b. Under How to create Web Services Security configuration, select the appropriate choice. For a
test case, select Template configuration, then select the Server type and the Security template from
the drop down menus (refer to “Creating secure Mobile Web Services providers” on page 289 for
more information):
v Use other Lotus Expeditor 6.x configuration
v Import WebSphere Application Server 6.x configuration
v Use template configurationc. Select Finish.
Note: The serverSample.jks is designed for testing only. You should use your own key store file instead
of serverSample.jks for commercial use to avoid security exposure. Anyone who can read this
document can access the serverSample.jks.
Note: The jclDesktop profile can’t read serverSample.jks that has been created using other profiles such
as J2SE due to the limitation of the jclDesktop profile’s ability to read a keystore and vice versa.
This limitation will be removed in a future release.
Creating secure Mobile Web services clients: In order to secure Web services clients, refer to “Creating
Mobile Web Services clients” on page 277. Ensure that you check Configure Security. Perform the
following procedure to configure Web services security for a secure Web services client:
1. Select the Web services name and port from the drop down menu.
2. Under ’How to create Web Services Security configuration’, select the appropriate choice. For a test
case, select Template configuration, then select the Client type and the Security template from the
drop down menus.
290 Lotus Expeditor: Developing Applications for Lotus Expeditor
v Use other Lotus Expeditor 6.x configuration
If you browse a folder including the existing WS-Security configuration for the Lotus Expeditor
under the other existing project, you can import the configurations into the working project.
v Import WebSphere Application Server 6.x configuration
If you browse a folder including the existing WS-Security configurations for the WAS 6.0 client, you
can import the configurations into the working project.
v Convert WCTME 5.x class-based configuration
If you browse a WS-Security configuration java file for WCTME 5.8 or 5.7, you can convert the
configuration for use with the Lotus Expeditor 6.0.
v Use template configuration
If you select one server type and one security template, you can use the predefined configurations
in the working project.
The following is a list of server types:
– Lotus Expeditor 6.x
– WebSphere Application Server 6.x
– WebSphere Application Server 5.x
The following is a list of security templates:
– Signing and encryption
– Signing only
– Encryption only
– Basic authentication only
– Signing and basic authentication
– Encryption and basic authentication
– Signing, encryption, and basic authentication3. Select Finish.
After completing the above procedure, the WS-Security Client Editor appears. Some files may be created
or modified, as follows:
If the Web services client is static and you specified a package in the Mobile Web Services Client
wizard
v The port files ’*.java’ and ’*_Stub.java’ are generated in the specified package. The name of the port
file is taken from the portType in the WSDL file
v WS-Security related code is inserted in ’*_Stub.java’
v The files ibm-webservicesclient-ext.xmi, ibm-webservicesclient-bnd.xmi, clientSample.jks, and
wssecurityclient.xml are generated in the export directory in the specified package
If the Web services client is static and you did not specify a package in the Mobile Web Services
Client wizard
v The port files ’*.java’ and ’*_Stub.java’ are generated in the package specified from the contents of
the WSDL file
v WS-Security-related code is inserted in the file ’*_Stub.java’
v The files ibm-webservicesclient-ext.xmi, ibm-webservicesclient-bnd.xmi, clientSample.jks, and
wssecurityclient.xml are generated in the export directory in the package specified from the WSDL
file
If the Web services client is dynamic
v A port file is generated in the package specified in the WSDL file
v WS-Security-related code is inserted in the file BundleActivator.java under the working project
Developing applications 291
v The files ibm-webservicesclient-ext.xmi, ibm-webservicesclient-bnd.xmi, clientSample.jks, and
wssecurityclient.xml are generated in the export directory in the package specified from the WSDL
file
CAUTION: The clientSample.jks is designed for testing purposes only. You should use your own key
store file instead of clientSample.jks for commercial use to avoid security exposure. Anyone
who reads this document can access the clientSampke.jks.
CAUTION: The jclDesktop profile cannot read the file clientSample.jks created using other profiles
such as J2SE due to the limitation of the jclDesktop profile’s ability to read a keystore and
vice versa. This limitation will be removed in a future release.
CAUTION: When you select ’Basic authentication’ as a template, the Mobile Web Services security
configuration declares username: @USERNAME@, password: @USERNAME@ by default. To run the
client properly, you must edit the Mobile Web Services security configuration in order to
modify these default values. Please refer to “Editing the Mobile Web Services security
configuration” on page 293.
Securing pre-existing Mobile Web Services clients: To secure a Web Services client that already exists,
perform the following procedure:
1. Launch the Web Services client security configuration wizard:
a. Select File > New > Other.
b. Select Lotus Expeditor Mobile Web Services > Lotus Expeditor Mobile Web Services Client
Security.
c. Click Next.
d. Enter the source folder containing the Java source code for the Web Services provider application.
e. Enter the URL of the WSDL exposed by the Web Services provider.
f. Click Next.2. Configure Web Services Security:
a. Select the Web Services name and port from the drop down menu.
b. Under How to create Web Services Security configuration, select the appropriate choice. For a
test case, select Template configuration, then select the Client type and the Security template from
the drop down menus (Refer to “Creating secure Mobile Web services clients” on page 290 for
more information):
v Use other Lotus Expeditor 6.x configuration
v Import WebSphere Application Server 6.x configuration
v Convert WCTME 5.x class-based configuration
v Use template configurationc. Click Finish.
CAUTION: The clientSample.jks is designed for testing purposes only. You should use your own key
store file instead of clientSample.jks for commercial use to avoid security exposure. Anyone
who reads this document can access the clientSampke.jks.
CAUTION: The jclDesktop profile cannot read the file clientSample.jks created using other profiles such
as J2SE due to the limitation of the jclDesktop profile’s ability to read a keystore and vice
versa. This limitation will be removed in a future release.
CAUTION: When you select ‘Basic authentication’ as a template, the Mobile Web Services security
configuration declares username: @USERNAME@, password: @PASSWORD@ by default. To run the
292 Lotus Expeditor: Developing Applications for Lotus Expeditor
client properly, you must edit the Mobile Web Services security configuration in order to
modify these default values. Please refer to “Editing the Mobile Web Services security
configuration.”
Editing the Mobile Web Services security configuration: In order to understand how to edit the Mobile
Web Services security configuration in the working project, this section introduces four typical scenarios.
To avoid confusion, they are called Scenario #1 through Scenario #4.
Note: You cannot edit the Mobile Web Services security configuration under WTP. It is supported on
RSDP and RSA only.
v Scenario #1: Basic Authentication
The request message contains a user name token containing a plaintext password in the Security
header. The receiver checks the user name and password. If no errors are detected, it returns the
response message without any security mechanisms.
v Scenario #2: Basic Authentication with Encryption
The request message contains a user name token containing a plaintext password in the Security
header. The user name token is encrypted using a public key provided out-of-band. The receiver
decrypts the token and checks the user name and password. If no errors are detected, it returns the
response message without any security mechanisms.
v Scenario #3: Sign and Encrypt
The request message contains a body, which is signed and then encrypted. The certificate for signing is
included in the message. The certificate for encryption is provided out-of-band. The receiver decrypts
the body and then verifies the signature. If no errors are detected, it returns the response message
signing and encrypting the message body.
v Scenario #4: Signed Token
The request message contains a body, which is signed. The signature also covers the token used for
signing by means of the STR Dereference Transform. The certificate for signing is included in the
message. The receiver verifies the signature. If no errors are detected, it returns the response message
signing the message body. The signature also covers the token used for signing by means of the STR
Dereference Transform.
Editing a Mobile Web Services security configuration for basic authentication (scenario #1): Before editing the
Mobile Web Services security configuration, this scenario assumes that you selected Use template
configuration under ’How to create Web Services Security configuration’ and Basic authentication only
as a security template. To edit a Mobile Web Services security configuration in this scenario for the Web
Services client, perform the following procedure:
1. Open the WS-Security Client Editor.
2. Expand the Security Request Generator Binding Configuration section of the WS binding tab.
3. Expand the Token Generator section, select the gen_unt item in a list, and select Edit. From the next
menu:
a. Remove the existing User ID (@USERNAME@) and the existing Password (@PASSWORD@).
b. Edit an appropriate User ID and Password.
c. Select OK.4. Save your changes.
You do not need to edit a Mobile Web Services security configuration in this scenario for the Web
Services provider.
Editing a Mobile Web Services security configuration for basic authentication with encryption (scenario #2):
Before editing the Mobile Web Services security configuration, this scenario assumes that you selected
Use template configuration under ’How to create Web Services Security configuration’ and Encryption
Developing applications 293
and basic authentication as a security template. To edit the Mobile Web Services security configuration in
this scenario for the Web services client, perform the following procedure:
1. Open the WS-Security Client Editor.
2. Expand the Security Request Generator Binding Configuration of the WS binding tab.
3. Expand the Token Generator section, select the gen_unt item in a list, and select Edit. From the next
menu:
a. Remove the existing User ID (@USERNAME@) and the existing Password (@PASSWORD@).
b. Edit an appropriate User ID and Password.
c. Select OK.4. Expand the Key locators section, select the gen_klocator item in a list, and select Edit. From the next
menu:
a. Edit an appropriate Key store storepass and Key store path.
b. Select JKS or JCEKS as a Key store type.
c. Edit an appropriate Alias and Key name for the encryption key.
d. Select OK.5. Expand the Key information section, select the gen_enckeyinfo item in a list, and select Edit. From
the next menu:
a. Select the key name you specified as the Key name in the key locator.
b. Select OK.6. Save your changes.
To edit the Mobile Web Services security configuration in this scenario for the Web services provider,
perform the following procedure:
1. Open the WS-Security Provider Editor.
2. Expand the Request Consumer Binding Configuration Details section of the WS binding tab
3. Expand the Key Locators section, select the con_encklocator item in a list, and select Edit. From the
next menu:
a. Edit the appropriate Key store storepass and Key store path.
b. Select JKS or JCEKS as a Key store type.
c. Edit the appropriate Alias, Key pass, and Key name to specify the decryption key.
d. Select OK.4. Save your changes.
Editing a Mobile Web Services security configuration for sign and encrypt (scenario #3): Before editing the
Mobile Web Services security configuration, this scenario assumes that you selected Use template
configuration under ’How to create Web Services Security configuration’ and Signing and Encryption as
a security template. You don’t need to edit a Mobile Web Services security configuration in this scenario
for the Web Services client if you don’t use your own key store. If you want to use your own key store,
perform the following procedure:
1. Open the WS-Security Client Editor.
2. Expand the Security Request Generator Binding Configuration section from the WS binding tab.
3. Expand the Token Generator section, select the gen_dsigtgen item in a list, and select Edit. From the
next menu:
a. Edit an appropriate Key store storepass and Key store path.
b. Select JKS or JCEKS as a Key store type.
c. Edit the appropriate Alias and Key name to specify the certificate to be inserted into the message.
d. Select OK.
294 Lotus Expeditor: Developing Applications for Lotus Expeditor
4. Expand the Key locators section, select the gen_klocator item in a list, and select Edit. From the next
menu:
a. Edit an appropriate Key store storepass and Key store path.
b. Select JKS or JCEKS as a Key store type.
c. Edit an appropriate Alias, Key pass , and Key name to specify the key used for digital signature.
d. Edit an appropriate Alias, Key pass , and Key name to specify the key used for encryption.
e. Select OK. 5. Expand the Key information section, select the gen_dsigkeyinfo item in a list, and select Edit. From
the next menu:
a. Select the key name you specified as the Key name in the key locator.
b. Select OK. 6. Expand the Key information section, select the gen_enckeyinfo item in a list, and select Edit. From
the next menu::
a. Select the key name you specified as the Key name in the key locator.
b. Select OK. 7. Expand the Security Response Consumer Binding Configuration section from the WS binding tab.
8. Expand the Trust Anchor section, select the dsigtrustanchor item in a list, and select Edit. From the
next menu:
a. Edit appropriate Key store storepass and Key store path.
b. Select JKS or JCEKS as a Key store type.
c. Select OK. 9. Expand the Key locators section, select the con_encklocator item in a list, and select Edit. From the
next menu:
a. Edit an appropriate Alias, Key pass , and Key name to specify the key used for decryption.
b. Select OK.10. Save your changes.
You don’t need to edit a Mobile Web Services security configuration in this scenario for the Web Services
provider if you don’t use your own key store. If you want to use your own key store, perform the
following procedure:
1. Open the WS-Security Provider Editor.
2. Expand the Request Consumer Binding Configuration Details section from the WS binding tab.
3. Expand the Trust Anchor section, select the dsigtrustanchor item in a list, and select Edit. From the
next menu:
a. Edit an appropriate Key store storepass and Key store path.
b. Select JKS or JCEKS as a Key store type.
c. Select OK. 4. Expand the Key locators section, select the con_encklocator item in a list, and select Edit. From the
next menu:
a. Edit an appropriate Key store storepass and Key store path.
b. Select JKS or JCEKS as a Key store type.
c. Edit an appropriate Alias, Key pass, and Key name to specify the key used for decryption.
d. Select OK. 5. Expand the Response Generator Binding Configuration Details section from the WS binding tab.
6. Expand the Token Generator section, select the gen_dsigtgen item in a list, and select Edit. From the
next menu:
a. Edit an appropriate Key store storepass and Key store path.
Developing applications 295
b. Select JKS or JCEKS as a Key store type.
c. Edit an appropriate Alias and Key name to specify the certificate to be inserted into the message.
d. Select OK. 7. Expand the Key locators section, select the gen_klocator item in a list, and select Edit. From the next
menu:
a. Edit an appropriate Key store storepass and Key store path.
b. Select JKS or JCEKS as a Key store type.
c. Edit an appropriate Alias, Key pass, and Key name to specify the key used for digital signature.
d. Edit an appropriate Alias, Key pass, and Key name to specify the key used for encryption.
e. Select OK. 8. Expand the Key information section, select the gen_dsigkeyinfo item in a list, and select Edit. From
the next menu:
a. Edit the key name you specified for the digital signature as the Key name in the key locator.
b. Select OK. 9. Expand the Key information section, select the gen_enckeyinfo item in a list, and select Edit:
a. Edit the key name you specified for the encryption as the Key name in the key locator.
b. Select OK.10. Save your changes.
Editing a Mobile Web Services security configuration for signed token (scenario #4): Before editing the Mobile
Web Services security configuration, this scenario assumes that you selected Use template configuration
under ’How to create Web Services Security configuration’ and Signing only as a security template. To
edit a Mobile Web Services security configuration in this scenario for the Web services client, perform the
following procedure:
1. Open the WS-Security Client Editor.
2. Expand the Request Generator Configuration section on the WS extension tab.
3. Expand the Integrity section, select the int_body item in a list, and select Add. From the next menu:
a. Select Add.
b. Select dsigkey as a Message parts keyword.
c. Select OK. 4. Expand the Response Consumer Service Configuration Details section on the WS extension tab.
5. Expand the Required Integrity section, select the reqint_body item in a list, and select Edit. From
the next menu:
a. Select Add.
b. Select dsigkey as a Message parts keyword.
c. Select OK. 6. Expand the Security Request Generator Binding Configuration section on the WS binding tab.
7. Expand the Token Generator section, select the gen_dsigtgen item in a list, and select Edit. From the
next menu:
a. Edit an appropriate Key store storepass and Key store path.
b. Select JKS or JCEKS as a Key store type.
c. Edit an appropriate Alias and Key name to specify the certificate to be inserted into the message.
d. Select OK. 8. Expand the Key locators section, select the gen_klocator item in a list, and select Edit. From the next
menu:
a. Edit an appropriate Key store storepass and Key store path.
b. Select JKS or JCEKS as a Key store type.
296 Lotus Expeditor: Developing Applications for Lotus Expeditor
c. Edit appropriate Alias, Key pass , and Key name to specify the key used for digital signature.
d. Select OK. 9. Expand the Signing Information section.
10. Expand the Part References section under the Signing Information section and select Add. From the
next menu:
a. Input an appropriate Part reference name.
b. Select the integrity name you specified as the Integrity part.
c. Select http://www.w3.org/2000/09/xmldsig#sha1 as a Digest method algorithm.
d. Select OK.11. Expand the Transforms section under the Signing Information section and select Add. From the next
menu:
a. Input an appropriate Name.
b. Select http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#STR-Transform as an Algorithm.
c. Select OK.12. Select Add again. From the next menu:
a. Input an appropriate Name.
b. Select http://www.w3.org/2001/10/xml-exc-c14n# as an Algorithm.
c. Select OK.13. Expand the Security Response Consumer Binding Configuration section on the WS binding tab.
14. Expand the Trust Anchor section, select the dsigtrustanchor item in a list, and select Edit. From the
next menu:
a. Edit appropriate Key store storepass and Key store path.
b. Select JKS or JCEKS as a Key store type.
c. Select OK.15. Expand the Signing Information section.
16. Expand the Part References section from the Signing Information section and select Add. From the
next menu:
a. Input an appropriate Part reference name.
b. Select the required integrity name you specified as the Required Integrity part.
c. Select http://www.w3.org/2000/09/xmldsig#sha1 as a Digest method algorithm.
d. Select OK.17. Select Add again. From the next menu:
a. Input an appropriate Name.
b. Select http://www.w3.org/2001/10/xml-exc-c14n# as a Algorithm.
c. Select OK.18. Expand the Transforms section under the Signing Information section and select Add. From the next
menu:
a. Input an appropriate Name.
b. Select http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#STR-Transform as a Algorithm.
c. Select OK.19. Save your changes.
To edit a Mobile Web Services security configuration in this scenario for the Web services provider,
perform the following procedure:
1. Open the WS-Security Provider Editor.
Developing applications 297
2. Expand the Request Consumer Service Configuration Details section on the WS extension tab.
3. Expand the Required Integrity section, select the reqint_body item in a list, and select Edit. From
the next menu:
a. Select Add.
b. Select dsigkey as a Message parts keyword.
c. Select OK. 4. Expand the Response Generator Service Configuration Details section on the WS extension tab.
5. Expand the Integrity section, select the int_body item in a list, and select Add. From the next menu:
a. Select Add.
b. Select dsigkey as a Message parts keyword.
c. Select OK
6. Expand the Request Consumer Binding Configuration Details on the WS binding tab.
7. Expand the Trust Anchor section and select Edit. From the next menu:
a. Edit an appropriate Key store storepass and Key store path.
b. Select JKS or JCEKS as a Key store type.
c. Select OK. 8. Expand the Signing Information section.
9. Expand the Part References section from the Signing Information section and select Add. From the
next menu:
a. Input an appropriate Part reference name.
b. Select the required integrity name you specified as the Required Integrity part.
c. Select http://www.w3.org/2000/09/xmldsig#sha1 as a Digest method algorithm.
d. Select OK.10. Expand the Transforms section from the Signing Information section and select Add. From the next
menu:
a. Input an appropriate Name.
b. Select http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#STR-Transform as an Algorithm.
c. Select OK.11. Select Add again. From the next menu:
a. Input an appropriate Name.
b. Select http://www.w3.org/2001/10/xml-exc-c14n# as an Algorithm.
c. Select OK.12. Expand the Response Generator Binding Configuration Details section on the WS binding tab.
13. Expand the Token Generator section, select the gen_dsigtgen item in a list, and select Edit. From the
next menu:
a. Edit an appropriate Key store storepass and Key store path.
b. Select JKS or JCEKS as a Key store type.
c. Edit an appropriate Alias and Key name to specify the certificate to be inserted into the message.
d. Select OK.14. Expand the Key locators section, select the gen_klocator item in a list, and select Edit. From the next
menu:
a. Edit an appropriate Key store storepass and Key store path.
b. Select JKS or JCEKS as a Key store type.
c. Edit an appropriate Alias, Key pass , and Key name to specify the key used for digital signature.
d. Select OK.
298 Lotus Expeditor: Developing Applications for Lotus Expeditor
15. Expand the Key information section, select the gen_dsigkeyinfo item in a list, and select Edit. From
the next menu:
a. Edit the key name you specified for the digital signature as the Key name.
b. Select OK.16. Expand the Signing Information section.
17. Expand the Part References section from the Signing Information section and select Add. From the
next menu:
a. Input an appropriate Part reference name.
b. Select the integrity name you specified as the Integrity part.
c. Select http://www.w3.org/2000/09/xmldsig#sha1 as a Digest method algorithm.
d. Select OK.18. Expand the Transforms section from the Signing Information section and select Add. From the next
menu:
a. Input an appropriate Name.
b. Select http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#STR-Transform as a Algorithm.
c. Select OK.19. Select Add again. From the next menu:
a. Input an appropriate Name.
b. Select http://www.w3.org/2001/10/xml-exc-c14n# as a Algorithm.
c. Select OK.20. Save your changes.
Deploying Mobile Web Services
Web services applications are deployed as OSGi bundles / Eclipse plug-ins. They run in the Lotus
Expeditor runtime platform and require no extra special handling.
Note: If you intend to deploy a Web service client and a Web service provider in the same runtime,,two
different mechanisms can be adopted to avoid any runtime conflicts within OSGi.
v Export the service interface package from the Web service provider MANIFEST.MF and import the
same package in the Web service client MANIFEST.MF
v Another option is to place the Web service client stub in a package that is different from the
package of the Web service provider. Please refer to “Creating Mobile Web Services clients” on
page 277 for information on how to provide a different package name.
Note: If your Web service provider or client needs to handle XML control characters, the data must not
be encoded as a string or a runtime exception will occur. To allow your Web service provider or
client to handle XML control characters, you must use a different encoding such as byte[].
Deploying Mobile Web Services providers: In order to deploy a Web Services provider, launch an
instance of the Lotus Expeditor runtime with the Web Services provider plug-in installed.
Following are the steps to deploy a Web services provider from the IDE:
1. From the IDE, run the Web services provider plug-in:
a. Select Run > Run.
b. Create a new Lotus Expeditor instance if necessary.
c. In the Plug-ins tab, ensure that your Web services provider plug-in is checked.
d. Select Run.
e. At the osgi> prompt in the Console view, type ss to display a list of all registered bundles and
their associated IDs. From the list, find the bundle ID for your Web services provider plug-in.
Developing applications 299
f. At the osgi> prompt in the Console view, type start <bundle ID>, where <bundle ID> is the
bundle ID for your Web services provider plug-in. This results in a call to the exposeService()
method of the Web services provider, and its WSDL will be immediately available for client use.2. Verify that the Web services provider has started successfully:
From a browser, enter the following URL:
http://<machine>:<port>/ws/pid/<servicepid>?wsdl
where <machine> is the name of the machine hosting the Web services provider, <port> is the port
used by the Web container (see Note below), and <servicepid> is the name used to register the Web
services provider (by default it is the base name of the class that implements the exposed Java
interface).
For example, if the machine hosting the Web service is the local host, the Web container is listening on
port 1477, and the Java interface used to expose the Web service provider is MyWebServiceImp1, the
WSDL URL will be:
http://localhost:1477/ws/pid/MyWebServiceImpl?wsdl
Note: Since by default the Web container port is dynamically selected by the Lotus Expeditor
runtime, refer to the Web Container Configuration section in the documentation Assembling
and Deploying Lotus Expeditor Applications in order to find the chosen port or to bind the Web
container to a static port instead. If the service is deployed in the same runtime environment,
and a dynamic port is preferred, you can refer to the section Static Mobile Web Services clients
to learn how to write a Web services client that can programmatically retrieve the chosen port.
There are two properties you may change in the OSGi service:
v com.ibm.pvcws.wsdl: The value for this property is either an empty String, or a String containing the
actual WSDL (not a URL to the WSDL, as above).
When set as an empty String, it indicates that the service should be exposed as a Web Service, and the
WSDL should be generated dynamically. You can also pass a String containing the WSDL that describes
the Web Service if the WSDL must be of a form that cannot be auto generated.
If you do not use the empty String, the WSDL must have a location attribute. The value of the location
attribute is unimportant since the Gateway will correct the location when it is served to clients.
v org.osgi.framework.Constants.SERVICE_PID: This is an optional String that can be used to change the
service PID URL part of the Web service WSDL. If changed, make sure to pass the same string to the
exportPid invocation within the exposeService method.
Deploying Mobile Web Services clients: In order to deploy a Web Services client, you must ensure that
the client implementation has been completed prior to launching an instance of the Lotus Expeditor
runtime with the Web Services client plug-in installed.
If you generated Web Services client code that is static you must instantiate the generated Soap_Stub
object and then call the methods you wish to exercise. Please refer to the section “Static Mobile Web
Services clients” on page 278 for information on how to complete a static Web Services client.
If you generated Web Services client code that is dynamic or that requires custom marshalling, you will
need to access the Web Service using the Gateway plug-in. Please refer to the sections “Dynamic Mobile
Web Services clients” on page 281 and “Custom serialization (marshalling)” on page 283 for information
on how to complete a dynamic Web Services client.
Once the client code is complete, launch an instance of the Lotus Expeditor runtime with the Web
Services client plug-in installed and ensure that the appropriate code gets executed to invoke the Web
Services provider.
Note: If you get an exception with the message “Parsing of the specified WSDL failed” followed by an
explanation, ensure that the WSDL is accessible through a browser. This exception could either be
300 Lotus Expeditor: Developing Applications for Lotus Expeditor
the result of a firewall message in HTML-form requiring authentication, or could be due to an
invalid WSDL. Please consult with your administrator.
Note: When deploying a Mobile Web service client for communicating with a Web service provider that
references byte arrays, you may need to differentiate between WSDLs that map byte arrays to
either byte unbounded or to base64Binary. You can set the
javax.xml.rpc.Stub.BYTE_ARRAY_ENCODING property to byteUnbounded or base64Binary depending
on your needs. If the property is not set, the client will encode byte arrays using base64Binary.
For example:
// static client
stub._setProperty(javax.xml.rpc.Stub.BYTE_ARRAY_ENCODING,
javax.xml.rpc.Stub.BYTE_UNBOUNDED);
// dynamic client
Dictionary props = new Hashtable();
props.put(javax.xml.rpc.Stub.BYTE_ARRAY_ENCODING,
javax.xml.rpc.Stub.BYTE_UNBOUNDED);
WSProxyService wsImpl =
(WSProxyService)bundleContext.getService(serviceReference);
if (wsImpl == null) {
System.err.println("Unable to find service Web service proxy");
return;
}
if (!wsImpl.register(wsdl, props)) {
System.err.println("Unable to consume WSDL");
return;
}
Note: When deploying a Mobile Web service client for communicating with a Web service provider
hosted in WebSphere Application Server 5.1 that references Hashtables or HashMaps, you may need
to set the javax.xml.rpc.Stub.KEY_VAL_FULLY_QUALIFIED property to “false” (see the previous Note
for an example). This will allow the proper SOAP decoding of the mentioned types. By default,
this property is not set.
Axis Web Services
The Lotus Expeditor Toolkit can be used to generate the Apache Axis based Web Services client code that
calls the Web Services providers via a static stub. The JAX-RPC (JSR-101) support is enabled using the
Apache Axis 1.3.
Developers who wish to develop JSR-101 based Web Services clients are able to do so using the modified
version of the WTP tools provided with the Lotus Expeditor Toolkit and the Apache Axis 1.3 runtime
bundled in Lotus Expeditor v6.1. The role for using the Apache Axis runtime is strictly for Client-Side
only. Also there is very minimal security supported. If the security is needed, SSL & Basic Authentication
must be used. A minimal support from the WS-Security specifications (User name Token) is provided
when locating the static stub object using the JNDI. You can find more information on Client-Side Axis
support on the Apache Axis web site at http://ws.apache.org/axis/java/client-side-axis.html.
Creating Axis Web Services
This section provides information on creating Axis Web Services.
Creating an Apache Axis based Web Services client: Following are the steps to create Apache Axis
based static client stubs:
1. Create a Client Services project. Please refer to “Creating a Client Services project” on page 19. Or
convert the project to a Client Services Project using Lotus Expeditor Toolkit, if needed:
Developing applications 301
a. Select File > New > Project.
b. Select Client Services > Client Services Project.
c. Select Next.
d. Type a name for the new project (for example, MyWebServicesClient).
e. Select Next.
f. Select Next.
g. Select Finish.A Client Services project is generated in your workspace.
2. Select File > New > Other >Web Services > Web Service Client. Click Next.
3. Input the WSDL location in the Service definition filed and select the Java Proxy as the Client type.
4. Select Client Services v6.1 as a Server and the Apache Axis for Client Services as the runtime.
5. Choose the existing Client Services project that you created above as the Client project.
6. Set the slider bar to Develop as the level for client generation. Uncheck Monitor the Web Service, as
this option is not supported at this time.
7. Click Next to select the output Folder and configure the package to namespace mapping, and then
Finish. Or just click Finish to use the defaults.
Typically, a client would perform the following steps to communicate with the remote service:
1. Obtain an instance of the interface stub.
2. Set the endpoint property of the stub to point to the service endpoint of the Web service.
3. Invoke the remote method.
In Lotus Expeditor, there are two ways to access the Web Services client stub from a client application:
1. By directly obtaining an instance of the interface stub.
2. By using the JNDI to lookup an instance of the interface stub.
Directly obtaining an instance of the interface stub: This option is used when the client has all the
information necessary to communicate with the remote service at compile time and does not have to rely
on the accounts to provide any information. It does not provide support for User name and LTPA Token
for SOAP messages. It should be used when no security is required.
No specific steps are needed before obtaining a stub instance using this mechanism. If the stub is
packaged in a different plug-in than the client, the plug-in is required to export the package so the client
can access it.
For example:
EchoServiceService service = new EchoServiceServiceLocator();
//endpoint address
URL endpoint =
new URL(http://localhost/EchoService/services/EchoService);
//get a stub which implements the SDI
EchoService stub = service.getEchoService(endpoint);
// invoke the remote method
stub.echoString("echo: Hello from EchoService");
Obtaining an instance of the interface stub using the JNDI mechanism: Using this option will provide
a full support of our platform which would integrate the stub instance with the Accounts & the Netfaults
framework. It allows the clients to obtain an initialized stub with properties like the endpoint address,
security property for User name Token & LTPA based authentication. These properties can be stored in
302 Lotus Expeditor: Developing Applications for Lotus Expeditor
user accounts. Now at runtime, the stub instance reads the properties from the accounts to invoke the
Web service. This mechanism is useful when multiple clients need to share the same service interface
object in the Lotus Expeditor runtime.
An example:
InitialContext ic = new InitialContext();
//JNDI lookup for the stub instance
EchoService stub = (EchoService)ic.lookup
("com.ibm.test.EchoService);
To be able to lookup an instance of the interface stub using JNDI, there are some requirements:
v Two extensions must be defined that provide the binding information for WSObjectFactory and the
service interface and account information for the proxy
v An account must be created with information such as Account name, endpoint, authentication type,
and so on
Defining the extensions to register the Service Interface to JNDI provider: The WSObjectFactory is
responsible for providing a pre-initialized instance of web services client stub for Apache Axis in the
Lotus Expeditor runtime. Upon a client lookup of the client stub using JNDI, the JNDI provider will use
the WSObjectFactory to locate the Web service stub based on the extension definition, which in turn will
cause the stub to initialize and be bound into JNDI.
The Lotus Expeditor Toolkit automatically generates the appropriate plugin.xml entries for the stub as a
part of the development process. The stub developer or the deployer will be required to fill in
appropriate values.
The following example shows how to register the service interface with the Wsfactoryobject.
<extension point="com.ibm.pvc.jndi.provider.java.binding">
<binding jndi-name="com.ibm.test.EchoService"
objectFactory-id= "com.ibm.rcp.ws.objectfactory.WSObjectFactory">
</binding>
</extension>
<extension point="com.ibm.rcp.ws.objectfactory.WSfactoryobject">
<WSobject
account-key="account-name"
class="com.ibm.test.EchoService"
jndi-name="com.ibm.test.EchoService">
</WSobject>
</extension>
Where:
v account-key is the name of the account.
v class is the class name of the service interface.
v jndi-name is the name client will use to lookup the stub instance
Setting the account key programmatically: Currently, the account-key is provided declaratively in
plugin.xml. One can also override the account key provided in the plugin.xml file at runtime during the
JNDI lookup as shown below.
For example:
InitialContext ic = new InitialContext();
EchoService stub = (EchoService)ic.lookup
("com.ibm.test.EchoService@account_name);
Where <account_name> is the name of the account.
Developing applications 303
Note: This feature is not available for the beta release.
Programmatically creating accounts for Apache Axis Web Services clients: One can create an account
programmatically that will be used by Axis. Here is an example that shows an account creation and
setting the values of various account attributes:
AccountsManager manager =
AccountsManagerFactory.getAccountsManager();
Account account =
manager.newAccount(AccountsManager.DEFAULT_ACCOUNT);
account.setName( Echo Service Account);
account.setType( HTTP);
account.setAuthType( HTTP);
account.setProperty( AuthProperties.SERVER,
http://localhost:9082/EchoService/services/EchoService);
//This can be set programmatically or
//If not set, the Accounts framework will prompt at runtime
account.setProperty(Account.USER_NAME, "user1");
account.getLoginContext().setPassword("password");
Where:
v setName sets the name of the account and that becomes the <account_key>. This is required.
v setType is the Type of the account. This must be HTTP and is required. AuthProperties.SERVER allows
you to set the compete endpoint URL of the Web Service. This must be provided and is required.
v setAuthType is the type of the authentication used with this account. Possible values are:
– HTTP - For Basic Authentication
– J2EE-FORM - For LTPA Token (WAS 6.x/WPS 6.x), also referred to as BinaryToken in the
WS-Security specs.
An empty string with no authentication information. Not setting the authType will default to HTTP.
So, the empty string must be set if no authentication is required.
– USERNAME_TOKEN - This is for Basic Authentication at the SOAP level. The User ID and
password are sent inside the SOAP message in plain text. This can only be set in a secondary
account.v AuthProperties.AUTH_SERVER allows you to set the authentication URI from where the LTPA token
can be obtained. In the case of WebSphere Application Server V6, it is /j_security_check. This
property only needs to be set when setting the authType to J2EE-FORM.
When the LTPA Token is expected by the server, the authentication type needs be set to J2EE-FORM in the
account and the authentication server needs to be set as well as shown below.
account.setAuthType(J2EE-FORM);
account.setProperty(AuthProperties.AUTH_SERVER, j_security_check);
When authentication type is set to USERNAME_TOKEN, it is required that the account is tied to a primary
account that has authentication type of HTTP or J2EE-FORM.
account2.setType( "HTTP");
account2.setAuthType( "USERNAME_TOKEN");
//setting the primary account for a account
account2.setProperty(Account.MASTER_PROPS, account.getUID());
Where account is the primary account and account2 is the secondary account for USERNAME_TOKEN.
For more detailed API information, please refer to the Accounts API. Above gives you enough to create
an account that will work with Axis.
Note: Attachments are not supported in SOAP messages.
304 Lotus Expeditor: Developing Applications for Lotus Expeditor
Developing wired applications
This section provides information on wired application development.
Portlet communication
The Portlet Container provides the capability for portlets to communicate with each other and eclipse
components using the Property Broker component. Portlet communication can be split up into three
categories:
1. Portlet-to-Portlet: This is JSR 168 inter-portlet communication. When an action is invoked on a JSR
168 portlet the Portlet Container determines the target JSR 168 portlet (s) and invokes the
processAction() method of the target JSR 168 portlet(s).
2. Portlet-to-Eclipse: This is the communication between a JSR 168 portlet and an eclipse component.
The eclipse components must have an action handler registered with the Property Broker. When an
action is invoked on a JSR168 portlet the Portlet Container determines the action and fires a
propertyChanged event through the Property Broker. The Property Broker determines the wires and
invokes the action handler of the target eclipse component.
3. Eclipse–to-Portlet: This is the communication between an eclipse component and a JSR 168 portlet.
The eclipse component must have an action handler that registered with the Property Broker. When
an action is invoked on an eclipse component, the component initiates a propertyChanged event
through the Property Broker. The broker determines the wires, and invokes the action handler of the
target JSR 168 portlet.
Defining actions and properties
The first requirement for inter-portlet and portlet-to-eclipse communication is to define the WSDL file
that declares the actions and properties of the portlets or eclipse components with the Property Broker.
The basic WSDL for the property broker is identical to that of Portal. The namespace and bindings should
all be consistent with any WSDL used to define properties and actions on Portal.
The WSDL file should be contributed to the Lotus Expeditor platform using an eclipse extension. This
extension is registered with the broker.
To wire two portlets or a portlet and an eclipse component, perform the following procedure:
1. Select a Client Services Portlet project or Client Services plug-in project you wish to add the WSDL
file to.
2. Right click the Client Services portlet project and select New > Other > XML > WSDL to create a new
WSDL file. Create the new WSDL file in the Web Content directory of the project.
3. Select Next.
4. Enter the name of the Client Services project you wish to add the WSDL file to.
5. Select Next.
6. Set the target namespace to http://www.ibm.com/wps/c2a. Use defaults for all other options.
7. Select Finish.
8. Right click the WSDL file and select Open With > WSDL Editor to edit the new WSDL file. Things to
remember when editing the WSDL file:
v <wsdl:types> elements are registered with the Property Broker and mapped to actions
v <wsdl:message> elements must reference a valid <wsdl:type>
v The name of the <wsdl:portType> element will be used to map the action to the operation
v <wsdl:binding> elements are used to define binding of the action to its input and output
parameters. The <input> and <output> elements are used to link the action to a <wsdl:type>
v The name of the <portlet:action> element is registered with the Property Broker. Wire definitions
should reference this name (see below).9. Add the required actions and properties to the WSDL file and save the WSDL file.
Developing applications 305
To register the WSDL file with the Property Broker, perform the following procedure:
1. Select the Client Services Portlet project that contains the WSDL file you want to register with the
Property Broker.
2. Right click the portlet.xml descriptor and select Open With > XML Editor.
3. Add the location of the WSDL as a portlet preference. The preference name must be
com.ibm.portal.propertybroker.wsdllocation, the value of the preference must be a valid location in
the portlet project. An example preference definition would look like this:
<portlet-preferences>
<preference>
<name>com.ibm.portal.propertybroker.wsdllocation</name>
<value>/wsdl/example.wsdl</value>
<read-only>true</read-only>
</preference>
</portlet-preferences>
Wiring portlets
The second requirement for inter-portlet communication or portlet-to-eclipse communication is to define
the wires between the source and target components. Wires can be defined and contributed to the Lotus
Expeditor platform in two ways:
1. In the Portal-managed environment, the wires should be created using the Portlet Wiring Tool. The
wire information will be stored in the Composite Application (CA) XML file and passed down to the
Lotus Expeditor platform through the Composite Application Infrastructure. The wires from the CA
XML file will be translated to a Property Broker wire by the Topology Handler.
2. In the non Portal managed environment, the wire can be defined and contributed using the Property
Broker wire extension point – com.ibm.rcp.propertybroker.PropertyBrokerWire.
To declaratively register the wire definitions with the Property Broker, perform the following procedure:
1. Select the Client Services Portlet project you want to add the wire definitions to.
2. Right click the plugin.xml descriptor file and select Open With > Plug-in Manifest Editor.
3. Select the Extensions tab.
4. Select Add.
5. Locate the com.ibm.rcp.propertybroker.PropertyBrokerWire extension point and select Finish.
6. Update the <wire> element attributes.
7. Save the plugin.xml file
Wire extension examples
The following are sample declarative wire definitions:
Portlet-to-Portlet Wire
<extension id="com.ibm.rcp.portlet.wire"
name="Portlet Wire"
point="com.ibm.rcp.propertybroker.PropertyBrokerWire">
<wire sourceparam=""
title=""
ordinal=""
type="PROPERTY_TO_ACTION"
sourcename="p2psample3_search_text"
targetparam="p2psample3_search_text"
targetentityid="/PortletCommunication/P2PSearchResult Portlet/default"
sourceentityid="/PortletCommunication/P2P QuickSearch/default"
targetname="P2PSearchResultAction"/>
</extension>
Portlet-to-Eclipse Wire
306 Lotus Expeditor: Developing Applications for Lotus Expeditor
<extension id="com.ibm.pvc.portlet.wire"
name="Portlet Wire"
point="com.ibm.rcp.propertybroker.PropertyBrokerWire">
<wire sourceparam=""
title=""
ordinal=""
type="PROPERTY_TO_ACTION"
sourcename="SearchText"
targetparam="result_text_to_plugin_B"
targetentityid="com.boa.teller.PluginB"
sourceentityid="/Portlet_Basic_Communication/PortletA/default"
targetname="ResultActionToPluginB"/>
</extension>
Eclipse-to-Portlet Wire
<extension id="com.ibm.pvc.portlet.wire"
name="Portlet Wire"
point="com.ibm.rcp.propertybroker.PropertyBrokerWire">
<wire sourceparam=""
title=""
ordinal=""
type="PROPERTY_TO_ACTION"
sourcename="search_text_to_portlet"
targetparam="ResultText"
targetentityid="/Portlet_Communication_E2P_Portlet/PortletB/default"
sourceentityid="com.boa.teller.PluginA"
targetname="ResultAction"/>
</extension>
The required fields are:
v type - type of the wire. For inter-portlet as well as portlet-to-eclipse communication, the type must be
″PROPERTY_TO_ACTION″.
v sourceentityid - the name of the source. If the source is an eclipse component, the value of this field is
the eclipse view ID. If the source is a JSR 168 portlet, the value of this field is the URI of the source
portlet window. The URI is comprised of the context root, portlet name, and portlet window name
v targetentityid - the name of the target. If the target is a JSR 168 portlet, the value of this field is the
URI of the target portlet window. The URI is comprised of the context root, portlet name, and portlet
window name. If the target is an eclipse component, the value of this field is the eclipse view ID.
v targetname - the name of the target action. The value must come from the WSDL of the target portlet,
and it is the name attribute of the <portlet:action> element.
v sourceparam - the name of the source parameter. The value must come from the WSDL of the source
portlet or eclipse component, and it is the name of the <portlet:param> element in the output section.
v targetparam - the name of the target parameter. The value must come from the WSDL of the target
portlet or eclipse component, and it is the name of the <portlet:param> element in the input section.
Using the portlet wiring tool
To create cross-page wires between your portlets, you must use the portlet wiring tool to make your
actions global. From the tool, select Manage Actions, and then select the Global checkbox.
Creating Cooperative Components with the Lotus Expeditor Property
Broker
The client property broker is a broker that allows for declarative properties, actions, and wires to be used
among completely decoupled components. The property broker is responsible for taking changed
properties and distributing the property values to the appropriate actions as defined by the wires that are
registered. The property broker separates itself from a traditional pub/sub in that it is a controlled
pub/sub that is driven by declarative markup. Meaning an XML or another data source defines how the
two components communicate with each – which property change is passed onto which action within the
Developing applications 307
component. The second differentiation is the chain effect that can be accomplished by taking input
parameters and posting output parameters. This ability allows for an infinite amount of Property >
Action > Property combinations.
Components that contribute to the broker most likely do not call into the API’s directly, and instead use
the extension point and the declarative WSDL to declare its actions and properties. The preferred model
allows as little knowledge of the broker and its API’s providing a good level of abstraction from the
broker implementation. At most, a component calls into the broker to post a changed property and then
performs an evaluation of the received property changes to complete the action on the changed property.
Lastly, the client side broker was designed after the IBM Portal property broker, but includes a level of
flexibility above and beyond the portal broker. The client broker allows for different kinds of components
to be contributing to the broker; different components as in SWT, AWT, Eclipse commands, OSGI Event
Admin, etc. This allows for currently established actions to participate in broker communications. The
framework allows for new handlers to be defined using an Eclipse extension point; giving the broker
complete adaptability and compatibility with other messaging systems.
Creating your components
Your components can be defined at many levels with the out of the box functionality of the Lotus
Expeditor broker. The broker supports core Eclipse commands that implement the IHandler
(org.eclipse.core.commands.IHandler) interface.
The Eclipse IHandler interface should be used in the case your application can not depend on any
features that require user interface packages. Use this implementation if your product runs in a device
that does not have SWT or AWT available. The property broker has no UI dependencies and can function
on a device with no UI abilities. The IHandler interface provides a method called execute() you must
implement. The PropertyChangeEvent is set as the event trigger and can be accessed by calling
ExecutionEvent.getTrigger(). The following some sample code processes a property broker change event
in an Eclipse IHandler action:
public Object execute(ExecutionEvent event) throws ExecutionException
{
if (event.getTrigger() instanceof PropertyChangeEvent){
final Display display = Display.getDefault();
final PropertyChangeEvent pce = PropertyChangeEvent)event.getTrigger();
...
}
}
The code to publish a property change is relatively straight forward. An SWT component should use the
SWTHelper (com.ibm.rcp.propertybroker.swt.api.SWTHelper) class to publish its properties. This helper
class removes the complexity of identifying the SWT View instance the property came from by having the
caller simply pass in a this pointer to the view. Here is some sample code where a URL is published on
the broker from an SWT based view:
PropertyValue value = PropertyFactory.createPropertyValue(prop, selection);
SWTHelper.changedProperties(new PropertyValue[]{value}, this);
The core broker also has a version of the changedProperties() method where a Java String is passed in
as the owner context of the property change. The string should match the EntityID of the source in the
wire. This is where the property broker does the run time resolution of a specific instances property
change to a specific instances action.
Since the action code is also a single class there is some coding that must be done in order to make sure
the correct view is updated when a property change has occurred. The SWTHelper has methods to assist in
getting the correct ViewPart.
308 Lotus Expeditor: Developing Applications for Lotus Expeditor
public void runWithEvent(Event event) {
//Make sure the Event is from the Property Broker
if (event instanceof PropertyChangeEvent){
final Display display = Display.getDefault();
final PropertyChangeEvent finalEvent = (PropertyChangeEvent)event;
display.asyncExec(new Runnable() {
public void run( ) {
//Get the wire definition from the event
Wire def = finalEvent.getWireDefinition();
//Our view object type
PreviewView pView = null;
//view for this action.
PreviewView pView =
(PreviewView)SWTHelper.locateView(def.getTargetEntityId());
...
}
Registering your definitions with the broker
Now that you have defined the concrete class that will be called by the broker for a property change, you
must register the action with the broker using the PropertyBrokerDefinitions extension point. This
extension point will register your action and also the input and output parameters of the action. Each
action cannot only receive one input property change, but also post as many output property changes as
needed. This is a key difference from a traditional pub sub. This means when you action is called by the
broker, it simply adds output properties to the passed in PropertyChangeEvent object by calling
addOutputProperty().
The schema definition of the PropertyBrokerDefinitions extension point has three primary fields:
v Class – Optional Java class that will be instantiated by the broker and should implement one of the
supported handler interfaces (Command, SWT, AWT).
v File – The Web Services Definition Language (WSDL) file that defines the action name, input and
output properties.
v Type – The type of action to be registered, for instance COMMAND, SWT_ACTION
– Map Element
- wsdlActionName - The ID of the action in the WSDL.
- actionID - The ID of the action in the Eclipse system.
- class - Optional class implementation for this specific action. If this class is specified, the property
broker will instantiate the object and contain it with the Action definition. The object will then be
called by the appropriate handler.
Working with the Property Broker WSDL file
The WSDL file is a base WSDL implementation with custom bindings defined. Since this WSDL file must
be compatible with the portal WSDL file we use the exact syntax on the client as we do in the Portal
server. This will be a big benefit when we talk about using the declarative wiring in Portal.
The following is a complete sample of a WSDL file where we define a single action, three base parameter
types, one input parameter and two output parameters:
Action name=produceURL
Input Param=URL,
type=tns:BaseURL
Output Param 1=URL From Tree
type=tns:BaseURL
Output Param 2=Progress
Developing applications 309
type=tns:Progress
<definitions name="LoadURLInBrowser_Service"
targetNamespace="http://www.ibm.com/wps/c2a"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:portlet="http://www.ibm.com/wps/c2a"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.ibm.com/wps/c2a"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<types>
<xsd:schema targetNamespace="http://www.ibm.com/wps/c2a">
<xsd:simpleType name="BaseURL">
<xsd:restriction base="xsd:string">
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="Progress">
<xsd:restriction base="xsd:string">
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
</types>
<message name="LoadURLRequest">
<part name="urlFromTree" type="tns:BaseURL"/>
</message>
<message name="OurResponse">
<part name="loadedURL" type="tns:BaseURL"/>
<part name="progress" type="tns:Progress"/>
</message>
<portType name="LoadURL_Service">
<operation name="loadURL_Operation">
<input message="tns:LoadURLRequest"/>
<output message="tns:OurResponse"/>
</operation>
</portType>
<binding name="ManyOutParamsBinding" type="tns:LoadURL_Service">
<portlet:binding/>
<operation name="loadURL_Operation">
<portlet:action name="produceURL"
type="standard"
caption="Load the new URL"
description="Load.a.new.url.in.the.main.window"
actionNameParameter="ACTION_NAME"/>
<input>
<portlet:param name="URL" partname="urlFromTree" caption="url.loader"/>
</input>
<output>
<portlet:param name="URL From Tree" partname="loadedURL" caption="Browser URL"/>
<portlet:param name="Progress" partname="progress" caption="Browser Progress"/>
</output>
</operation>
</binding>
</definitions>
310 Lotus Expeditor: Developing Applications for Lotus Expeditor
Using the Composite Application Infrastructure
In most cases you should be using Portal to define your wires however if you are creating components
for Lotus Notes® you can use the client side Composite Application Editor to create your applications in
Eclipse and have them deployed to Portal or Domino. Both options are fine and both are completely
compatible. In this section we focus on using the portal admin tools to create an application that will
define pages with our portlets on them and the application is then installed onto the Lotus Expeditor
Client using the Composite Application Portal catalog.
Both tools provide a visual editor to layout your components and then wire them together. This is the
point in the application development where an application aggregator pieces the separate components
together to give them context within a defined solution, and why it is important to create your
components in a completely decoupled fashion where they can be used under different contexts.
Declarative wiring with the Portal Admin tool
Now that you have a client side component that can register its actions you will want to deploy a portlet
that contains the same WSDL file onto your portal server. By having a portlet register the properties and
actions on the portal server you will be able to use the portal wiring tool to define the wires between
your components. This of course is only one option and the focus of this section is to use declarative
wiring in the Composite Application Infrastructure (CAI). CAI gives the client the ability to connect to a
portal and install an application that is defined with the template application model. The wiring
information for the portlet instances is contained within this markup and will allow the CAI client code
to wire the components together declaratively. Another option is to wire your components using the
PropertyBrokerWire extension point. Under the covers the CAI code uses this extension point by
providing dynamic extensions to it.
You can create your portlets with whatever tool you wish. For the basic broker to work on the client the
portlet does not have to really do anything so creating a “stub portlet” by using the samples in Rational
Application Developer you can simply add your WSDL to the portlet and register the actions and
properties. The Lotus Expeditor does however support locally running JSR 168 portlets that can
intercommunicate with any other registered component with the broker. What this means is you can have
an SWT version of your component and/or a portlet version of your component and deploy them to the
client under different contexts. Once your portlet is created and you export your portlet as a WAR file
you simply import the WAR file into your portal server.
When you put the portlet on a page you can now wire the components together using the normal Portal
Wiring tool. The Lotus Expeditor also supports cross page wiring however in order to enable an action
for cross page wiring you need to mark the action as a ‘Global Action’ using the Manage Actions button
on the wiring screen.
From the wiring tool; you simply select the source portlet, the output property, the target page (defaults
to current), the target portlet, the target property and whether or not this wire is public or private.
Specifying a custom owner for your SWT action
The property broker automatically assigns the plug-in ID as the owner of the action by knowing what
plug-in registered the extension. In the rare case where you need to override what the owner is you can
simply map the instance of the view to a new custom defined owner. This gives generic containers like
the JSR 168 container and the Lotus Notes composite application plug-in the ability to override the action
and specify a domain specific owner. In the case of Lotus Notes for instance they use the replication ID of
the database the action is contained in for the owner.
public void addInstanceToOwner(String instance, Object owner);
The addInstanceToOwner() call now maps the specific view instance with a specific new owner. Now,
during runtime resolution of wiring, the correct instance will be called with the proper action and owner
information.
Developing applications 311
Developing for serviceability
This section provides information on developing applications for serviceability.
Understanding serviceability
Most application development environments provide APIs to support the creation and logging of
messages. The OSGi framework, Eclipse framework and the 1.4 JDK (JSR47) all provide different systems
for logging messages. Due to this variety of logging and tracing options, in the Lotus Expeditor runtime,
OSGi bundles, Eclipse applications and standard Java components use different methods to record log
messages. Other groups such as the Apache open source group have also created façade APIs to help
abstract their developers from one specific logging implementation. The Apache commons logging APIs,
for example, support applications written to these facades in the Lotus Expeditor runtime, and will be
redirected to the JSR47 logger for formatting and persistence.
The selection of the appropriate logging APIs is an application specific task in the Lotus Expeditor
runtime. In some cases it is quite simple to determine the best candidate for logging APIs, such as an
eclipse application, which will already be reliant on eclipse APIs, in this case the Eclipse logging APIs
would be the most appropriate choice. In the case of a pure OSGi application with no other eclipse
dependency, it may be most appropriate to use the OSGi LogService to continue to support independence
from the Eclipse APIs and allow the pure OSGi application to execute on OSGi frameworks besides the
Lotus Expeditor runtime which is based on the Eclipse implementation of the OSGi runtime. Finally, if
the application has code leveraging the JRE logging APIs there is no reason to attempt to modify the
application to leverage either the Eclipse APIs or the OSGi logging APIs, rather it can log directly to the
much more rich JRE logger and its messages will be federated into the runtime system log via the Lotus
Expeditor core logging framework. While a specific developer or project may have an affinity for one API
or the other, it should be noted that the most feature rich and flexible of these APIs is the JRE
java.util.logging APIs. With that said, it is recommended that all new projects leverage these APIs, and
existing projects consider leveraging the JRE logging APIs in the future if/when their logging architecture
is redesigned.
Note: It is recommended that new applications for devices use Eclipse logging, as JSR 47 logging may
not be supported in future releases of Lotus Expeditor for Devices due to JSR 47 not being a JME
JSR. Existing applications that use JSR 47 or OSGi logging may continue to do so for this release.
Lotus Expeditor works hard to differentiate between the terms logging and tracing. In Lotus Expeditor,
logging is enabled at all times and provides all of the information needed for standard problem
determination of the product, while tracing produces a much more detailed set of information for
advanced diagnostics/problem determination. While logging is enabled at all times for all components,
tracing is not, but can be enabled simply on a component level by the end user or administrator. For
more information on enabling/disabling logging and tracing please refer to the Assembling and Deploying
Lotus Expeditor Applications and the Troubleshooting guide.
Enabling projects for serviceability
Lotus Expeditor Client provides Client Services target definition support to assist in managing project
dependencies and launch configurations. These target definitions simplify the creation and configuration
of application projects for serviceability, enabling you to select the target logging and tracing APIs, and
provide automatic management of the requisite logging and tracing libraries. When enabling a project for
serviceability, you can select any of the Client Services target definitions for your Client Services project.
The following table provides a list of tasks and the appropriate target feature selections for each profile in
a Client Services project.
312 Lotus Expeditor: Developing Applications for Lotus Expeditor
Table 24. Client Services project tasks
Task Target Features
JSR47 logging and tracing No extra target features required since these APIs are
provided in the Base JRE included with Lotus Expeditor
and are made available via the system class path.
Eclipse Logging and Tracing Eclipse Core components.
Note: This target feature is selected by default in all
Lotus Expeditor profiles.
OSGi LogService logging and tracing Eclipse Core components.
Note: This target feature is selected by default in all
Lotus Expeditor profiles. Also note That while there is a
specific target feature called Log Service, this does not be
selected on your project target profile since it is an actual
Implementation of the OSGi LogService interface, not the
interface definitions that are provided in the Eclipse Core
components target feature.
Apache Commons logging and tracing Apache Commons Components
Developing serviceability logic
Lotus Expeditor 6.1 provides a collection of APIs for logging and tracing:
v The JRE Logger provides the J2SE 1.4 java.util.logging APIs and framework for standard J2SE
applications to produce and submit log messages that can then be collected, filtered and persisted all
via pluggable components as defined by the JRE Logger APIs.
v The Eclipse Logger provides the Eclipse logging APIs and a logging framework for Eclipse applications
to produce and submit log messages that can be collected and persisted by the Eclipse logging
framework.
v The OSGi Log Service provides OSGi specified APIs for OSGi applications to produce and submit log
messages that can be collected, filtered, and then provided to LogReader implementations which can
then provide more filtering and then persist the log records if desired.
v Apache Commons Logging APIs provide a façade interface to abstract developers from one specific
implementation of logging. This is an external interface and is used by several components in the open
source Java world.
JSR47 Logging (java.util.logging)
The logging methods are grouped in five main categories:
v There are a set of ″log″ methods that take a log level, a message string, and optionally some
parameters to the message string.
v here are a set of ″logp″ methods (for ″log precise″) that are like the ″log″ methods, but also take an
explicit source class name and method name.
v There are a set of ″logrb″ method (for ″log with resource bundle″) that are like the ″logp″ method, but
also take an explicit resource bundle name for use in localizing the log message.
v There are convenience methods for tracing method entries (the ″entering″ methods), method returns
(the ″exiting″ methods) and throwing exceptions (the ″throwing″ methods).
v Finally, there are a set of convenience methods for use in the very simplest cases, when a developer
simply wants to log a simple string at a given log level. These methods are named after the standard
Level names (″severe″, ″warning″, ″info″, etc.) and take a single argument, a message string.
For the methods that do not take an explicit source name and method name, the Logging framework will
make a ″best effort″ to determine which class and method called into the logging method. However, it is
important to realize that this automatically inferred information may only be approximate (or even
wrong). Virtual machines are allowed to do extensive optimizations when JITing and may entirely
remove stack frames, making it impossible to reliably locate the calling class and method.
Developing applications 313
All methods on Logger are multi-thread safe.
The Lotus Expeditor 6.1 logging framework leverages the JSR47 implementation provided with the
runtime JREs (both JRE 1.5 and JCLDesktop). It works directly with java.util.logging.LogRecord objects.
For more detailed information on JRE Logging APIs, please see the Sun Javadoc at http://java.sun.com/j2se/1.4.2/docs/.
JSR47 Tracing (java.util.logging)
Tracing is integrated into the JSR47 logging framework as part of the standard log event message flow.
While some logging systems provide completely different APIs for logging and tracing, JSR47 only
differentiates them by the severity. SEVERE, WARNING and INFO in Lotus Expeditor are considered log
messages, while all other levels CONFIG, FINE, FINER and FINEST are considered trace messages.
OSGi Logging
The LogService interface allows bundle developers to log messages that can be distributed to other
bundles, which in turn can forward the logged entries to a file system, remote system, or some other
destination.
The LogService interface allows the bundle developer to:
v Specify a message and/or exception to be logged.
v Supply a log level representing the severity of the message being logged. This should be one of the
levels defined in the LogService interface but it may be any integer that is interpreted in a user-defined
way.
v Specify the Service associated with the log requests. By obtaining a LogService object from the
Framework service registry, a bundle can start logging messages to the LogService object by calling
one of the LogService methods. A Log Service object can log any message, but it is primarily intended
for reporting events and error conditions.
The LogService interface defines these methods for logging messages:
v log(int, String) – This method logs a simple message at a given log level.
v log(int, String, Throwable) – This method logs a message with an exception at a given log level.
v log(ServiceReference, int, String) – This method logs a message associated with a specific service.
v log(ServiceReference, int, String, Throwable) – This method logs a message with an exception
associated with a specific service.
While it is possible for a bundle to call one of the log methods without providing ServiceReference
object, it is recommended that the caller supply the ServiceReference argument whenever appropriate,
because it provides important context information to the operator in the event of problems.
For more information on the OSGi Logging APIs please see the OSGi_R4_Service_Compendium,
specifically the OSGi Log Service Specification chapter, as well as “OSGi specification” on page 366.
OSGi Tracing
The OSGi LogService definition is similar to the JSR47 logging framework in the sense that it does not
provide separate APIs for tracing; it simply leverages the log event severity as the way to differentiate
between log messages and trace messages. The LogService defines the following levels for log messages –
ERROR, WARNING, and INFO - and defines the level DEBUG for trace messages. All of the logging
method signatures are valid for tracing so the information provided above is valid for OSGi tracing as
well.
Eclipse Logging
For plug-in developers, the Eclipse logging and tracing mechanism consists of just a few objects and
methods. The following code provides a simple example of logging from a plug-in:
314 Lotus Expeditor: Developing Applications for Lotus Expeditor
import org.eclipse.core.runtime IStatus;
import org.eclipse.core.runtime Status;
import org.eclipse.core.runtime Platform
IStatus status= new Status (IStatus.ERROR,
"Test",
0,
"Testing Eclipse Error Logging",
(Throwable) null);
getDefault().getLog().log(status);
In order to send a message to the Eclipse logging system, a Status object must be constructed and
populated with all required data. Once the Status object is instantiated it is then passed to the Eclipse
logger via the log() method. The handle to the Eclipse log is located via the getLog() method of the
Plugin class (the Plugin class is accessed via the getDefault() method here).
Eclipse Tracing
Eclipse’s tracing mechanism is based on methods from the Eclipse Platform class, inDebugMode() and
getDebugOption(String) and one tracing configuration file called .options located in the plug-in
directory. Details on Eclipse tracing can be found in the online help at PDE Guide > Getting Started >
Basic Plug-in Tutorial > Running a plug-in > Running with tracing.
Once the trace files have been set up appropriately for the plug-in, and tracing has been enabled via the
workbench configuration screens, the following code shows an example of how the tracing setup is used
by the developer:
if ( Platform.inDebugMode() ) {
if ("true".equalsIgnoreCase (Platform.getDebugOption("T2")) {
IStatus status= new Status(IStatus.INFO,
"Test",
0,
"Testing Eclipse Error Logging", (Throwable)null);
getDefault().getLog().log(status);
}
}
The above example begins by checking to see if debugging has been enabled for this plug-in, if it has
been enabled, then the example confirms that trace level ″T2″ is set to ″true″, if and only if these two
checks are true will the Status object be created and logged to the Eclipse logger.
While the above example illustrates the trace check and a log entry created to the Eclipse logger, plug-in
developers often use System.out or System.err to write out trace information. The Eclipse log file is
typically reserved for Error or Warning conditions that occur during application execution.
Apache Commons Logging
The following code example shows a simple programming example for the Apache Commons Logging
APIs.
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
private static final Log apachelog = LogFactory.getLog("Apache");
apachelog.error("Testing the Apache log");
Java.util.logging best practices
The following is a set of best practices for JSR47 usage:
1. Is there a good way to get logger?
The following pattern can be used in each class to obtain a JSR47 logger:
Developing applications 315
private static final String CLAZZ_NAME = MyClass.class.getName();
private static final String PKG = MyClass.class.getPackage().getName();
private static final String LOG_RB = PKG + "." + “Messages.properties);
private static Logger _logger = Logger.getLogger(PKG,LOG_RB);
Substitute your class name for MyClass and ensure you have a properties file with the same name as
the leaf node of the package.
Note: If you use this pattern, use caution when later renaming your packages.
2. Why not just use LogManager.getLogger(″com.ibm.rcp.mypackage″);?
java.util.logging.LogManager.getLogger(String name) returns the logger with a matching name if
it already exists. Otherwise it returns null. There is no guarantee that a given logger will exist when
your class gets instantiated since the order of class instantiation is not determinate. Even if it were
currently, the order could change later. Therefore, it is necessary to use the get methods from the
Logger class to ensure that a Logger is returned. Whether you use LogManager or Logger to get a
logger, no more than one instance of a named logger is ever in existence within a VM at any given
time.
Additionally, since it is possible to instantiate a logger at first with no resource bundle and then later
use it with a resource bundle, consistently requesting it by means of the Logger class ensures that the
expected resource bundle will be available. If you use LogManager you will get the logger (if it
exists). However, it must already have an associated resource bundle. There are alternatives, such as
testing to see if the logger has a ResourceBundle already, and if not, using Logger,getLogger(String
name, String resourceBundleName) to fix the situations. Alternatively, you could adopt the
convention of only using the logbr methods of Logger instead.
3. How do I change the log level for my loggers?
The LogManager is initialized from values in the file <workspace>/.config/rcpinstall.properties.
There, you can set the logger level. For example, if you want to see all log entries for your code in
the package com.ibm.rcp.mypackage, add the following string:
com.ibm.rcp.mypackage.level=FINEST
If you want to hear less from other JSR47 loggers, simply change the default values in the
rcpinstall.properties to SEVERE.
4. Where are my logs?
The system log can be found in <workspace>\logs\error-log-0.xml where n is a number 0 (current
log) or 1 log from the last time it was run.
The system trace file can be found in <workspace>\logs\trace-log-0.xml where n is a number 0
(current log) or 1 log from the last time it was run.
5. What is ″guarded logging″ and why do I need it?
″Guarded logging″ is a pattern that checks to see if a log statement will result in output before it is
executed. Since the logger itself makes this check, it may seem redundant to do this for every call.
However, as almost every logging call creates String objects, it is critical. The accumulation of these
Strings causes memory fragmentation and unnecessary garbage collection. Garbage collection, while
beneficial, also produces significant performance loss. To reduce this as much as possible, always
guard logging statements with a severity less than SEVERE. Since SEVERE is always expressed by
the logger, these do not need a guard. Here is an example, given the declarations in FAQ 1.
if(_logger.isLoggable(WARNING)){
_logger.logp(WARNING,CLAZZ_NAME,method,
"warn.unable.to.get.preference",
new Object[]{pid,key});
}
6. What are guidelines for using each logging level?
INFO
Do: Use INFO for events that represent the normal operation of a component, and will be useful in
resolving problems that may occur in other code segments. For example, After verifying that install
operations are complete, the following message is logged from the launcher:
316 Lotus Expeditor: Developing Applications for Lotus Expeditor
Done with install operations
Don’t: Use INFO for debug information, for tracing program execution, in potentially frequently
repeating events, or to simply confirm an event unless it will be useful in determining the cause of a
problem with a related event.
WARNING
Do: Use WARNING when a problem has occurred that will effect normal operations adversely, but
will not prevent most operations to continue. A good example would be the following message from
provisioning:
Failed to retrieve page file for URL EXPEDITOR.png.
Don’t: Use WARNING if the problem completely or severely eliminates the use of a function, or for
information messages that do not indicate impaired function.
SEVERE
Do: Use SEVERE to indicate an event causing a significant or complete loss of some function. An
example of a SEVERE message is written by the platform plug-in when the
plugin_customization.ini can not be read:
Error loading plugin customization file
Don’t: Use SEVERE if all or most functionality will continue to be available or if the problem is
transient.
7. How tracing accomplished? What about developer-only tracing?
Tracing is off by default. Trace is intended for use by developers, Quality Engineers and Support.
There are three trace levels: FINE, FINER and FINEST.
FINE: Use this level for significant events that explain the flow or state of the system when trying to
understand or debug a problem. These are usually at the level of object creation, catch clauses for
exceptions that do not constitute errors, and so on.
FINER: There is a set of Logger methods that generate messages with a FINER level. These are
entry(...), exit(..), finer(..) and throwing(..).
v Entering and exiting should be used for method entry and exit.
v Finer can be used for any tracing that is more verbose than desired for fine, but is not method
entry/exit or used before throwing an exception.
v Throwing should be used when you are about to throw or re-throw an exception.
FINEST: Finest is usually thought of as developer or debug tracing. This is used during
development, when attempting to learn the behavior of a system at a fine level of detail and when
trying to diagnose difficult problems once the code is released.
Tracing is a valuable tool, both during and after development. There is a common misunderstanding
that there is ″Developer Only″ tracing. Tracing that developers want to be able to turn on and off
should be done using the java.util.logging.Logger API. The level should be FINEST. Such tracing
can be valuable to support when diagnosing a difficult customer problem. Having it available will be
one factor in reducing the support team’s dependence on development. The rule of thumb should
be: If it is valuable now, it may be valuable later. Use Java logging at the FINEST level and you will
have virtually no impact on performance (with proper guarding), and you will create a valuable tool
for someone else. If you use println or printStackTrace you are not tying into the manageable Java
logging framework, and are likely going to create a distraction for someone else.
8. What should I do about Logging Exceptions?
All exceptions should be noted in a logging statement. Normally, this would be at the WARNING or
SEVERE level unless the exception is trivial or being rethrown. Some further guidelines:
v Always provide a specific message for the log entry including the exception. This should explain
the impact of the exception.
v Log the exception, not its message. Unless you set it yourself, you have no guarantee that the
message will be intelligible or even exist.
Developing applications 317
v Do not call printStackTrace on the exception. The logger will do this for you, and in a way that
will allow things like autonomics to respond to the exception. .
v If you catch the exception and then throw a new one (frequently the case in well written API) you
should use the caught exception as the cause of the thrown exception. Generally, you should not
log the caught exception in this case, assuming everybody up the stack follows the same
guidelines.
v Sometimes it makes sense to log the exception anyway, even though you are going to throw
another one if you believe this information may be of use to the user. 9. What is an example of logging?
The following two code samples are common logging patterns.
if (_logger.isLoggable(Level.INFO)) {
_logger.logp(Level.INFO, CLAZZ_NAME, "execute", "info.process.reqs");
}
And...
_logger.logp(Level.SEVERE,CLAZZ_NAME,"run","err.rcpapplication.error",
throwable);
10. What is an example of tracing?
The following code examples illustrate tracing.
Tracing example 1:
if (_logger.isLoggable(Level.FINE))
_logger.fine("Setting activities for page
" + _model.getName() + ":");
Tracing example 2:
private static final String CLASSNAME =
CompositeApplicationAdapterServiceImpl.CLASSNAME;
// If you refactor the class name changes
private GUID methodX(URL url, IProgressMonitor pM, boolean overrideCache) {
if (logger.isLoggable(Level.FINER)) {
logger.entering(CLASSNAME, "methodX", url==
null?"null":url.toExternalForm());
}
GUID res = null;
...
}
In this example, only the URL is logged on enter, as the other arguments are not that useful during
debugging; often when an application does not load, it is a problem with a partially corrupted URL
which is partially computed, so it is very useful to see exactly what URL is being passed into the
load call. Also, this is a private method; there are many public methods that do their job by calling
this private method, so it funnels the logging into this method instead of having the URL logged
several times by the public calls.
} finally {
if (logger.isLoggable(Level.FINER)) {
logger.exiting(CLASSNAME, "methodX", res);
}
}
The ″exiting″ call is in a finally clause so that it logs the exit even if the method throws an exception.
It also logs the result being return from this method, which is, again, very useful for debugging
purposes to figure out what, if anything, went wrong.
Tracing example 3:
public void showApplication(URL url, String pageId, IProgressMonitor
progressMonitor)
throws IllegalArgumentException, CompositeApplicationException {
if (logger.isLoggable(Level.FINER)) {
logger.entering(CLASSNAME,
318 Lotus Expeditor: Developing Applications for Lotus Expeditor
"showApplication(URL,String,IProgressMonitor)",
new Object[] {url, pageId, progressMonitor});
}
} catch (RuntimeException e) {
logger.log(Level.SEVERE,
PortalCaiUIPlugin.getString(
"str.caiuiplugin.calling.applications.update.job.error"), e);
throw new CompositeApplicationException(e);
}
11. Should I cache my logger level?
It may seem tempting to check the level of your logger once during your execution, and then simply
refer to that cached level instead of asking for the level each time you want to guard your calls. This
is not recommended, however, since it can cause your code to not respond to dynamic updates to
logger level configuration such as via the OSGi command prompt.
Migrating applications
Lotus Expeditor Toolkit provides two methods to migrate projects created with previous versions of the
toolkit. The toolkit can migrate Extension Services projects from a WebSphere Studio Device Developer
5.7 workspace, or Client Services 6.0 projects in a workspace created by a Rational SDP 6.0 platform. Both
methods convert projects in the same way. The toolkit sets the JRE and Target Definition for all migrated
projects to default values.
The toolkit tries to migrate the application service from the Target Features in SMF Bundle Developer, to
the latest target definition in the Lotus Expeditor Toolkit. After migrating a project, you may edit/add
target features and change target definitions. To do this, right click on the project and select Properties.
Choose Client Services, then select the Application Profile tab. For more information on this screen,
refer to “Setting Client Services project properties” on page 21.
Automated migration
Lotus Expeditor Toolkit allows you to migrate your Extension Services projects from a WebSphere Studio
Device Developer 5.7 workspace, or Client Services 6.0 projects in workspace created by a Rational SDP
6.0 platform. Web and Embedded Transaction Container projects require that the migration take place in
one of the supported Rational platforms (either RAD or RSA). Upon startup on an existing workspace,
the toolkit detects and automatically converts all workspace projects. Select Yes, when you are prompted
to set the Lotus Expeditor Toolkit configuration.
This modifies your projects without any user interaction. If you would like to preserve your project
format and data, you should back up the workspace prior to opening the Lotus Expeditor Toolkit on the
folder.
Note: During migration, the class path is updated to match current Target Definitions, and the manifest
is updated to match current runtime requirements. The toolkit will update the MANIFEST.MF file if
one exists, creating a backup called MANIFEST.MF.BAK. It will also set the Java Build Path, the JRE,
and Target Definition to default values.
Manual migration
You may choose to import an existing 5.7 Extension Service, or Client Services 6.0 project into a
workspace. Web and Embedded Transaction Container projects require that the migration take place in
one of the supported Rational platforms (either RAD or RSA). If you would like to preserve your existing
project format and data, back up the project directory prior to using the Migration wizard. To migrate,
perform the following procedure:
1. Select File > Import.
2. Select Existing project into workspace.
Developing applications 319
If there is no Client Services category, select Show All Wizards to display the Client Services
category.
3. Select either a WebSphere Studio Device Developer 5.7.1 Extension Services web project, or a
WebSphere Everyplace Client 6.0 Client Services web project in the file system, from CVS, or a project
interchange zip file.
4. Select Finish.
Migrating component logic
As the client platform continues to expand and evolve, new capabilities are added to the platform that
may result in other changes to the platform. It is the intent that plug-ins that were written using the
public APIs and extension points will continue to function on the latest version of the client platform.
Application developers may have inadvertently added dependencies on plug-ins that were intended for
internal use only and may require changes in order to continue to function on the latest release.
Compatibility plug-ins provided
Many capabilities provided by WebSphere Everyplace Deployment 6.0 have been updated in this version
of the client platform. The following compatibility plug-ins have been provided to ease migration to the
current client platform. If you had plug-in requirements on com.ibm.eswe.workbench or
com.ibm.icu.icu4j, you should make the following changes in order to improve future compatibility and
migration.
v com.ibm.eswe.workbench
This plug-in provided the user interface for the WebSphere Everyplace Deployment 6.0 platform. Two
extension points, com.ibm.eswe.workbench.WctApplication and
com.ibm.eswe.workbench.WctWebApplication, were provided to enable contributions to the application
launcher. These extension points are still provided, but the plug-in identifier has changed.
If you used these extension points, the Eclipse Plug-in Project or the Client Services projects created
using previous editions of the tools may have added a statement to the plug-in manifest to create a
dependency on this plug-in. Specifically, the com.ibm.eswe.workbench may have been added as a
plug-in dependency.
Corrective action: Remove com.ibm.eswe.workbench from the plug-in dependency list. You may continue
to use the extension points
v com.ibm.icu.icu4j
This plug-in provided additional Unicode capability beyond the capability provided by the Java
standard class libraries. In WebSphere Everyplace Deployment 6.0, this plug-in provided ICU4J v3.2.0
packages. In this release, Eclipse has provided v3.4.5, but in a plug-in named com.ibm.icu.
Plug-ins written to use the ICU4J packages provided by the com.ibm.icu.icu4j plug-in provided by
WED 6.0 may have the plug-in com.ibm.icu.icu4j listed as a plug-in dependency. An implementation
of the com.ibm.icu.icu4j has been provided in this release that re-exports the packages from
com.ibm.icu.
Corrective Action: The preferred method is to switch from a plug-in dependency on com.ibm.icu.icu4j
to do an Import-Package on the packages from com.ibm.icu that you need to use. Alternatively, you
can change the dependency from com.ibm.icu.icu4j to com.ibm.icu.
Plug-ins removed in this release
This release of Lotus Expeditor has made several packaging changes that have resulted in the removal of
the following plug-ins. These plug-ins provided no public APIs nor did they provide any extension
points, and should not have been dependencies for any plug-ins. However, some plug-ins may have
inadvertently added these plug-ins as a dependency. As a result, plug-ins dependent upon the following
plug-ins will no longer work in the client platform without taking corrective action.
v eclipse/plugins/
– com.ibm.pvc.wct.platform.autostart_6.0.0.20050921v rcp/eclipse/plugins/
320 Lotus Expeditor: Developing Applications for Lotus Expeditor
– com.ibm.esupport.client.SSCRVP_6.0.0.20050921
– com.ibm.esupport.client.SSEPGG_6.0.0.20050921
– com.ibm.esupport.client.SSFKUX_6.0.0.20050921
– com.ibm.esupport.client.SSSKRX_6.0.0.20050921
– com.ibm.eswe.help.appserver_6.0.0.20050921
– com.ibm.eswe.help.webapp_6.0.0.20050921
– com.ibm.eswe.installupdate_6.0.0.20050921
– com.ibm.eswe.uiworkbench.patch_6.0.0.20050921
– com.ibm.osg.service.device_2.3.0.20050921
– com.ibm.osg.service.http_2.1.3.20050921
– com.ibm.osg.service.log_2.2.0.20050921
– com.ibm.osg.service.metatype_1.1.0.20050921
– com.ibm.osg.service.prefs_1.2.0.20050921
– com.ibm.osg.service.useradmin_1.2.0.20050921
– com.ibm.pvc.persistence_1.1.0
– com.ibm.pvc.wct.internal.logredirector_6.0.0.20050921
– com.ibm.pvc.wct.mgmtservice.application_1.0.0.20050921
– com.ibm.pvc.wct.mgmtservice.com.linux.x86_1.0.0.20050921
– com.ibm.pvc.wct.mgmtservice.com.win32.x86_1.0.0.20050921
– com.ibm.pvc.wct.mgmtservice.com_1.0.0.20050921
– com.ibm.pvc.wct.platform.help_6.0.0.20050921
– com.ibm.pvc.wct.platform.provisioning_6.0.0.20050921
– com.ibm.pvc.wct.platform_6.0.0.20050921
– com.ibm.rcp.swt.browser.dom.moz.win32_1.3.0.005
– com.ibm.rcp.swt.browser.moz.win32_1.3.0.005
– com.ibm.rcp.ui.browser.gtk_1.3.0.005
– com.ibm.rcp.ui.browser.win32_1.3.0.005
There are two methods for taking corrective action:
v Removing the dependency
v Adding your own compatibility plug-ins
To remove the dependency, you will need to edit the manifest.mf (or plugin.xml) to remove from the
plug-in dependencies attribute any plug-ins shown above. You will then need to re-deploy the application
with the changes.
To add your own compatibility plug-in to fulfill any plug-in dependencies, you will need to create a new
plug-in in your workspace.
1. Select File > New > Project > Client Services > Client Services Project.
2. Use the plug-in name as the project name, and deselect Create a Java project, then select Next.
3. Fill in any ID, Name, Version or Provider information that you wish to change, then select Finish.
You will then need to deploy the compatibility plug-in with your application plug-ins. You may also use
a standard Eclipse Plug-in Project to create a new compatibility plug-in.
After you have either removed the dependency, or have created a new compatibility plug-in, you may
see compilation errors as a result of missing packages or classes. You will need to use only public APIs.
Developing applications 321
Metatype Service changes
WCTME EO 5.8.1 and WebSphere Everyplace Deployment 6.0 contained the OSGi Metatype Provider
service implementation. At this time, OSGi Release 3 did not define a service that enabled access to this
provider. IBM provided its own service implementation to access the Metatype Provider implementation.
It was registered as com.ibm.osg.service.metatype.MetaTypeService, and implemented the service
interface com.ibm.osg.service.metatype.MetaTypeService. This package was provided by the
com.ibm.osg.service.metatype plug-in.
OSGi Release 4 updated the definition of the Metatype Service to include a new service implementation.
This new service implementation is included in plugin org.eclipse.equinox.metatype. The IBM
proprietary instance of the service has been removed.
Corrective Action: Change references from com.ibm.osg.service.metatype.MetaTypeService to
org.osgi.service.metatype.MetaTypeService. Remove any package imports for
com.ibm.osg.service.metatype, or any plug-in dependencies on com.ibm.osg.service.metatype. The
meta data XML schemas have been updated and any existing meta data XML files will need to be
updated to conform to the new schema definitions.
Changes to plug-in startup
There have been significant changes to the framework that enables the startup of plug-ins. The following
corrective actions may need to performed for any application code:
1. The DB2 Everyplace (com.ibm.db2e), DB2 Everyplace ISync (com.ibm.mobileservices.isync), and
Cloudscape (org.apache.derby.core) plug-ins no longer start automatically on platform startup. These
plug-ins now rely upon class access to autostart. Application code that creates an instance of
the javax.sql.DataSource class for each of the database types will not be affected. Application code
that used the java.sql.DriverManager class to obtain a connection, and did not use a Class.forName()
method to load the database driver class, may encounter errors if the plug-ins are not already started.
A java.sql.SQLException: No suitable driver exception will be a typical exception that is
encountered.
If you run into problems where the database plug-ins need to be started, there are two possibilities for
correcting the problem. The preferred recommendation is to update the application code to use the
javax.sql.DataSource object (defined by JDBC 3.0 specification) to access databases. This provides for
portability of application code to Java class libraries following the Java ME Foundation profile. Refer
to “Data access application development best practices” on page 103.
Alterntiavely, you will need to use the lifecycle capabilities provided by the platform to start these
bundles. Refer to Managing lifecycle for more information about adding plug-ins to the lifecycle.
2. In previous releases, plug-ins that did not contain the Eclipse-AutoStart attribute were automatically
started by the platform. These bundles are now automatically started by the default
com.ibm.rcp.platform.personality as legacy plug-ins. It is strongly suggested that any existing
plug-ins be updated to include the Eclipse-LazyStart attribute, and plug-ins that will not activate
due to class access requests be added to the lifecycle of the platform. Refer to Managing lifecycle for
more information about adding plug-ins to the lifecycle.
Migrating OSGi services
ConfigAdmin, Preferences, and UserAdmin store their data in a different format than in previous versions.
A migration tool provides a migration path for previous users of ConfigAdmin, Preferences, and
UserAdmin to migrate their data to the new platform. This migration occurs automatically at install time
and will migrate all old data in the current workspace.
Note that data will only be migrated if the bundle containing the data has a unique bundle symbolic
name. Also, when ConfigAdmin data is migrated to the new ConfigurationAdmin service, the location
property of any Configurations will be set to empty. This allows for the new Configurations to be
mapped to the correct bundle.
322 Lotus Expeditor: Developing Applications for Lotus Expeditor
Debugging and testing applications
You can use either the Expeditor Server or the Expeditor Toolkit’s Client Services Launcher to run and
debug applications. Typically, the debugging mechanism you have used in the past will be the easiest to
use for debugging plug-ins.
Regardless of the launch mechanism used for debugging, the critical requirements for successful
debugging are access to the source for your applications, and Java class files that contain debugging
information.
Local debugging and testing
There are two methods available for running and debugging applications in a local instance of the Lotus
Expeditor runtime. Both are equally capable of running applications, but they have individual capabilities
which may make one more preferable.
The Client Services Launcher is ideal if you are familiar with Eclipse Plug-in development tools. It is also
recommended if you are developing bundles that are not J2EE projects, as the Client Services launcher
lets you easily select all bundle projects in your workspace.
The Client Services server is ideal if you are familiar with Rational J2EE development tools. It is also
recommended if you are developing non-Client Services EJB or Web projects, as the Client Services server
can easily add them to the list of configured projects to run. Since the server framework coincides with
the existing RAD J2EE tools, it may be more natural to use if you are primarily doing J2EE development,
especially if you are running the project on both Lotus Expeditor and non-Lotus Expeditor runtimes.
Client Services Launcher
The launcher supports the ability to run and debug Client Services projects from your workspace. The
Client Services runtime launch extends the Eclipse runtime workbench launch. It is suggested that you
use the Client Services launcher rather than the Eclipse runtime workbench launcher for running Client
Services projects, since it automatically handles setting other parameters for the Lotus Expeditor runtime.
Perform the following procedure to run or debug a project using the Lotus Expeditor runtime launch:
1. Select either Run > Run... or Run > Debug... to run or debug using the Lotus Expeditor runtime.
2. Select Client Services under configurations, and select the New button to create a new configuration.
Note: If Client Services runtime configurations have already been created, you can directly select one.
3. On the main tab, ensure that the JRE is set to a desired Client Services supported JRE, such as
jclDesktop.
4. By default, the launcher selects all the external plug-ins and features from the default Client Services
target definition. Use the profile tab to change the plug-ins and features selected for this launch
configuration.
You can also control which language support plug-ins are included in the launch by using the
National Language Support check boxes on the Main tab. These allow you to select group 1, group 2,
and group 3 language support. Refer to “IBM language groups” on page 333 for a definition of these
language groups.
5. By default, the launcher selects all your workspace plug-ins and Client Services projects. To change
the project selection, select the Workspace Plug-ins section from the Plug-ins tab.
6. Select either the Run or Debug button to launch the runtime.
© Copyright IBM Corp. 2004, 2006 323
Note: If you choose to manually select plug-ins, do not include fragments from Linux in your
Windows environment, or fragments from Windows in your Linux environment. If both
operating system fragment types are included, the platform will not work correctly. To avoid
this problem, use the Profile tab to select your plug-ins.
Note: If your machine is disconnected from the network (Linux only), then you must manually add
the following entry to the /etc/hosts file:
127.0.0.1 <your_machine_hostname>
This allows the Web Container to function correctly when you are disconnected from the
network.
For more information on launch option, refer to the section Running a Plug-in of the PDE Guide.
For more information on debugging, refer to the section Using the Java integrated development
environment > Concepts > Local Debugging of the Developing Java Applications Guide.
Client Services Server
The server supports the ability to run and debug J2EE projects from your workspace, including Client
Services Web projects, Portlet projects, and Client Services Embedded Transaction as well as existing web
projects and EJB projects.
Note: You should be developing in the J2EE or Web perspective when using the server tools to run J2EE
applications.
Creating a server
Perform the following procedure to create a Expeditor Server:
1. Select New > Other... to bring up the New Wizard.
2. Select Server and click Next to bring up the New Server dialog
3. Select IBM > Client Services v6.1 as the server type and select either Next to choose a Target
Definition or Finish to use the default.
4. If modifying the profile, perform your selections on this page then select Finish.
5. A new server will be created with the default name Client Services v6.1 @ localhost and will be
visible from the Servers view.
Editing a server
You can edit a server’s definition by double clicking the server in the Servers view. To modify the server’s
target definition or selected features, navigate to the Profile tab. To modify the advanced features of the
launch, select Advanced on the main tab. For descriptions of the tabs and fields in this dialog, refer to
Running a Plug-in of the PDE Guide.
Adding projects to a server
Projects that are associated with a server are automatically loaded onto the Lotus Expeditor runtime
when it is started.
To view the list of J2EE projects associated with a server, or to modify the projects on the server,
right-click the server in the Servers view and choose Add and Remove Projects... to display the Add and
Remove Projects dialog.
If you are working with one J2EE project that you want to test, you can right-click the project and choose
Run on Server. The wizard adds that project to a new or existing server, and automatically starts the
server.
324 Lotus Expeditor: Developing Applications for Lotus Expeditor
In order to add a non-J2EE Client Services project to a server, you must go to the advanced editing screen
(refer to “Editing a server” on page 324). You will be able to select Client Services projects from the set of
Workspace plug-ins on the Plug-ins tab.
Starting a server
From the Servers view, right click the server and select Start to run the server using the Lotus Expeditor
runtime. Or, select Debug to debug using the Lotus Expeditor runtime.
Remote debugging and testing
This section describes methods for remote debugging and testing of projects.
In order to debug the Lotus Expeditor platform, you will need to launch it using specific debug options.
You can launch using either a Debug Launch Configuration from the Client Services launcher in the
toolkit, or by using the command line.
To launch a Lotus Expeditor platform using Client Services launch configuration in the toolkit, create a
Launch Configuration as described in “Client Services Launcher” on page 323. Be sure to use the Debug
> Debug... option in step 1.
You can also launch the Lotus Expeditor platform for debugging using the command line. Add the
parameters
-vmargs -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,address=zzzz
to the Expeditor launch command line, where zzzz is an available port on your system.
In the Eclipse based IDE, select Debug..., then create a new Remote Java Application launch
configuration. Enter the name of any project, and select a Connection Type of Standard (Socket Attach).
For the Connection Properties, you can use localhost as the host (or supply the IP address or host name
for a remote system), and specify the same port number as you specified when launching the Lotus
Expeditor platform.
Debugging and testing applications 325
326 Lotus Expeditor: Developing Applications for Lotus Expeditor
Packaging and deploying applications
This section assumes you have completed some level of code development and are interested in one of
the two situations below:
v You have completed code development and are now prepared to start constructing installation artifacts
to turn over to an administrator or IT team for distribution to client systems.
v You want to run your code in an actual installed instance of the client platform on your local
development system (and not within an instance launched from the workspace)
For information on constructing installation artifacts for distribution, refer to “Packaging applications for
distribution.”
For information on performing quick deployments of plug-ins to local instances for test purposes, refer to
“Deploying projects for local testing” on page 331.
Packaging applications for distribution
Updates to the client platform are provided in the form of features. Features may contain other features,
or a set of related plug-ins. The Update Manager component of the client platform handles the
installation of the features, and a user interface is provided to manage the installed features.
Features may be provided to the Update Manager by connecting to an update site, or by the included
Enterprise Management agent. Update sites organize features for installation. The Update Manager
typically connects to an update site to determine the features that are available for installation.
The Enterprise Management agent enables the client platform to be managed remotely. Administrators
create software distribution jobs that define the artifacts to be installed, and the Enterprise Management
agent handles the installation tasks.
Understanding application lifecycle
Of the three lifecycle extension points, only the lifecycle.application extension point is supported on
devices. In order to utilize lifecycle.application to start bundles, the developer must extend the
extension point com.ibm.rcp.lifecycle.application.startBundles and provide the application id and
bundle id attributes. The application id specifies the application that is associated with this extension,
while the bundle id field specifies the bundle to start when the associated application is started.
Below is an example of the OrderEntry web application making use of lifecycle.application:
<extension point="com.ibm.rcp.lifecycle.application.startBundles">
<application id="com.ibm.rcp.samples.orderentry.webapp.OrderEntryWebApp">
<bundle id="com.ibm.rcp.samples.orderentry.service" />
</application>
</extension>
The OrderEntry plug-in has it’s plug-in name as com.ibm.rcp.samples.orderentry.webapp and it’s
extension id for the extension point com.ibm.eswe.workbench.WctWebApplication is OrderEntryWebApp.
Together, they are used as the application id for the lifecycle plug-in. The attribute bundle id indicates a
bundle that needs to be started for the OrderEntry web application to run correctly.
For an eRCP application, identified by an org.eclipse.ercp.eworkbench.applications extension, an
extension id is not required. Therefore, the application attribute in this extension is used as the
application id for the lifecycle plug-in. Note that only the application id is used. Do not place the plug-in
name of the eRCP application in front of the application id. For example:
© Copyright IBM Corp. 2004, 2006 327
<extension point="org.eclipse.ercp.eworkbench.applications">
<application id="MySample.application" name="My Sample">
<views normal="mysample.views.SampleView" />
</application>
</extension>
Only MySample.application should be used as the application id for the lifecycle plug-in
Finally, if a developer wishes a bundle to be started when the workbench starts, the
lifecycle.application extension can associate to the workbench application id,
org.eclipse.ercp.eworkbench.
Understanding methods of installation
There are two mechanisms to install applications via features. The application can either be installed from
each system by using the Update Manager, or by using an enterprise distribution system.
Local installation
To enable local installation, you will need to provide an update site configuration to the platform. If you
provide an installation program, in addition to any other tasks that you perform, you should provide an
update site for the users to use to install your application. The update site could be provided on the
distribution media, or could be created on the hard drive of the system on which you execute the
installation program.
The customers will need to start their client platform, and use the Update Manager to connect to the site,
and install the application.
For more information on update sites, refer to the documentation Plug-in Development Environment Guide
> Getting Started > Update Sites.
Enterprise installation
In addition to a local installation process, you should also consider providing an installation process to
enable enterprise distribution. To enable the enterprise installation, you should clearly identify each file
that should be installed, and the appropriate installation location.
The client platform provides an Enterprise Management Agent that connects to Tivoli® Device Manager
provided by WebSphere Everyplace Device Manager.
Tivoli Device Manager provides for software distribution as well as configuration jobs to be applied to
the client platform. Tivoli Device Manager uses bundles as the distribution artifacts for the Enterprise
Management Agent.
While the Enterprise Management Agent is capable of accepting a distribution at a plug-in or bundle
level, it is strongly recommended that Eclipse update sites packaged as a bundle using the
NativeAppBundle tool be used as the distribution artifact.
The update site provided to an administrator for distribution is the same update site that is built for local
installation. Therefore, developers can create the same artifacts for enterprise installation as they would
create for local installation.
For more information on update sites, refer to the documentation Plug-in Development Environment Guide
> Getting Started > Update Sites.
Understanding the types of install artifacts
For any successful application installation, you need to provide the appropriate set of installation artifacts
from the following:
328 Lotus Expeditor: Developing Applications for Lotus Expeditor
Installer/Uninstaller
A program to handle installation (and uninstallation of your application) may be needed if you need to
do anything more than provide an update site to allow installation of your application.
Update site
An Update Site is the key mechanism to enable installation of the application.
For more information on update sites, including how to create one, please see the Getting Started >
Update Sites section of the PDE Guide.
Features
A feature is the only level of installable unit that exists. You cannot choose to install only certain plug-ins
from a feature. The Plug-in Development Platform provides wizards for creating Features.
Once the feature project is created, you can make additional updates to the feature definition by editing
the feature.xml file.
Additionally, if a feature has already been created, you can import the feature as a binary project into
your workspace. Select File > Import > External Features to launch the wizard. Enter the name of an
update site to browse, and you can select the features to import.
Importing External Features is useful if you are attempting to create an Update Site, and someone else
has already created the features that need to be installed.
To enable Lotus Expeditor users to use the Scan for Updates action within the Application Management
dialog, you must add an update URL to the feature.xml file. Using the Feature Manifest Editor, on the
Overview tab, right click on the Update URLs entry in the Feature URLs section, then select New >
Update URL. You can then enter the appropriate update site information in the Properties view that is
displayed.
If no update URLs are provided in the feature.xml file, the Scan for Updates action will still be
displayed as an available action, but will not return any update information.
Lotus Expeditor uses the eclipse directory to contain all of the features and plug-ins from Eclipse. The
rcp directory contains the remainder of the features and plug-ins that are part of the client platform. The
shared directory can be used to install features that are to be used by all users on the client system.
Additionally, there is a feature install directory in the workspace associated with each user.
When new versions of features are provided, they will be installed into the same directory as the
previous version. The installation directory for a feature upgrade cannot be changed.
Versions for features are specified using major.minor.service.qualifier. For example, a version of 4.0.1
has a major version of 4, a minor version of 0, and a service version of 1. An equivalent version is a
version that differs first at the service level. A compatible version is a version that differs first at the
minor level. For example, using our version 4.0.1 above, a version of 4.0.2 would be an equivalent
version, since the service value is the first value that changed. A version of 4.1.2 would be a compatible
version, since the minor value is the first value that changed.
The Scan for Updates capability provided as part of the Application Management dialog enables updates
of only equivalent or compatible versions, according to the preferences selected in the Manage >
Preferences > Install/Update dialog. The default value is for Equivalent feature updates.
A feature version that changes at the major level, for example, a version 5.0.0 that would replace our
version 4.0.1, must be installed through the Application > Install mechanism. Scan for Updates will not
show this feature as being available.
Packaging and deploying applications 329
For additional information on versioning, refer to the Feature manifest section of the Platform Plug-in
Developer’s Guide, and the Getting Started > Features section of the PDE Guide .
Plug-ins
Plug-ins provide the core logic capability for the application, but they must be grouped into features in
order to be installed via the Update Manager.
If you choose to use a Feature project, or an Update Site project within your workspace, you will need to
provide the plug-ins within the workspace as well. These plug-ins can either be in source form - if you
are responsible for developing the plug-ins - or they can be in binary form - if another person will be
providing these artifacts to you. If another person is providing the artifacts, then you can import these
artifacts as binary plug-ins so that you can use the Feature and Update Site project capabilities. Select File
> Import > External Plug-ins and Fragments to launch the Import Wizard. Once plug-ins exist in
projects within the workspace, you can use the Feature Manifest Editor to add plug-ins to the Feature,
and the Site Manifest Editor to add features to the Update Site.
For more information on plug-ins, refer to the Getting Started > Basic Plug-in Tutorial section of the
PDE Guide
Native libraries
Plug-ins may use the Java Native Interface (JNI) to access native library code. Native libraries are by
convention placed in a fragment specific to an operating system or architecture to the plug-in providing
the Java classes (native libraries can all be placed within a single plug-in). The organization of the
fragment is the following:
directory\fragment.xml
directory\os\<osgi.os>\<osgi.arch>\*.dll or *.so
Where directory is typically <fragment_name>_<fragment_version>
The value of the osgi.os value above is the value corresponding to the value of the Java System property,
osgi.os. The value of the osgi.arch corresponds to the value of the Java System property, osgi.arch.
The osgi.os value is generally based on the os.name property value, although the value of osgi.os may
alias a set of values for os.name. For example, the osgi.os value of win32 is used to represent an os.name
value of Windows 2000.
The osgi.arch value is generally based on the os.arch property value.
For the runtime environment, since it is targeting Windows 2000, Windows XP, and Linux, the values of
osgi.os would be either win32 or linux.
The value for arch will be x86.
As an example, the DB2 Everyplace component requires native libraries. The plug-in id is com.ibm.db2e
and the version is 8.2.0. The native libraries required for Windows reside in a fragment for this plug-in,
com.ibm.db2e.win32_8.2.0. Within this fragment, the directory os\win32\x86 contains the DLLs required
by DB2 Everyplace.
If there is a single native library required by the plug-in, and it is loaded via the System.loadLibrary()
method, then packaging the fragment or plug-in in this organization is sufficient.
If there are multiple native libraries required by the plug-in, and each one is individually loaded by the
System.loadLibrary() method, and the DLLs do not depend on each other or statically or dynamically
load each other, this organization is sufficient.
330 Lotus Expeditor: Developing Applications for Lotus Expeditor
If there are multiple native libraries required by the plug-in, and there are dependencies between the
libraries such that a System.loadLibrary() method call loads one of the libraries, but that library
statically or dynamically loads the other libraries, then an additional step is required. Because these
directories are not provided on the System PATH or LIBPATH, the operating system is unable to handle the
loading of the shared library. To cause these directories to be added to the system PATH or LIBPATH so that
the operating system can load these libraries, update the rcpinstall.properties file to add the
appropriate plug-in or fragment directory to the library.path.prepend or library.path.append
properties.
As an example, there are multiple native libraries required for DB2 Everyplace. Because these libraries
have dependencies between them, they must be present on the system PATH or LIBPATH. Therefore, the
fragment directory, C:/Program Files/IBM/Lotus/Expeditor/rcp/eclipse/plugins/com.ibm.db2e.win32.x86_8.2.1.20050620/ os/win32/x86, has been added to the library.path.append
property in the rcpinstall.properties file.
Code that behaves differently between operating systems that are aliased to win32, such as Windows 2000
or Windows XP, should be handled within the native libraries, and should not be placed into separate
plug-ins/fragments, or separate osgi.os directories, since changing the operating system name for the
runtime environment will prevent proper loading of components such as DB2 Everyplace or SWT.
Configuration file updates
Most of the changes required when installing an application can be accomplished by providing the
appropriate plug-ins. However, some of the configuration files used by the Lotus Expeditor platform may
need to be updated. Changes to these files can be made either by installation programs, or by using an
Install Handler associated with the application Feature being installed. An Install Handler is invoked at
certain checkpoints within the installation process. During the applicable checkpoints, code provided
within the Install Handler can make the required changes to the configuration files.
Installation instructions
You should make sure that any installation instructions for your application are clearly provided to your
customers. Your customers will need to know, for example, the location of the update site from which to
install the application, any preferences that may need to be updated, how to start your application, and
so on.
Enterprise distribution instructions
You should also consider the needs of enterprises to use an enterprise distribution mechanism to install
the application. Any artifacts that must be installed should be clearly identified. This will allow the
administrator to easily define the necessary steps to distribute your application. You should supply
Eclipse update sites to the enterprise administrator to allow for distribution of your application.
Using Ant tasks to build a deployable bundle
ANT is a scripting framework often used in task automation for Java. It is commonly used to automate
the building of code from source into the resulting binary artifacts.
The Plug-in Development Environment (PDE) provides a framework for building features and plug-ins
using ANT technology. The automated PDE build technology will dynamically create build scripts based
upon the build.properties file associated with a project.
Deploying projects for local testing
In order to run your application plug-ins in a locally installed instance of the client platform, and not an
instance started from the workspace application launcher, you will need to export your plug-ins to the
local file system. The Client Services runtime does not support the Deploy action available through the
project’s pop-up menu. Rather, you must export the project as discussed below.
Packaging and deploying applications 331
This section covers how to export your projects to the local file system for local testing. The steps
suggested here provide the quickest route for exporting your projects to the local file system. Please note
the following:
v Using this method to deploy your plug-ins to the local instance does not allow the plug-ins to be
removed through the Application Management menu
v Removing the plug-in requires that you use local system tools to physically remove the directory or
directories that contain the plug-ins
v The preferred method for creating binaries for distribution to others is to create features and/or update
sites (as covered in “Packaging applications for distribution” on page 327).
v The toolkit does not support the ability to right-click a project, and deploy it from the resulting
submenu.
Exporting plug-ins using the PDE
The PDE provides an Export wizard that will allow you to export a Client Services plug-in project to a
local runtime for deployment.
Select File > Export > Deployable Plug-ins and fragments. This wizard will allow you to select multiple
projects for deployment. You will also be able to select different output formats:
v A single ZIP file containing all plug-ins
v Individual JARs for each plug-in for use on an update site
v A directory structure
When using the directory structure option, you should refer to an eclipse directory, such as
<installation_root>/rcp/eclipse. The export process will automatically put your plug-in into the
plug-ins directory.
Notes:
1. While your plug-in may build successfully in the workspace, the Export mechanism runs a
separate compilation of your plug-in. If you have added entries to the Java build path for your
plug-in, you should also make sure that the build.properties file in your plug-in project
contains any required extra JAR files in the jars.extra.classpath property. The
build.properties file can be updated either through the Build Properties Editor or the Bundle
Manifest Editor.
2. If you will be debugging these plug-ins from another IDE, then you should make sure that
you check the Include source code option in the Export Wizard, and that the Compile source
with debug information option is selected in the Build Options available in the Export
Wizard.
332 Lotus Expeditor: Developing Applications for Lotus Expeditor
Globalizing your application
You can globalize an application that you build on the Lotus Expeditor platform by using the
International Components for Unicode (ICU) technology. ICU4J is a set of Java classes that extend the
capabilities provided by the J2SE class libraries in the areas of Unicode and internationalization support.
The ICU4J classes (at version 3.4.5) are provided in the com.ibm.icu_3.4.5.jar, and enable you to:
v Support multiple locales
v Support bidirectional text layouts
v Create translatable plug-ins
The following Web site provides more information about the icu4j package: http://www-306.ibm.com/software/globalization/icu/index.jsp
Support for multiple locales
A locale represents a geographic place. A user‘s geographic location implies certain preferences for
operating system and application settings, such as language character sets, date, time, and currency
formats, and the direction in which text is displayed.
The default locale for a Lotus Expeditor application is the same as the locale for the operating system of
the machine on which the client is running. If you design your application to support multiple locales,
the user can specify -nl <locale code> as a command line option when starting the client to change the
default locale for the client application running on their machine.
Things to keep in mind when implementing support for multiple locales:
v Call java.util.Locale.getDefault() to get the current locale. If a user supplies the -nl parameter
when starting the client, it resets the default locale value. Calling java.util.Locale.getDefault()
would return the newly specified locale code.
v Use icu4j whenever possible to generate locale-sensitive data dynamically. The ICU4J classes provide
the following objects among others:
– DateFormat
– MeasureFormat
– MessageFormat
– NumberFormatv Do not expect the same behavior you witness in the locale you are developing in to occur in another
locale. For example, ″i″.to UpperCase() does not return ″I″ in the Turkish locale.
v Keep in mind that sort orders vary in each locale. Call com.ibm.icu.text.Collator to compare
international text.
Note: The Javadoc information for the icu4j.jar package is available from the following Web site:
http://oss.software.ibm.com/icu4j/doc/index.html
IBM language groups
IBM identifies the following language groups:
Table 25. Group 1 languages
Locale code Language
de German
© Copyright IBM Corp. 2004, 2006 333
Table 25. Group 1 languages (continued)
Locale code Language
es Spanish
fr French
it Italian
ja Japanese
ko Korean
pt_BR Portuguese (Brazil)
zh Chinese (Simplified)
zh_TW Chinese (Traditional)
Table 26. Group 2 languages
Locale code Language
ar Arabic
cs Czech
da Danish
el Greek
fi Finnish
hu Hungarian
iw Hebrew
nl Dutch
no Norwegian
pl Polish
pt Portuguese
ru Russian
sv Swedish
tr Turkish
Table 27. Group 3 languages
Locale code Language
be Belorussian
bg Bulgarian
ca Catalan
et Estonian
hi Hindi
hr Croatian
is Icelandic
lt Lithuanian
lv Latvian
mk Macedonian
ro Romanian
sk Slovak
334 Lotus Expeditor: Developing Applications for Lotus Expeditor
Table 27. Group 3 languages (continued)
Locale code Language
sl Slovenian
sq Albanian
sr Serbian
th Thai
uk Ukrainian
Supporting preferred fonts and bidirectional layouts
Each locale has a preferred set of fonts. Display your application text using these fonts whenever possible
to maintain a unified look and feel for the user. Bi-directional (bi-di) support is important for Arabic and
Hebrew.
To support preferred fonts and bi-di:
1. Instead of using a hard-coded font to format the text you display in the application, retrieve the font
that is preferred for a specific locale by calling the org.eclipse.jface.resource.JFaceResources class.
The JFaceResources class provides the following methods for retrieving preferred fonts:
v getBannerFont()
v getDialogFont()
v getHeaderFont()
v getTextFont()
v getViewerFont()2. Test your plug-in in RTL mode to be sure it responds correctly.
Creating translatable plug-ins
Customize your plug-in to display appropriately to an international audience by:
v Separating out all hard-coded text strings that appear in the user interface, in error messages, message
boxes, or titles that display in title bars
v Enabling the date and number objects you use to be formatted based on the user‘s preferred locale
To ensure that a plug-in is translatable, do the following:
1. Define all translatable strings in a .properties file associated with the plug-in. For example, instead
of defining the name of the plug-in in the plugin.xml file, define it in a file called plugin.properties,
which associates the .properties file to the plugin.xml file. For example, define the name of the
plug-in in the plugin.xml file as follows:
<plugin name="%plugin.name"
2. In the plugin.properties file, include the following text to define the %plugin.name keyword:
plugin.name = My Super-useful Plugin
3. Use the Rational Software Development Platform or a similar product to search your source code for
all translatable strings. Replace each string with a keyword and define the keyword in a .properties
with the same name as the file that contained the translatable string.
4. Identify the locale-specific objects in your plug-in, then organize them into categories and store them
in different ResourceBundle objects accordingly. For example, you can store a series of String objects in
a PropertyResourceBundle, which is backed up by a set of properties files, or you can manage all
locale-specific objects using a ListResourceBundle, which is backed up by a class file. Though the
ListResourceBundle object requires you to code and compile a new source file to support any
Globalizing your application 335
additional locales, ListResourceBundle objects are useful because unlike properties files, they can store
any type of locale-specific object, not just text objects.
5. Use the standard Java library classes that localize the formatting for numbers, dates, times, and
currencies. None of these object types can be displayed or printed without first being converted to a
String. The formatting classes enable you to use the proper format for the user‘s locale when
converting an object into a String object. For example, if you use the factory methods provided by the
icu4j.jar package NumberFormat class, you can get locale-specific formats for numbers, currencies,
and percentages. The DateFormat class provides predefined formatting styles for dates and times that
are locale-specific and easy to use.
6. Create a plug-in fragment to contain all .properties files associated with a specific language group.
IBM uses the following convention when naming the language fragments:
Language pack fragment containing IBM Group 1 languages =
<plugin id>.nl1_<version>
Language pack fragment containing IBM Group 2 languages =
<plugin id>.nl2_<version>
7. Provide a feature to group the Language packs you are including in your application. Use the
following convention when naming the features:
Language pack feature containing IBM Group 1 languages =
<feature id>.nl1_version
Language pack feature containing IBM Group 2 languages =
<feature id>.nl2_version
Specify any translatable text in the feature.xml file in an associated feature.properties file.
See the eclipse.org Web site for more details on internationalizing a plug-in at http://www.eclipse.org/articles/Article-Internationalization/how2I18n.html.
336 Lotus Expeditor: Developing Applications for Lotus Expeditor
Securing applications and data
The Lotus Expeditor platform is a secure platform that protects your application data. Single sign-on with
the operating system capabilities are built into the platform by default.
It secures the applications and application data running on the client by doing the following:
v Limiting access to Target Features to only users with valid authentication credentials.
v Protecting user credentials by storing authentication information, such as user names and passwords,
in an encrypted key store.
By default, the client uses the key store provided by IBM’s JCE provider on jclDesktop. This
implementation of the Java Cryptography Extension (JCE) uses a password-based encryption with Triple
DES (Data Encryption Standard) to protect the store’s contents. The key store also provides storage for
database and account-related passwords created using the PBEKey (Password Based Encryption Key)
interface. The location of the platform key store is specified as the value of the keystore.url security
property set in the java.security file. If no value is specified for the keystore.url, the location defaults to
the following directory:
WORKSPACE\.metadata\.plugins\com.ibm.rcp.security.auth\.keystore.jks.J9
where WORKSPACE is the absolute path to the user’s workspace. If no key store exists, a key store is
created when an application calls the login method. The SecurePlatform class provides methods that you
can use to access and log into the platform key store.
You can implement authentication mechanisms in client applications using the following APIs:
v Accounts API -- Recommended. Enables you to store, access, and use properties that are required to
make a connection to, and communicate with, local and remote services, including user passwords,
which it directly accesses using the AccountsLoginContextService. Use the Accounts API to access the
key store and make password changes outside of the login process. The Accounts API also simplifies
HTTP and J2EE-FORM logins to a remote WebSphere Portal server.
v Java Authentication and Authorization Service (JAAS) APIs -- Not recommended. Connects to servers
and manages user information, including validated passwords, which it handles using a Subject object.
Uses login modules to access the key store and retrieve or store passwords as part of the login process.
You can use the existing LoginConfigurations (HTTP or J2EE-FORM) for HTTP and J2EE-FORM logins
to authenticate with remote servers. The challenge of using straight JAAS APIs with the provided login
configurations is that the Subject object must contain specific classes for the login modules to work. If
you use the Accounts API, it handles the Subject object interaction for you, while enabling you to
directly access the Subject and its contents, if necessary.
Accounts framework
The Accounts framework enables you to store, access, and use properties that are required to make a
connection to, and communicate with, a local or remote service.
Some examples of accounts include:
v An HTTP account which is used to connect to a web based service. This account contains a URL for
the location of the service, and a user name and password to log onto the service.
v An Instant Messaging account which is used by an instant messaging client to connect to an IM server,
such as IBM Lotus Sametime. This account includes a server name, and a user name and password to
connect to the instant messaging server. The account could also be used to store user preferences such
as the text people see when the user’s status is “Away.”
© Copyright IBM Corp. 2004, 2006 337
An account can store both connection properties, and properties or preferences specific to that connection.
The Accounts API provides a way to get, add, update, remove, and listen for changes to an account. The
component ensures that once user accounts have been created, either manually by a user or automatically
during provisioning, users will only ever have to enter one password to access all of the services for
which they have accounts. The Accounts component also provides a common user interface from which
users can see and change account information and passwords for all of their services.
Adding accounts
The Account class represents an account and all of its data.
An account contains data necessary to connect to a local or remote service. The account class stores data
as key and value pairs. Several keys are defined in this class as public static final Strings. The keys UID,
NAME, DESCRIPTION, and TYPE must be defined for all account objects. (DESCRIPTION is initially set
to an empty String). A random UID is generated for an account object if you do not provide a UID when
you create it. All other required keys depend on the TYPE key defined for the account, but in general, are
optional.
The Account class contains the following two data stores:
v Persistent store – Writes properties to a database, file, or other store when a call to add or update
account is made.
v In memory store – Properties only exist in this class. Stores data that is only relevant for a given user
session without writing it to disk. Useful for securely handling information such as LTPA cookies.
If you do not specify a store when you create the account, the account data is stored in persistent
properties, by default.
With the exception of operations made using the AccountsLoginContextService, changes to the Account
object are not made permanent until you make a call to update or add the account.
To create an account, perform the following steps:
1. Use the AccountsManagerFactory to access a singleton instance of the platform implementation of the
AccountsManager interface.
AccountsManagerFactory.getAccountsManager();
2. Create the account by doing one of the following:
v To add an account object based on the standard Account class to the persistent store, use the
following methods:
addAccount(Account account)
or:
newAccount(java.lang.String id)
where id is either null or set to DEFAULT_ACCOUNT.
v To create an account object based on a custom account class, use the following method:
newAccount(java.lang.String id)
where id identifies the TYPE property of the custom account class that you defined using the
Accounts extension point. If AccountsManager cannot find a class that matches the given TYPE,
then it uses the base Account class to create the new account object.3. Optional: The Account object returned by the AccountsManager contains all the properties of the
account. You can change the properties using the following methods inherited from the
com.ibm.rcp.security.auth.AuthProperties class (There are get methods that correspond to each of
these set methods):
v setName
v setType
338 Lotus Expeditor: Developing Applications for Lotus Expeditor
v setDescription
v setAuthType
v setProperty
v setProperties
The UID, NAME, DESCRIPTION, and TYPE properties must have values, but other properties are
optional.
Retrieving accounts
You can retrieve an existing account using methods provided by the AccountsManager class.
To retrieve an existing account, perform the following steps:
1. Use the AccountsManagerFactory to access a singleton instance of the platform implementation of the
AccountsManager interface.
AccountsManagerFactory.getAccountsManager();
2. Retrieve a single account by calling one of the following methods:
v getAccount(java.lang.String uid) – Returns an Account object with the given UID from persistent
store.
v getAccountByName(java.lang.String name) – Retrieves the account with the given name or returns
null if no such account exists.3. Retrieve multiple accounts, in a java.util.List object, using one of the following methods:
v getAccounts(java.lang.String field, java.lang.String value) – Returns all accounts that have the
property field and value that matches property value for that account.
v getAccountsByServer(java.lang.String server) – This method does a partial match on HTTP(s) Web
addresses. It first searches for the exact string, and if one or more accounts are found, it returns
those. If no accounts are found, then it breaks down the path of the Web address and tries each
directory separately. For example, if an account has a server name of http://www.ibm.com, a query
String of http://www.ibm.com/lotusnotes returns that account. If you only want exact matches,
call getAccounts(Account.SERVER, <server_name>).
v getAllAccounts() – Returns all of the accounts in the data store, or an empty List if no accounts
have been added.
Updating accounts
You can update existing accounts using methods provided by the AccountsManager class.
When you call the updateAccount method of AccountsManager, you completely replace the existing
account object referenced by the method. Be sure to retrieve the account you want to update and make
changes to it before calling updateAccount().
To update an account, perform the following steps:
1. Use the AccountsManagerFactory to access a singleton instance of the platform implementation of the
AccountsManager interface.
AccountsManagerFactory.getAccountsManager();
2. Retrieve the existing version of the account object you want to change and make the properties
changes.
3. Use one of the following methods to update the account:
v updateAccount(Account) – Updates an existing Account object in persistent store. (completely
replaces existing account object)
v newAccount(java.lang.String id) – Use this method to make changes to an account object and then
change the UID to match that of the existing account.
Securing applications and data 339
Listening for account changes
Add an implementation of the AccountChangeListener to listen for changes to existing accounts or for
accounts being created.
Once an Account object is returned from one of the getter methods in AccountsManager, its properties are
not effected by calls made to updateAccount() using another instance of the Account. To keep an Account
instance up to date, add an implementation of AccountChangeListener An AccountEvent is passed to the
listener when a change happens specifying which account has changed, and what type of change took
place. For example, if an account has registered a change listener, the following method is called when an
Account is changed:
accountChanged(AccountEvent event)
To listen for account changes, perform the following steps:
1. Use the AccountsManagerFactory to access a singleton instance of the platform implementation of the
AccountsManager interface.
AccountsManagerFactory.getAccountsManager();
2. Add an AccountChangeListener to the account you want to monitor.
addAccountChangeListener(AccountChangeListener, Account)
3. To find out what change occurred after an event, call getEvent() to retrieve the AccountEvent, which
contains the event details.
4. When you no longer want to monitor an account, remove the listener.
removeAccountChangeListener(AccountChangeListener listener)
Implementing a custom account type
You can extend the Account class using the Accounts extension point.
Every account has an account type. Each account of a given type can either use the base Account class
provided by the API or create and register a class which extends the Account class and provides
additional functionality.
To implement a custom account type, perform the following steps:
1. Extend the Account extension point and define the required attributes.
2. Write a class that extends the Account class and provides the added functionality you want to provide
in the new account.
3. To implement an object of this type, use the newAccount(java.lang.String id) method and pass the
account TYPE as the id parameter.
Managing secure passwords
The Account class provides access to the AccountsLoginContextService. The AccountsLoginContextService
integrates with the Java Authentication and Authorization Service (JAAS) and enables you to get, set and
remove secure account passwords.
The AccountsLoginContextService uses the platform implementation of the Java Keystore to securely
store and encrypt passwords. To add, remove and update passwords the Account must contain a value
for the Account.CREDENTIAL_ID property. A random value is generated when a new Account object is
created. The credential id is an alias to the password and if two or more Accounts share a password, they
must have matching credential ids.
The stage needs to be set just so.
1. Retrieve the account with the password you want to manage.
2. Initialize the service.
340 Lotus Expeditor: Developing Applications for Lotus Expeditor
account.getLoginContextService()
A call made on this interface uses the information in the Account object, such as user names and Web
addresses along with the password stored in the keystore, if it exists. It does not use the Account
properties from persistent store, which allows the Account object to be modified without affecting
other users of the same account.
3. Manage the password using one of the following methods. These methods communicate directly with
the Key Store, so any changes made are permanent and no call to updateAccount() or addAccount() is
required:
v getPassword() – Gets the password as a String.
v setPassword(String) – Sets a String password.
v removePassword() – Removes the password.
Note: Remove passwords using the remove() method carefully. The service does not check to see if
other accounts share the password before performing the removal. If you call
removeAccount() in the AccountsManager to remove a password, the framework removes
the password only if no other accounts are sharing it.
SSL and accounts
You can also create and SSL connection using accounts. If the Account has an HTTPS URL scheme, it
establishes a connection when login is called on it’s associated LoginContext. The caller of accounts can
then use data in the JAAS subject (returned by the login call) to continue the connection.
Alternatively, after creating an Account with an HTTPS server specified, you can then open a connection
to the HTTPS server using the java.net.URLHandler. The URLHandler will use the data from Accounts to
open an SSL connection to the server.
Login configurations
A login configuration tells the application which LoginModule to use to authenticate users.
A LoginModule describes the interface implemented by authentication technology providers. You do not
need to understand the inner workings of the LoginModule interface to use the modules provided by the
client in your application. However, if you want to write your own login module, refer to the JAAS
LoginModule Developer’s Guide for more information. You can find the Guide at the following Web site:
http://java.sun.com/j2se/1.4.2/docs/guide/security/jaas/JAASLMDevGuide.html
The LoginConfigurationProvider tells the application which configuration to use. It implements the
following method:
public AppConfigurationEntry[ ] getAppConfigurationEntry( String configName)
This method returns an array of AppConfigurationEntry objects. An AppConfigurationEntry object
represents a login module. For example, if the configName parameter specifies a configuration that
defines three login modules, the method would return an array of three AppConfigurationEntry objects.
If the provider does not have a configuration for the specified configName, the method returns null and
the system moves to the next configuration provider. If all of the configuration providers return null, a
LoginException is thrown.
LoginModules occassionally need to communicate with the user of the application to obtain
authentication information or to provide status information to the user. LoginModules use a
CallbackHandler to do so. Applications implement the CallbackHandler interface and pass it to the
LoginContext, which forwards it to the LoginModule. By default, Lotus Expeditor Client ships with one
CallbackHandler for use during platform login. The unique identifier of this CallbackHandler is
Securing applications and data 341
com.ibm.rcp.security.auth.ui.defaultKSCallbackHandler. This CallbackHandler prompts the user for
information using an org.eclipse.jface.dialog.TitleAreaDialog. If the callback handler is passed one
PasswordCallback parameter, the resulting dialog box contains one field, labeled Password. If the callback
handler is passed two PasswordCallback parameters, the resulting dialog box contains two fields, labeled
Enter password and Confirm password. You can specify the title and image to display in the dialog box
using Eclipse themes.
Logging into the platform
Lotus Expeditor Client uses the Java Authentication and Authorization Service (JAAS) to provide a
configurable and flexible platform login experience.
JAAS is a Java implementation of the standard Pluggable Authentication Module (PAM) framework and
supports user-based authentication, which means it reliably and securely determines who is currently
executing Java code, regardless of whether the code is running as an application, an applet, a bean, or a
servlet, and supports user-based authorization, which means it ensures users have the access control
rights or permissions required to perform the actions they are trying to perform.
For more information about JAAS, refer to http://java.sun.com/products/jaas/overview.html.
The default client personality automatically logs into the platform when a user starts the client. If you are
using a different personality, you can use the SecurePlatform class to log in. The SecurePlatform class
provides methods to access and log into the platform keystore. It uses the KeyStoreLoginModule to
unlock the keystore. The KeyStoreLoginModule is a non-public login module. Before you implement the
login module, you must determine when you want to initialize the platform login. You can code it to
occur at any time during a client session, but the following two times are most commonly used:
v At startup -- Only grants users access to the Workbench if their login is successful. Otherwise, the
platform exits. This method is useful when the applications installed on the client contain default
views that display secure information. For example, if the default application is an e-mail application
that displays the Inbox view when a user first opens the application.
v Lazily -- Login occurs only when access to the platform key store or the JAAS Subject object is
requested. This method is useful when the applications installed on the client do not display secure
information by default. For example, if the default application is a word processing application that
does not display any documents when a user first opens the application.
To log into the client using the default platform login module, perform the following step:
Log into the client using the SecurePlatform class.
import com.ibm.rcp.security.auth.SecurePlatform;
public void doLogin() {
try {
SecurePlatform.getLoginContext().login();
}catch (LoginException e) {
// Login failed... exit platform or take corrective action
}
}
After you log into the client, you can use the Accounts API. You can use the
AccountsLoginContextService provided by the Accounts API to call the PBEKeyReaderModule and
PBEKeyWriterModule classes to read and write passwords to the key store. The PBEKeyReaderModule
and PBEKeyWriterModule are non-public classes.
Logging into remote servers
To log into a remote server, you can implement your own login module and configuration or you can use
one of the JAAS login modules provided by the Lotus Expeditor.
342 Lotus Expeditor: Developing Applications for Lotus Expeditor
The following login modules are provided by the client:
v HTTP basic authentication
v Form-based authentication
Using HTTP basic authentication
HTTP basic authentication is an authentication method that requires only a user name and password to
authenticate users who are transferring data over the HTTP protocol.
When you implement the HTTP basic authentication login module, the PBEKeyReaderModule reads the
password from the key store. If the credentials are not found in the inputs supplied by the caller, the
login module invokes the callback handlers supplied by the caller to get the user name and password.
The login module persists the passwords it retrieves from the callback handler in a shared list maintained
by the calling account and updates the user name in the AuthProperties object in the subject. The
HTTPBasicLoginModule validates the password and then the PBEKeyWriterModule writes the validated
password to the key store.
To implement HTTP basic authentication, perform the following steps:
1. Create an account or retrieve an existing account.
2. Call account.setProperty() to set the value of each of the following properties:
v SERVER – Complete URL for the server, containing the protocol (HTTP or HTTPS), domain, path,
and optionally, the port.
v USER_NAME – The http user name.
v CREDENTIAL_ID – Alias which references a password in the keystore. If the password does not
already exist, it is created and this value is set as its alias. Accounts sharing the same password
must have the same credential id.
v MASTER_PROPS (Optional) – The UID of another Account. Master properties are useful when two
or more accounts are accessing services that use the same user directory and share more than just
passwords. When account.getLoginContext() is called the “master” account is used to authenticate
instead of the account making the method call. The “slave” account can then access the “master”
account’s authenticated credentials through the Subject and use them to communicate with the
service. If this value is set, only the SERVER property needs to have a value; the login module
retrieves values for the other properties from the Master account.
Note: You do not need to set the value of the AUTH_TYPE property. ″HTTP″ is the default value.
3. Log in using the specified account.
account.getLoginContext().login();
4. Use the following method to extract the user name from the subject:
String username =
((AuthProperties)subject.getPublicCredentials(AuthProperties.class).
toArray()[0]).getProperty(AuthProperties.USER_NAME);
The getSubject() method returns the authenticated Subject which contains a user name and password
as well as an optional LTPA or other token.
Using form-based authentication
The form-based authentication module provided with the client performs form based logins to remote
WebSphere Portal servers.
Form-based authentication is one of the standard authentication mechanisms in the J2EE architecture. It is
an authentication mechanism that uses a custom login form supplied by the application to collect a user
name and password. The user supplies a user name and password. These are passed to the WebSphere
Application Server’s user registry to be matched with information stored there. If the user name and
password supplied fails to match the information stored in the user registry, the login form reappears.
Even if the user manually enters an HTTP request for a resource, she is redirected to the login form. The
Securing applications and data 343
user must enter a valid user name and password before she can successfully access any system resources.
If the user name and password succeeds in matching the information stored in the user registry,
authentication is successful.
The form-based authentication login module also supports single sign-on, which means that users only
have to log into the remote server once and can access any services available from that server for which
they have permissions. If Global security is enabled on the WebSphere Application Server and
Lightweight Third-Party Authentication (LTPA) is specified as the authentication mechanism, then when
the user authentication succeeds, the form based authentication login module returns an LTPA token.
When you implement the form-based authentication login module, the PBEKeyReaderModule reads the
password from the key store. If the credentials are not found in the inputs supplied by the caller, the
login module invokes the callback handlers supplied by the caller to get the username and password. The
login module persists the password retrieved from the callback handlers in a shared list maintained by
the calling account and updates the user name in the AuthProperties object in the subject. The
J2EEFormLoginModule validates the password, and then the PBEKeyWriterModule writes the validated
password to the key store. Additionally, this login module adds an object of
com.ibm.rcp.security.auth.SingleSignonToken type, which contains the LTPA token, to the list of private
credentials in the Subject.
To implement form-based authentication, perform the following steps:
1. Create an account or retrieve an existing account.
2. Call account.setProperty() to set the values of each of the following properties:
v SERVER – Complete URL for the server, containing the protocol (HTTP or HTTPS), domain, path,
and optionally, the port.
v USER_NAME – The HTTP user name.
v CREDENTIAL_ID – An alias which references a password in the key store. If the password does
not already exist, it is created and this value is set as its alias. Accounts sharing the same password
must have the same credential id property value.
v AUTH_SERVER -- The URL used for authentication only. This can be a complete URL like the
SERVER value, or it can be a path which is appended to the base domain of the SERVER value.
Callers can just use this property as long as it contains both the context URL and the host name.
v AUTH_TYPE -- Tells JAAS which login configuration to use. This property must be set to
J2EE-FORM. The default value is HTTP.
v MASTER_PROPS: (Optional) – The UID of another Account. Master properties are useful when two
or more accounts are accessing services that use the same user directory and share more than just
passwords. When account.getLoginContext() is called the “master” account is used to authenticate
instead of the account making the method call. The “slave” account can then access the “master”
account’s authenticated credentials through the Subject and use them to communicate with the
service. If this value is set, only the SERVER property needs to have a value; the login module
retrieves values for the other properties from the Master account.3. Log in using the specified account.
account.getLoginContext().login();
4. Use the following method to extract the LTPA token from the Subject:
SingleSignonToken token =
(SingleSignonToken)subject.getPrivateCredentials(SingleSignonToken.class).
toArray()[0];
The getSubject() method returns the authenticated Subject which contains a user name and password,
as well as an optional LTPA or other token.
344 Lotus Expeditor: Developing Applications for Lotus Expeditor
Using platform single sign-on
Single sign-on (SSO) authenticates users by prompting them for a user name and password a single time.
Enabling platform single sign-on gives users secure access to the platform key store without displaying
additional authentication prompts.
To implement platform single sign-on, perform the following steps:
1. In the plugin_customization.ini file in the com.ibm.rcp.platform.personality.branding plug-in, set the
value of the following preferences to true:
v com.ibm.rcp.security.auth.ui/ssoAllowed – Boolean value. Determines whether or not users have
the option of using single sign-on. You can set this preference value during the client installation or
later using a managed setting. This preference should not be surfaced to users.
v com.ibm.rcp.security.auth.ui/ssoEnable – Boolean value. Determines whether or not users have the
option of turning single sign-on on or off. If set to true, single sign-on can be used. If set to false,
single sign-on is disabled. The value of this preference is relevant only if ssoAllowed is set to true.
For example:
com.ibm.rcp.security.auth/loginEnabled=true
com.ibm.rcp.security.auth.ui/ssoAllowed=true
2. In the plugin_customization.ini file in the com.ibm.rcp.platform.personality.branding plug-in, set the
value of the following preference to SSO-KS:
v com.ibm.rcp.security.auth/loginConfigName
For example:
com.ibm.rcp.security.auth/loginConfigName=SSO-KS
3. Log into the client using the SecurePlatform class.
import com.ibm.rcp.security.auth.SecurePlatform;
public void doLogin() {
try {
SecurePlatform.getLoginContext().login();
}
catch (LoginException e) {
// Login failed... exit platform or take corrective action
}
}
Implementing single sign-on with remote servers
Single sign-on means that users only have to log onto a remote server once and can access any services
available from that server for which they have permissions.
The form-based authentication login module that is provided with the client performs a form based login
against a remote WebSphere Portal server and retrieves a LTPA token. The LTPA token is returned in the
form of a SingleSignonToken object. The SingleSignonToken object has a getSsoTokens() method which
returns an array of org.apache.commons.httpclient.Cookie objects. You can use these cookies to leverage
the LTPA token for authentication. This object also implements the JAAS Refreshable and Destroyable
interfaces, allowing the caller to refresh and destroy the tokens. You can provide your own form-based
login modules which provide SSO tokens for platform consumers by providing your own implementation
of the SingleSignonToken interface.
To implement single sign-on with a remote server, perform the following step:
Do one of the following:
v Implement single sign-on with a remote WebSphere Portal server by implementing form based
authentication.
v Implement single sign-on with another remote server by providing a custom implementation of the
SingleSignonToken interface.
Securing applications and data 345
Contributing a login configuration
You can contribute a login configuration to tell the client to use a specific LoginModule when
authenticating users.
Lotus Expeditor Client provides public login modules which you can extend to implement authentication
to remote servers in your application. The login modules provided enable you to log into a remote
WebSphere Portal server. The client supports the following remote server login configurations:
v HTTP -- Uses HTTP basic authentication to authenticate users who are transferring data over the HTTP
protocol.
v J2EE-FORM -- Uses a form based login to log into a remote WebSphere Portal servers. Form-based
authentication is authentication that uses a custom login form supplied by the application to collect a
user name and password.
Lotus Expeditor Client also provides public login modules which you can extend to implement
authentication to the platform key store in your application. The client supports the following platform
login configurations:
v KS – Uses the KeyStoreLoginModule. The KeyStoreLoginModule gets the password used to load the
key store by first checking the shared state for the value associated with the key “SSO_PASSWORD.” If
such a value exists, the login module uses it. If it does not exist, the provided CallbackHandler
prompts the user for a key store password. After it retrieves the password, the module places the key
store password in the platform Subject.
v SSO-KS – Stacks the SSOLoginModule on top of the KeyStoreLoginModule. If single sign-on is enabled,
the login module loads the platform key store, but does not prompt the user for a password. If single
sign-on is disabled, the user experience is the same as it is for the KS configuration. The
SSOLoginModule generates a unique password composed of a set of 128 random bytes. This password
is persistently and securely* stored using the secureWrite() method in the operating system library
component. The password is available to other LoginModules as the value of the “SSO_PASSWORD”
property in the shared state. The key associated with the password is composed of a set of 64 random
bytes, Base64 encoded, and stored as the value of the com.ibm.rcp.security.auth.ui/sk preference.
*Note: The secureWrite() method used to securely store passwords is platform dependent. Single
sign-on on a Linux operating system is not fully secure; the password is only obfuscated.
Administrators must ensure that the workspace is secured such that only appropriate users have read
access.
If you use a platform login configuration other than the two default configurations, you must guarantee
that the configurations meet the following conditions:
v The platform key store must be successfully loaded using a password retrieved from either the
SSO_PASSWORD item in the shared state or from the CallbackHandler. If the platform key store does
not exist, it must be created using the given password.
v The password used to store and load the platform key store must be placed in a private Credential set
in the Platform Subject.
To contribute a login configuration, perform the following steps:
1. Define the login module to use for the application by doing one of the following:
v Use one of the login modules provided by the client.
v Contribute a Login Module using the com.ibm.rcp.security.auth.loginModule extension point.
If you want to write your own login module, refer to the JAAS LoginModule Developer’s Guide for
information about how to write one. You can find the Guide at the following Web site:
http://java.sun.com/j2se/1.4.2/docs/guide/security/jaas/JAASLMDevGuide.html
Provide values for the following attributes of the <extension> element:
– point - A fully qualified identifier of the target extension point, which in this case is
″com.ibm.rcp.security.auth.loginModule″.
346 Lotus Expeditor: Developing Applications for Lotus Expeditor
– id - A required identifier of the extension instance that applications can use to reference it.
– name - An optional name of the extension instance.
In the <loginmodule> element, provide values for the following attributes:
– class - Fully qualified name of the class that implements the javax.security.auth.spi.LoginModule
interface.
– description – Short description of the task performed by this LoginModule.
For example:
<extension
id="keystoreLoginModule"
name="KeyStore LoginModule"
point="com.ibm.rcp.security.auth.loginModule">
<loginModule
class="com.ibm.rcp.internal.security.auth.module.KeyStoreLoginModule"
description="LoginModule for KeyStore"/>
</extension>
2. Contribute a login configuration using the com.ibm.rcp.security.auth.loginConfigurationProvider
extension-point.
Provide values for the following attributes of the <extension> element:
v point - A fully qualified identifier of the target extension point, which in this case is
″com.ibm.rcp.security.auth.loginConfigurationProvider″.
v id - A required identifier of the extension instance that applications can use to reference it.
v name - An optional name of the extension instance.
In the <loginConfigurationProvider> element, provide a value for the following attribute:
v class - Fully qualified name of the class that extends the javax.security.auth.login.Configuration
class.
Optionally, in the <loginConfigurationUrl> element, provide a value for the following attribute:
v url – Location of a configuration file for this loginConfigurationProvider to use.
For example:
<extension
id="ksConfigurationProvider"
name="Default Configuration Provider for KeyStore"
point="com.ibm.rcp.security.auth.loginConfigurationProvider">
<loginConfigurationProvider class="com.ibm.rcp.internal.security.auth.
KSConfigurationProvider"/>
</extension>
3. Instruct the platform to use your custom configuration by setting the value of the AUTH_TYPE
property in the account equal to the login configuration String value you defined in the login
configuration class.
account.setProperty(Account.AUTH_TYPE, "THUMB")
4. Optional: Contribute a callback handler to the platform using the
com.ibm.rcp.security.auth.callbackHandler extension point.
Note: If you do not want to contribute a custom callback handler, you can use the default callback
handler provided by the platform.
Provide values for the following attributes of the <extension> element:
v point - A fully qualified identifier of the target extension point, which in this case is
″com.ibm.rcp.security.auth.callbackHandler″.
v id - A required identifier of the extension instance that applications can use to reference it.
v name - An optional name of the extension instance.
In the <callbackHandler> element, provide a value for the following attribute:
Securing applications and data 347
v class - fully qualified name of the class that implements the
javax.security.auth.callback.CallbackHandler interface.
For example:
<extension
id="defaultUsernamePasswordCallbackHandler"
name="Default CallbackHandler for retrieving a username/password
point="com.ibm.rcp.security.auth.callbackHandler">
<callbackHandler
class="foo.auth.dialog.NameAndPasswordLoginDialog"/>
</extension>
5. Use the com.ibm.rcp.security.auth.callbackHandlerMapping extension point to do one of the
following:
v Specify to use a callback handler provided by the client, such as
com.ibm.rcp.security.auth.ui.defaultKSCallbackHandler.
v Specify the custom CallbackHandler you defined in the previous step as the handler that should be
used by your custom configuration.This handler is passed to all LoginModules used by the Configuration.
Provide values for the following attributes of the <extension> element:
v point - A fully qualified identifier of the target extension point, which in this case is
″com.ibm.rcp.security.auth.callbackHandlerMapping″.
v id -- A required identifier of the extension instance that applications can use to reference it.
v name - An optional name of the extension instance.
In the <callbackHandlerMapping> element, provide values for the following attributes:
v configName – name of the Configuration to bind this CallbackHandler to, for example, “KS”,
“SSO-KS”, ″J2EE″, or ″HTTP″.
v callbackHandlerId – the unique identifier of the CallbackHandler to bind to the specified
Configuration.
For example:
<extension id="defaultHttpBasicCallbackHandler"
name="Default CallbackHandler for HTTP-BASIC Login Configuration"
point="com.ibm.rcp.security.auth.callbackHandler">
<callbackHandler class=
"com.ibm.rcp.internal.security.auth.dialog.HttpBasicLoginDialog"/>
</extension>
6. Optional: Instruct the client to use your custom login configuration as the default platform login
configuration by setting the value of following application preference equal to the name of your
configuration:
com.ibm.rcp.security.auth/loginConfigName
348 Lotus Expeditor: Developing Applications for Lotus Expeditor
Enabling applications for configuration
This section provides information for enabling applications for configuration.
Using preferences
Plug-in preferences are key/value pairs, where the key describes the name of the preference, and the
value is one of several different types, including Boolean, double, float, int, long, and string. The Eclipse
platform provides support for storing plug-in preferences and showing them to the user on pages in the
workbench Preferences dialog box.
Lotus Expeditor extends the Eclipse capabilities by including the Managed Settings framework, which
can control Eclipse settings based on an administrator’s specification.
Creating preference pages
The references to Preferences in this section cover a wide range of information, from user choices on how
to display information, to configuration options needed to connect to enterprises. The client platform
framework provides built-in capabilities to help manage preferences. You may choose to use one or both
of these options, or to construct your own mechanisms.
When you write the code that defines the content of the preference page, follow these formatting rules:
v Use group boxes to separate areas, if you feel that grouping is necessary. Capitalize only the first letter
of the first word of the group box heading.
v Begin each preferences page with a sentence that describes what the user can do.
v Add a colon after field labels.
v Always provide the Restore Defaults and Apply buttons. You can add other command buttons as
necessary
Using the Managed Settings framework
This framework allows an administrator to initialize, and optionally control, settings values in the Eclipse
preference store. They do this by defining policy and preference settings, or any other name and value
pairs, on the back-end system of their choice. Updates run periodically to populate the designated scopes
and qualifiers in the Eclipse preference store with any new settings or settings changes specified by the
administrator. The administrator may also designate a setting as being read-only. If this is the case, any
user changes to the value will be disallowed.
Administrators can use the managed settings framework to populate both the standard Eclipse scopes
and a custom scope called the ManagedSettingScope. You may use either one to access your application’s
settings. However, there are two advantages to using the ManagedSettingsScope:
v The ManagedSettingScope allows you to determine if the administrator has designated a particular
setting as read-only. If so, you can disable any user interface that would allow the user to change it. If
you do not do this, and allow the end user to change the value using the standard Eclipse Scopes, the
Managed Settings framework will revert the change, causing confusion and frustration for the end
user. Also, if there are any plug-ins monitoring settings changes, they will get two events, one for the
users change and one for the managed settings reversion. Not only could this cause flicker in the UI,
but the reversion could come too late to stop an action that the administrator meant to prevent. If you
do use the ManagedSettingScope to set the value, but forget to check first if it was read-only, the change
will be prevented entirely instead of being reverted after the fact. While this does not improve the
end-user experience, it does prevent any side-effects from having two change events, and more
importantly, enforces the administrator’s intent.
© Copyright IBM Corp. 2004, 2006 349
v The standard Eclipse preference scopes use text files to store their data, allowing end users to easily
change the values even if there is no UI in the application to do so. If they do edit a read-only value,
the managed settings framework will revert the value in as timely a manner as possible, but there
could be short periods where the administrator’s value is not the one being used. The
ManagedSettingScope obfuscates its data as a deterrent to end-user tampering. If you retrieve the
settings from the ManagedSettingsScope, you will always be getting the administrator value and not
the user value.
The Portlet Container uses the Managed Settings Framework to implement the Portal Policy API on the
client. If you have an existing applications that uses the Portal Policy API to retrieve values from the
Portal Policy Manager, it can use the same code to retrieve Managed Settings from the Eclipse Preference
store.
Creating a managed settings-aware application:
You do not need to know whether a particular setting is managed in order to use the
ManagedSettingsScope to retrieve its value. If the ManagedSettingsScope does not find the setting
internally, it will automatically search the standard Eclipse scopes and return the value of the setting from
there. This also means that you can still set defaults in the standard ways. Designing your application
using the ManagedSettingsScope accommodates settings that are not managed currently, but may be
managed in the future.
Note: If you are using the Portal Managed Client, you can skip Steps 1 and 2. The Portal Managed Client
automatically performs these steps.
1. By default, the first managed settings update occurs asynchronously when the
com.ibm.rcp.managedsettings plug-in starts. If you want to ensure that the managed settings for an
application have been retrieved before the application begins to run, you can call the following
methods in your initial setup sequence. This also has the effect of starting the Managed Settings
plug-in:
if (!ManagedSettings.hasData()){
ManagedSettings.runSynchronousUpdate(false)
}
This method forces a synchronous update because it waits for the update to complete before it
returns. The false parameter indicates that an update should not be run if the update interval has not
expired. Setting this parameter to true forces an update to run even if the update interval has not
expired. A parameter setting of true should rarely be needed and is only provided to support the
retrieval of settings that the application somehow knows are new and that it needs immediately.
2. If you do not do step 1., you must use another method to start the Managed Settings plug-in,
com.ibm.rcp.managedsettings, early in the client startup cycle. One way to do this is to use a life
cycle extension point. See the Eclipse documentation for other ways to start plug-ins. Note that this
plug-in must be started in order for the administrator’s settings to be brought to the client, even if
individual plug-ins in the application are only using the standard Eclipse scopes and are not accessing
the settings through the Managed Settings scope.
3. Publish a list of the settings used by your application, and the scopes and qualifiers you are using to
access them from the Eclipse Preference store, so that administrators know how to specify values for
the settings.
4. When writing user interface code that allows the end user to change the values of settings, check the
read-only status of each setting before enabling the associated control.
For example, to find out if the setting, canSendAttachments, that is accessed through the qualifier,
com.ibm.workplace.messaging, is a read-only setting, use the following code:
ManagedSettingsScope msScope = new ManagedSettingsScope();
IEclipsePreferences messagingSettings =
msScope.getNode(("com.ibm.workplace.messaging");
boolean canSendAttachments =
messagingSettings.get(“canSendAttachments”, false);
350 Lotus Expeditor: Developing Applications for Lotus Expeditor
boolean canUserChangeValue =
msScope.isReadOnly("com.ibm.workplace.messaging", (“canSendAttachments”);
The false parameter passed to the get method is the default value that is returned if a value is not
found on the client, even in Default Scope.
5. Be careful with caching the value of a setting because it can be refreshed from the back-end system at
any time. If you do cache it, be sure to register a listener to update the cache if it changes.
6. Register an implementation of the org.eclipse.core.runtime.preferences.IEclipsePreferences.
IPreferenceChangeListener listener on any preference nodes that you want to keep track of.
For example:
messagingSettings.addPreferenceChangeListener(listener)
Using the Portal Policy API on the client:
Portal Policy settings that are brought to the client using Managed Settings framework are also accessible
using the Portal Policy API.
To get Policy Settings using the Portal Policy API:
1. Create a portlet. Select File > New > Portlet Project, and then select Basic Portlet as the portlet type.
2. Select view in Content types and modes, and then click Finish.
3. Use the RenderResponse object to retrieve a managed setting by adding the following code to the
doView(RenderRequest request, RenderResponse response) method of the ClientPolicyPortlet class.
For example:
This code retrieves and displays the policy setting key named ″testMS″ in View mode.
com.ibm.test.myportlet is the policy type of the policy setting.
Creating Eclipse Preference Sets with the Policy Type Editor: The Policy Type Editor for Preferences
removes much of the complexity from the process of updating preferences. With the Policy Type Editor
for Preferences, any Portal Managed Client (PMC) client application that requires centrally administrated
preferences only needs to provide the XML Preference Set/Policy Type definition which defines one or
more preferences. This template is then imported to the Policy Type Editor for Preferences. No additional
code is needed.
public void doView(RenderRequest request, RenderResponse response)throws PortletException, IOException {
// Set the MIME type for the render response
response.setContentType(request.getResponseContentType());
javax.naming.Context ctx = new javax.naming.InitialContext();
PolicyManagerService pms = (PolicyManagerService) ctx.lookup("portal:service/policy/PolicyManager");
PolicyManager policyManager = pms.getPolicyManager();
Target target = policyManager.createTarget(new PortletRequestRuleContext((PortletRequest)request));
Object testMS = policyManager.getValueByTarget(target,"com.ibm.test.myportlet","testMS");
testMSStr = (String) testMS;
response.getWriter().println("<b>[testMS]: " + testMSStr + "</b>");
return;
}
Enabling applications for configuration 351
As a developer, you are responsible for writing an XML template defining a Preferences Set / Policy Type
to be managed, including a definition of the preferences, appropriate user friendly labels for displaying
the preferences, and any information that the GUI would use to restrict and/or validate data entered for
the preferences.
Working with Preference Sets: Preference Sets are defined in XML descriptor files referred to as templates.
The templates are written using a format that allows preferences to be generically described. The
template must include all information required to render and validate each preference included.
Each preference is defined with sufficient information that the Editor can construct GUI pages that look
and act almost as if written specifically for those data items. In the XML template, the plug-in developer
can:
v Set a name for the Preference Set/Policy Type.
– Provide translated display names for the Policy Type
– Provide translated descriptions for the Policy Typev Create individual preferences within a Policy Type
– Provide translated display names for the preference
– Set the scope
– Set the qualifier
– Set the key
– Optionally, set a default for the preference’s value.
– Set the item’s type, such as ″URL″, “Integer″ or “boolean”. This is used by the GUI to draw to an
appropriate input entry and validate input to meet the requirements of that type.
– Provide a limited set of possible values to select from. These are usually presented in a drop down
list.
– Set whether a value is required to be entered or can be left blank
The Editor looks at each preference, and provides appropriate validation and error messages when the
administrator enters invalid data. The GUI also uses the type to determine what format of input field to
generate. A boolean will get a check box, a string will get an edit field. Once input is provided, it is
validated to ensure the data provided matches the designated type. The complete list of types supported
is:
v string -- a java.lang.String, no restrictions on values
v boolean – defaults to “true” and “false”
v integer -- convertible to a java.lang.Integer
v real -- convertible to a java.lang.Double
v url -- convertible to a valid java.net.URL
Preferences can have additional constraints defined. Constraints set additional limits on the values that
can be entered. The current list of supported constraints is:
v Enumeration -- a restricted list of values to choose from. The GUI will generate a drop down list box
containing only these values.
v Minimum value <min> -- for integer and real types, the value must be equal to or greater than the
minimum
v Minimum length <min> -- for non-numeric types, the minimum number of characters that must be
entered
v Maximum value <max> -- for numeric types, the value must be equal to or less than the maximum
v Maximum length <max> -- for all other types, the maximum number of characters that may be
entered
352 Lotus Expeditor: Developing Applications for Lotus Expeditor
v BooleanValues -- the value must conform to one of two values determined by the user. The default is
true/false, but any two string values can be set using <trueValue> and <falseValue>
Each Preference Set contains enough information to allow the Editor to dynamically generate appropriate
data entry entities, with translated labels. Many errors in data entry can be caught on the server by
validating the data against the definitions in the Preference. Translated error messages are displayed,
prompting the administrator on how to correct their entries.
Configuration set Document Type Definition:
<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT preferenceSet (name, default-locale, language*, preference*)>
<!ATTLIST preferenceSet
version CDATA #REQUIRED>
<!ELEMENT preference (value?, constraints?, language* )>
<!ATTLIST preference
scope CDATA #IMPLIED
qualifier CDATA #REQUIRED
key CDATA #REQUIRED
type (boolean | integer | real | string | url ) #REQUIRED
required (yes | no) "no">
<!ELEMENT language (display-name?, description?) >
<!ATTLIST language
locale CDATA #IMPLIED >
<!ELEMENT values (value*)>
<!ELEMENT value (#PCDATA)>
<!ELEMENT constraints (min?, max?, enumeration?, booleanValues? )>
<!ELEMENT enumeration (value*)>
<!ELEMENT booleanValues (trueValue, falseValue) >
<!ELEMENT display-name (#PCDATA)>
<!ELEMENT description (#PCDATA)>
<!ELEMENT max (#PCDATA)>
<!ELEMENT min (#PCDATA)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT trueValue (#PCDATA)>
<!ELEMENT falseValue (#PCDATA)>
<!ELEMENT default-locale (#PCDATA)>
The following table provides additional element and attribute description information for the above
Document Type Definition:
Table 28. Additional Element Descriptions
Element or Attribute Name Description
preferenceSet Root Element for defining a Preference Set
version Version level for the preference set
provider-name Name of company providing the preference set
preference Root Element for each key/value pair definition
default-locale Defines which language to use when the requested
language has no values
Enabling applications for configuration 353
Table 28. Additional Element Descriptions (continued)
Element or Attribute Name Description
Language Defines display data for a specific language
display-name Localized name used by the GUI as the label for the
input field
description Localized description
scope scope – see Eclipse Preferences Documentation
qualifier Qualifier, typically the plug-in name– see Eclipse
Preferences Documentation
key Key within the qualifier. – see Eclipse Preferences
Documentation
type See descriptions in (ADD LINK TO PREVIOUS
SECTION)
required Forces a default value to be provided. (Blank is not
allowed)
value The default value for an item
constraint, min, max, enumeration See descriptions in (ADD LINK TO PREVIOUS
SECTION)
Preference Set sample template: The following is an example of a Preference Set template:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE preferenceSet SYSTEM "preferenceSet.dtd">
<preferenceSet version="1.0" provider-name="IBM">
<name>TraderPreferences</name>
<default-locale>en</default-locale>
<language locale= "en">
<display-name>Stock Trade Preferences</display-name>
<description>Description of a Sample Preference Set</description>
</language>
<language locale= "ja">
<display-name>JA-Stock Trade Preferences</display-name>
<description>JA-Discription of a Sample Preference Set</description>
</language>
<language locale= "fr">
<display-name>FR-Stock Trade Preferences</display-name>
<description>FR-Description of Sample Preference Set</description>
</language>
<preference scope="instance" qualifier="com.ibm.plugin.preference"
key="UpdateFrequency" type="string" required="yes">
<value>weekly</value>
<constraints>
<enumeration>
<value>never</value>
<value>hourly</value>
<value>daily</value>
<value>weekly</value>
<value>monthly</value>
</enumeration>
</constraints>
<language locale= "en">
<display-name>Update Interval</display-name>
<description>Frequency of automatic report updates.</description>
</language>
<language locale= "ja">
<display-name>JA-Update Interval</display-name>
354 Lotus Expeditor: Developing Applications for Lotus Expeditor
<description>JA-Frequency of automatic report
updates.</description>
</language>
<language locale= "fr">
<display-name>FR-Update Interval</display-name>
<description>FR-Frequency of automatic report
updates.</description>
</language>
</preference>
<preference scope="" qualifier="com.ibm.plugin.preference"
key="MaximumTransactionAmount" type="integer" required="yes">
<value>200</value>
<constraints>
<min>5</min>
<max>5000</max>
</constraints>
<language locale= "en">
<display-name>Maximum Transaction Amount</display-name>
<description>The maximum dollar amount allowed per
transaction.</description>
</language>
<language locale= "ja">
<display-name>JA-Maximum Transaction Amount</display-name>
<description>JA-The maximum dollar amount allowed per
transaction.</description>
</language>
<language locale= "fr">
<display-name>FR-Maximum Transaction Amount</display-name>
<description>FR-The maximum dollar amount allowed per
transaction.</description>
</language>
</preference>
<preference scope="" qualifier="com.ibm.plugin.preference"
key="RequiredApprovals" type="integer" required="yes">
<value>1</value>
<constraints>
<min>1</min>
<max>8</max>
</constraints>
<language locale= "en">
<display-name>Manager Approval Levels</display-name>
<description>The number of Managers required to
approve. </description>
</language>
<language locale= "ja">
<display-name>JA-Manager Approval Levels</display-name>
<description>JA-The number of Managers required to
approve. </description>
</language>
<language locale= "fr">
<display-name>FR-Manager Approval Level</display-name>
<description>FR-The number of Managers required to
approve.</description>
</language>
</preference>
<preference scope="instance" qualifier="com.ibm.plugin.preference"
key="Division" type="string" required="no">
<value>CompanyAccess</value>
<constraints>
<min>8</min>
<max>20</max>
</constraints>
<language locale= "en">
<display-name>Company Access Code</display-name>
Enabling applications for configuration 355
<description>The Company’s Access Code.</description>
</language>
<language locale= "ja">
<display-name>JA-Company Access Code</display-name>
<description>JA-The Company’s Access Code.</description>
</language>
<language locale= "fr">
<display-name>FR-Company Access Code</display-name>
<description>FR-The Company’s Access Code.</description>
</language>
</preference>
<preference scope="instance" qualifier="com.ibm.plugin2.preference"
key="CompURL" type="url" required="no">
<value>http://www.myCompany.com</value>
<language locale= "en">
<display-name>Company Web Site</display-name>
<description>URL of the company web site.</description>
</language>
<language locale= "ja">
<display-name>JA-Company Web Site</display-name>
<description>JA-URL of the company web site.</description>
</language>
<language locale= "fr">
<display-name>FR-Company Web Site</display-name>
<description>FR-URL of the company web site.</description>
</language>
</preference>
<preference scope="instance" qualifier="com.ibm.plugin.preference"
key="AfterHoursAccess" type="boolean" required="no">
<value>no</value>
<constraints>
<booleanValues>
<trueValue>yes</trueValue>
<falseValue>no</falseValue>
</booleanValues>
</constraints>
<language locale= "en">
<display-name>Allow after hours access</display-name>
<description>Allow transations after business
hours.</description>
</language>
<language locale= "ja">
<display-name>JA-Allow after hours access</display-name>
<description>JA-Allow transations after business
hours.</description>
</language>
<language locale= "fr">
<display-name>FR-Allow after hours access</display-name>
<description>FR-Allow transations after business
hours.</description>
</language>
</preference>
<preference scope="instance" qualifier="com.ibm.plugin.preference"
key="MultipleLogon" type="boolean" required="no">
<value>enable</value>
<constraints>
<booleanValues>
<trueValue>enable</trueValue>
<falseValue>disable</falseValue>
</booleanValues>
</constraints>
<language locale= "en">
<display-name>Allow simultaneous logon</display-name>
<description>Allow account to be logged on in multiple
356 Lotus Expeditor: Developing Applications for Lotus Expeditor
locations.</description>
</language>
<language locale= "ja">
<display-name>JA-Allow simultaneous logon</display-name>
<description>JA-Allow account to be logged on in multiple
locations.</description>
</language>
<language locale= "fr">
<display-name>FR-Allow simultaneous logon</display-name>
<description>FR-Allow account to be logged on in multiple
locations.</description>
</language>
</preference>
<preference scope="instance" qualifier="com.ibm.plugin.preference"
key="AutoLockout" type="boolean" required="no">
<value>0</value>
<constraints>
<booleanValues>
<trueValue>1</trueValue>
<falseValue>0</falseValue>
</booleanValues>
</constraints>
<language locale= "en">
<display-name>Automatic Lockout Enabled</display-name>
<description>Locks account when unusual activity
detected.</description>
</language>
<language locale= "ja">
<display-name>JA-Automatic Lockout Enabled</display-name>
<description>JA-Locks account when unusual activity
detected.</description>
</language>
<language locale= "fr">
<display-name>FR-Automatic Lockout Enabled</display-name>
<description>FR-Locks account when unusual activity
detected.</description>
</language>
</preference>
</preferenceSet>
The following table gives some additional information on how the GUI was created in this sample. It also
provides information on the sample’s data validation settings.
Table 29. Additional Sample Information
Title of Preference Preference Qualifier/Key Rule Details
Update Interval instance/com.ibm.plugin.preference/
UpdateFrequency
The GUI utilizes a drop down
selection box. This preference’s
value is restricted to one of an
enumerated set of valid values
that were defined in the template.
Maximum Transaction Value instance/ com.ibm.plugin.preference/
MaximumTransactionAmount
An entry field is used, but the
data must be an integer between 5
and 5000.
Manager Approval Levels com.ibm.plugin.preference/RequiredApprovals
An entry field is used, but the
data must be an integer between 1
and 8. This item could also have
been implemented with an
enumerated set of valid values
and a drop down box would be
created.
Enabling applications for configuration 357
Table 29. Additional Sample Information (continued)
Title of Preference Preference Qualifier/Key Rule Details
Company Access Code com.ibm.plugin2.preference/ Division This entry field may be left blank,
or a string value of between 8 and
20 characters in length can be
entered.
Company Web Site instance/com.ibm.plugin2.preference/CompURL
A URL must be entered.
Allow after hours access com.ibm.plugin.preference/
AfterHoursAccess
A check box where the underlying
value is “yes” or “no”.
Allow simultaneous logon instance/ com.ibm.plugin.preference/
MultipleLogon
A check box where the underlying
value is “enable” or “disable”.
Automatic Lockout Enabled instance/ com.ibm.plugin.preference/
AutoLockout
A check box where the underlying
value is “0” or “1”.
Testing Preference Set definitions: Developers creating new Preference Sets should thoroughly test them
before deploying them to a production environment. This should include verifying each preference is
tested to assure that changes on the server are applied on the client. This can only be done with an
end-to-end test:
v Verifying the template has the correct preference name. The smallest error in the name of the
preferences will result in the preference not being recognized by the client.
v Verify that all values, or at least a representative sample of values, for each preference is tested and
produces the correct client behavior.
v Verify that the correct behavior occurs on the client when the Policy Type is loaded and no subPolicies
have been created.
Understanding preference options
Preferences provide the ability for applications to be configured and managed using one of the provided
management systems, and eliminate the need to change source code or plug-in contents to change
application parameters. While there are several preference models, developers are strongly encouraged to
use the Eclipse preference model, either directly, or through the Managed Settings provider.
Eclipse preferences
The Eclipse framework provides an extensible preference store that permits preference information to be
stored at various levels. Preference information is stored as key value pairs. Preference pages are
generally provided to set or update the preference information stored within the system. Information
stored within the Eclipse preference framework will usually be related to display or operating
characteristics that the user may change to suit his choices. Eclipse-based preferences are not connected to
the enterprise management system. Refer to the Platform Plug-in Developer’s Guide for more information on
using Eclipse preferences.
Configuration admin
The OSGi core framework also provides a configuration management capability known as Configuration
Admin. Configuration Admin provides capabilities to store preference or configuration information based
on key value pairs.
Developers should consider using the OSGi Configuration admin service only if their bundle must also
run in a non-Eclipse OSGi environment. Otherwise, developers are strongly encouraged to use the Eclipse
preferences model.
Applications that use Configuration Admin to store information will need to implement the
ManagedService interface. By implementing this interface, the application will be notified when
configuration information changes. Applications that use Configuration Admin to store configuration and
358 Lotus Expeditor: Developing Applications for Lotus Expeditor
preference information can also use the Metatype service to provide a metadata description of the
information. The metadata can describe the parameter types, default values, and validation logic to be
applied for each parameter.
If Configuration Admin is used to store configuration information, system administrators can query and
update configuration values via the Enterprise Management Agent.
If configuration information is stored using Configuration Admin, preference pages can be used to
provide a user interface to view and modify preferences. The Lotus Expeditor platform provides classes
to assist in using preference pages to interact with Configuration Admin.
The com.ibm.eswe.preferences.ConfigAdminPreferencePage has subclassed the
org.eclipse.jface.preference.FieldEditorPreferencePage to provide preference pages for configuration
information stored within Configuration Admin. The ConfigAdminPreferencePage creates a
ConfigAdminPreferenceStore, that uses Configuration Admin and Metatype services to obtain the required
data to automatically build the preference page.
To use the ConfigAdminPreferencePage, you will need to create your own subclass of the
ConfigAdminPreferencePage, and supply the Bundle and Persistent ID for the properties that you intend
to display. Within your subclass, you can also affect the set of properties displayed, as well as add your
own validation logic to the page. You will also need to define a preference page via the standard Eclipse
extension point for preference pages, and supply the appropriate metadata files (i.e. METADATA.XML,
METADATA.properties). Refer to “Using the Meta Type Service” on page 362 for more information . The
METADATA.XML and METADATA.properties files are loaded from either the plug-in files or from the plug-in’s
class path. The METADATA.* files must be placed within the META-INF directory of either the project or the
src directory for the project
The ConfigAdminPreferencePage supports only scalar metatype definitions for non-factory PIDs. Integer,
String, and Boolean fields are handled by default. You will need to provide implementations for
FieldEditors for types such as Byte, Char, Long, Float, Double, BigDecimal and BigInteger
Refer to the Javadoc information for the com.ibm.eswe.preference package for more information.
OSGi preference service
The OSGi Preference Service defines a preferences model for OSGi Services. The OSGi Preferences model
enables services to maintain their own preferences information, but the information is not available to
other services or bundles, unless access is provided by the service itself. Developers should consider
using the OSGi Preferences Service for preference information only if their bundle must also run in a
non-Eclipse OSGi environment. Otherwise, developers are strongly encouraged to use the Eclipse
preferences model. For more information, refer to the OSGi R4 Specification.
Using the XML parser services
Applications requiring use of XML Parsers should use the XML Parser Service interface. By using the
XML Parser Service interface, applications are able to dynamically select the parser at runtime, and are
notified of parser service events by the XML Parser Service. However, to use the XML Parser service, you
must modify existing applications.
Applications can use the standard JAXP calls without using the service interfaces. The APIs providing the
parser factories use the underlying service interfaces. In this situation, applications will not be able to
dynamically choose their parser at runtime, and they will not receive event notifications if parser services
are removed. Applications might receive javax.xml.parsers.FactoryConfigurationError if parser actions
are attempted and no parsers exist.
Typical usage of a SAX Parser involves obtaining a reference to the SAX Parser Factory, and then
obtaining a new parser:
Enabling applications for configuration 359
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
The following example shows how you can obtain a reference to the SAX Parser Factory using XML
Parser Services:
ServiceReference ref =
context.getServiceReference(SAXParserFactory.class.getName() );
SAXParserFactory factory = context.getService( ref );
SAXParser parser = factory.newSAXParser();
It is not necessary to issue the newInstance call once the factory reference is obtained.
Similarly, when using the DocumentBuilderFactory, the typical sequence using JAXP is:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
The following example shows how you use the DocumentBuilderFactory using the XML Parser Service.
ServiceReference ref =
context.getServiceReference( DocumentBuilderFactory.class.getName() );
DocumentBuilderFactory factory = context.getService( ref );
DocumentBuilder builder = factory.newDocumentBuilder();
It is not necessary to issue the newInstance call once the factory reference is obtained.
SAX interfaces permit setting properties on the SAXParser object. Since MicroXML does not support
validation, attempting to set the validation property on MicroXML will result in a
org.xml.sax.SAXNotSupportedException being thrown. If your code is not specific to either parser, then
you should be prepared to receive exceptions when attempting to set features, and handle them
appropriately.
Attempts to set other features can result in exceptions if different parser implementations have been
plugged in. If specific parser attributes are required, you can specify them when requesting the parser
factory service.
It is possible to perform validation of XML using DTD or XSD when the XML resources are loaded from
a plug-in. Because the resource is loaded from a plug-in, you need to provide indication of the location of
the DTD file. Create an InputSource object for the XML document that requires parsing. Use the
Bundle.getEntry() or Bundle.getResource() method to obtain a URL or load the resource from a stream.
Set the System ID for the InputSource to the URL used to load the file. By using this process, you do not
need to create an EntityResolver instance to locate the DTD.
Use the following approach to permit DTD or Schema validation against files contained within bundles:
1. Set up an InputSource to specify the stream and the desired URL.
2. Do not use only an input stream, because an attempt to find a DTD or Schema file will result in
unexpected URLs.ServiceReference ref =
context.getServiceReference(SAXParserFactory.class.getName() );
SAXParserFactory factory = context.getService( ref );
SAXParser parser = factory.newSAXParser();
XMLReader xmlReader = parser.getXMLReader();
xmlReader.setContentHandler(this);
xmlReader.setErrorHandler(this);
xmlReader.setFeature("http://xml.org/sax/features/namespaces", true);
xmlReader.setFeature("http://xml.org/sax/features/validation", true);
xmlReader.setFeature("http://apache.org/xml/features/validation/schema",
true );
360 Lotus Expeditor: Developing Applications for Lotus Expeditor
URL url = getClass().getResource(uri);
java.io.InputStream is = url.openStream();
[1] InputSource input = new InputSource(is);
[2] input.setSystemId( url.toExternalForm() );
xmlReader.parse(input);
Locating the Web Container ports using the HttpSettingListener
Service
Applications running on Lotus Expeditor 6.1 runtime can use the HttpSettingListener service to receive
notification of the Web Container configuration. The Web Container configuration consists of settings
such as the port and host address that the Web Container is listening on.
To use the HttpSettingListener service, application developers must perform the following steps:
1. Update the application manifest to include a dependency on the com.ibm.pvc.webcontainer.listeners
package (Optionally, the application manifest can be updated to require the
com.ibm.pvc.sharedbundle plug-in)
2. Add a Java class to the application project that implements the HttpSettingListener interface.
The following example shows what the listener class would look like:
public class MyListener implements HttpSettingListener {
/* (non-Javadoc)
* @see com.ibm.pvc.webcontainer.listeners.HttpSettingListener#settingsAdded
(java.lang.String, java.util.Dictionary)
*/
public void settingsAdded(String pid, Dictionary properties) {
//TODO – developer needs to implement this method
}
/* (non-Javadoc)
* @see com.ibm.pvc.webcontainer.listeners.HttpSettingListener#settingsModified
(java.lang.String, java.util.Dictionary)
*/
public void settingsModified(String pid, Dictionary properties) {
//TODO – developer needs to implement this method
}
/* (non-Javadoc)
* @see com.ibm.pvc.webcontainer.listeners.HttpSettingListener#settingsRemoved
(java.lang.String)
*/
public void settingsRemoved(String pid) {
//TODO – developer needs to implement this method
}
}
3. Register the application as an HttpSettingListener . The class registering the HttpSettingListener
service must have access to the application’s bundle context object. The following example shows how
to register the application as an HttpSettingListener service in the bundle activator of the
application:
public void start(BundleContext context) throws Exception {
context.registerService(HttpSettingListener.class.getName(),
new MyListener(),
null);
}
Upon activation, the Web Container will send notifications to all the HttpSettingListener service
implementations registered with it. The service implementation will be notified of additions or changes to
the listener configuration (settingsModified() method is invoked).
The following example shows processing of the settings Dictionary passed on the service call:
Enabling applications for configuration 361
public void settingsModified(String pid, Dictionary properties)
throws Exception {
String hostName = null;
int portNumber = -1;
String scheme = (String)settings.get( HttpSettingListener.SCHEME );
if (HttpSettingListener.SCHEME_HTTP.equals( scheme )) {
Object iPort = settings.get( HttpSettingListener.HTTP_PORT );
if (iPort instanceof Integer[]) {
portNumber = ((Integer[])iPort)[0].intValue();
} else {
portNumber = ((Integer)iPort).intValue();
}
String sHost = (String)
settings.get( HttpSettingListener.ADDRESS );
if (HttpSettingListener.ALL_ADDRESSES.equals( sHost )) {
hostName = "localhost";
} else {
hostName = sHost;
}
}
}
Note: The settingsAdded() and settingsRemoved() methods are deprecated.
Using the Meta Type Service
The OSGi Configuration Admin Service allows bundles to persistently store their configurations. The
OSGi Meta Type Specification allows for a programmatic description of a bundle’s metadata. The OSGi
MetaType Service ties those two pieces together and enables an administrative bundle to dynamically
discover what another bundle’s configuration looks like and make changes to it. For example, the
configuration screens for the LogService, HttpService, and WebContainer. Likewise, the Configuration
Admin Preferences pages use Configuration Admin and the Meta Type Service to automatically build
preference pages based on the configuration information.
The MetaType Service acts as a middle-man between an administrative bundle and a configurable bundle.
The admin bundle may ask the MetaType Service for a MetaType Provider for a given bundle object. The
MetaType Provider can then be queried to discover the Object Class Definitions and Attribute Definitions
contained within.
For this scheme to work, configurable bundles must provide a way for the MetaType Service to discover
the bundle’s configuration information. Each configurable bundle must have a METADATA.XML file in
the /META-INF directory of their plug-in/bundle. This METADATA.XML file contains a description of the
configuration in XML format and is packaged in the bundle’s JAR file.
This file describes the data in a format defined by METADATA.DTD. Within the METADATA.XML file is a list of
all supported Locales. An additional set of files, METADATA_<locale>.properties should contain the
translatable strings for a locale in key=value format. The METADATA.XML file contains strings for the default
language only, which are used by default if the properties file for the desired locale can not be found.
The Meta Type Service is registered with OSGi under the class name
org.osgi.service.metatype.MetaTypeService and provides a way to define and retrieve
org.osgi.service.metatype.MetaTypeProvider objects for an OSGi bundle. For more information on the
package org.osgi.service.metatype see the OSGi Release 4 specification. The MetaTypeProvider data for
each bundle is stored as a bundle resource at /META-INF/METADATA.XML.
362 Lotus Expeditor: Developing Applications for Lotus Expeditor
The Meta Type data stored in /META-INF/METADATA.XML is used to define information on how to configure
a bundle using the OSGi service org.osgi.service.cm.ConfigurationAdmin. For example, the Managed
Service PIDs, the Managed Service Factory PIDs, and the Attributes associated are defined in the
METADATA.XML file.
Enabling applications for configuration 363
364 Lotus Expeditor: Developing Applications for Lotus Expeditor
Reference information
This section provides additional reference information for Lotus Expeditor.
Lotus Expeditor top level menus
The Lotus Expeditor platform defines a set of default menu items. This section provides the identifiers
that are required to be used for either of the following cases:
v You want to add additional menu items in a specific location to one of the pre-existing menus
v You want to define activities to allow you to group menu items within an activity group.
The Top Level Menu Items as shown in the table below are defined by Lotus Expeditor.
Table 30. Top level menus
Menu Name Menu ID
File file
View com.ibm.rcp.ui.viewmenu
Help help
File menu
The menu items, markers, and separators defined for the File menu are defined in the following table.
Table 31. File menu
Menu Item Sub-menu Item Menu ID
Close com.ibm.rcp.ui.filemenu.close
separator
Application > applications
Reset
group marker
group marker
Install
Application Management
Preferences... preferences
separator
Close All closeAllTabs
Exit quit
View menu
The menu items, markers, and separators defined for the View menu are defined in the following table.
Table 32. View menu
Menu Item Sub-menu Items Menu ID
Toolbar >
© Copyright IBM Corp. 2004, 2006 365
Table 32. View menu (continued)
Menu Item Sub-menu Items Menu ID
Show > com.ibm.rcp.ui.showmenu
side bar com.ibm.rcp.ui.sidebar
Banner show_banner
Help menu
The menu items, markers, and separators defined for the Help menu are defined in the following table:
Table 33. Help menu
Menu Item Menu ID
Help Contents helpContents
Support com.ibm.esupport.client.Browser
About <product name>... about
OSGi
This section provides OSGi information.
OSGi specification
One activity of the OSGi Alliance was to define a Java framework that:
v Enables multiple applications to coexist within a single VM
v Manages the life cycle of components within the framework
v Specifies a set of required and optional services on the platform
Lotus Expeditor is built on the Eclipse Rich Client Platform, which includes an OSGi framework. The
framework is based upon the OSGi Service Platform Release 4 specification with additional extensions
provided by the Eclipse 3.2.1 implementation of the OSGi framework. Application developers partition
applications into services and other resources. Services and resources are packaged into bundles, which
are files that serve as the delivery unit for applications. Bundles have manifests with special headers that
enable you to share classes and services at the package level. Within the Eclipse based platforms, all
plug-ins are OSGi bundles, so you can think of the terms plug-in and bundle as being interchangeable.
The OSGi R4 Specifications can be obtained from the OSGI Alliance website at http://www.osgi.org.
Working with OSGi bundles
OSGi™ bundles consist of a JAR file that contains Java classes, resources, and a manifest file. Bundles can
register services for other bundles to use, use services registered by other bundles, export Java packages
for other bundles to use, and import Java packages from other bundles.
Creating OSGi bundles
This section describes how to create an OSGi bundle. For more detailed information about writing
bundles, refer to the OSGi Service Platform Release 4.
Bundles: A bundle is the smallest unit of management for the Framework. Bundles are Java Archive
(JAR) files with a manifest that contains special headers. These headers describe the bundle to the OSGi
framework and list the bundle’s dependencies, such as the packages and services required by the bundle.
Bundles can register services with the OSGi framework that other bundles can use.
366 Lotus Expeditor: Developing Applications for Lotus Expeditor
The descriptive information in the manifest file differentiates bundles from other JAR files. Non-bundle
JAR files often keep very little information in the manifest file. However, a bundle’s manifest file usually
contains descriptive information, such as the bundle’s name and version, and a list of the packages and
services it requires.
Bundle life cycle: The framework manages the life cycle of bundles. As you install and run a bundle, it
goes through various states. The possible states of a bundle are:
v INSTALLED - the bundle has been installed, but all of the bundle’s dependencies have not been met. The
bundle requires packages that have not been exported by any currently installed bundle.
v RESOLVED - the bundle is installed, and its dependencies have been met, but it is not running. If a
bundle is started and all of the bundle’s dependencies are met, the bundle skips this state.
v STARTING - a temporary state that the bundle goes through while the bundle is starting.
v ACTIVE - the bundle is running.
v STOPPING - a temporary state that the bundle goes through while the bundle is stopping.
v UNINSTALLED - the bundle no longer exists in the framework.
Conventions for creating bundles: When you create bundles, use the following conventions:
v Clean up objects and threads properly during your stop method. The framework does not terminate
lingering threads.
v Return promptly from BundleActivator start() and stop() methods. These methods are invoked
synchronously by the framework. Delays in returning from these methods will affect the ability of the
framework to process other bundle actions. It is recommended that substantial activities be handed off
to another thread for processing, or be delayed until first service invocation.
v Return promptly from Framework and Bundle Listeners events. These event methods are invoked by
the framework. Delays in returning from these methods may adversely affect performance of the
framework.
v Allow for service life cycle events. The OSGi framework provides the ability to dynamically install and
remove bundles. As a result, it is possible in some frameworks that services may not always be
present. A service is only present when the bundle that registered the service is available. See “Getting
and un-getting services from the OSGi Framework” on page 370 for conventions to solve this problem.
Creating manifest files: Each bundle must contain either a manifest file. The bundle’s manifest file
contains data that the framework needs to correctly install and activate the bundle. Legacy Eclipse
bundles can provide some manifest information in their plugin.xml files, but META-INF/MANIFEST.MF
files are the recommended files for Manifest information.
Note: A plugin.xml may contain similar information, however, a plugin.xml also contains extensions and
extension points.
If a bundle contains only a plugin.xml, the Eclipse platform will generate a MANIFEST.MF equivalent when
the platform starts. When you specify data in a manifest file, you must use the headers that were defined
by the OSGi™ specification. You can use user-defined headers; however, the framework ignores any
headers that it does not understand. Refer to the OSGi Service Platform Release 4 specification for more
information about the OSGi Manifest file format and syntax.
The MANIFEST.MF file is located in the META-INF directory of your bundle project. The plugin.xml file, if
present, should be under the root directory.
The following headers are defined in the OSGi Service Release 4 specification and by the Eclipse 3.2.x
extensions to the OSGi framework.
v Import-Package
Reference information 367
Use this header to specify the names of any package that you want your bundle to import from the
runtime. If you do not specify the package your bundle needs in this header, you may get a
NoClassDefFound exception when the bundle loads.
Note: You must also specify the package you want to import (using Import-Package) in the
Export-Package header of the bundle that contains the package.
v Export-Package
Use this header to specify the name of any package that you want your bundle to export to the
runtime. If you do not specify the packages needed by other bundles in this header, the dependent
bundles may not resolve.
v Require-Bundle
Use this header to specify the specific bundles that provides packages you use in your bundle. If you
do not specify the bundle which provides the packages you need, you may get a NoClassDefFound
exception when the bundle loads.
v Bundle-Activator
Use this header to specify the fully-qualified name of the BundleActivator class.
A bundle designates a special class to act as a Bundle Activator. The Framework must instantiate this
class and invoke the start and stop methods to start or stop the bundle as needed. The bundle’s
implementation of the BundleActivator Interface enables the bundle to initialize a task, such as
registering services, when the bundle starts and to perform clean-up operations when the bundle stops.
The org.eclipse.core.runtime.Plugin class implements the org.osgi.framework.BundleActivator
interface. When creating Client Services projects, a subclass of Plugin will be created and will become
the BundleActivator for the plug-in.
You may define your own class to implement the org.osgi.framework.BundleActivator interface.
You can specify this header in the Class field on the Overview Page of the Bundle Manifest Editor.
v Bundle-SymbolicName
The Bundle-SymbolicName manifest header can be used to identify a bundle. The Bundle Symbolic
Name and Bundle Version allow for a bundle to be uniquely identified in the Framework. It does not
replace the need for a Bundle-Name manifest header, which provides a human readable name for a
bundle.
You can specify this header in the ID field on the Overview Page of the Bundle Manifest Editor.
Refer to the OSGi Service Platform Release 4 for descriptions of other bundle headers, such as the
following, which provide bundle description information:
v Bundle-Name
v Bundle-Description
v Bundle-Copyright
v Bundle-Vendor
v Bundle-Version
v Bundle-DocUrl
v Bundle-ContactAddress
v Bundle-Fragment
Packages: Bundles can use code that is defined within other bundles by declaring the packages as
imported packages in the manifest file. Although you can create a bundle that does not rely on any
classes other than the Java base packages, most bundles import code from other bundles or the base
runtime class path.
You must import any class that you use within a bundle that is not defined in the bundle or that is not a
base Java class, meaning classes within packages that begin with java.. To import another class, include
368 Lotus Expeditor: Developing Applications for Lotus Expeditor
an import clause for the class’s package in the bundle’s manifest. You can explicitly import only whole
packages; individual classes cannot be explicitly imported.
A bundle can make the classes the bundle defines available to other bundles by exporting packages. To
enable other bundles to access a particular package, include an export clause for the package in the
manifest of the bundle that contains the package.
Understanding services
In the OSGi environment, bundles are built around a set of cooperating services that are available from a
shared service registry. The service interface defines the OSGi service, which is implemented as a service
object.
Services decouple the provider from the service user. The only code a service provider and a service user
share is the service definition. You can use Java interfaces to define services. Any class that implements
this interface can provide the service.
Bundles that use services that are not provided by the bundle can notify the framework by including an
Import-Service header in the bundle manifest. However, this is not required. When code within a bundle
requests a provider of the service from the framework, the bundle imports the service at runtime.
A bundle that provides services can also include an Export-Service header in its manifest. When code
within a bundle makes a provider available to the framework, the bundle exports the service at runtime.
Registering and unregistering a service with the OSGi Framework: The framework passes a
BundleContext object to your bundle when it invokes your BundleActivator’s start method. Your bundle
can use the BundleContext object to interact with the framework by calling the methods of the
BundleContext object. One method that your bundle can call is registerService, which uses a service
object and an interface name to register a service with the framework’s service registry.
The recommended approach for using services is to provide all interface and object classes referred to in
the service definition in a bundle separate from the service implementation. The service implementation
bundle then imports the packages from the defining bundle, and exports no packages of its own.
Therefore in a typical service usage, there are three bundles involved – a service interface bundle, the
service implementation, and the service consumer.
In the following example, three bundles are created:
v InterfaceBundle
v ServiceImplBundle
v ServiceConsumerBundle
Interface Bundle
The InterfaceBundle exports the com.ibm.osg.example.mtservice package that contains the
com.ibm.osg.example.mtservice.MyTestService interface. The InterfaceBundle adds an Export-Package:
com.ibm.osg.example.mtservice to its MANIFEST.MF file. Since this bundle has no initialization or startup
needs, no BundleActivator is required for this bundle. This interface defines a service than can print a
message:
package com.ibm.osg.example.mtservice;
public interface MyTestService {
// One method is provided by the service.
// This method will simply print
// the message to standard out.
public void printMessage(String message);
}
Service Implementation Bundle
Reference information 369
The ServiceImplBundle provides an implementation of the MyTestService (Other bundles could provide
alternative implementations). The ServiceImplBundle exports no packages, but does contain an
Import-Package: com.ibm.osg.example.mtservice in its MANIFEST.MF file so that it can have access to the
MyTestService interface.
The following class provides the implementation for our service. In the following example, a service
called com.ibm.osg.example.mtservice.MyTestService registers with the framework. This implementation
of the service prints the message to the standard output. Generally, packages containing service
implementation classes should not be exported to other bundles.
package com.ibm.osg.example.mytestservice;
public class MyTestService implements com.ibm.osg.example.mtservice.MyTestService{
public void printMessage(String message){
System.out.println("MyTestService - " + message);
}
}
The following BundleActivator class registers the com.ibm.osg.example.mtservice.MyTestService service
with the framework.
package com.ibm.osg.example.mytestservice;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
public class MyBundleActivator implements BundleActivator {
ServiceRegistration registration;
/*Create a new instance of the TestService
and then use the BundleContext object to
register it.
Store the registration object
to use to unregister the service when the
bundle is
stopped by the framework.
*/
public void start(BundleContext context)
{
MyTestService testservice = new MyTestService();
if( registration == null ){
registration =
context.registerService(
"com.ibm.osg.example.mtservice.MyTestService",
testservice,
null);
}
}
public void stop(BundleContext context) {
if ( registration != null ){
registration.unregister();
}
registration=null;
}
}
The ServiceConsumer bundle, like the ServiceImplBundle, must contain an Import-Package:
com.ibm.osg.example.mtservice in its MANIFEST.MF file. It may optionally contain an Import-Service:
com.ibm.osg.example.mtservice.MyTestService. This is recommended as the tools will use this to ensure
the framework has the proper prerequisites, but this is not required. See the section “Getting and
un-getting services from the OSGi Framework” for an example of the ServiceConsumer bundle.
Getting and un-getting services from the OSGi Framework: Bundles register and unregister services.
Bundles that depend on services must account for the possibility that the requested service might not be
370 Lotus Expeditor: Developing Applications for Lotus Expeditor
available. The service can register or unregister with the framework at any time. You can use a
ServiceTracker to enable your bundles to query or listen for service registrations and to react
accordingly.
package com.ibm.osg.example.mygetservice;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import com.ibm.osg.example.mtservice.MyTestService;
public class MyBundleActivator
implements BundleActivator, Runnable
{
private boolean done=false;
private ServiceTracker testServiceTracker;
// Bundle Activator Start Method
public void start(BundleContext context)
{
/* Here we initialize and open our ServiceTracker.
It will track any service registering under
the "com.ibm.osg.example.mtservice.MyTestService"
interface.
*/
testServiceTracker =
new ServiceTracker(context,
"com.ibm.osg.example.mtservice.MyTestService",
null);
testServiceTracker.open();
// Here we start a thread that will continue
// to use our service until
// the bundle is stopped.
Thread t = new Thread(this);
t.setName("mygetservice thread");
t.start();
}
/*Bundle Activator Stop Method -- here we stop
the thread and close the
ServiceTracker*/
public void stop(BundleContext context)
{
done=true;
testServiceTracker.close();
}
//Here is a method that uses the service
//we are tracking. First we get
//the service
//from the tracker, then we call its printMessage
//method.
public void useService(String message){
MyTestService testService = (MyTestService)
testServiceTracker.getService();
if( testService != null )
{
// If the service is available then use it.
testService.printMessage(message);
}
else{
// If the service is not available then perform an acceptable action.
Reference information 371
// Here we just print the message to standard out and indicate the service
// was not available.
System.out.println("No MyTestService available - " + message);
}
}
// Simply continues to use the test service
// every second until the done flag is set.
public void run(){
int i = 0;
done = false;
while (!done) {
useService("message from test " + i++);
try{
Thread.sleep(1000);
}
catch( InterruptedException ie ){
}
}
}
}
For an example that uses ServiceTrackers and getting services, refer to the Service Tracker example in
the Samples Gallery > Technology Samples > Lotus Expeditor > OSGi section.
Lotus Expeditor Toolkit
This section provides reference information for using the Lotus Expeditor Toolkit.
Wizards
The Lotus Expeditor Toolkit provides a variety of wizards:
New Client Services Project Wizard
Use this wizard to create a new Client Services project. This wizard can be accessed as follows:
1. Select File > New > Project. The new project wizard displays.
2. Expand the Client Services folder and select Client Services Project.
Refer to the following tables for a description of the options and their default values.
Table 34. Client Services Project page
Option Description Default value
Project name Enter a name for your new Client Services Project. None
Create a Java project Select this if the project will contain Java code. Deselect this
if the project will only contain non-Java resources.
Selected to create a
Java project.
Source Folder Name Folder name for Java source files. src
Output Folder Name Folder name for Java class files. bin
372 Lotus Expeditor: Developing Applications for Lotus Expeditor
This is followed by the Client Services Content page:
Table 35. Client Services Content page
Option Description Default Value
Plug-in ID This is a unique bundle symbolic name. It is suggested that
you update this from the default value. The bundle name
should be a unique URI, following the Java package naming
conventions.
The project name is
used as the default
value.
Plug-in Version The bundle version. The version is in the form of major,
minor, and micro numbers, separated by ‘.’.
1.0.0
Plug-in Name A descriptive bundle name. The default name is
constructed by
appending “Bundle” to
the project name.
Plug-in Provider A description of the bundle provider. None
Class path The class path for the project None
Generate an activator, a
Java class that controls the
plug-in’s life cycle
Selecting this will generate an activator that controls the
plug-in’s life cycle.
Selected
This plug-in will contribute
to the Rich Client Platform
Select this option if you intend for the bundle plug-in to
contribute to the UI.
Selected
Auto-Management
Preference
Select this option to specify whether the application will
search for dependencies based on Required-Bunlde or
Import-Package.
Import-Package
This is followed by the Target Profile page:
Table 36. Target profile options
Option Description Default value
Target Select from the list the Target Definition this Client Services
Project will target. You can change your selection later in the
Client Services property page.
Default Target
Target Definition Features Check the Target Definition Features that your Client
Services Project will require. You can change your selection
later in the Client Services property page. Grey entries are
required by the Target Definition and cannot be un-checked.
The required features
for the selected Target
Definition.
New Client Services Fragment Project Wizard
Use this wizard to create a new Client Services fragment project. This wizard can be accessed as follows:
1. Select File > New > Project. The new project wizard displays.
2. Expand the Client Services folder and select Client Services Fragment Project.
Refer to the following tables for a description of the options and their default values.
Table 37. Client Services Fragment Project page
Option Description Default value
Project name Enter a name for your new Client Services fragment project. None
Reference information 373
Table 37. Client Services Fragment Project page (continued)
Option Description Default value
Project Contents You may deselect ″Use default″ and click Browse to select a
file system location for your new Client Services Fragment
Project.
The ″Use default
location″ creates the
project in your current
workspace
Create a Java project Select this if the project will contain Java code. Deselect this
if the project will only contain non-Java resources.
Selected to create a
Java project.
Source Folder Name Folder name for Java source files. src
Output Folder Name Folder name for Java class files. bin
This is followed by the Fragment Content page:
Table 38. Fragment Content page
Option Description Default Value
Fragment ID This is a unique bundle symbolic name. It is suggested that
you update this from the default value. The bundle name
should be a unique URI, following the Java package naming
conventions.
The project name is
used as the default
value.
Fragment Version The fragment version. The version is in the form of major,
minor, and micro numbers, separated by ‘.’.
1.0.0
Fragment Name A descriptive bundle name. The default name is
constructed by
appending “Fragment”
to the project name.
Fragment Provider A description of the fragment provider. None
Class path The name of the JAR file in which the project’s built contents
will be placed.
The project name is
used as the JAR file
based name.
Plug-in ID Specify the ID, version, and optional version match rule for
the parent bundle that this fragment contributes to. You may
use the Browse button to select a parent from a dialog of
other bundles in the target platform.
None
Minimum Version The minimum version of the host plug-in Host plug-in’s
minimum version
Maximum Version The maximum version of the host plug-in Host plug-in’s
minimum version
Search for dependencies
automatically upon
resource changes
Select this option to enable the tools to search for package
dependencies whenever the user modifies referenced files.
When this option is deselected, the tooling will not search for
any unresolved or unused dependencies in your project.
Selected
Attempt to automatically
resolve Manifest
dependencies
Select this option to enable the tools to automatically manage
the package dependency information in the manifest file.
Package dependencies in your project’s Java code will
automatically be reflected through proper updates to the
manifest file. When this option is deselected, package
dependencies that are not properly reflected in the manifest
are flagged with problem markers, along with quick fixes to
resolve the problems.
Selected
374 Lotus Expeditor: Developing Applications for Lotus Expeditor
Table 38. Fragment Content page (continued)
Option Description Default Value
Give preference to
Require-Bundle
Require-Bundle will be used to automatically resolve a
package dependency in cases where either Require-Bundle or
Import-Package can be used.
Not selected
Give preference to
Import-Package
Import-Package will be used to automatically resolve a
package dependency in cases where either Require-Bundle or
Import-Package can be used.
Selected
This is followed by the Target Definition page:
Table 39. Target definition options
Option Description Default value
Target Definition Select from the list the Target Definition this Client Services
Project will target. You can change your selection later in the
Client Services property page.
Default Target
Target Definition features Check the Target features that your Client Services Project
will require. You can change your selection later in the Client
Services property page. Grey entries are required by the
Target Profile and cannot be unchecked.
The ″Core OSGi
Interfaces″ Target
Feature is required by
all Target Definitions.
Convert Project to Client Services Project Wizard
Use this wizard to convert a Java or Plug-in project to a Client Services project. This wizard can be
accessed as follows:
1. Select File > New > Other. The new wizard displays.
2. Expand the Client Services folder and select Convert Project to Client Services Project.
Table 40. Convert Existing Project Page
Option Description Default Value
Available projects Select the project to be converted. None
Copy project before
conversion
This option will copy the project before conversion. The
user is required to provide the name of the new convert
project if this option is selected.
Not selected
This is followed by the Target Definition page:
Table 41. Target Definition options
Option Description Default value
Target Select the target for this application from the list of Targets.
You can change your selection later in the Client Services
property page.
Default Target
Target Definition Features Check the target features that your Client Services Project
will require. You can change your selection later in the Client
Services property page. Grey entries are required by the
Target Definition and cannot be unchecked.
The ″Core OSGi
Interfaces″ Target
Feature is required by
all Target Definitions.
Reference information 375
Client Services Project Properties page
Use this properties page to update the properties of a Client Services Project. Both Target Definition
selections and project options can be updated. To access this page, right click on the project in the
Package Explorer view, select Properties, and then select Client Services.
Table 42. Target Definition tab
Option Description Default Value
Target Definition Select a Target definition this Client
Services project will target.
None
Target Definition Features Select the Target Features that the
project requires.
The “Core OSGi Interfaces” Target
Feature is required by all Target
Definitions.
Table 43. Options tab
Option Description Default Value
Search for dependencies
automatically upon resource changes
Select this option to enable the tools
to search for package dependencies
whenever the user modifies source
files. When this option is deselected,
the tooling will not search for any
unresolved or unused dependencies
in your project.
Selected
Attempt to automatically resolve
Manifest dependencies
Select this option to enable the tools
to automatically manage the package
dependency information in the
manifest file. Package dependencies
in your project’s Java code will
automatically be reflected through
proper updates to the manifest file.
When this option is deselected,
package dependencies that are not
properly reflected in the manifest are
flagged with problem markers, along
with quick fixes to resolve the
problems.
Selected
Give preference to Require-Bundle Require-Bundle will be used to
automatically resolve a package
dependency in cases where either
Require-Bundle or Import-Package
can be used.
Not selected
Give preference to Import-Package Import-Package will be used to
automatically resolve a package
dependency in cases where either
Require-Bundle or Import-Package
can be used.
Selected by default for projects that
are not contributing to the Rich
Client Platform.
Dialogs
The Lotus Expeditor Toolkit includes the following dialogs:
Client Services Launch Configuration dialog
The Lotus Expeditor Toolkit supports launching a local instance of the Lotus Expeditor runtime. This
supports the ability to both run and debug Client Services projects from your workspace. The Lotus
Expeditor runtime launch extends the Eclipse run-time workbench launch. It is suggested that you use
376 Lotus Expeditor: Developing Applications for Lotus Expeditor
the Lotus Expeditor launcher for running Client Services projects, since it automatically handles setting
up the proper native library environment for the Lotus Expeditor runtime.
Perform the following procedure to run or debug a project using the Lotus Expeditor runtime launch:
1. Select Run > Run... to run under the Lotus Expeditor runtime, or select Run > Debug... to debug
under the Lotus Expeditor runtime.
2. Select Client Services under configurations, and click New to create a new configuration.
Note: If Lotus Expeditor runtime configurations have already been created, you can directly select
one.
3. On the arguments tab, insure that the JRE selected is the jclDesktop.
4. By default, the launcher selects all the plug-ins/features from the Client Services default target
definition. To change the plug-ins/features in your Client services instance, please go to the Profile
tab.
5. Click either the Run or Debug button to launch the runtime.
Refer to the Running a Plug-in section of the PDE Guide for further information on launch options.
Debugging a remote Client Services runtime: To debug a Client Services runtime that has been started
through other means than the Client Services runtime launcher, refer to “Remote debugging and testing”
on page 325.
Lotus Expeditor Toolkit Preference Dialog
To access and set preferences for the Lotus Expeditor, select Window > Preferences > Client Services.
Refer to the following tables for a description of the options and their default values of the resulting
dialog screens:
Auto-Management Preferences
Table 44. Auto-Management Preferences
Option Description Default Value
Search for dependencies
automatically upon resource changes
Select this option to enable the tools
to search for package dependencies
whenever the user modifies source
files. When this option is deselected,
the tooling will not search for any
unresolved or unused dependencies
in your project.
Selected
Attempt to automatically resolve
Manifest dependencies
Select this option to enable the tools
to automatically manage the package
dependency information in the
manifest file. Package dependencies
in your project’s Java code will
automatically be reflected through
proper updates to the manifest file.
When this option is deselected,
package dependencies that are not
properly reflected in the manifest are
flagged with problem markers, along
with quick fixes to resolve the
problems.
Selected
Reference information 377
Table 44. Auto-Management Preferences (continued)
Option Description Default Value
Give preference to Require-Bundle Require-Bundle will be used to
automatically resolve a package
dependency in cases where either
Require-Bundle or Import-Package
can be used.
Not selected
Give preference to Import-Package Import-Package will be used to
automatically resolve a package
dependency in cases where either
Require-Bundle or Import-Package
can be used.
Selected by default for projects that
are not contributing to the Rich
Client Platform.
Default Target Selection
Use the drop-down list to choose the default Target Definition. This means that when creating a new
Client Services project or a new Client Services launch configuration, the default Target Definition
selection will be obtained from this setting.
Show plug-in objects in editors and dialogs using...
v Identifiers
This selection displays the plug-ins and features objects using their given ID attribute. A plug-in or
feature will always have an ID value associated with them. This is not the case with the NAME.
v Presentation names
This selection displays the plug-ins and features objects using their given NAME attribute.
Tag library
The following tag library is provided by the platform to assist in web and portlet application
development.
Aggregation tag library
The following tags are supported in the Aggregation Tag Library:
v init
Description - This tag initializes the portlet framework and must be used in the beginning of the JSP. All
other tags described in this section are only valid in the body of this tag, therefore the init tag usually
encloses the whole body of a JSP. In case the current URL contains an action flag, the action method of
the corresponding portlet is called.
Attributes
– portletURLPrefix="<any string>" (mandatory) This url defines the prefix used for PortletURLs.
Portlet URLs are created either by the state tag or within a portlet’s render method (which is called
by using the insert tag).
– portletURLSuffix="<any string>" (optional) This url defines the suffix used for PortletURLs. Portlet
URLs are created either by the state tag or within a portlet’s render method (which is called by
using the insert tag).
– portletURLQueryParams="<any string>" (optional) This url defines the query parameters used for
PortletURLs. Portlet URLs are created either by the state tag or within a portlet’s render method
(which is called by using the insert tag).
Sub-Tag - state, insert
Tag Library URI - http://ibm.com/portlet/aggregation
v state
378 Lotus Expeditor: Developing Applications for Lotus Expeditor
Description - This tag creates a URL pointing to the given portlet using the given state. This URL can
either be placed into a variable specified by the var attribute, or be written directly to the output
stream. This tag is especially useful to create URLs for HTML buttons, images, and so forth. When the
URL is invoked, the state changes defined in the URL will be applied to the given portlet.
Attributes
– url="<context>/<portlet-name>" (mandatory) Identifies the portlet for this tag by using the context
and portlet-name to address the portlet.
– windowId="<any string>" (optional) Defines the window ID for the portlet URL created by this tag.
– var="<any string>" (optional) If defined, the URL is not written to the output stream but rather
written into a variable with the given scope and name.
– scope="page|request|session|application" (optional; Default:page) Only valid if var is being used.
If defined the URL is not written to the output stream but rather a variable is created in the given
scope with the given name.
– portletMode="view|help|edit|<custom>" This attribute sets the portlet mode.
– portletWindowState="maximized|minimized|normal|<custom>" This attribute sets the window state.
– action="true/false" (optional; default:false) This attribute defines whether this is an action URL or
not.
Parent-Tag - init
Sub-Tag - urlParam
Tag Library URI - http://ibm.com/portlet/aggregation
v urlParam
Description - This tag calls the render method of the portlet and retrieves the content as well as the title.
The content and title of the specified portlet can optionally be placed into variables using the
contentVar and titleVar attributes.
Attributes
– url="<context>/<portlet-name>" (mandatory) Identifies the portlet for this tag by using the context
and portlet-name to address the portlet.
– windowId="<any string>" (optional) Defines the window ID of the portlet.
– contentVar="<any string>" (optional) If defined the portlet’s content is not written to the output
stream but rather written into a variable with the given scope and name.
– contentScope="page|request|session|application" (optional - Default:page) Only valid if
contentVar is being used. If defined, the portlet’s content is not written to the output stream but
rather written into a variable with the given scope and name.
– titleVar="<any string>" (optional) If defined, the portlet’s title is written into a variable with the
given scope and name. In case it is not defined, the title be disregarded and not written to the
output stream.
– titleScope="page|request|session|application" (optional - Default:page) Only valid if titleVar is
being used. If defined the portlet’s title is not written to the output stream but written into a
variable with the given scope and name.
Parent-Tag - init
Tag Library URI - http://ibm.com/portlet/aggregation
v initBranding
Description - This tag provides the branding theme information to the portlet aggregator page. The
theme includes: a stylesheet, the title bar images, the title bar background color and the content
background color.
Attributes - None.
Parent-Tag - None.
Tag Library URI - http://ibm.com/portlet/aggregation
v initUrlPrefix
Reference information 379
Description - This tag provides the URL prefix information for all portlet URLs, including the render
and action URLs. The URL prefix includes the server address and the port number.
Attributes - None.
Parent-Tag - None.
Tag Library URI - http://ibm.com/portlet/aggregation
Message reference
This section provides message reference information.
Web Container messages
The following is a list of the application server messages that you might encounter when using the Web
Container, and the appropriate user responses.
SRVE0016E: Illegal Argument Exception: Invalid header format
Problem User response
Attempting to output a buffer of size less than 0. Only write buffers of size of 0 or greater.
SRVE0014E: Uncaught service() exception root cause {0}: {1}
Problem User response
Uncaught exception thrown in servlet service method. Varies by root cause.
SRVE0015E: Failure to initialize Web application {0}
Problem User response
Could not initialize the web application specified in the
message. Check the error log for more details.
Look in the error log to determine the root cause of this
problem.
SRVE0016E: Illegal Argument Exception: Invalid header format
Problem User response
Invalid format found for a request header. Check client header creation.
SRVE0021I: Servlet unloaded: {0}
Problem User response
The specified servlet has been unloaded after the destroy
method has been called.
none.
SRVE0031E: Illegal Argument Exception: {0} is not a directory.
Problem User response
The specified path is not a directory. Make sure the directory exists or that you are requesting
the right directory.
380 Lotus Expeditor: Developing Applications for Lotus Expeditor
SRVE0035E: WebAppSecurityCollaborator
Problem User response
WebAppSecurityCollaborator Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
SRVE0041E: Illegal Argument Exception: Bootstrap file not found
Problem User response
This is an internal system error and is probably caused
by an earlier failure.
Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
RVE0042E: Illegal Argument Exception: Invalid content length
Problem User response
This is an internal system error and is probably caused
by an earlier failure.
Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
SRVE0053E: Illegal Argument Exception: Invalid date format
Problem User response
Invalid date format. Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
SRVE0055I: Servlet wait for destroy time-out has expired, destroy will be forced: {0}
Problem User response
Servlet is being destroyed after wait has timed out. none.
SRVE0056E: Illegal Argument Exception: Unsupported flag
Problem User response
This is an internal system error and is probably caused
by an earlier failure.
Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
SRVE0058E: Did not realize destroy() exception thrown by servlet {0}: {1}
Problem User response
Could not destroy specified servlet. Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
Reference information 381
SRVE0060E: Unable to bind host name [{0}] to servletHost [{1}]
Problem User response
Could not bind the specified host name to the specified
servlet host.
Make sure the host name has been specified correctly
and that the servlet host has been specified in
virtualhosts.xml.
SRVE0061E: Illegal Argument Exception: Invalid directory specified: {0}
Problem User response
The specified directory is invalid. Check directory specification for errors.
SRVE0066I: Waiting for servlet to finish servicing requests: {0}
Problem User response
Servlet is still handling the request. Waiting for request
to complete before calling destroy.
none.
SRVE0068E: Uncaught exception thrown in one of the service methods of the servlet: {0}. Exception
thrown : {1}
Problem User response
Uncaught exception thrown in one of the service
methods of the servlet.
Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
SRVE0070E: Error initializing for next request
Problem User response
Internal web container error. Gather server logs and contact IBM service.
SRVE0080E: Invalid content length
Problem User response
Internal server error. Gather logs and contact IBM service.
SRVE0086E: Illegal Argument Exception: Missing resource bootstrap properties
Problem User response
Internal server error. Gather logs and contact IBM service
SRVE0097I: Servlet unload initiated: {0}
Problem User response
The specified servlet is being unloaded from service. None
SRVE0100E: Did not realize init() exception thrown by servlet {0}: {1}
Problem User response
Could not initialize the specified servlet. Make sure the servlet class file is available on the
application classpath.
382 Lotus Expeditor: Developing Applications for Lotus Expeditor
SRVE0103E: Illegal Argument Exception: ScriptName must be the first part of the URI
Problem User response
This is an internal system error and is probably caused
by an earlier failure.
Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
SRVE0105E: An exception occurred in Session.releaseSession()
Problem User response
Problem releasing a session. Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
SRVE0108E: Illegal Argument Exception: Missing flag value
Problem User response
Missing flag value. Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
SRVE0109E: Illegal Argument Exception: Invalid ObjectPool instantiated.
Problem User response
This is an internal system error and is probably caused
by an earlier failure.
Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
SRVE0115E: Error occurred while invoking error reporter {0}
Problem User response
Problem invoking the specified error reporter. Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
SRVE0120E: IO Error {0}
Problem User response
IO error encountered. Varies depending on reported root cause.
SRVE0121E: Illegal Argument Exception: Trying to write less than 0 bytes
Problem User response
Attempting to output a buffer of size less than 0. Only write buffers of size of 0 or greater.
Reference information 383
SRVE0126E: Invalidation Exception: {0} was created
Problem User response
This is an internal system error and is probably caused
by an earlier failure.
Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
SRVE0133E: An error occurred while parsing parameters. {0}
Problem User response
Problem encountered parsing servlet parameters. Check parameter definitions in the web.xml file.
SRVE0138E: postInvoke Security Exception
Problem User response
Security problem encountered during request processing. Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
SRVE0139E: Exception in Security preInvoke {0}
Problem User response
Security problem encountered during request processing. Varies with root cause.
SRVE0140E: Could not instantiate the security collaborator {0}
Problem User response
Internal server error. Gather logs and contact IBM service.
SRVE0155E: Failed to load encoding.properties file {0}
Problem User response
Could not load the specified file. Ensure the specified file exists.
SRVE0156E: Failed to load converter.properties file {0}
Problem User response
Could not load the specified file. Ensure the specified file exists.
SRVE0157E: setBufferSize() called after first write to Output Stream/Writer
Problem User response
An attempt was made to set the response buffer size
after the response was already committed.
Remove the setBufferSize() call or move it to a position
before the response commit.
384 Lotus Expeditor: Developing Applications for Lotus Expeditor
SRVE0160E: Exception while rolling back UserTransaction: {0}
Problem User response
Problem encountered rolling back the specified user
transaction.
Examine the server logs in order to determine the root
cause of the problem. If there are no related messages or
they do not help to resolve the problem, please contact
IBM Support.
SRVE0161I: IBM WebSphere Application Server - Web Container. Copyright IBM Corp. 1998-2006
Problem User response
Application server identifier. None
SRVE0162I: Servlet Specification Level: 2.4
Problem User response
The servlet specification level supported by the
application server.
None
SRVE0163I: Supported JSP Specification Level: 2.0
Problem User response
The JSP specification level supported by the application
server.
None
SRVE0164E: Web Application {0} uses the context root {1}, which is already in use by Web Application
{2}. Web Application {3} will not be loaded.
Problem User response
Two web applications on the same virtual host share the
same context root.
Either change the context root of one of the web
applications or move one to a different virtual host.
SRVE0169I: Loading Web Module: {0}.
Problem User response
WebModule is starting and being made available for
service.
None
SRVE0180I: [{0}] [{1}] [Servlet.LOG]: {2}
Problem User response
Informational message {2} logged via ServletContext.log
from application {0} with context root {1}.
None
SRVE0181I: [{0}] [{1}] [Servlet.LOG]: {2}: {3}
Problem User response
Informational message {2} logged via ServletContext.log
from application {0} with context root {1} and stack trace
{3}.
None
Reference information 385
SRVE0185E: An error has occurred while processing request:
Problem User response
Exception that occurred when processing the request. Review the message to determine the root cause of the
problem.
SRVE0186E: Can’t set buffer size after data has been written to stream
Problem User response
Some data has already been written to the stream, and
hence the buffer size can no longer be controlled
Either set the buffer size before writing data or reset the
buffer before setting the size.
SRVE0187E: Check your classpath to ensure that all classes required by the servlet are present.
Problem User response
A required class was not found Make sure your classpath reflects all the classes you may
be accessing
SRVE0188E: Class {0} does not implement servlet
Problem User response
The class mentioned needs to implement
javax.servlet.Servlet or extend one of
javax.servlet.GenericServlet or
javax.servlet.http.HttpServlet
The class mentioned needs to implement
javax.servlet.Servlet or extend one of
javax.servlet.GenericServlet or
javax.servlet.http.HttpServlet
SRVE0189E: Error occurred while finishing request
Problem User response
This is an error that occurs when the request was being
completed. This could have happened because of a
communication error within the Application Server but
should not have affect the processing of the request.
None
SRVE0190E: File not found: {0}
Problem User response
The mentioned file was not found. Ensure that the mentioned file is in place.
SRVE0194E: Illegal from included servlet
Problem User response
The attempted operation cannot be performed in an
included servlet/JSP (Refer to the Servlet specification)
None
SRVE0196E: Missing required initialization parameter: {0}
Problem User response
The parameter with the mentioned name has no value.
This is a required parameter.
Please supply a value to the mentioned parameter.
386 Lotus Expeditor: Developing Applications for Lotus Expeditor
SRVE0199E: OutputStream already obtained
Problem User response
Application requested a PrintWriter after the
ServletResponse OutputStream had already been
obtained.
Review application to determine if both PrintWriter and
OuputStream were obtained.
SRVE0200E: Servlet [{0}]: Could not find required class - {1}
Problem User response
Servlet could not find a required class needed to service
the request.
None
SRVE0201E: Servlet [{0}]: not a servlet class
Problem User response
The class mentioned needs to implement
javax.servlet.Servlet or extend one of
javax.servlet.GenericServlet or
javax.servlet.http.HttpServlet
The class mentioned needs to implement
javax.servlet.Servlet or extend one of
javax.servlet.GenericServlet or
javax.servlet.http.HttpServlet
v
SRVE0202E: Servlet [{0}]: {1} was found, but is corrupt:
Problem User response
The servlet class was found to be corrupted Please re-compile the class and try again.
SRVE0203E: Servlet [{0}]: {1} was found, but is missing another required class.
Problem User response
A required class was not found Make sure all required classes are placed in the
folders/Jars representing the classpath
SRVE0204E: The host {0} has not been defined
Problem User response
The virtual host was not found Please check the virtual host configuration in the
virtualhosts.xml file, or through the admin console
SRVE0205E: The host {0} on port {1} has not been defined
Problem User response
Unable to locate Virtual Host defined to handle this
request URI.
None
SRVE0206E: This error typically implies that the servlet was originally compiled with classes which
cannot be located by the server
Problem User response
None Please recompile the servlet with all required
components placed in the class path.
Reference information 387
SRVE0207E: Uncaught initialization exception thrown by servlet
Problem User response
The target threw an exception during initialization that
was not caught by the user code.
Please account for the exception in target code.
SRVE0208E: Unsupported conversion
Problem User response
The code tried to do an illegal conversion of a header
value. For example, from int to Data, when such a
conversion is not possible
None
SRVE0209E: Writer already obtained
Problem User response
The writer for this response has already been obtained.
Please use the existing writer
Review application to determine if both PrintWriter and
OuputStream were obtained.
SRVE0210I: This problem can be debugged by recompiling the servlet using only the classes in the
application’s runtime classpath
Problem User response
None None
SRVE0213E: class not found
Problem User response
Internal exception. Contact WebSphere Support for further assistance.
SRVE0214E: invalid count
Problem User response
Internal exception. Contact WebSphere Support for further assistance.
SRVE0215E: non-HTTP request or response
Problem User response
The servlet chain that has been defined can only handle
HTTP requests.
None
SRVE0216E: post body contains less bytes than specified by content-length
Problem User response
The incoming request may be corrupted. None
SRVE0217E: {0} is not a valid class
Problem User response
This error occurred when the webcontainer tried to load
an internal implementation class.
Check to make sure the original WAS classpath has not
been modified.
388 Lotus Expeditor: Developing Applications for Lotus Expeditor
SRVE0218E: Forbidden: Web Security Exception
Problem User response
The request tried to access a forbidden resource. Please make sure the requesting entity has the required
privileges.
SRVE0219I: No Error to Report
Problem User response
DefaultErrorReport was invoked but no error was found. Review application to determine cause of problem.
SRVE0220I: Wrapped Error-
Problem User response
Error that caused the problem. None
SRVE0221E: Serving contents of JSP files is not allowed
Problem User response
Cannot serve the contents of a JSP file None
SRVE0223I: StackTrace
Problem User response
None None
SRVE0224I: Target Servlet:
Problem User response
None None
SRVE0225I: Root Error-
Problem User response
None None
SRVE0227I: 1. Check that the class resides in the proper package directory.
Problem User response
Package name of resource does not match the compiled
class file’s package.
None
SRVE0228I: 2. Check that the classname has been defined in the server using the proper case and fully
qualified package.
Problem User response
None None
Reference information 389
SRVE0229I: 3. Check that the class was transferred to the filesystem using a binary transfer mode.
Problem User response
None None
SRVE0230I: 4. Check that the class was compiled using the proper case (as defined in the class
definition).
Problem User response
None None
SRVE0231E: 5. Check that the class file was not renamed after it was compiled.
Problem User response
None None
SRVE0233E: Error Report
Problem User response
None None
SRVE0234I: Application classpath=[{0}]
Problem User response
None None
SRVE0235E: [{0}] reported an error
Problem User response
See reported error None
SRVE0236E: Error occurred while invoking initialization collaborator on starting() call
Problem User response
Problem User response
Check the FFDC logs for more details on the error Check the FFDC logs for more details on the error
SRVE0237E: Error occurred while invoking initialization collaborator on started() call
Problem User response
Check the FFDC logs for more details on the error Check the FFDC logs for more details on the error
SRVE0238E: Resource paths should have a leading slash
Problem User response
None Please supply a leading slash before the path and try
again.
390 Lotus Expeditor: Developing Applications for Lotus Expeditor
SRVE0239I: Extension Factory [{0}] was registered successfully.
Problem User response
None None
SRVE0240I: Extension Factory [{0}] has been associated with patterns [{1}].
Problem User response
None None
SRVE0241I: Using [{0}] as the server root in getTempDirectory().
Problem User response
None None
SRVE0242I: [{2}] [{1}] [{0}]: Initialization successful.
Problem User response
The init() method of the target resource executed
successfully and the target has been places into service.
None
SRVE0243I: Reaper thread interval: [{0}] and inactive limit:[{1}] started.
Problem User response
None None
SRVE0244I: Reaper thread unloading servlet: [{0}].
Problem User response
The reaper thread removes servlets that have been
inactive for longer than the reaper inactive limit.
None
SRVE0245I: Reaper thread removing mapping: [{0}] for servlet: [{0}].
Problem User response
The reaper thread removes the mappings for jsps and
other extensions that have been inactive longer the the
reaper inactive limit.
None
SRVE0246I: Reaper thread destroying servlet: [{0}].
Problem User response
None None
SRVE0247E: Error during reaper thread execution.
Problem User response
Exception occurred in the reaper thread processing. Reaper processing is just a form of garbage collection.
Reference information 391
SRVE0248I: Requests are currently being processed. Waiting up to 60 seconds before forcing filter
destroy.
Problem User response
None None
SRVE0249W: Application {0} has requested SyncToOSThread, but the server is not enabled for
SyncToOSThread
Problem User response
SyncToOSThread synchronizes the J2EE role identity to
the OS thread, meaning that the OS thread identity is
made equal to the J2EE role identity for the duration of
the request. This message indicates that while an
application has been configured to utilize
SyncToOSThread functionality, the server it is installed
into is not configured to support SyncToOSThread
capabilities.
If SyncToOSThread capabilities are desired for the server
in question, please refer to the InfoCenter for a
description as to how to enable SyncToOSThread for a
server.
SRVE0250I: Web Module {0} has been bound to {1}.
Problem User response
The specified web module has been associated to the
virtualhost mentioned, and can be accessed through any
of the host/port combinations for that virtualhost.
None
SRVE0251W: The threadpool configured under the webcontainer is not being used.
Problem User response
There exists a ThreadPool configuration under the
webcontainer. Any changes made to this configuration
item will not result in changes in the system.
None
SRVE0252W: Transports and Chains have been detected! The transports have been changed to use a
new model. Please use the migration utilities to migrate the transports to the new model. The
threadpool configuration under the webcontainer will not be used with these transports.
Problem User response
Transports have been changed to use Channel Chains.
Please refer to the Infocenter to review information on
Channel Chains, and how to migrate the existing
transports to be channel chain based. The thread pool
configuration under the webcontainer will not be used to
these transports.
Use the migration utilities to migrate your configuration
model from the transports to the new channel chains.
SRVE0253I: [{2}] [{1}] [{0}]: Destroy successful.
Problem User response
The destroy() method of the target resource executed
successfully and the target has been removed from
service.
None
392 Lotus Expeditor: Developing Applications for Lotus Expeditor
SRVE0254E: Failed to set request character encoding: [{0}].
Problem User response
Invalid request encoding specified on the request. Review logs to determine whether the specified encoding
is a valid encoding.
SRVE0255E: A WebGroup/Virtual Host to handle {0} has not been defined.
Problem User response
Could not find a web group (web module) or virtual
host to handle the request.
Be sure the web group and virtual host is defined and
deployed.
SRVE0256E: WebContainer has not been initialized.
Problem User response
WebContainer is not initialized until an application has
been installed.
None
Reference information 393
394 Lotus Expeditor: Developing Applications for Lotus Expeditor
Appendix. Notices
This information was developed for products and services offered in the U.S.A. IBM might not offer the
products, services, or features discussed in this document in other countries. Consult your local IBM
representative for information on the products and services currently available in your area. Any
reference to an IBM product, program, or service is not intended to state or imply that only that IBM
product, program, or service may be used. Any functionally equivalent product, program, or service that
does not infringe any IBM intellectual property right may be used instead. However, it is the user’s
responsibility to evaluate and verify the operation of any non-IBM product, program, or service.
IBM might have patents or pending patent applications covering subject matter in this document. The
furnishing of this document does not give you any license to these patents. You can send license
inquiries, in writing, to:
IBM Director of Licensing
IBM Corporation
North Castle Drive
Armonk, NY 10504-1785
U.S.A.
For license inquiries regarding double-byte (DBCS) information, contact the IBM Intellectual Property
Department in your country or send inquiries, in writing, to:
IBM World Trade Asia Corporation Licensing
2-31 Roppongi 3-chome, Minato-ku
Tokyo 106, Japan
The following paragraph does not apply to the United Kingdom or any other country where such
provisions are inconsistent with local law: INTERNATIONAL BUSINESS MACHINES CORPORATION
PROVIDES THIS PUBLICATION “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some
states do not allow disclaimer of express or implied warranties in certain transactions, therefore, this
statement may not apply to you.
This information could include technical inaccuracies or typographical errors. Changes are periodically
made to the information herein; these changes will be incorporated in new editions of the information.
IBM may make improvements and/or changes in the product(s) and/or the program(s) described in this
information at any time without notice.
Any references in this information to non-IBM Web sites are provided for convenience only and do not in
any manner serve as an endorsement of those Web sites. The materials at those Web sites are not part of
the materials for this IBM product and use of those Web sites is at your own risk.
IBM may use or distribute any of the information you supply in any way it believes appropriate without
incurring any obligation to you.
Licensees of this program who wish to have information about it for the purpose of enabling: (i) the
exchange of information between independently created programs and other programs (including this
one) and (ii) the mutual use of the information which has been exchanged, should contact:
IBM Corporation
Intellectual Property Law
© Copyright IBM Corp. 2004, 2006 395
Department LZMS
11501 Burnet Road
Austin, TX 78758-3400
U.S.A.
Such information may be available, subject to appropriate terms and conditions, including in some cases,
payment of a fee.
The licensed program described in this document and all licensed material available for it are provided
by IBM under terms of the IBM Customer Agreement, IBM International Program License Agreement, or
any equivalent agreement between us.
Any performance data contained herein was determined in a controlled environment. Therefore, the
results obtained in other operating environments may vary significantly. Some measurements may have
been made on development-level systems and there is no guarantee that these measurements will be the
same on generally available systems. Furthermore, some measurement may have been estimated through
extrapolation. Actual results may vary. Users of this document should verify the applicable data for their
specific environment.
Information concerning non-IBM products was obtained from the suppliers of those products, their
published announcements or other publicly available sources. IBM has not tested those products and
cannot confirm the accuracy of performance, compatibility or any other claims related to non-IBM
products. Questions on the capabilities of non-IBM products should be addressed to the suppliers of
those products.
All statements regarding IBM’s future direction or intent are subject to change or withdrawal without
notice, and represent goals and objectives only.
All IBM prices shown are IBM’s suggested retail prices, are current and are subject to change without
notice. Dealer prices may vary.
This information is for planning purposes only. The information herein is subject to change before the
products described become available.
This information contains examples of data and reports used in daily business operations. To illustrate
them as completely as possible, the examples include the names of individuals, companies, brands, and
products. All of these names are fictitious and any similarity to the names and addresses used by an
actual business enterprise is entirely coincidental.
COPYRIGHT LICENSE:
This information contains sample application programs in source language, which illustrate programming
techniques on various operating platforms. You may copy, modify, and distribute these sample programs
in any form without payment to IBM, for the purposes of developing, using, marketing or distributing
application programs conforming to the application programming interface for the operating platform for
which the sample programs are written. These examples have not been thoroughly tested under all
conditions. IBM, therefore, cannot guarantee or imply reliability, serviceability, or function of these
programs. You may copy, modify, and distribute these sample programs in any form without payment to
IBM for the purposes of developing, using, marketing, or distributing application programs conforming
to IBM’s application programming interfaces.
You may copy, modify, and distribute these sample programs in any form without payment to IBM for
the purposes of developing, using, marketing, or distributing application programs conforming to IBM’s
application programming interfaces.
396 Lotus Expeditor: Developing Applications for Lotus Expeditor
Each copy or any portion of these sample programs or any derivative work, must include a copyright
notice as follows:
© (your company name) 2004, 2006. Portions of this code are derived from IBM Corp. Sample Programs.
© Copyright IBM Corp. 2004, 2006 All rights reserved.
If you are viewing this information softcopy, the photographs and color illustrations may not appear.
Trademarks
The following terms are trademarks or registered trademarks of International Business Machines
Corporation in the United States, or other countries, or both:
Everyplace
IBM
IBM logo
Lotus
Rational
Rational Suite
WebSphere
Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other
countries, or both.
UNIX is a registered trademark of The Open Group in the United States and other countries.
Linux is a registered trademark of Linux Torvalds in the United States, other countries, or both.
Microsoft and Windows are trademarks of Microsoft Corporation in the United States, other countries, or
both.
Other company, product or service names may be trademarks or service marks of others.
Appendix. Notices 397
398 Lotus Expeditor: Developing Applications for Lotus Expeditor
����
Printed in USA