How to Create a Composite Application Container
Extending the Composite Application Generic Container Infrastructure
Is a container the right choice for you?
The artifact we will be containing is a simple multi page SWT application. For simplicity, we are building it when we build our view part. Typically the view part is our mapping layer to the actual container programming model like the DOM for the browser container.
The Container Sample
The contained artifact has two pages with two text entry fields per page. For illustration purposes, each field is labeled with the actual SWT id. Also for illustration purposes, the page is labeled. The “PAGE” will be used as our Landmark type.
Our goal is to let an assembler use our container to map these fields to properties across the different pages in any manner they would like. The properties are defined by the assembler. These properties should have clear meaning so that they are intuitive when wiring them together with other components (setURL, setText are property examples). This mapping will then be saved by the assembler thereby creating an instance of our container which will be used as a component in a composite application.
While building our sample container, we will incrementally build it in four steps relative to how an assembler will leverage the container. These steps are as follows:
A. Assembler will listen for a Property Broker property change and set a field in our container to its value (Data Change Event – Receive).
B. Assembler will retrieve a Property Broker property that changed in the system before our page was initialized when our component changes between pages. (Content Complete Event – Receive).
C. Assembler will map a field in our container to fire as a property change when component initialization is completed (Content Complete Event – Publish).
D. Assembler will map a field in our container and fire a property broker change when the users focus leaves the field (Data Change Event – Publish
To start building a container, you will need an eclipse environment and the Lotus Expeditor toolkit installed. You can either have Lotus Notes 8.5.1 or Lotus Expeditor 6.2.1 runtime installed to execute your container.
Classes and files required for the container sample
Create a new project and at least a minimum of the aforementioned 3 classes:
extends AppContainer. This is the class that will communicate directly with the generic container infrastructure.
, extends IViewPart. This is the class that gets instantiated by the system. This class is responsible for instantiating your application container class, and it is responsible for being the bridge component to the contained artifact. Even though this is a view part, this class acts more like a controller between the container logic and the contained artifact logic. In the sample, I have one more class, which emulates the contained artifact named SampleAppPageWidget.
extends LandmarkIdentifier. This class is required for evaluating a landmark expression with the current state of the contained artifact and returning a string identifier. Ours is very simple in this sample, but I will try to include a more extensive sample as an appendix to this article.
Containers require a definition at runtime in order to operate. That definition is called a container configuration. That container configuration can be manually generated via text editor, or it can be generated visually in the Composite Application Editor.
Container, CAE and Container Configuration
For starters, our container is targeted for use by assemblers. The assembler’s tool is the Composite Application Editor (CAE). We have to instrument our container so that CAE will put our container on CAEs component palette and let CAE know that we are a container so it will provide the default landmark page where the assembler will do all their configuration from.
We add our container to the Composite Application Editor Component palette by leveraging the palette extension point.
(1) The name and category attributes of the palette extension point notify CAE that you want this container on the component palette under the “Sample” Category.
(2) When the assembler drags the container onto the CAE canvas, CAE will open the view part identified in the palette extension. This is a mandatory configuration item.
(3) When the Sample View part initializes, the view class is responsible for creating the instance of the sample container class. This can be done in either the init or createPartControl methods.
(4) Once the Sample Application Container is initialized, it will be accessible from the generic container infrastructure and CAE. Since we are in the process of creating a configuration file through CAE, the assembler will need to [Edit Component Properties] of the container instance they just dropped onto the canvas.
There are two tabs we are going to focus on next; the [Advanced Properties Tab] and the [Landmark Tab]. In order to tell CAE that you want it to display the default landmark tab, we need to tell it that we are a container. This is done with the com.ibm.rcp.extensiontype configuration item in the Palette Entries Extension Point.
Methods to support edit tools
So the first property our assembler is going to instrument on the container will be to receive a property from the system anytime after the component has initialized. In this exercise the assembler will listen for a “setText” property change from the system.
The assembler will need to go to the [Advanced Tab] and add the property “set Text” and select it as wirable. Now the assembler can go to the [Landmarks Tab] and map that property to a field in the contained artifact.
The first thing the assembler will see is the type of landmark they can add. By default this will be blank, and the assembler can add anything they want. This is leveraged by the container to allow for different ways to identify a specific state. Like in the browser container they support a URL or a Title. In our sample we are going to represent our state as a “page”.
So CAE will call us with the following method in our sampleAppContainer class:
This will populate the “Type of Landmark” combo box. Again this is optional, but recommended.
The next step will be for the assembler to create a landmark of that type. So they select the [Add Landmark] button in order to start configuring properties they want to make active on this landmark or “page” in our case.
So the assembler will be presented with another combo box. This is blank by default, and does not have to be implemented by the container. For example, in the case of the browser container, the assembler can enter a URL or a URL expression if the landmark type was a “URL”. Since a URL is well understood by an assembler, it is left blank for them to add their own URL expression. In our case, we need to present the user with the name of the page they are currently on, since they will be instrumenting fields that are tied to that page. We do that by implementing the following method in our sampleAppContainer class:
This will present the user with the valid state identification tag for this page. You have the flexibility to have more than one landmark per state. For example, our landmark type could have been a table, and if we hade multiple tables on the current page you would have multiple table landmarks.
Now that the assembler has scoped what page they want to listen for the Property Broker property change, they can choose when in the life cycle of the component that they want to receive it. We have determined that the first Step was anytime the property changed in the system after the component was initialized. That is a Data Change Event notification. So the assembler will now select [Add Event], and select the Data Change event from the combo box.
Notice that there are several events in the combo box. These are the default events that are provided by the generic container infrastructure. You can add or filter events but that will be saved for an advanced topic at a later time.
Now the assembler can select the [Add Operation] button to map the property they created to a field on the contained artifact.
The first thing you will notice is the property the assembler created in the [Advanced Tab]. All the properties that have been checked as wirable will show up in this combo box. The second piece to focus on is the “action” combo box (where receive is currently displayed). This defines what action the assembler wants to take on that specific property. These actions can be filtered and enhanced by the container via implementing the getSuggestedActions()
method. Now the assembler will select the receive action, meaning they want to listen for PB property changes.
The last field is also blank by default. This container can again contribute fields which can be used by the assembler since these are specific to the contained artifact. These fields are specific to the page (landmark) that is currently being instrumented. For Illustration purposes, as mentioned above, the SWT field name is actually labeled on the page. This is for the assembler’s ease of awareness, since normally this would not be presented to the user. This is where our container bridges the contained artifact and requests the fields from it. For example, the browser container will ask/interrogate the DOM for the current URL for the fields in the DOM. In our sample, we are going to ask the container artifact emulator (the widget) for the fields for the current “page”.
So our container implements:
This in turn calls the view to get the current fields for the current page. If the assembler were to change the page using the <][> buttons, the fields would be returned and presented to the assembler for the correct page.
Now the assembler selects [OK] to complete the container configuration for this component. The assembler has now exposed a property “setText” to the system, which can be wired from another component.
For testing purposes, we will add a managed browser instance to the composite application, since the managed browser has a well defined set of properties we can use. In CAE we switch to the wiring page and now drag a wire from the “Status text for browser” to the “Set setText” (our new property) for our container instance.
What should happen is any time the status on the managed browser instance updates it will send the context of that change to our new container instance. If we are on page1, FIELD_A will be updated with that value.
Notice the browsers status area and the text in FIELD_A.
How this works and what has to be implemented in a container will be covered in the next section.
Methods used at run time
When we leave CAE, the composite application is persisted and then reloaded in the runtime. The views for composite application will be loaded by the composite application infrastructure. Just like at edit time, the sample Composite Application view will get instantiated by the system, this time in the context of the configuration instance that the assembler just created. This instance will now instantiate a sample Application Container instance.
The container configuration we just generated for this component will get registered with generic container infrastructure. This information will be leveraged by the core application container when interacting with the instantiated sample Application Container.
So to continue Step A, we will now walk through the methods that are required at run time when another component fires a Property Broker (PB) property change to the input property the assembler configured above.
1) The core application container will figure out if the property is valid for the current landmark that our container instance is on. In order to do this, it will query our application container for the landmark, and then will cross reference the property with the landmark in the container configuration for this instance.
Since the landmark can actually be an expression where the expression is defined by the container, the core application container is going to ask for a Landmark Identifier object.
The core application container will then call the doBuildLandmark method on this identifier object to get the actual Landmark string representation.
Our sample application container does not use a www-10.lotus.com/ldd/compappwiki.nsf/dx/Leveraging_Landmark_Expressions" title="landmark expression">landmark expression
, so the only thing our builder needs to do is retrieve the current landmark of the contained artifact. In this sample, the sample application view is the keeper of the current state, so our method queries the view for the current landmark.
Now that the core application container has the current landmark, it can resolve whether the current property should be forwarded on to our sample application container. If the resolution is successful, the core will call our setField()
method in our sample application container.
Our sample application container will pass that to our bridging view class which knows how to set the field in the contained artifact and the filed will be updated to the user.
That completes the methods called for an incoming Data Change event.
Let’s move on to the second scenario; setting a field based on a PB property that changed before our landmark was instantiated. That is achieved with the Content Complete event.
So to illustrate this functionality, we will set Field_Y on page 2 to the “setText” property we create during Step A. The scenario is that the user will perform an action that changes the status text of the browser while we are in page one of our container instance. Then the user will switch to page 2 and see the last status change in the browser in Field_Y.
Note that, this content complete functionality is providing the container component the cached last value of all properties that were fired in the system before the container instance or any of the landmarks were instantiated. This provides containers with a level of system state provided by the system, which before containers had to be maintained by the component.
The assembler will configure the new landmark as illustrated above.
The run time difference here from a data change event - receive action is that the trigger will not be from a PB property change, but rather from when a landmark has completed initialization. That awareness is only known by the container itself which must notify the generic container infrastructure when this event has taken place per landmark.
(1) When the container changes state (page in our sample) the sample application view notifies the core of the new Landmark with the publishCurrentLandmark()
(2) Then the sample application view notifies the core that the Landmark has completed initializing with the fireContentCompleteEvent()
(3) Once the core has been notified of the content complete, it will then resolve the properties to be updated with the container configuration for that instance. The core container will query Property Broker for the latest value of those properties and then call the container setField() method (same method used in Step A) for each property defined.
Steps A and B focused on setting a field within the contained artifact. Steps C and D will go into retrieving the value from the contained artifact and providing that to the system, typically termed as publish or publish Property. Step C will extend the content complete event that we just instrumented for the Step B. This time the assembler will publish FIELD_Z after the page is initialized.
In this scenario, the assembler would create another property called “sendURL” on the [Advanced Tab] and then add an operation on the “page2” landmark to publish the value of FIELD_Z after content complete. The assembler then closes (saves) the new container configuration and now can wire the new “sendURL” property to the existing managed browser instance.
What will happen now is that every time the user changes to “page2” in our container instance, the value of FIELD_Z ( www.espn.com
) will be sent to the browser and the browser will update to that URL.
When the user changes to “page2”, steps (1) and (2) will get processed for the content complete just like Step B.
(3) The difference for this scenario is that the generic container infrastructure will process the properties that need to be published during a content complete and call the sample application container with the publish()
method for each publish property under this landmarks content complete event. Our sample application container will call the sample application view to get the field for the property.
(4) The container will then call the sample application view part to get the value from the field and call the core publishProperty()
For this Step we will publish a property when the field changes on a landmark. Here the assembler will use our last provided field on “page1”. They will add a “sendURL” (the property we created in Step C) operation to the Data Change event for that landmark. The operation will publish the “setURL” when FIELD_B changes ( moe specifically when the user leaves the text entry field). The assembler will save the modified container configuration. Since the assembler already wired the setURL property to the browser, we will get that for free.
Now when the user enters a URL in FIELD_B of “page1” the URL will be sent to the browser and the browser will refresh with the new URL.
From our container perspective, we have to be able to set a listener or be notified when a field in the contained artifact changes. In our case, we can put a listener on any SWT widget, so we have that capability. In the browser container for example, you can set listeners on text input fields.
The second decision we have to make for our sample container is which kind of listener to use. In our example, we could listen for key, mouse, focus, and several other events. In our sample, we will listen for focus events and more specifically we will listen for when we lose focus on the field, making the assumption that the user entered the field, changed something, and the then left.
So once we know how to listen on the contained artifact, we need to know from the generic container infrastructure when to activate listeners for each landmark.
(1) So when the user changes to a new page, our sample application view (our controller) will tell our sample application container to setup the input listeners for this landmark.
(2) gets the landmark configuration information from the core, finds the current landmark, extracts the publish properties from it and then
(3) calls the sample Application Container’s addListener with the container artifacts field ID, and the property that will get fired when the filed listener is triggered for each Data Change property defined for this landmark.
(4) Now that the fields listeners are registered, any time the user leaves a field that has been registered the SampleAppPageWidgetFieldListener will be triggered which will call the application container with it’s publish property method.
Note: notice that the publish property requires not only the value, but the property in order to publish it. In our sample we store the property with the widget ( id = field id ) in its data element. This way when the listener is triggered, we can pull the property directly from the widget.
That completes the container implementation details for the four Steps that were defined for this article. Look for follow-on articles on how to extend functionality in your container, like
and component properties tabs
, for extending the generic container framework independently of creating a container.
– One who assembles components (layout and data flow) within the Composite Application Editor to create a composite application.
– The end user of the composite application created by an assembler.
– Composite Application Editor, the tooling for an assembler to create a composite application.
Container Configuration –
the internal persisted description of a configured container, also know as a container instance.
– Property Broker, the system service that manages the information flow between components that were configured by the assembler.
– Designates a state of the container that properties can be defined against.
– Each landmark can be configured to listen for certain events, like Content Complete or Data Change
– is an assembler defined sequence based on a property, field or content complete change.
– defines what to do when the property or field changes, i.e. receive, publish.
an identifier that will be recognized by the composite application infrastructure.
– an identifier of a contained artifact
– the artifact that will be bridged by a container, examples; the DOM for the browser container, the WORKSHEET for the spreadsheet container.
Is a container the right choice for you?
Leveraging a Container Context
Introduction to Custom Actions
Leveraging Landmark Expressions
Adding a Toolbar to your Container
Adding Component Edit Tabs to your Container