Chapter 9. Notifying Extensions

When we notify an extension, we don't have any control over what happens next. The contract we would like to maintain is that we notify all extenders regardless of failures. Consider a malicious listener:

public static class BadListener implements ITestRunListener {
  ...
  public void testsStarted(int testCount) {
    throw new NullPointerException();
  }
}

If we wrote a simple loop to notify all extensions, when we got to our BadListener an exception would be thrown, terminating the loop prematurely. So, here's another Eclipse rule:

  • GOOD FENCES RULEWhen passing control outside your code, protect yourself.

Applying this rule, when we notify our extensions, we need to do so safely. Because this is a common idiom in Eclipse, ISafeRunnable wraps potentially dangerous code. You can look at implementors of ISafeRunnable for lots of good examples. Our usage of it will be simple:

Example . org.eclipse.contribution.junit/JUnitPlugin

public void fireTestsStarted(final int testCount) {
  for (Iterator all= getListeners().iterator(); all.hasNext();) {
    final ITestRunListener each= (ITestRunListener) all.next();
    ISafeRunnable runnable= new ISafeRunnable() {
    public void handleException(Throwable exception) {
    }
    public void run() throws Exception {
      each.testsStarted(testCount);
    }
   };
   Platform.run(runnable);
  }
}

ISafeRunnable defines the hook methods run() and handleException(), which are invoked by the Platform. If anything goes wrong, the Platform takes care of the exception handling.

What if you have an extension that once it has gone crazy will continue throwing exceptions? A refinement of the idiom above is to remove an extension once it has thrown an exception. Applied to the situation above, we would insert the following line into the code above:

Example . org.eclipse.contribution.junit/JUnitPlugin.fireTestsStarted()

public void handleException(Throwable exception) {
  all.remove();
}

We use Iterator.remove() to avoid the problem of modifying a collection while iterating over it. You would only want to remove extensions if you were certain that their problems weren't transient.

We need to implement analogous methods fireTestsFinished(), fireTestStarted(String klass, String method), and fireTestFailed(String klass, String method, String stacktrace).

The functionality for Circle One is done. We have a small but complete plug-in. Before we leave the circle, we will prepare what we've done so far for use by others. Reviewing this chapter, we:

  • Designed an extension point that could be extended multiple times

  • Loaded the extensions only when they were about to be used

  • Safely notified extensions using ISafeRunnable and Platform.run()

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset