Editor's note: This article is the fifth in a series of articles on composite applications to be published on developerWorks Lotus over the next few months. See the previous developerWorks articles, "Designing composite applications: Unit testing," "Designing composite applications: Design patterns," "Designing composite applications: Component design," and "The Lead Manager application in IBM Lotus Notes V8: An Overview."
Composite applications are a key element in a Service-Oriented Architecture (SOA) and contextual collaboration strategy. They support business flexibility for companies and organizations that must rapidly respond to changing demands in today's competitive markets. Composite applications are aggregations of user interface components that are loosely coupled to support intercomponent communication.
Components can be reused in multiple composite applications. The ability to combine multiple technologies into a single application provides significant business value. It enables companies to protect and extend their existing assets with increasing degrees of flexibility, and it allows them to respond quickly and cost-effectively to their emerging business requirements with applications that are significantly easier to create than multiple application development environments.
This loose coupling of the composite application architecture also enables diverse groups in different locations to leverage each others' efforts and to interoperate with each other. For example, one department may be working on the accounting application for a company, while another group is working on a sales lead tracking application. The composite application vision is to add to the accounting application some components from the sales lead tracking application to give pertinent views of assets in an accounting context. Similarly, the sales lead tracking application could incorporate components from the accounting application to give accounting information within an asset context. As your company develops more and more composite applications, the potential for integration increases exponentially. The goal is that the whole is greater than the sum of the parts.
IBM Websphere Portal developers are already familiar with the composition application model. This approach has been extended to IBM Lotus Notes 8, enabling Lotus Notes developers to surface their Lotus Notes applications as one or more components in a composite application. IBM Lotus Domino Designer has been extended to leverage the property broker and to provide a more intuitive user environment. Lotus Notes 8 also supports the inclusion of Eclipse components. A composite application may have any combination of Lotus Notes and Eclipse components. These components may be presented together in the same user interface (UI) for on-the-glass integration, or if extended to use the property broker, they can fully interoperate. You can define composite applications using the Composite Application Editor or the WebSphere Portal Application Template Editor.
The development of components for composite applications is different from traditional Eclipse or Lotus Notes application development. It is highly desirable to create components that are flexible enough to be deployed in composite applications at later dates without significant rework.
The Composite Applications - Design and Management section of the Lotus Domino Designer 8 help discusses how to build Eclipse components, providing a reference to the APIs used and an example. There is also a tutorial that walks you through creating a simple composite application based on Notes components. Additional material and examples can be found in the developerWorks article, "Using IBM Lotus Expeditor Toolkit V6.1 with IBM Lotus Notes V8 and IBM Lotus Sametime V7.5.1," and in the Developing Applications for Lotus Expeditor section of the IBM Lotus Expeditor information center.
This article builds on that information and introduces some helper classes so that you can quickly build and deploy feature-rich, reusable, Eclipse-based components. The components we discuss are usable in both IBM Lotus Expeditor and Lotus Notes, but for simplicity, our examples refer to Lotus Notes. The code referenced in this article can be found in the com.ibm.cademo.sl.comp.cloud project contained in the Help - About document in the Composite Application Component Library posted to OpenNTF.org.
A component view is more than just a piece of UI. It is a programmatic unit that communicates and participates with the outside world. Through this communication capability, several component views are wired together to form a composite application. We now look at some features of the Lotus Expeditor and Lotus Notes platforms that allow for this.
- Topology handler. This feature lets you set static values at assembly time for a component view. The topology handler uses these values at runtime to lay out the component view within the Lotus Notes client. Other values may be component view specific and used at runtime to customize its presentation or function. Using the Composite Application Editor (CAE) in Lotus Notes, you can access properties for a component view through the Advanced Component Properties dialog box (see figure 1), allowing for assembly-time configuration of a component view.
Figure 1. Advanced Component Properties in the CAE
For example, the tag cloud component lets you set advanced component properties called drawHeader and drawFooter at assembly time. Then, at runtime, the component view checks the values of these properties and, if true, the corresponding section of UI is drawn. - Property broker. This feature allows component-view-to-component-view connections to be created. A property is a source value that a component view can set to indicate a change of value. An action is a destination value that a component view can indicate that it consumes.
- Wiring. Wires can be established at assembly time to connect properties output by component views to actions in component views that process changes to those properties. In Lotus Notes, the CAE is used to wire components.
At runtime, a component view can indicate to the property broker that one of its property's values has changed. The property broker responds by passing the new values to the component views that have been connected with a wire. The new value of the property is passed as a parameter to the action selected. For example, the wiring in figure 2 shows how the tag cloud's FocusedEntity value is transmitted and set into the document viewer's Set Column Filter value at runtime when the tag cloud selection changes.
Figure 2. Properties, actions, and wires in the wiring tool in the CAE
- Data model. Both properties and actions involve getting or setting the value of data elements in your component view. The conventional structure for storing collections of data elements in Java is a JavaBean, which, conveniently, also has a well defined mechanism for propagating changes. Note that there are many other methods to maintain your data model in Java besides JavaBeans. Eclipse-based component views are not required to use JavaBeans; however, we use them in this article as an example of a data model with which most Java programmers are familiar. You may apply these techniques to any data model you like.
We introduce a number of helper classes over the course of this example. Although not required for the simple component view we are making, they can be reused across components and are of great value when developing more complicated components.
An Eclipse component view is based on a View part, which is coupled with a data model. Each instance of the component view has its own instance of a data model. All additional work consists of coupling the pieces together. Your data model is coupled with your view to present the data to the user and to allow the user to interact with and change the data. On the backend, the data model is coupled with the property broker to broadcast changes to values in the data model and to listen for changes imposed from other component views. Last, the data model is coupled with the topology handler to read initial instance values into the data model. This strategy is easily compatible with the popular Model-View-Controller (MVC) design pattern (see figure 3).
Figure 3. MVC design pattern

