Version: 0.1 |
Date: April 29, 2005 |
· Overview
· Creating the Widget Providers
· Summary
The GMF Service Provider infrastructure provides a framework for building Services for clients to execute a unit of work. The Service depends on Providers who actually perform the work on behalf of the Service. Providers have a Priority and an optional Policy which dictate how the Service will determine which Provider(s) to use when executing a request on behalf of a client. The Service may be optimized in some capacity.
This tutorial assumes the reader is familiar with Eclipse extension point architecture. There is an abundance of on-line help in Eclipse for those unfamiliar with extension points. For reference, the full source for this tutorial is available here.
To demonstrate the Provider Service we will create a simple example Widget Service and two Widget Provider. Then we will create a client to uses the Widget Service to create Widgets. This tutorial requires only the org.eclipse.gmf.runtime.common.core plugins and its dependencies to run.
The first step in creating a Widget Service is defining the API for your
Widget Providers. This is done using a Java interfaces. The Service Provider
infrastructure defines an IProvider
interface that is used to define the base interface for clients. This interface
provides the call-backs to Providers via the provides(IOperation)
method. IProvider
also provides a mechanism for Providers to receive provider change events from
the Service. It is expected that new Providers extend this interface for their
own purposes. In order to keep this tutorial simple, the Providers created in
this tutorial do not listen to provider changes.
To support the creation of Widgets, we will create IWidgetProvider. The IWidgetProvider extends the IProvider interface and also defines the createWidget() method where Providers carry-out the actual work of creating widgets when asked by the WidgetService.
public interface IWidgetProvider
extends IProvider {
Object createWidget(int orderSize);
}
Next, create the actual WidgetService. This is done by extending the abstract base class Service and implementing IWidgetProvider. Take a minute to read the code before we go through it in more detail.
public class WidgetService
extends Service
implements IWidgetProvider {
private final static WidgetService service = new WidgetService();
public static WidgetService getInstance() {
return service;
}
public Object createWidget(int orderSize) {
return execute(new CreateWidgetOperation(orderSize));
}
public Object execute(IOperation operation) {
List results = execute(ExecutionStrategy.FIRST, operation);
return results.isEmpty() ? null : results;
}
protected Service.ProviderDescriptor newProviderDescriptor(IConfigurationElement element) {
return new WidgetProviderDescriptor(element);
}
protected static class WidgetProviderDescriptor
extends Service.ProviderDescriptor {
private WidgetServiceProviderConfiguration providerConfiguration;
public WidgetProviderDescriptor(IConfigurationElement element) {
super(element);
this.providerConfiguration =
WidgetServiceProviderConfiguration.parse(element);
assert null != element : "NULL configuration element"; //$NON-NLS-1$
}
public boolean provides(IOperation operation) {
if (getPolicy() != null)
return getPolicy().provides(operation);
if (provider == null) {
if (isSupportedInExtension(operation)) {
providerConfiguration = null;
return getProvider().provides(operation);
}
return false;
}
return getProvider().provides(operation);
}
private boolean isSupportedInExtention(IOperation operation) {
if (operation instanceof CreateWidgetOperation) {
return providerConfiguration.supports(((CreateWidgetOperation)operation).getOrderSize());
}
return false;
}
}
}
First, notice that the WidgetService is a static instance. This is needed to support the initialization of the Widget Providers, we will get to that in more detail in a minute.
Next, notice that the WidgetService implements IWidgetProvider by delegating the service request to the actual Service. This is done by calling the Service’s execute(ExecutionStrategy, IOperation) method. Again, don’t worry about the understanding the ExecutionStrategy, we explain the ExecutionStrategies that later. At this point the Service will find Providers for the given IOperation and delegate the work required in the IOperation to them. Then return the results for the work done by the Providers back to the client.
Now back to the Provider initialization. In order to know about potential Providers for a given IOperation, the Service will find them by examining all the extensions of widgetProvider extension-point. These widgetProviders are registered with the Service as WidgetProviderDescriptors. Which are used by the service to determine if a particular Provider provides for the operation via provides(IOperation). Since the providers are registered this way we don’t have to worry about a plugin not being loaded in order to contribute to the Service. The service will load the Provider’s corresponding plugin when required.
Registration of the Providers is done in the startup() method of the WidgetService’s corresponding xxPlugin.java. The following code fragment shows the mechanics of the static initialization of Providers.
public static final String WIDGET_SERVICE_EXTENSION_POINT = “widgetProviders”;
public void startup() {
super.startup();
configureWidgetProviders();
}
public void configureWidgetProviders() {
WidgetService.getInstance().configureProviders(
Platform.getExtensionRegistry().getExtensionPoint(getPluginId(),
WIDGET_SERVICE_EXTENSION_POINT).getConfigurationElements());
}
Finally, define the “widgetProviders” extension-point for Providers to extend in the plugin.xml.
<extension-point id="widgetProviders" name="%extPoint.widgetProviders" schema="schema/widgetProviders.exsd"/>
Now we create the Providers that actually carry-out the requests on behalf of the Service. The first step in creating a Provider is to create an extension of the widgetProvider extension-point for the WidgetProvider. In the extension, you also define what type of request the Provider can handle and the Priority of the Provider. For our WidgetProvider, we have a low Priority Provider that supports widget order sizes between 50 and 5000 widgets.
<extension point="org.eclipse.gmf.examples.runtime.common.service.widgetProviders">
<widgetProvider class="org.eclipse.gmf.examples.runtime.common.service.providers.WidgetProvider">
<Priority name="Low"/>
<orderSize max="5000" min="50"/>
</widgetProvider>
</extension>
Next we implement WidgetProvider.java as follows. The key point here is to ensure that your XML based configuration matches the Provider itself. In this case we are concerned with matching the order size.
public boolean provides(IOperation operation) {
if (operation instanceof CreateWidgetOperation) {
int orderSize = ((CreateWidgetOperation)operation).getOrderSize();
return (orderSize >= 100 && orderSize <= 10000);
}
return false;
}
public Object createWidget(int orderSize) {
List widgets = new ArrayList(orderSize);
for (int i = 0; i < orderSize; i++) {
widgets.add(I, “widget”);
}
return widgets;
}
To create the second provider follow the same steps are the first. However, there are a few subtle differences. Change the Provider priority from Low to High and change the order size to 100 <= order size <= 10000.
<extension point="org.eclipse.gmf.examples.runtime.common.service.widgetProviders">
<widgetProvider class="org.eclipse.gmf.examples.runtime.common.service.providers.SuperWidgetProvider">
<Priority name="High"/>
<orderSize max="10000" min="100"/>
</widgetProvider>
</extension>
Again, create SuperWidgetProvider by implementing IWidgetProvider.
public boolean provides(IOperation operation) {
if (operation instanceof CreateWidgetOperation) {
int orderSize = ((CreateWidgetOperation)operation).getOrderSize();
return (orderSize >= 50 && orderSize <= 5000);
}
return false;
}
public Object createWidget(int orderSize) {
List widgets = new ArrayList(orderSize);
for (int i = 0; I < orderSize; i++) {
widgets.add(i, “widget”);
}
return widgets;
}
Provider Priorities are closely tied to ExecutionStrategies (described in the next section). For now know that a Provider must have a Priority and that Priority assigns an importance to the Provider. The following Priorities are defined by GMF.
ProviderPriority.HIGHEST
ProviderPriority.HIGH
ProviderPriority.MEDIUM
ProviderPriority.LOW
ProviderPriority.LOWEST
When collecting Providers for a given IOperation the Service can be instructed to find the Providers in a well-defined way. Table 1 Execution Strategies below defines that ways in which the Service finds Providers to carryout the unit of work.
Strategy |
Description |
ExecutionStrategy.FIRST |
The Service will select the first Provider with the highest Priority that is capable of the service request. |
ExecutionStrategy.LAST |
The Service will use the last Provider with the lowest Priority that is capable of the service request. |
ExecutionStrategy.FORWARD |
The Services will use all the Providers in order of highest Priority to the lowest that are capable of providing the service request. The results from the Providers are placed in a List that matches the relative descending order of the Provider priorities. |
ExecutionStrategy.REVERSE |
The Service will use all the Providers in order of lowest Priority to the highest that are capable of providing the service request. The results from the Providers are placed in a List that matches the relative ascending order of the Provider priorities. |
Table 1 Execution Strategies
To use the WidgetService we’ll simply create a client in the form of a Workbench action. Add the following to the plugin.xml.
<extension point="org.eclipse.ui.actionSets">
<actionSet
label="Widget Serice ActionSet"
visible="true"
id="org.eclipse.gmf.examples.runtime.common.service.client.actionSet">
<menu id="org.eclipse.gmf.examples.runtime.menu"
label="Widget Service"
path="additions">
<separator name="group1"/>
</menu>
<action id="org.eclipse.gmf.examples.runtime.common.service.client.RunExampleAction"
label="Run Widget Example"
menubarPath="org.eclipse.gmf.examples.runtime.menu/group1"
class="org.eclipse.gmf.examples.runtime.common.service.client.RunExampleAction">
</action>
</actionSet>
</extension>
Next, implement the RunExampleAction’s run() method with the following code snipet.
public void run(IAction action) {
Object widgets = WidgetService.getInstance().createWidget(1000);
System.out.println(widgets == null ? "No widgets created" : "Created " + ((Object[])widgets).length + " widgets");//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
widgets = WidgetService.getInstance().createWidget(30);
System.out.println(widgets == null ? "No widgets created" : "Created " + ((Object[])widgets).length + " widgets");//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
widgets = WidgetService.getInstance().createWidget(76);
System.out.println(widgets == null ? "No widgets created" : "Created " + ((Object[])widgets).length + " widgets");//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
}
Run the workbench and execution the “Run Widget Action”. You should see following in the Console view. Not very exciting, but enough to demonstrate the Service Provider infrastructure. In all cases the ExecutionStrategy.FIRST is used. The first client request for a 1000 widgets is provided by SuperWidgetProvider since is has a higher Priority than the WidgetProvider and it is capable of handling the order size. In the second client request, no widgets were created since neither of the widgetProvider handles the order size. Lastly, the small order is provided by the WidgetProvider since the SuperWidgetProvider does not handle order smaller than 100 widgets.
Created 1000 widgets
No widgets created
Created 76 widgets
As a simple exercise change the ExecutionStrategy used by the WidgetService to see the behavior change. You can also change the order sizes and Priority of the Providers.
To create and demonstrate the Service Provider Infrastructure we:
1. Defined the IWidgetProvider interface by extending the base IProvider interface,
2. Created the WidgetService by extending Service and implementing IWidgetProvider,
3. Create the WidgetDescriptor to load the WidgetProvider’s XML descriptors,
4. Created the widgetProvider extension-point,
5. Add the widgetProvider extensions to the plugin.xml,
6. Created a widget Provider by implementing IWidgetProvider, and
7. Finally, created a simple client to use the Widget Service.
Copyright (c) 2000,2005 IBM Corporation and others. All Rights Reserved.