Prerequisites:
Introduction to the Composite Application Component Library
Intro
Components for Composite applications may be based on NSF constructs or written from Eclipse. Those with a Notes development background might not be completely familiar with the Eclipse development environment. It can be a little intimidating but isn't that hard once you get used to it. Developing simple components does not have to be hard. Using the components distributed in the Composite Application Component Library (v2) and some of the helper functions in there it is pretty easy to create components based on the patterns used there.
In this article we will walk you through designing, creating and testing a component build using the techniques and code in the Component Library.
What You Will Need
The Eclipse integrated development environment is required to develop the plugins that components are based on. Any recent version of this can be downloaded from eclipse.org. Technically Domino Designer is based on the Eclipse IDE and can be used. However it can get kind of confusing developing for Notes with Notes. It's generally recommended to install a separate environment.
You should download the Composite Application Component Library from OpenNTF.org. In the documentation there is a ZIP file containing all of the sources. This can be imported to your Eclipse IDE by choosing File -> Import -> General -> Existing Projects Into Workspace and specifying the ZIP file. Opening Windows -> Preferences -> Plug-in Development -> Target Platform and browsing to \framework\rcp\eclipse should be enough to set you up.
Designing our Component
We're going to pick a relatively trivial component to develop for this exercise. The purpose is to show you how to create and deploy a component that connects and operates with the Notes framework. Once you understand that, you can use the techniques and approach to make as complicated components as you desire.
We're going to create a "Radio Button" component. There are four main interfaces we have to design: the User Interface, the Advanced Component Properties supported, the Properties supported, and the Actions supported. Generally we want the component to display a series of radio buttons driven by text set in the Advanced Component Properties. The current setting may be set via an action, and if so set, or the user makes a different selection, that values is broadcast as a property. More formally:
User Interface:

A series of radio buttons are to be displayed vertically in the confines of the component using standard SWT controls.
Advanced Component Properties
The "labels" property may be set in the advanced component properties. This is a comma delimited list of values that are to be used as the display value for the radio buttons and data value for the properties and actions.
Actions
A single action "setValue" is supported of type xsd:string. If this value corresponds to a radio button displayed, then that radio button is selected.
Properties
A single property "getValue" is supported of type xsd:string. If this value corresponds to the data value of the selected radio button. If the selection is changed, the value is broadcast.
Creating the Projects
Several Eclipse projects need to be created to manage the component. A plug-in project is needed for each component. Technically you can put more than one component into a plug-in, but we recommend that they be kept separate for ease of maintenance. A feature project is needed to group components together into units of deployability. This is the granularity that Eclipse Rich Client Platform operates at for deployment. Our Plugin-in project (or projects) will be referenced by the feature project. Lastly an Update Site project is required. This groups together multiple features. This is is the structure that the Eclipse Rich Client Platform uses to make features (and their included plugins) available to be consumed.
Creating the Plugin Project
In Eclipse IDE choose File->New->Project->Plug-in Development->Plug-in project. Enter a project name of "com.demo.comp.radio". (This, and the other project names given, are suggestions only. They are not required to relate to each other. However, you will probably find it convenient to adopt a similar naming convention.) Click Next to review further options and Finish when you have finished examining them. (No changes are needed for this example.) When the project is created, a project editor is displayed where you can adjust various values relating to the plugin. If you close it, it can be later opened by double clicking "plugin.xml" or "MANIFEST.MF" in the Package Explorer in the left column. For now, navigate to the "Dependencies" tab of the editor. Click on "Add" and select the "com.ibm.cademo.core" plugin. This indicates that this plugin depends on the core plugin. The resources of that plugin are now available for use by this platform. Core contains many useful helper classes we will use when creating this plugin.

Creating the feature project
In Eclipse IDE choose File->New->Project->Plug-in Development->Feature project. Enter a project name of "com.demo.feature". Click on Next. On the Referenced Plug-ins and Fragments page, select the com.demo.comp.radio plugin project you just created, plus the com.ibm.cademo.core plugin that we know we need. There may be other plugins that aren't part of the Notes platform that we should include in the feature. After we click Finish, we are presented with the feature project editor. (If you close it, you can open it later by double clicking feature.xml in the project.) On the dependencies tab in that editor you can click on "Compute" to list all the dependencies of the plugins so far included. This is a handy way to check to see if you have forgotten to package something into your feature that is needed for deployment.

