Why I think @UIEventTopic / @EventTopic have been a bad idea

I’ve been working on a document since some time who holds my thoughts on the future direction of Eclipse 4 or better the Eclipse 4 Application Platform.

At first I’d like to mention that these as my personal views, not aligned with the e4 committers, so you’ll most likely find others who disagree.

It’s not my intention put e4 or any e4 committer down and if I’d blame someone it would be myself because I was part of the e4 effort since day 1.

The document is not yet ready but inspired by a tweet from Marcel Bruch

I’d like to take one of the points I’ll mention in this big vision/reflection document and explain why I think in retrospect providing those 2 annotations is wrong.

OSGi-Leakage

The first problem I have with those annotations is not an architectural one but caused by the bundle they are currently shipped in. We currently find them in “org.eclipse.e4.ui.di” and “org.eclipse.e4.core.di.extensions” who themselves require things like the Eclipse-DI-container, Equinox-container.

In my world of e4 components this is a deal breaker, as I want my code have no dependency on any of those bundles at compile time nor runtime but in general I think this is a minor problem and fixable. The architectural is the real blocker.

Event data is different to any other DI-Information

The real problem I have with @UIEventTopic and @EventTopic is the information they carry with them is totally different to any other data available in the DI-System (starting from OSGi-Services to Preference or IContextFunction derived values) because it’s only temporary. I think this characteristic makes @UIEventTopic/@EventTopic data not suited for DI.

The guess I have for those annotations being so popular is that they free you from the useage of IEventBroker (who leaks OSGi at an even larger scale by showing OSGi-Classes in its API, once more for no good reason) and the verbose code you need to write to subscribe (wild guess once more nobody was really worried about the OSGi-Class-Leakage).

@PostConstruct
void init(IEventBroker b) {
   b.subscribe( "my/event/Topic", new org.osgi.service.event.EventHandler() {
     public void handle(org.osgi.service.event.Event event) {
       handleEvent( (String)event.getProperty(IEventBroker.DATA) );
     }
   } );
}

private void handleEvent(String data) {
   // ...
}

In a Java8 world the verbosity can be reduced with the help of lamdas and method refs


import static my.sample.Util.extractEventData;

@PostConstruct
void init(IEventBroker b) {
  b.subscribe( "my/event/Topic", extractEventData(this::handleEvent));
}

private void handleEvent(String data) {
   // ...
}

// Util.java
public static <T> Consumer<Event> extractEventData( Consumer<T> dataConsumer) {
   return e -> (T)e.getProperty(IEventBroker.DATA);
}

The extractEventData could be provided by IEventBroker in a Java8 world BTW.

You loose typesafety for no good reason

IEventBroker and @UIEventTopic/@EventTopic trade IMHO typesafety for no good reason.

One can argue that this is the case for DI in general as well but you trade in this case typesafety against loose coupeling freeing your business code from dependencies on large framework (and yes I consider e4 and OSGi large frameworks) and providing you better testing support – so you get something in return.

This is not true for IEventBroker nor is it true for @UIEventTopic/@EventTopic, while IEventBroker could be fixed to provide a certain amount of typesafety (see e(fx)clipse EventBus) this will never be possible for @UIEventTopic/@EventTopic!

One could argue that on the receiver side you’ll have less framework bindings (in case one splits out the annotations from their existing owner bundle) but because the publisher requires the event bus anyways I don’t see any reason the receiver should not and the EventBus-Service is easy enough to be mocked in Unit-Tests.

And because it is not unlikely that many events are sent through the event system it puts pressure on the DI system which could have been avoid if you used the EventBus directly.

e(fx)clipse 2.2.0 – Typesafe EventBus

With e(fx)clipse we ship as part of our core-bundle (the one able to run in any OSGi and Plain-Java-Env) since 2.1 an org.eclipse.fx.core.event.EventBus which we enhanced in 2.2.0 to provide a certain level of typesafety.

