A Context, a Capability, and a Perspective Walk Into a Bar: Capabilities

I previously glossed over context, capabilities, and perspectives with the intention of explaining them further. Well, that was almost a year ago, so I figured it was time to get to that. I decided to start with capabilities for several reasons. First is that they are probably the most esoteric of the three and have the least relevant documentation and secondly I wrote the code. Yah, ego. Live with it. :)

Crafting Activity Definitions

Capabilities are little more than state-full identifiers that are bound to one or more regular expressions. If a given UI contribution matches one of these regular expressions and the capability is enabled then the UI contribution appears and if it is disabled then it is hidden. This is pretty simple in theory although the practice is a little more tricky due to the expertise required to write regular expressions and the verbosity in describing capabilities and their relationships to one another. An example might be helpful.

First, let’s imagine that there is a plug-in that provides very simple tooling for a new programming language (conveniently called Foo). Some extensions from this plug-in (org.foo.tooling) might look like the following:

<extension         point="org.eclipse.ui.editors">      <editor            class="org.foo.tooling.editor.FooEditor"            contributorClass="org.foo.tooling.editor.FooContributor"            extensions="foo"            icon="foo.gif"    default="true"            id="org.foo.tooling.editor.fooEditor"            name="Foo Editor">      </editor></extension><extension         point="org.eclipse.ui.perspectives">      <perspective            class="org.foo.tooling.editor.FooPerspectiveFactory"            icon="fooPerspective.gif"            id="org.foo.tooling.fooPerspective"            name="Foo">      </perspective></extension><extension         point="org.eclipse.ui.newWizards">      <wizard            canFinishEarly="true"            class="org.foo.tooling.wizards.NewFooWizard"            hasPages="true"            icon="foo.gif"            id="org.foo.tooling.fooWizard"            name="Foo Project"            preferredPerspectives="org.foo.tooling.fooPerspective"            project="true">      </wizard></extension>

In addition to the basic tooling the plug-in author has devised some extended (complicated, painful, super-frickin’cool) editing abilities that they’ve isolated in their own plug in (org.foo.tooling.advanced):

<extension         point="org.eclipse.ui.editors"><editor            class="org.foo.tooling.editor.AdvancedFooEditor"            contributorClass="org.foo.tooling.editor.AdvancedFooContributor"            extensions="foo"            icon="foo.gif"            id="org.foo.tooling.editor.advancedFooEditor"            name="Foo Editor">      </editor></extension>

There are probably some other extensions – commands, key bindings, additional wizards, utility views – that look very similar to this.

If you were trying to generate capabilities for your product and your product happened to make use of the Foo plug-ins you might decide to make a Foo capability to control the visibility of the Foo components. Of course since you are including functionality for other development tooling you want to integrate the activities for Foo into the overall capability story for your product. The definition of that capability might look something like this:

<extension         point="org.eclipse.ui.activities">      <category            description="Capabilities relating to development in various languages."            id="my.product.development"            name="Development">      </category>      <activity            description="Functionality used in development of projects using the Foo language."            id="my.product.fooActivity"            name="Foo Development">      </activity>      <defaultEnablement            id="my.product.fooActivity">      </defaultEnablement>      <activity            description="Advanced development in the Foo language."            id="my.product.fooActivity.advanced"            name="Advanced Foo Development">      </activity>      <activityRequirementBinding            activityId="my.product.fooActivity.advanced"            requiredActivityId="my.product.fooActivity">      </activityRequirementBinding>      <categoryActivityBinding            activityId="my.product.fooActivity"            categoryId="my.product.development">      </categoryActivityBinding>      <categoryActivityBinding            activityId="my.product.fooActivity.advanced"            categoryId="my.product.development">      </categoryActivityBinding>      <activityPatternBinding            activityId="my.product.fooActivity"            pattern="org\.foo\.tooling/.*">      </activityPatternBinding>      <activityPatternBinding            activityId="my.product.fooActivity.advanced"            pattern="org\.foo\.tooling\.advanced/.*">      </activityPatternBinding></extension>

Before I explain this markup let me first describe some intentions we had with the activity extension. First and foremost was our intention that those who are writing plug-ins NOT define activities for them in the plug-in that define their functionality. Any activity definitions provided by the plug-in developer should be isolated so that product developers (those who aggregate plug-ins from various sources) are able to totally own the capabilities for their product. Depending on the focus of the product the capabilities could be drawn along component lines, experience lines, role lines, or any number of other dimensions. Another thing to note is the verbosity of this extension point. It takes a lot of elements to define your activities and while it can be bothersome to those using it, it’s intentional. This verbosity allows us a great deal of flexibility when it comes to how we lay out our activities. For instance, it’s possible for a top-level architect to lay out all the activity categories in one plug-in and have sub-teams contribute activities in their own. Alternatively, it’d be possible such an architect to define a few key activities (Beginner, Intermediate, and Advanced) and simply have the sub-teams contribute pattern bindings.

