Thursday, September 9, 2010

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

Posted by pookzilla on October 11, 2006

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.

title pic Reusing Editors

Posted by pookzilla on October 5, 2006

Someone asked me this question in email and I figure it’s better to commit the results to Google than to just answer via the original medium. The question, in a nutshell, is this:

I have some model object that is backed by two files, one primary and one secondary. In my own model navigator I show only the logical resources and when a user clicks on a model element I open my editor on the primary file. If the user views my project in the resource navigator they see both the primary and secondary files. If they open the primary file my editor is opened but if they try and open the secondary file there’s a problem. If I associate an editor with the secondary file then I have two copies of the same model object open at the same time. If I don’t associate an editor then the user cannot edit the model via the secondary file. What do I do?

The solution is org.eclipse.ui.IEditorMatchingStrategy. This interface was created in Eclipse 3.1 to help address this class of issues. IEditorMatchingStrategy is very simple and contains only one method : boolean matches(IEditorReference editorRef, IEditorInput input). You associate implementations of this class with your editor via the matchingStrategy attribute in your editor extension. Whenever a user requests to open a file via org.eclipse.ui.IWorkbenchPage.openEditor(IEditorInput input, String editorID) (or via the three arguement open method with IWorkbenchPage.MATCH_INPUT) all open editors will be consulted. If they have an associated IEditorMatchingStrategy this class will be instantiated and its matches() method will called with the opened editor and the new file the user wishes to open. If this method returns true than instead of opening a new editor the existing editor is shown instead.

Imagine the following plugin.xml:

<plugin>   <extension         point="org.eclipse.ui.editors">      <editor            class="org.eclipse.ui.editormatchingexample.EditorMatchingEditor"            default="false"            filenames="primary.file,secondary.file"            icon="sample.gif"            id="org.eclipse.ui.editorMatchingExample.editor1"            matchingStrategy="org.eclipse.ui.editormatchingexample.ExampleStrategy"            name="Editor Matching Editor">      </editor>   </extension>

Here we have an editor type associated with files whose name is either “primary.file” or “secondary.file”. We also provide a matchingStrategy class that looks something like this:

public class ExampleStrategy implements IEditorMatchingStrategy { public boolean matches(IEditorReference editorRef, IEditorInput input) {  // see if this input is backed by a file.  If not then exit early  IFile inputFile = ResourceUtil.getFile(input);  if (inputFile != null && inputFile.getParent() instanceof IProject) {   String name = inputFile.getName();   // we can match primary files and secondary files   if (name.equals("primary.file") || name.equals("secondary.file")) {    try {     IFile editorFile = ResourceUtil.getFile(editorRef       .getEditorInput());     // if they're in the same project they represent the same logical object     return editorFile != null       && inputFile.getProject().equals(         editorFile.getProject());    } catch (PartInitException e) {     e.printStackTrace();     return false;    }   }  }  return false; }}

If we create a file called “primary.file” it will open an instance of org.eclipse.ui.editormatchingexample.EditorMatchingEditor. Should we create a file called “secondary.file” and attempt to open it the Workbench will iterate through open editors looking for one it can reuse. When it reaches our existing editor on “primary.file” org.eclipse.ui.editormatchingexample.ExampleStrategy.matches() will be called with the editor reference to the editor on “primary.file” and an IEditorInput matching “secondary.file”. As you can see above, our ExampleStrategy will test for these file names and if both files exit in the same project it will return true. The Workbench will then activate our editor on “primary.file” instead of opening a new one on “secondary.file”.

Clearly there is more to this problem than simply reusing editors. Your editor implementation needs to understand some model residing in more than one file and react accordingly on activation. For example, it would be possible to open “secondary.file” first and then reuse its editor when “primary.file” is opened. It’s up to you as an editor implementor to handle this case as well as all of the other issues that come with this level of editor complexity.

title pic Clickity Click, Ctrl Trick

Posted by pookzilla on August 15, 2006

A small bug (in implementation time, not importance) came in today regarding the working set drop down menu. It was a very reasonable request for the ability to have selection in this drop down exclusive rather than additive. Currently when you have multiple working sets and you select one in this menu it is checked. If you select another, it too becomes checked, and the window working set becomes the combination of the two working sets. This is fine most of the times but sometimes you really want that second check to switch the working set rather than augment it. Without such behaviour switching the working set requires two clicks or (god forbid) bringing up a dialog.

The above bug was addressed by adding code that would check the state of the primary modifier key (ctrl on Windows and Linux, cmd on OS X) and if it was depressed at the time of the click would invoke the switching rather than the addition behavior.

As it happens, we use this trick in several other cases… but you might never know it. For instance, if you hold down the primary modifier key and invoke an item from the run/debug pulldown menus it does not launch the selected session but rather opens it in the launch dialog for editing. This can be a very useful gesture that you’d have no way of knowing about without a) looking through the source code (NERD!) b) reading documentation (who does THAT nowadays?) or c) working with someone who knows everything there is to know about the platform (the power of omnipotence and osmosis).

So… we have useful features that you’d never know to look for. This seems like something we could have a UI guideline for at the very least and possibly even support in code. Imagine a little tip bubble appearing when you pop up a drop down menu that tells you what will happen if any of the available modifier keys is pressed. Do we have enough cases where this happens to justify the work? Hard to say.

I’m just procrastinating, don’t mind me. :)

title pic This’ll Be Funny One Day

Posted by pookzilla on August 9, 2006

Some time ago I was asked to help out with an Eclipse-based application. This drew me away from my workbench responsibilities for quite awhile and now that the other project is drawing to a close I find myself trying to re-integrate into the day to day life of an Eclipse developer. I’ve set out to start fixing certain small bugs as I encounter them and I found one today that instantly drew my attention – for some reason we were showing the Preference and Quit action on OS X not only in the Application menu (where they belong) but in the File and Window menus as well. I logged myself a bug, checked out a fresh copy of the workbench source into a fresh copy of I20060809 and set out to fix it. The fix involved tweaking the workbench window advisor and the carbon fragment that ships with the OS X version of Eclipse but after a short time I had a patch that addressed the issue.

Then deja vu struck.

Hesitantly I entered a quick search in bugzilla – bugs owned by me, on the Macintosh platform, that contained the word “pref” in the title.

Bug 73729 was returned in the results. Not only had this bug been noted before, but it had been assigned to me and I had already written an almost identical patch for it already.

That’ll teach me to let my attention to Eclipse lapse. She is a harsh mistress whose lessons do not go down easily.

title pic On The Subject of Organized Imports…

Posted by pookzilla on August 1, 2006

Does this give anyone ideas? *coughscriptingcough*

class MyExecutionListener implements org.eclipse.core.commands.IExecutionListener {

    ... <snip uninteresting bits > ...

    public void preExecute(String commandId, ExecutionEvent event) {        if ("org.eclipse.ui.file.save".equals(commandId)) {            Command command = service .getCommand("org.eclipse.jdt.ui.edit.text.java.organize.imports");               if (command != null) {                    ExecutionEvent newEvent = new ExecutionEvent(                        command,                         event.getParameters(),                         event.getTrigger(),                         event.getApplicationContext());                    try {                        command.executeWithChecks(newEvent);                    } catch (Exception e) {                    }                }            }        }    }}

title pic Enough Rope To Hang Yourself With

Posted by pookzilla on

Summer can be really fun in Eclipse-land. We’ve just released, everyone is on vacation, and the plan for the next release is wide open and largely undefined. It’s time to play with weird and wonderful ideas.

Awhile ago I got it in my head that it might be cool if RCP developers had a tool that would allow them to totally control the plug-ins that their product is based upon. For instance, crop and remove problematic action sets, re-organize preference pages, rename editors, etc. One idea I had to accomplish this idea was to hook into the platform the ability to run the registry through a stylesheet on startup. I managed to get this working in the most minimal way awhile ago and recently people have been asking to see it. Until there is a suitable PR/plan item to attach it to I’ll just post the patch here for people to try out. No warranty, no support, etc etc. This patch was made against 3.2 but I’ve confirmed that it works with the latest code from head as of August 1 2006.

After applying the patch you can easily see the effects by launching your runtime workspace with the -Declipse.registryStylesheet argument. The value of this property should be a path to an XSL stylesheet. Below is an example stylesheet that can be used to remove the “line delimiter” action set from the file menu.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">    <xsl:template match="actionSet[@id='org.eclipse.ui.edit.text.actionSet.convertLineDelimitersTo']">    </xsl:template>    <xsl:template match="node()|@*">        <xsl:copy>                <xsl:apply-templates select="node()|@*"/>        </xsl:copy>    </xsl:template></xsl:stylesheet>

title pic Macbook vs. Powerbook: Boxing Matches and Big Ugly Tables

Posted by pookzilla on July 12, 2006

I should be ashamed of how long it’s taken me to get these results out of the database. I should be, but I’m not – life happens. ;)

First, the tale of the tape.

In the red corner wearing silver trunks with silver detail and weighing in as a 1.67Ghz PPC, 1 GB RAM, 5400RPM HHD – Dishwasher! In the blue corner also wearing silver trunks with silver detail and weighing in as a 2.16Ghz Intel Core Duo, 1 GB RAM, 7200RPM HHD – Dishwasher II! The referee this evening will be a 1.4GHz Mac Mini running a Cloudscape database server.

Each machine was configured with a modified (as per previous posts) version of the Eclipse SDK (eclipse-SDK-I20060602-1317-macosx-carbon.tar.gz) and matching JUnit zip (eclipse-junit-tests-I20060602-1317.zip). Each machine was hardwired to the network and set up to send the results of tests to the database server. Each suite was run one time and for the most part all tests passed. As you’ll see in the table below some tests are missing results. The reasons were varied and to my mind irrelevant to the purpose I was trying to achieve. In a perfect world (with more time) I’d find a way to isolate the tests and try running them on their own but I couldn’t find an easy way to do that with the performance framework. Also in this hypothetical “perfect world” the entire suite would’ve been run multiple times. In that perfect world I’d have three laptops, not only two, to sate my internet addictions. But I digress… :)

Using a heavily mangled version of View.java I was able to produce the following table. As you can see the average gain is somewhere between 2x and 3x – not quite the 5x Apple has been advertising for the MacBook but impressive and welcome nonetheless. Most of these numbers weren’t too surprising but I was expecting the SWT gains to be more given how much more responsive Eclipse feels. This could be a consequence of the SWT test coverage, however – much of the speed difference I felt was concentrated in text manipulation and the gains in the jdt.text packages seem to be the best of the lot.

The tests on OS X record Elapsed Process, CPU Time, and Java Heap. Presented here are the results for Elapsed Process in big ugly table form. Note the big and ugly!

As I’ve mentioned before getting the tests to run on the Mac wasn’t as easy as I had hoped. There were numerous changes to different script and build files as I have already mentioned. If anyone is interested in reproducing these results I can provide a zip of the testing environment that I used as well as the modified View.java although I’m sure someone less lazy than me could have come up with something far more elegant involving Data Tools and BIRT. Mmm. BIRT.

title pic Foundation? Not Exactly.

Posted by pookzilla on June 30, 2006

Some of us committers made their way to the Eclipse Foundation today in order to harass the staff and steal their booze. I must say the Foundation was very disappointing to me. I mean, when I hear ‘Foundation’ I think of large blocks of concrete. There were none. Not only that, what kind of Foundation is on the top floor of a building? It’s utter madness!

Good beer though…

Ps: I have the Mac results sitting here. I’m just struggling trying to find the time to massage the data into something reasonable.

title pic Macbook vs. Powerbook: Jiggerypokery II

Posted by pookzilla on June 21, 2006

(This will be a bit rushed – I have a bus to catch in half an hour :)

After managing to get the tests running I discovered that the results were not being logged to the database. In the end there were several hurdles.

First was that the appropriate system properties (-Declipse.perf.dbloc and friends) were not being passed to the VMs that were being used to launch the tests. This was corrected by supplying the appropriate flags via extraVMArgs in the test.xml file.

Once done, however, the tests complained that they didn’t have the appropriate JDBC drivers. The problem here lies in the fact that the documentation for the performance tests says that the Derby plug-in needs to be called “org.apche.derby” when in fact it needs to be called Cloudscape. The test plug-ins themselves have no problem with org.apache.derby – they happily try and load several derivatives of the name as well as Cloudscape – but the ant scripts that set up the test VMs require the plug-in to be called Cloudscape otherwise it is not copied to the test Eclipse instances. After renaming the plug-in and putting into the test zip file everything worked.

Almost. See, now it was complaining about the inability to connect to the database. I had set up my Mac Mini as a database server to contain the results from both machines but for some reason I couldn’t connect to it. As it happens Derby out of the box comes with very strict security settings and will only allow connections to ‘localhost’. Once this was fixed (simply adding -h to the derby start script) I could connect to the database.

But no results were being posted. I could verify that the test machines were making connections to the database but they weren’t writing any results. The answer to this was really tricky. In trying to debug the database connection woes I had created a database called “perf” from the Derby command line. As it happens, the eclipse tests will always append “/perfDB” to any connection url you give it. As a result when I was passing “net:///perf” as a connection string to the tests it was trying to connect to “net:///perf/perfDB”. The connection would succeed but there is no such database. What’s more, one can’t be created in that location because its parent was already a database. Either Derby was silently ignoring the problem and returning success for all writes to this location or the Eclipse tests were eating failures and not reporting them.

After deleting the initial database and running the tests again I am now seeing results in the database from both machines.

Now to get out of the house before I sully these results by fiddling with the setup any more than I am by making this post from the database machine. :)

title pic Macbook vs. Powerbook: Jiggerypokery

Posted by pookzilla on June 16, 2006

Getting the performance tests to run has require a bit more jiggerypokery than I had hoped for. Out of the box the performance test framework will not run on OS X (both Intel and otherwise) due to several bugs that have been reported against the releng team. Each should have a fairly straightforward fix – the hacks have been easy enough. ;)

The bugs I’ve found so far are :

Bug 147513 – runtests[.bat] does not take into account the existane of x86 Macs
Hacked to work by adding a new OR statement for macosx-carbon-x86 that allows the script to proceed.

Bug 147519 – Performance tests on OS X need additional VM parameters
Hacked to work by adding a new line to test.xml that always sets the “extraVMargs” property to “-XstartOnFirstThread”.

Bug 147520 – runtimeArchive property not set for Intel macs
Hacked to work by adding a new <condition> element for macosx-carbon-x86.

That’s it so far. The tests appear to be running fine at this point on my old G4 Powerbook but I can’t get a sense of the results. Hopefully the results from both machines will be available tomorrow.