Useage is straight forward:

  1. Define a Topic-Instance
    public class Constants {
      public static final org.eclipse.fx.core.event.Topic<String> MY_TOPIC 
        = new org.eclipse.fx.core.event.Topic<>("my/sample/Topic");
    }
    
  2. Publishing is done with
    @PostConstruct
    public void init(org.eclipse.fx.core.event.EventBus eventBus) {
      eventBus.publish( MY_TOPIC, "Hello World!", true );
    }
    
  3. Subscribing to events
    import static my.sample.Constants.MY_TOPIC;
    import static org.eclipse.fx.core.event.EventBus.data;
    
    @PostConstruct
    public void init(org.eclipse.fx.core.event.EventBus eventBus) {
      eventBus.subscribe( MY_TOPIC, data(this::handleTopic) );
    }
    
    private void handleTopic(String data) {
      // ...
    }
    

If you are interested in other new features of 2.2.0 look at this overview page

e(fx)clipse 2.2.0 – Maximize an UI-Component

I think allowing to maximize a certain UI-Element in your e4 on JavaFX application has been the most demanded features missing for e4 on JavaFX. e(fx)clipse 2.2.0 adds support to maximize an UI-Element as shown in the following video:

All you need to do is:

  • Add org.eclipse.fx.ui.workbench.renderers.base.addons.MinMaxAddon to your Application.e4xmi addons list
  • (Optional) Register an OSGi-Service of type org.eclipse.fx.ui.workbench.renderers.base.services.MaximizationTransitionService if you want some nice transitions, we ship a default transition as see in the above video named org.eclipse.fx.ui.workbench.renderers.fx.services.ProgressiveMaximizationTransitionService

I think this one coolest features we added since a long time and what makes it even cooler this support was contributed by one of the frameworks adopters Sun Volland.

If you are interested in other new features of 2.2.0 look at this overview page

e(fx)clipse 2.2.0 – Loading Media files from OSGi-Bundles – sponsored by 360T

As JavaFX is not only a UI-Toolkit but also provides a “fullblow” multimedia stack you can play sound files and videos without residing to other APIs/Libraries. Unfortunately you can not feed any URL into it.

Only the following protocols are supported

  • http:
  • file:
  • jar:file:

For most developers those protocols are enough but if you are running in an Equinox-OSGi-Environment and want to play a sound file packaged with in your bundle you are in trouble because the URL you’ll get is eg on Equinox bundleresource://.... so you can’t directly pass this URL to the JavaFX Media API because it will fail to resolve the resource.

Even if you know how to fix this and translate the URL to one of the above protocols your business code should never have a direct dependency on OSGi so you can not deal with this problem yourself but need a library to do this.

One of our JavaFX customers (360T) approach us a few days ago and so we implemented API giving you exactly this feature and you can/should always create Media instance like this:

public void playSound() {
  URL url = getClass().getResource("mysound.mp3");
  MediaLoader.createMedia( url )
    .map( MediaPlayer::new )
    .ifPresent( MediaPlayer::play );
}

If you are interested in other new features of 2.2.0 look at this overview page

Bringing OSGi-DS to plain java applications

Fairly everything in e(fx)clipse is done with DS-Services when you run in an OSGi-Environment.

Still many of them don’t have any dependency on OSGi at all so most components we have can also run/get used in an ordinary Java-Environment (see the blog post about the code editor framework as an example). Since the beginning we published some of them through the ServiceLoader-API (and we’ll still keep it for those) but that has the draw back that you can not express relations between services.

Tonight I had a crazy idea: Could I read the DS-Component-Registration files in an none-OSGi environment and wire services together without using the ServiceLoader-API.

The result is JavaDSServiceProcessor. Our public service lookup API has been retrovited to use this internal service instead of ServiceLoader so if one now eg looks up our AdapterService like this:

import org.eclipse.fx.core.Util;
import org.eclipse.fx.core.adapter.AdapterService;

public class Test {
  public static void main(String[] args) {
    AdapterService adapterService = Util.getService(AdapterService.class).get();
  }
}

will get a fully configured AdapterService. We currently don’t support everything from the DS-Spec (eg Properties are not yet support) but I’ll fill this gap soon.