Enhanced RCP: How views can communicate – The e4 way


Some weeks ago I published how views can communicate using the EventAdmin-Service. To get things working in an 3.x application one has to write some glue code but more importantly one has to know about all those nifty things about event publishing, getting access to OSGi-Services, … .

One of the main topics of the Eclipse 4 Application Platform was to make easy things easy by removing the need to know about all the concepts by hiding them using DI. The service responsible to deliver events in application built on top the Eclipse 4 Application Platform is the EventBroker-Service:

package org.eclipse.e4.core.services.events;

public interface IEventBroker {
	public String DATA = "org.eclipse.e4.data"; //$NON-NLS-1$

	public boolean send(String topic, Object data);
	public boolean post(String topic, Object data);

	public boolean subscribe(String topic, EventHandler eventHandler);

	public boolean subscribe(String topic, String filter, EventHandler eventHandler,
			boolean headless);

	public boolean unsubscribe(EventHandler eventHandler);
}

This is all we need to know (assuming we have already understood how DI-works) to implement our sender and receiver views:

public class SenderView {
  @Inject
  private IEventBroker eventBroker;
  private Button b;

  @PostConstruct
  void init(Composte parent) {
    parent.setLayout(new GridLayout());
    b = new Button(parent, SWT.PUSH);
    b.setText("Send Event");
    b.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        Date d = new Date();
        eventBroker.send("viewcommunication/syncEvent",d);
        eventBroker.post("viewcommunication/asyncEvent", d);
      }
    });
  }

  @Focus
  void focus() {
    b.setFocus();
  }
}

These are some fewer lines of code which is good but IMHO the more important fact is that you are independent from OSGi running now so the code you have there is much easier testable by simply mocking IEventBroker!

Let’s look now at the receiver side of the story. If you take a look at the IEventBroker you see the subscribe methods which allows us to subscribe to various topics so we could as a first step implement it like this:

public class ReceiverView {
  @Inject
  private IEventBroker eventBroker;
  private TableViewer viewer;

  @PostConstruct
  public void init(final Composite parent) {
    parent.setLayout(new FillLayout());
    viewer = new TableViewer(parent);
    viewer.getTable().setHeaderVisible(true);
    viewer.getTable().setLinesVisible(true);
    viewer.setLabelProvider(new ColumnLabelProvider() {
      @Override
      public String getText(Object element) {
        return DateFormat.getDateTimeInstance().format(element);
      }
    });

    EventHandler handler = new EventHandler() {
      public void handleEvent(final Event event) {
        if( parent.getDisplay().getThread() == Thread.currentThread() ) {
          viewer.add(event.getProperty(IEventBroker.DATA));
        } else {
          parent.getDisplay().syncExec(new Runnable() {
            public void run() {
              viewer.add(event.getProperty(IEventBroker.DATA));
            }
          });
        }
      }
    };
    eventBroker.subscribe("viewcommunication/*",handler);
  }

  @Focus
  void focus() {
    viewer.getTable().setFocus();
  }
}

This is once more making your code looser coupled because you are independent from OSGi running but we can do even better by fully leveraging the Eclipse DI-Framework. By default Eclipse DI injects data it has stored in its IEclipseContext (you can think of the IEclipseContext as a Map where the value is thing that gets injected into you “POJO”).

The important thing for us is that one can extend Eclipse DI to consult other resources like e.g. Preferences (@Preference) and e.g. Event-Data (@EventTopic) – a blog post explaining how this works will follow hopefully soon.

So we can rewrite our receiver code like this:

public class ReceiverView {
  private TableViewer viewer;

  @PostConstruct
  public void init(final Composite parent) {
    parent.setLayout(new FillLayout());
    viewer = new TableViewer(parent);
    viewer.getTable().setHeaderVisible(true);
    viewer.getTable().setLinesVisible(true);
    viewer.setLabelProvider(new ColumnLabelProvider() {
      @Override
      public String getText(Object element) {
        return DateFormat.getDateTimeInstance().format(element);
      }
    });
  }

  @Inject
  void eventReceived(@EventTopic("viewcommunication/*") Date date) {
    Display d = viewer.getControl().getDisplay();
    if( d.getThread() == Thread.currentThread() ) {
      viewer.add(date);
    } else {
      parent.getDisplay().syncExec(new Runnable() {
        public void run() {
          viewer.add(date);
        }
      });
    }
  }