First, the <category>. Categories are hints that tell Eclipse how to display capabilities within the UI should it desire to show them. Beyond a logical grouping these elements serve no other purpose in the system. In our example our product architect has decided that having a Development category would be appropriate. In fact, the Eclipse SDK ships with a Development category of its own that contains activity definitions for Java and Plug-In development.

Second, the <activity>. Each activity element represents one state-full identifier in the activity system. By default these identifies are disabled but this can be changed via the <defaultEnablement>. In our example, we define two activities – Foo Development and Advanced Foo Development. The later is made enabled by default via a subsequent <defaultEnablement> tag.

Next we have the <activityRequirementBinding> tag. The arrangement of activities is a hierarchical one. It is possible to specify that the enablement of one activity requires the enablement of another. In our example, we specify that the enablement of the Advanced Foo Development activity requires the enablement of the Foo Development activity.

Next we have <categoryActivityBinding> tags. These elements place the specified activities in the specified categories.

Finally we have the <activityPatternBinding> tag. This is probably the most interesting element in the schema and definitely the trickiest to author. Every activityPatternBinding element binds a regular expression (following the Java regex package syntax) to a given activity. While it’s possible to bind any regular expression to an activity the Eclipse workbench is designed to work with a particular pattern. In particular, the workbench will attempt to match strings of the form “plug-in id/contribution id” against these expressions. For instance, when the workbench sees the Foo Editor listed above it will create an identifier “org.foo.tooling/org.foo.tooling.editor.fooEditor” to pass to the activity framework. If that identifier matches any pattern binding associated with an enabled activity or NOT associated with a disabled activity than the Foo Editor will be available. For instance, in the above XML, we bind the expression “org\.foo\.tooling/.*” to the “my.product.fooActivity” activity. If “my.product.fooActivity” is enabled then the Foo Editor is available. If “my.product.fooActivity” is not enabled, then Foo Editor is hidden.

The pattern “plug-in id/.*” is often seen in activity definitions because it is often the case that activities are grossly drawn along plug-in lines. The pattern effectively says “everything in the plug-in”. Of course, it is possible to define more granular bindings (“org\.foo\.tooling/org\.foo\.tooling\.editor\.fooEditor”) or more general ones (“org.foo.*/.*”). One thing to note with the more specific bindings is the nature of the contribution id after the “/” character. This never applies to the id attribute of the extension tag (which is rarely used within the Eclipse UI extensions) but rather the id of the relevant component. For instance, when filtering on editors the id attribute of the <editor> element is used.

Using The Framework

As someone who wants to write activity bindings the above is all you need to know about activities. If you’re a plug-in developer that wishes to leverage capability filtering in your own extension points there is more to story. There are two aspects of this support. The first is using the activity support to filter disabled contributions from your UI. The second is providing a means in your UI to enable activities. The first part is easy. Let’s imagine our advanced Foo editor. Imagine there are language extensions to the Foo language that the editor can use. These extensions are contributed to an extension point in the foo plug-in called “languageExtensions”. The languageExtension schema is very simple and a languageExtension extension might look like this:

<extension         point="org.foo.tooling.languageExtension">      <languageExtension           id="myExtension" ...>  <contributions .../>  </languageExtension></extension>

Imagine that the Foo language was graphical and that each languageExtension would contribute some number of items to a palette editor. As a client of the activities framework, the Foo plug-in developer would want to suppress palette items that are bound to disabled activities. Their code might look like this:

for (int i = 0; i < contributions.length; i++) { FooPaletteContribution c = contributions[i]; final String pluginId = c.getDefiningPluginId(); final String contributionId = c.getId();

 IPluginContribtion pc = new IPluginContribution() {

  public String getLocalId() {   return contributionId;  }

  public String getPluginId() {   return pluginId;  }};

 if (WorkbenchActivityHelper.filterItem(pc))  continue; showContribution(c);}

This code is pretty straightforward. First, we grab the defining plug-in id and the extension id from the FooPaletteContribution and create an IPluginContribution object with those values. This interface is used by the activity framework to construct ids for comparison. We pass this object to the WorkbenchActivity helper which compares it against all of the activities in the system and answers the question “should this object be hidden from the user”. If the answer is yes, we do nothing further with this item but if it’s false we show the item in some capacity. If we were smart we could make this code cleaner by having FooPaletteContribution implement IPluginContribution directly but otherwise this is the standard pattern for filtering contributions via the activity framework.