For this component view, we create a tag cloud component view. We have an SWT control (see TagCloud.java in the com.ibm.cademo.sl.comp.cloud package) that we want to expose. We may use this to display the categories in a discussion database as shown in figure 4.
Figure 4. Composite application with a discussion database and a tag cloud

The first important decision to be made is which data elements to expose. For simplicity, we discuss two: PrimaryData as a HashMap of key-value pairs that drive the display model, and FocusedEntity, which represents the current tag under focus. For example, figure 4 shows a composite application with a discussion database Notes component view and a tag cloud. Here information about all the categories in the database is weighted by the number of articles and sent for display to the tag cloud. Additionally, categories can be selected in the tag cloud and that focus passed back to the discussion database component view. This uses view filtering to show only articles in that category.
For Lotus Expeditor 6.x and Lotus Notes 8, only strings can be sent as property values between Eclipse components and Lotus Notes components. A HashMap, though, is a complex type. The technique for dealing with this is to devise a form of serialization that represents the complex type in a simple string. Even though only base types of strings are supported, it is possible to add typing data to distinguish between types of strings semantically. Let's do that to ensure that only values of TagCloudDataString are passed in for the tag cloud. Because the FocusedEntity is just a key, we use the generic xsd:string type.
The data model we use is a simple JavaBean, and to aid this we introduce our first helper class: PCSBean.java. This is a very simple class that uses the java.beans.PropertyChangeSupport class to create APIs to support the propagation of JavaBean property changes to property broker property changes.
Now we can create our bean as a subclass of this. In TagCloudViewBean.java (see listing 1) you can see where we've created the two data members, PrimaryData and FocusedEntity, and added setters and getters for them. Last, we extend the setters to call into the functions of the base class to propagate notification of property changes.
Listing 1. TagCloudViewBean.java
public class TagCloudViewBean extends PCSBean {
private String mPrimaryData;
private String mFocusedEntity;
public TagCloudViewBean() {
mPrimaryData = "";
}
public String getPrimaryData() {
return mPrimaryData;
}
public void setPrimaryData(String PrimaryData) {
queuePropertyChange("primaryData", mPrimaryData, PrimaryData);
mPrimaryData = PrimaryData;
firePropertyChange();
}
public String getFocusedEntity() {
return mFocusedEntity;
}
public void setFocusedEntity(String focusedEntity) {
queuePropertyChange("focusedEntity", mFocusedEntity, focusedEntity);
mFocusedEntity = focusedEntity;
firePropertyChange();
}
}
|
The last part of the data model is the creation of the Web Services Description Language (WSDL) file that describes it to the property broker. The WSDL file describes the public interface to the component view. This is used, for example, by the CAE to present what properties and actions are available for wiring and what is contained in TagCloudView.wsdl. This file can be used as a template for creating WSDL files for your own components.
A Property Broker Editor, which ships with Lotus Domino Designer, provides a user interface that lets you define properties, types, and actions without writing an WSDL file. An understanding of the structure of the WSDL file is helpful, though, even when you use the editor.
The Eclipse integrated development environment (IDE) framework also provides a graphical WSDL editor. That editor, though, uses WSDL-formatted files for defining Web services. Although the format is the same for composite applications, the specific usage is different enough that the Eclipse IDE editor is not always the best choice.
For each element you have in your data model, you must add to the WSDL file in a number of areas: <types>, <message>, <portType>, and <operation>.
Types determine the type of your property. In this implementation, they are all strings; however, we can add a semantic type to it in the WSDL file. These must be consistent from component to component. You can create wires only from properties to actions that are of the same semantic type. For the PrimaryData property, we decided to call the format TagCloudDataType. We declare it within the <types> element as shown in listing 2.
Listing 2. TagCloudDataType
<types>
<xsd:schema targetNamespace="http://www.ibm.com/wps/c2a">
<xsd:simpleType name="TagCloudDataType">
<xsd:restriction base="xsd:string"/>
</xsd:simpleType>
</xsd:schema>
</types>
|
For other types, duplicate the <xsd:simpleType> tag and rename TagCloudDataType with the desired type.
NOTE: In addition to having the same semantic type, properties must also lie in the same namespace. Be sure to be consistent with your use of namespaces within your WSDL files across multiple components.
Messages are collections of parameters used as profiles in determining the input and output values for a function call. You must have one for each getter and one for each setter as shown in listing 3.
Listing 3. Example messages
<message name="getPrimaryDataResponse">
<part name="getPrimaryDataValue" type="tns:TagCloudDataType"/>
</message>
<message name="setPrimaryDataRequest">
<part name="PrimaryDataValue" type="tns:TagCloudDataType"/>
</message>
|
For other elements, add these same two messages again, change PrimaryData to the base name of your property, and add TagCloudDataType to the type for your property. Or you can use xsd:string for the generic string type.
The port type links an operation to messages for its input and output parameters. Because we are dealing with simple accessor methods for beans, we have one <portType> for the setter containing a single input message, and one for the getter containing a single output message (see listing 4).
Listing 4. Example port type
<portType name="TagCloudView_Service">
<operation name="getPrimaryData">
<output message="tns:getPrimaryDataResponse"/>
</operation>
<operation name="setPrimaryData">
<input message="tns:setPrimaryDataRequest"/>
</operation>
</portType>
|
For other properties, duplicate the two <operation> tags, and change PrimaryData to the base name of your property.
Last, we create our operations in the <binding> element (see listing 5). These fully define the profile and refer to the previously defined constructs. We also declare the captions and descriptions used for these properties. This is what is used for display in the CAE. (Note that you can declare a Properties file along with your WSDL file, using the conventional naming scheme for localization. Prefixing the value in your caption or description with % indicates that the CAE should look up the value in the appropriate property file for the locale.)
Listing 5. Binding operation
<binding name="TagCloudViewBinding" type="tns:TagCloudView_Service">
<portlet:binding/>
<operation name="getPrimaryData">
<portlet:action name="getPrimaryData" caption="PrimaryData"
description="data to drive weighting of tag cloud"/>
<output>
<portlet:param name="getPrimaryData" partname="getPrimaryDataValue"
caption="PrimaryData"
description="data to drive weighting of tag cloud"/>
</output>
</operation>
<operation name="setPrimaryData">
<portlet:action name="setPrimaryData" caption="PrimaryData"
description="data to drive weighting of tag cloud"/>
<input>
<portlet:param name="setPrimaryData" partname="PrimaryDataValue"
caption="PrimaryData"
description="data to drive weighting of tag cloud"/>
</input>
</operation>
</binding>
|
For other properties, duplicate the two <operation> tags, change PrimaryData to the base name of your property, and add appropriate descriptions.
Note that, in this example, we have exposed both the set and get access functions for our data type. For something like the FocusedEntity, it makes sense to have both. An outside component view can tell this component view where it wants the focus to be. If the user clicks in the right area, this component view can broadcast to other component views what the new selection is.
While it is clear that we want to be able to set the PrimaryData on this component view, the use case is not as clear for letting other component views get it. Because assembly takes place after component creation, and possibly by someone else in a completely different application, it is worth considering exposing such things, even if there is no immediate use for them. It is better to build in flexibility up front.
Now that we have created and defined our data model, we must hook it up programmatically to the property broker mechanism.
Here we introduce the PBBroadcast.java helper class for managing the broadcasting of property changes from our data model to the property broker (see listing 6). When constructed, we pass it into the view with which it is associated and in the data model bean. In the constructor, it attaches itself to the bean as a listener.
When a value in the bean changes, the propertyChange() method is called. To broadcast the changed value, we need to assemble a collection of property-value pairs and publish them to the broker. In this simple case, we deal only with single property changes. In a more specific implementation, you have the option of publishing several property changes at once.
Listing 6. PBBroadcast.java helper class example
public void propertyChange(PropertyChangeEvent evt) {
Property pbProp = mBroker.getProperty(NAMESPACE_ROOT, makePropertyName
(evt.getPropertyName()));
PropertyValue[] pbPropValues = new PropertyValue[1];
pbPropValues[0] = PropertyFactory.createPropertyValue(pbProp,
evt.getNewValue());
mBroker.changedProperties(pbPropValues, mPartName);
}
|
In the first line of listing 6, we get the PropertyBroker Property class corresponding to the property changed from the name of the changed property in the event. In the second line, we create the array to store the changed values. In the third line, we create the PropertyValue object from the value of the changed property in the event. In the last line, we broadcast the changed value to the property broker. (Error checking has been removed for clarity of discussion. See the referenced source code for full details.)
Every component view must have a registered handler to receive notifications of property changes. We introduce the PBHandler.java helper class to help manage this. Because this is constructed by the Lotus Expeditor framework, we cannot initialize this specific instance with our data model. In fact, the same handler is used for all instances of a component view. The event notification, though, includes information about which Eclipse view is the target of the notification. We retrieve that Eclipse view, which is required to implement the IDataProvider helper interface, and through this interface we retrieve our data model. We then extract the name of the changed property, the new value, and pass that into a function that sets the value through reflection (see listing 7).
Listing 7. PBHandler.java helper class example
public void runWithEvent(Event event) {
PropertyChangeEvent pEvent = (PropertyChangeEvent)event;
IDataProvider view = (IDataProvider)SWTHelper.locateView
(pEvent.getWireDefinition().getTargetEntityId());
setValue(view.getData(), pEvent.getActionDefinition().getName(),
pEvent.getPropertyValue().getValue());
}
|
In the second line of listing 7, we cast the event to the PropertyChangeEvent specific to this operation. In the third line, we extract the wire from the event and the view ID from the wire, and we use SWTHelper to find the Eclipse view from the framework. In the fifth line, we call the setValue helper function with the data model (extracted from the view), the name of the changed property (extracted from the action definition in the event), and the new value (extracted from the event). (Error checking has been removed for clarity of discussion. See the referenced source code for full details.)
Helper class for the Topology Handler
The TopologyHelper class is provided to help set up initial values in your data model. It contains a single static function to do the initialization. This is passed to the context of the plug-in, the data model, and the ID of the view. The function accesses the TopologyHandler, extracts the keys specifically set for this component view (identified by the view ID), finds the values for each of those keys, and attempts to set them into the data model (see listing 8).
Listing 8. TopologyHelper.java class example
public static void initialize(BundleContext context,
PCSBean bean, String secondaryID) {
ServiceReference ref = context.getServiceReference
(TopologyHandler.class.getName( ));
TopologyHandler handler = (TopologyHandler )context.getService(ref);
ComponentData data = handler.getComponentData(secondaryID);
String[] keys = data.getPreferenceKeys();
for (int i = 0; i < keys.length; i++) {
String[] vals = data.getPreference(keys[i]);
PBHandler.setValue(bean, "set"+keys[i], vals[0]);
}
}
|
Here, the first two lines retrieve the TopologyHandler from the context. The third and fourth lines retrieve the settings that are specific to this component view. We then loop through them. Within the loop, we extract the value for the iterated key, and then we attempt to set that value into the bean through reflection, using the helper function defined in the PBHandler class.
We use all these helper classes together with our data model to create our View class. The framework requires this class to extend ViewPart, and we need the class to implement the IDataProvider interface so that we can get the data model from it later:
public class TagCloudView extends ViewPart implements IDataProvider
When we construct it, after we create the data model, we create an instance of the PBBroadcast class. To this, we pass the view, the data model, and a list of the namespaces used. Because this links itself to the data model, we don't need to keep a reference to it (see listing 9).
Listing 9. PBBroadcast class instance
public TagCloudView() {
mData = new TagCloudViewBean();
new PBBroadcast(this, mData, "http://www.w3.org/2001/XMLSchema");
}
|
In the createPartControl() method, which we must implement for the ViewPart, we do four things as shown in listing 10.
Listing 10. createPartControl() method
public void createPartControl(Composite parent) {
// create SWT widget for displaying tag cloud
mCloud = new TagCloud(parent, SWT.NULL);
mData.addPropertyChangeListener(new PropertyChangeListener(){
public void propertyChange(PropertyChangeEvent evt) {
doDataChange(evt.getPropertyName();
}
});
mCloud.addPropertyChangeListener(new PropertyChangeListener(){
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("focused"))
mData.setFocusedEntity(mCloud.getFocusedKey());
}
});
registerData();
TopologyHelper.initialize(Activator.getDefault().getContext(), mData,
getViewSite().getSecondaryId());
setupNames();
}
|
First, in line 1 we create the actual control and place it in our view. Second, we establish the connection from our data model to the control by registering a listener for changes on the data model. When it receives one, it processes the change and passes that on to the control (changing the format if required).
Third, we establish the connection from the control to our data model. We do this in the same way as in the previous step, but this time propagating change back when detected. Last, we use some helper functions to do some setup. The registerData() method binds the data model to an internal map so that the action handler functions can find it. The initialize function on the TopologyHelper class and the setupNames() function help to set up the default values into our data model. Because we've already connected it up with the control, these automatically propagate.
Because each component view needs a unique handler, we must create a TagCloudHandler class, which can subclass the PBHandler helper class. It doesn't need to add any methods. We cannot use the PBHandler helper class directly because the calling of actions is determined by the class name of its handler. A trivial subclass fills the need of having a class with a unique name and leaves all the actual handling to be common.
Now that all the code is in place, we must let the PropertyHandler know of it by filling in an extension point. We create an extension under the com.ibm.rcp.propertybroker.ProperBrokerAction extension point, filling out the class field to point at our handler. We then fill out the file field and point it at our WSDL file (see figure 5).
Figure 5. Setting the PropertyBrokerAction extension point in the plugin.xml file in the Eclipse IDE

There are a number of circumstances in which an Eclipse component view can be used to surface Lotus Notes data other than using traditional Lotus Notes UI elements. In fact, you can do this by accessing the Lotus Notes Java API. To make things easier, the Java API is packaged into a plug-in that is available in the environment. On the Dependencies tab of your plug-in editor, select the com.ibm.notes.java.api plug-in, and all the APIs are immediately available.
It is best to keep the access to Lotus Notes data asynchronous. When you want to look up data, use a NotesThread object to execute the lookup. When finished, if the data involves a UI update, remember to spawn off the update into a thread that is run with the UI thread. The code in listing 11 can be used as a template for this. You might also look at the com.ibm.cademo.sl.comp.leadbrow package included with the Lead Manager example for an example.
Listing 11. NotesThread object template
public class CalculateData implements Runnable
{
public void run()
{
try {
Session ssn = NotesFactory.createSessionWithFullAccess();
...
ssn.recycle();
} catch (Exception e) {
e.printStackTrace();
}
}
public void execute() {
NotesThread nt = new NotesThread(this);
nt.start();
}
}
|
If you want to extend an existing component view, perform the following steps:
- Create the additional fields in your data model as well as the accessor functions. As part of this, you must ensure that the accessor functions broadcast changes through the JavaBean property-change mechanism. Also, add those values to your WSDL file with unique type definitions if required.
- Connect those fields to the ViewPart to visualize these new values and, if appropriate, to allow the user to change those values. No change is necessary to ensure that our new values are broadcast.
The PBBroadcast helper class listens for all changes to the JavaBean and as long as the names of the fields are in sync with the names defined in the WSDL file, they are appropriately broadcast. Similarly, no change is required to accept changes to those values from other component views. Adding their values to the WSDL file ensures they can be wired up, and the PBHandler class accepts those values and ties them to the data model through introspection.
- If you want to create another, entirely new component, there are other simple steps. The helper functions are not tied to a specific component; instead, they are provided in the OpenNTF library as a separate plug-in. Each component you produce that uses them has a dependency on that plug-in.
NOTE: It is recommended that you place only one component view in each plug-in. The WSDL file is associated with the component itself, meaning that all declared values are valid for all component views in a single plug-in. Placing a single component view in each plug-in resolves whatever confusion may result from having properties from several components display when referring to a single one in the CAE UI.
- Create the new plug-in, mark it to depend on the plug-in with the helper classes in it, and then create your data model, inheriting from PCSBean and its corresponding WSDL file. Finally, create the ViewPart that instantiates the data model and that implements the IDataProvider interface.
- In the constructor, the PBBroadcast class can be instantiated with a reference to the data model. In the createPartControl() function, a call can be made into the TopologyHelper to set initial values.
- Last, a facade helper class is created as a subclass from PBHandler, and that class along with the WSDL file are registered against the com.ibm.rcp.propertybroker.PropertyBrokerAction extension point.
You can repeat these steps for each component you want to add. All the Eclipse-based plug-ins in the OpenNTF library are done in this manner, forming a fertile ground of examples that can be copied and changed for your own needs.
Through this exercise, we introduced several helper functions for creating a component that can be deployed in a composite application. More than that, though, we showed you how to create a foundation on which other components can be created quickly and easily. These functions are not required as simple components can encompass these steps in their actual code, or you may find other ways of establishing common usage of the APIs. They are presented here as a helping hand to get beyond simple components and onto your first suite of components.
Learn
-
Get started with IBM Lotus Notes and Domino V8 technical content.
-
Read the introductory article in this series, "The Lead Manager application in IBM Lotus Notes V8: An overview."
-
Read the developerWorks article, "Designing composite applications: Component design."
-
Read the developerWorks article, "Designing composite applications: Design patterns."
-
Read the developerWorks article, "Designing composite applications: Unit testing."
-
Read the developerWorks article, "Designing composite applications: IBM Lotus Notes components."
-
Read the developerWorks article, "Designing composite applications: Composite application assembly, part 1."
-
Read the developerWorks article, "Designing composite applications: Composite application assembly, part 2."
-
Read the developerWorks article, "What's new in IBM Lotus Notes and Domino V8."
-
Read the developerWorks article, "Extending the IBM Lotus Notes V8 mail with Eclipse."
-
Read the developerWorks article, "Integrating IBM Lotus Notes data into the Lotus Notes V8 sidebar and toolbar."
-
Read the developerWorks article, "Extending the IBM Lotus Notes V8 sidebar and toolbar."
-
Read the developerWorks article, "Leveraging user context in the IBM Lotus Notes V8 sidebar and toolbar."
-
Get started with IBM Lotus Domino Designer 8 help.
-
Refer to the developerWorks Lotus composite applications page.
-
Read the "Lotus Notes and Domino 8 Reviewer's Guide."
-
Read about the Eclipse project resources on developerWorks.
Get products and technologies
-
Download a trial of IBM Lotus Domino.
-
Download a trial of IBM Lotus Notes, Domino Designer, and Domino Administrator clients.
Discuss
Comments (Undergoing maintenance)