  @Focus
  void focus() {
    viewer.getTable().setFocus();
  }
}

[Update 2011-02-07] – Start
But we can do even better so that we don’t need to take of the UI-Thread syncing all we need to do is use another annotation:

@Inject
  void eventReceived(@UIEventTopic("viewcommunication/*") Date date) {
    viewer.add(date);
  }

Now the DI-Framework takes care of the Event loop syncing. Thanks to Brian de Alwis for pointing this out.
[Update 2011-02-07] – End

If you are interested in how you can use things like this in your Eclipse 3.x applications and you are going to be at EclipseCon you should come to my talk about “Singlesourcing for Eclipse 4.x and Eclipse 3.x

Advertisement
This entry was posted in Enhanced RCP, tutorials, Uncategorized. Bookmark the permalink.

13 Responses to Enhanced RCP: How views can communicate – The e4 way

  1. Pingback: Tom Schindl: Enhanced RCP: How views can communicate – The e4 way

  2. philk says:

    Interesting that in e4 one still has to manually sync with the SWT components. That’s something I never have to do in plain win32 (c++) coding.
    I had hoped finally there would be an easy way like Display.exec(Runnable) which would determine the threading issue under the hood. Its just a PITA to remember to sync all the time and write the same sync code over and over again.

    • Tom Schindl says:

      well to keep the scope as narrow as possible I thing there is no way around Display.exec() having said this we could may add an annotation which guarantees the call is made on the display-thread.

      • philk says:

        The thing is: Why do I even *have* to sync. Its not an issue in plain Win32. I can update the “display” from any thread I want. There are only race conditions (because of the message loop) with old OLE kind of stuff.
        And if it is an issue of constant mistakes by programmers the tools should help him to make less mistakes. How often have I forgotten to sync to the display thread? Its just annoying and I have written my own little helper class.

      • Tom Schindl says:

        I’ve just updated the blog to show how to sync at least things coming in through the event system to the UI-Thread

  3. Cool stuff Tom, thanks… didn’t know about @UIEventTopic

  4. Mark says:

    Just watched “Leveraging Eclipse 4 API to Build Components for the Future”. Would loved to have come to your EclipseCon “Singlesourcing for Eclipse 4.x and Eclipse 3.x”. Is that on the internet in some form (slides, webinar, video)

  5. Christian says:

    Tom, is this solution meant to be used with the compatibility layer in 3.x applications?
    I have the following problem: If i create an event handler like the one in ReceiverView, i’m getting an InjectionException (“unable to process ReceiverView#eventReceived: no actual value was found …”) when the view is created the first time. So i marked the parameter with @Optional. But then the eventReceived method is called during the injection phase with null as parameter value, without any sender having created the event.
    My current solution is to mark the whole method as optional, not the parameter.
    Are there any better solutions?

    • Tom Schindl says:

      Marking the method with @Optional is the way to use it in e4, so it is also the way to use it in the with the e4-bridge.

  6. Reblogged this on FunThomas Blog (de) und kommentierte:
    Gerade habe ich angefangen mich in Eclipse e4 einzuarbeiten. Was soll ich schreiben wenn es andere schon viel besser beschrieben haben.
    Zunächst eine kleine Beispielanwendung erstellen nach dem Tutorial von Lars Vogel: http://www.vogella.com/articles/EclipseRCP/article.html#tutorial_maps
    Und dann die Communikation zwischen den einzelnen Parts implementieren nach dem Beitrag vom Tomsondev Blog.

    Wünsche allen viel Spaß beim Ausprobieren

  7. metamurk says:

    It’s good to know that you have something to do there. But this DI bullshit simply doesn’t work reliable. It costs us hours over hours to hack workarounds over this, it’s an undebugable software technichial plaything. I can’t get in my head why almost rock solid plattform is rewritten, made a completly instable mess and forced thousands of developers using something working.

    • Tom Schindl says:

      Not sure i understand what you think i have to do there but DI is not a play thing it provides real value – if you are not happy with e4 go and fork the 3.x codebase, find people who want to help and move it forward – DI has caused fairly no problems to me and i make heavy use of it in my applications but if you have a testcase to show your problem i’m happy to take a look and fix it

Leave a Reply to metamurk Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.