Creating the update site project
In Eclipse IDE choose File->New->Project->Plug-in Development->Update Site project. Enter a project name of "com.demo.update". Click on Finish. We are presented with the update site project editor. (If you close it, you can open it later by double clicking site.xml in the project.) On you can click on New Category if you wish and create a new category with a name of "demo_components" and a label of "Demo Components". You can select this and click on "Add Feature" and select the "com.demo.feature" you created above. Categories are optional. If you do not assign a category to your feature it will be presented under "Other".

Creating the Plugin Classes
Three classes are needed to define our component. First we need a Java bean to use with the helper classes to maintain our data model. Actions appear as update to the bean and calling the setters on the bean trigger property broadcasts. Second we need the View Part. This provides the primary user interface for the component. Lastly we need a action handler shell for unique identification by the helper functions.
Data Model
Create a class in the com.demo.comp.radio package (Right Click -> New -> Class) called "RadioBean". Select a superclass for it by browsing to the "com.ibm.cademo.core.ViewBean" helper class. Click on Finish.

The contents of this class are, simply, the code necessary to make it a well behaved Java Bean. For our design we just require a single data element "Value" that is set with a setValue() setter and a getValue() getter. The setValue() function must alert any listeners that and updated value is available. There are helper functions in the ViewBean base class to make this easy. Here is the code listing:
package com.demo.comp.radio;
import com.ibm.cademo.core.ViewBean;
public class RadioBean extends ViewBean {
private String mValue;
public String getValue() {
return mValue;
}
public void setValue(String value) {
queuePropertyChange("value", mValue, value);
mValue = value;
firePropertyChange();
}
}
Note: naming conventions are important here. Our actions and properties should all follow the convention of using "setXXX" and "getXXX" as the programmatic names for their values. This naming convention is so that the helper classes can form a relationship between declared properties and actions, and methods on your data model. As we will see later, we can add any sort of textual label we want for the user to see.
View Part
Create another class in the com.demo.comp.radio package called "RadioView". Select a superclass for it by browsing to the "com.ibm.cademo.core.ComponentViewPart" helper class. Additionally add "com.ibm.cademo.core.IDataProvider", "org.eclipse.swt.events.SelectionListener", and "java.beans.PropertyChangeListener" to the list of supported interfaces. The IDataProvider interface is needed to make the helper layer aware of the bean you've used for the data model. The SelectionListener is needed to react to a radio button being clicked. The "PropertyChangeListener" is needed to react to changes in the data model. Click on Finish.

First we have to create the member variable to store the Data model (1) and initialize it in the constructor (2). Some further steps are needed to make sure data flows correctly. In line (3) we create a helper object that will subscribe to the Bean's update events and broadcast to the property broker when one of them changes. This covers getting properties published. In line (4) we, ourselves, subscribe to the bean's update evens so that when an action changes one of them, we are informed through the propertyChange() member function.
(1) private RadioBean mData;
public RadioView() {
(2) mData = new RadioBean();
(3) new PBBroadcast(this, mData, "http://www.w3.org/2001/XMLSchema");
(4) mData.addPropertyChangeListener(this);
}
Note: as you add in code you may enter classes that you don't have imports yet for. Eclipse provides several methods to help automatically add imports. You can press Ctrl+Space when typing in a class name. It will prompt you with all known classes beginning with that name. When you select one, it will complete it for you and add in the import. Or, if you type in a class name you haven't referenced, a little red X appears in the left column. If you click that, it will give you the option of resolving the error by adding the import. Lastly, Ctrl+Shift+O will attempt to resolve all unknown classes in your entire file at once.
In the next section of code we create the user interface and link it up. This is usually done in the createPartControl() method. However, we need to query the advanced component properties for our values before we create the UI. Because of how Notes starts up, we cannot guarantee that the advanced component property values are present when the UI starts up. So we have to deferr UI set up until after we are informed the data is ready. Fortunately this is very easy to do with the helper functions. We just create a variable to store the parent UI element in (1), save it there in createPartControl() (2), We then call the helper function registerData(). This performs necessary set up and also registers a listener for the advanced component properties. When they are ready, it calls the postRegistry() helper function (4) which we can override and continue with the UI setup.
The first step in the postRegistry() handler is to call the same function on the superclass (5). This ensure that other setup is done. After that we can go about creating the UI. We use the GridLayout() layout (6) as that is the most generally useful, and we have some more helper functions to make it easy to use. Next we retrieve the value of "labels" from the advanced component properties (7). The details are hidden behind a helper function. We just need to supply the advanced component property name, and the default value for it. Since this is defined to be a comma delimited list, we can use a StringTokenizer() to iterate through it (8).
For each element in the list we retrieve the label (9), create the Button() object (10), and set the text for it (11). We then set its layout parameters using the GridUtils helper class (12). In this case we are saying that we want the button to expand as much as it wants in the horizontal direction. Other values here would allows us to gave the control span any number of horizontal or vertical cells, control it's justification within its placement, or give it a specific size. Lastly we register ourselves as a listener on the object (13) so that we can receive updates when the object is selected.
(1) private Composite mParent;
public void createPartControl(Composite arg0) {
(2) mParent = arg0;
(3) registerData();
}
(4) protected void postRegistry() {
(5) super.postRegistry();
(6) mParent.setLayout(new GridLayout(1, false));
(7) String labels = getSetting("labels", "Yes,No,Maybe");
(8) for (StringTokenizer st = new StringTokenizer(labels, ","); st.hasMoreTokens(); ) {
(9) String label = st.nextToken();
(10) Button radio = new Button(mParent, SWT.RADIO);
(11) radio.setText(label);
(12) GridUtils.setLayoutData(radio, "fill=h");
(13) radio.addSelectionListener(this);
}
}
The rest of the class just fills out the various methods we've created to react to what is going on.
The setFocus() method is called by the framework when our component gets focus. We just set the focus to the containing Composite (1). The getData() method is called by the helper functions to get our data model. We just return the class instance of our data model (2). widgetDefaultSelected() does not have to perform any operation.
widgetSelected() is called when any of the radio buttons is clicked. We retrieve which radio button is clicked from the event (4), get its text value (5), and then set it into the data model (6). The help functions take care, at this point, of broadcasting that as a property.
We have slightly more processing to do when the propertyChange() method is called in response to a change in the data model. We first find out the new value set (7). Then we have to find out which radio button that corresponds to. We get the children of the main container (8), loop through them (9), get the label for each one (10), and compare it against the new value (11). If there is a match, we set that radio button to be selected, otherwise we set it unselected.
public void setFocus() {
(1) mParent.setFocus();
}
public PCSBean getData() {
(2) return mData;
}
public void widgetDefaultSelected(SelectionEvent ev) {
(3) nop
}
public void widgetSelected(SelectionEvent arg0) {
(4) Button radio = (Button)arg0.getSource();
(5) String value = radio.getText();
(6) mData.setValue(value);
}
public void propertyChange(PropertyChangeEvent arg0) {
(7) String value = (String)arg0getNewValue();
(8) Control[] radios = mParent.getChildren();
(9) for (int i = 0; i < radios.length; i++) {
Button radio = (Button)radios[i];
(10) String label = radio.getText();
(11) if (label.equals(value))
(12) radio.setSelection(true);
else
radio.setSelection(false);
}
}
Action Handler
This class helps the helper functions work out where to send incoming actions. Create another class in the com.demo.comp.radio package called "RadioHandler". Select a superclass for it by browsing to the "com.ibm.cademo.core.pb.PBHandler" helper class. Click on Finish.
That's it!
Connecting Everything Up
Eclipse uses extension points to form late binding connections between services. We're going to fill out a few extension points to hook into these frameworks to get everything working for our component.
Go back the the plugin-project editor (by double clicking plugin.xml or MANIFEST.MF in the project if you have closed it) and change to the Extensions tab. (Note: not the "Extension Points" tab! That's for creating new points of connectivity.) First we're going to register our component with the Eclipse framework as a view. This is needed so that it can be displayed on a page in a composite application. Click Add, and select "org.eclipse.ui.views". Click on Finish. Unfortunately there is not a wizard to help us fill in this extension point. Click on the "plugin.xml" tab and enter in the following XML code:
point="org.eclipse.ui.views">
allowMultiple="true"
class="com.demo.comp.radio.RadioView"
id="com.demo.comp.radio.RadioView"
name="Radio Buttons"/>
Now flip back to the "Extensions" tab. We can now see the details of what the properties correspond to in the extension point. AllowMultiple is required for a view to be considered as a component. The class value must point to the View class we created. ID can be anything unique, but it's a good convention to make it the same as the class name. The name value is what is displayed for selection.
Now we want to add in a connection to the helper functions. Click Add again and select "com.ibm.cademo.core.PropertyBrokerConnections" and click OK. Right click the new entry and select New -> component. Set the OwnerID to the same ID used for your view (com.demo.comp.radio.RadioView in our case) and use Browse to select the RadioHandler class for the handler.
Right click the (component) entry and select New -> Property. Fill in a name of "Value". (The helper functions will add the "get" themselves, since it is mandatory.) If you wish, you can give a different textual label here by filling in title (say "Selected Radio Button") and more details under Description (say "When a radio button is selected, the value is broadcast by this property"). By default, an xsd:string type is assumed. If you are using other types, you can specify them here.
Although there is a wizard for this extension, there is a bug in the Component Library V2 that prevents you from adding more than one property or action. To work around this click on the "plugin.xml" tab and add the following XML after the tag:
description="Set a radio button"
name="Value"
title="Set Radio Button">
These values directly correspond to the ones set for a property.
We are done with the component's creation and are now ready to package and deploy it!
Deploying and Creating a Unit Test
Now that we've created the component, lets bundle it up and deploy it to our Notes desktop. As part of that we're going to write a simple Unit Test in order to exercise it's functionality.
Components are deploy via features in an update site. We've created the feature and update site project, now all we have to do is assemble it. Go back to the update site editor (or double click site.xml in it's project) and click "Build All". When that finishes, the on-disk location of the project is a fully formatted update site. You can find out where on your disk the project is located by right clicking site.xml and selecting "Properties". It will be listed in the Location field. You may want to leave this up to copy and paste form in the next step.
Back in your Notes client it's time to create a composite application to house your unit test. Select File->Application->New, give it a title (say Radio Unit Test), pick a location, and pick "Blank Composite Application". Click OK. This will create and launch the composite application. Select Actions->Edit Application to bring it up in the Composite Application Editor.
To deploy a component, you first need to get it onto your component palette. You can't edit the default "Component Library" palette, but if you switch palettes by selecting the drop down in the right column you can pick, say, "My Palette" to add your new component to. To add the component, right click the background of the palette and select Add Components -> Add Components From Update Site. In the "Update Site Details" dialog box, select "Local Update Site" and browse (or paste in) the path to the site.xml file you called up in the Eclipse IDE. Click OK and it will scan the update site, and ask you which components you want to install. You can select them all and click OK.
This will add the selected components to your palette, but won't deploy them yet. Components are deployed on a first-used basis. You can trigger this by dragging the component entry from your palette to the layout area in the CAE. This says to put this component on that page. This will also trigger provisioning and prompt you to install the component. Choose to install, accept any license agreements that come up, and if it requires you to restart Notes, do so (and then open your application again, with the CAE).
You should now see the component there with it's default settings: three radio buttons labelled "Yes", "No" and "Maybe". We can now test that it correctly reads specified text by right clicking the component in the navigation tree in the left column, and selecting Edit Component Properties. Switch to advanced and add an advanced component property named "labels" with value "One,Two,Three". Once saved the component should update with the new labels.
To test the properties and actions, you want to add in a Standard Types Tester component from the Composite Application Component Library. Add this component to your palette following the instructions included with the Component Library. Deploy one of these along side of the Radio Buttons component.
To wire them together, right click the Radio Buttons component in the left side navigator and select "wiring". You should see a single property "Selected Radio Button" (or whatever you used the property a title) appear for the Radio Buttons component. Drag that and drop it over the "Misc 1" action listed on the Standard Types Tester. When the Radio Buttons control broadcasts that property now, the Standard Types Tester will receive it and display it in it's Misc 1 field.
Now right click the Standard Types Tester control and select "Select as Wire Source". Drag the "Misc 2" property from it and drop it on the "Select this button" action (or whatever you used for the action title). Now if you enter in a new value for Misc 2 in the Standard Types Tester and hit Set, it will send it to the action on Radio Buttons. You can now save all of the changes and view the application in the Notes client.

We can now test the properties of the components by selecting radio buttons in the UI. Their values should appear in the Misc1 field of the tester. We can test the actions by filling in a test value in the Misc2 field of the tester and clicking "Set". With this we can validate that our component is ready for use in a real application.
Conclusion
Although the functionality of this example is pretty trivial, it should illustrate the techniques necessary to develop components of arbitrary complexity. Instead of simple SWT controls, anything can be displayed from aggregations to entire other applications reparented within Eclipse. Further properties, actions, and advanced component properties can be added to allow for any sort of input required and any sort of output desired.
We encourage developers to look through the source code included with the Composite Application Component Library. You will find many examples of the use of the helper functions described here. Often such a component can be use as the starting component for your own development. Or else code from them can be cut and pasted into your own component and modified to your specific functionality.
We have given you the keys to the kingdom. Go forth and create!
|
|
|
|
| Version 1 |
October 11, 2009 |
7:55:36 PM |
by Deanna Drschiwiski |
|
|