The above code shows how to populate a static list with contributions that are potentially filtered by activities but the activity system is not static. Activity enablement can change over the course of the application life-cycle so it is up to those using the system to monitor the system for changes. The easiest way to do this is to hook a listener onto the activity support that refreshes the presentation:

PlatformUI.getWorkbench().getActivitySupport()    .getActivityManager()    .addActivityManagerListener(new IActivityManagerListener() {

 public void activityManagerChanged(  ActivityManagerEvent activityManagerEvent) {  if (activityManagerEvent.haveEnabledActivityIdsChanged())   repopulatePresentation();      }});

Don’t forget to unhook the listener when you’re finished showing contributions.

So now that we have these extensions being filtered how will the user ever see them? They could go to the preferences and specifically enable the appropriate activities but this isn’t ideal. What we need is for the Foo editor to provide some means to enable the defined language extensions. This is where trigger points come into play.

Using trigger points is a two-step affair. The first step is to define the trigger point in plug-in XML. Since this trigger is owned by the Foo tooling plug-in, it should reside in its plug-in XML. The extension would look like this:

<extension         point="org.eclipse.ui.activitySupport">      <triggerPoint            id="org.foo.tooling.languageExtensionTriggerPoint">         <hint               id="interactive"               value="true">         </hint>      </triggerPoint>   </extension>

Note that these extensions reside on the org.eclipse.ui.activitySupport extension point rather than the org.eclipse.ui.activities extension point. Trigger point definitions are pretty simple. They are really nothing more than an id value and some optional hints. In the above example we’ve defined the language extension trigger point with a an interactive hint. To use the trigger point in our code is also straightforward. Imagine that we have a series of toggle buttons in our editor that specifies the language extensions we want to use. This list seems like a logical place to hook our trigger point. In our event listener we’d have code that looks something like this:

public void widgetSelected(SelectionEvent e) { final LanguageExtension ext = (LanguageExtension) e.widget.getData(); IPluginContribtion pc = new IPluginContribution() {

  public String getLocalId() {   return ext.getId();  }

  public String getPluginId() {   return ext.getDefiningPluginId();  }};

 ITriggerPoint triggerPoint = PlatformUI.getWorkbench()   .getActivitySupport()   .getTriggerPointManager()   .getTriggerPoint(     "org.foo.tooling.languageExtensionTriggerPoint");

 if (!WorkbenchActivityHelper.allowUseOf(triggerPoint,   pc)) {  ((Button) e.widget).setSelected(false); }}

The above should be fairly straightforward. In our button listener we get the LanguageExtension the user wishes to use and create an IPluginContribution from it. We then obtain the languageExtension trigger point from the activity system and hand both off to the workbench, asking the question “should this operation proceed”. If the answer is no, we should abort the selection event and reset the button selection to false. If the answer is yes, we do nothing. The WorkbenchActivityHelper.allowUseOf() method has some interesting effects that we need to discuss. When called, the plug-in contribution is compared against the known activity bindings and a collection of disabled activities is collected. This collection, if enabled, would cause a subsequent call to WorkbenchActivityHelper.filterItem() with the same IPluginContribution to return false. If this collection is not empty, the activity support consults the trigger point. If the interactive hint is set a dialog is opened informing the user that if they wish to proceed the collection of activities will be enabled. If they agree, the collection is enabled and WorkbenchActivityHelper.allowUseOf() returns true. If they do not, no activities are enabled and WorkbenchActivityHelper.allowUseOf() returns false. If the interactive hint is not set, then all activities are enabled and WorkbenchActivityHelper.allowUseOf() returns true. We’ve now hooked up code that will enable activities based on the use of our extensions.

Let’s run through a scenario that shows both the filtering and trigger points in action. We have a disabled activity A that binds to plug-in P. P contains a language extension called “My Extension.” Our Foo editor is showing a list of language extensions that contains My Extension in the disabled state. The palette in the Foo editors shows contributions from other language extensions but not those contained within My Extension. The user clicks on My Extension and the activity system is consulted with the interactive foo trigger point. It finds that IPluginContribution “P/My Extension” is bound to activity A. Since A is disabled, we bring up a dialog asking the user to confirm that they wish to enable activity A. They consent, My Extension becomes toggled in the list and the palette contributions contained within the extensions appear in the palette.

Extending The Framework

We’ve now covered capabilities from two sides – the capability set architect and the plug-in developer that wishes to use the framework. There is one other way in which the capabilities framework can be extended, however. In the above example we prompted the user with a dialog in the event that we encountered an interactive trigger points that had disabled activities. In most cases this is appropriate but in others (RCP applications that make use of capabilities, for instance) this isn’t desirable. To accommodate such rare cases we’ve made it so that the entire trigger point engine can be replaced by those applications that require it. This is done via the org.eclipse.ui.activitySupport extension point:

<triggerPointAdvisor            class="my.product.MyTriggerPointAdvisor"            id="my.product.myTriggerPointAdvisor">      </triggerPointAdvisor>      <triggerPointAdvisorProductBinding            productId="my.product"            triggerPointAdvisorId="my.product.myTriggerPointAdvisor">      </triggerPointAdvisorProductBinding>

We define the advisor by an id and an implementing class (which we will cover shortly). In addition to the definition we also bind the advisor to a particular product. I wont go into product documentation here as it’s a topic unto itself. There is plenty of documentation existing for this, however, such as the Rich Client Tutorial written by Ed Burnette.

The implementation for the advisor must implement org.eclipse.ui.activities.ITriggerPointAdvisor. This interface has one method, Set allow(ITriggerPoint triggerPoint, IIdentifier identifier); which is consulted whenever someone calls WorkbenchActivityHelper.allowUseOf(). Example:

public Set allow(ITriggerPoint triggerPoint, IIdentifier identifier) { if (triggerPoint.getId().equals("some.trigger.point"))  return null; else  return identifier.getActivityIds();}

The above advisor would disallow any usage from the trigger point with the identifier “some.trigger.point” but would silently enable usage from any other trigger point.

The majority of the capability scenarios are covered by the above three solutions. There are other uses for the activity framework (poor mans provisioning, for instance) but I wont go into those here.

This time next year: contexts.

This entry was posted in Eclipse. Bookmark the permalink. Follow any comments here with the RSS feed for this post. Trackbacks are closed, but you can post a comment.

8 thoughts on “A Context, a Capability, and a Perspective Walk Into a Bar: Capabilities

  1. Villane

    If capabilities are originally meant for product builders, not plugin developers, is there a solution for using capabilities for plugins that are not originally included in the product, but are added by the user.

    For example, by adding a SVN provider to Eclipse SDK, I would like to see a capability for it under Team, just like CVS has. But it may (for example) also be used in Rational Software Architect and Borland JBuilder. So the SVN provider vendor can’t know what capabilities the products define…

    As a user what could I do? Define my own product or just plugins which define capabilities?
    Or maybe the SVN provider could include fragments for all known product bundles?

  2. Hasan Ceylan

    Hi,

    Just wanted to thank you for taking time and explaining all this.

    I have had hard time trying to locate resources and hopelessly just about to go to the sources to make it out, came across your blog. Now no need to dig in the sources.

  3. Anis

    Hi Kim,
    First of all thank you for your “useful” blog and your contribution the eclipse community.

    I’m trying to restrict access to some views on my RCP application
    depending on user profile, users can have access to perspectives but
    not to one view of the perspective.

    I’ve tried with the ExtensionRegistry.removeExtension(…) method but I found that with non master token I can just add/remove “non-persisted” extension from the ExtensionRegistry.

    I sow also that Eclipse 3.3 (M5) propose XSLT transformation on the OSGI Level to filter the plugin.xml file, but it still in the incubator stage,

    Could you please advise me if there is an other “Eclipse compatible” way to Restrict access to my views ?

    Regards,
    Anis

  4. Justin Beck

    Hi,

    I guess I’m struggling to make the jump. What I need to do is make a perspective visible (or not) based on a user role… Would this be the desired approach? I’m working on it now but some validation would be great.

    Thanks!

    Justin Beck

  5. J.P.

    Well written and has good info… XML examples don’t render properly on firefox 3.5.9 or IE 6. They are all on one line and do not show beyond the text column (they are mostly covered up by the right side panel). Had to dig into the page source to see the example xml correctly.

  6. J.P.

    So here is another question… can you make categories hierarchical? If not, suppose you have two related activities inside a category… I can easily make one depend on the other, but how do you go about making them both dependent upon each other if at least one of them has to depend upon the category root?

  7. Yuri L

    Thanks. Your explanation is very usefull to understand Eclipse “inside”. I have a question – what is the “Debug Support” activity ID? It looks that Eclipse 3.5.2 does not include the “Development” capability by default. So I had to add Java,Plug-in and Debug Support activities explicitly. Is this a good way?
    Thanks and Regards,
    Yuri

Leave a Reply

Your email address will not be published. Required fields are marked *

* Copy This Password *

* Type Or Paste Password Here *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

  • About Me

    Hi! My name is Kimberly Horne and I have absolutely nothing interesting to say. Unfortunately for you I DO have an overpowering need to tinker with technology which is explains the presence of this journal. I mostly talk about games (video and tabletop), technology, tattoos, and my pets.

    If you're an Eclipse user you may find my Eclipse category more interesting.

    Similarly, if you're an Arduino nerd then maybe my projects might be of interest.

    Since I've discovered Twitter this journal has been neglected somewhat. If you really want to stalk me your best bet is to follow me on twitter.