e4 – News from ModelTooling

It’s been a very long time since I’ve blogged about my work on the e4 model tooling front but this does not mean nothing happened. I think I have some interesting news to share on features I’ve implemented:

Edit/View XMI-File

Beside editing the Application model using the form based ui the editor now allows you to view and update the model by directly editing the XMI-representation of the model similar to how what you can do with the plugin.xml-Editor provided by PDE.

The editor is currently only provids highlighting and error markers. The reason is that I’m not able to simply include the XML-Editor provided by WTP because that would drag in too many dependencies. To add such a feature would be a nice job for a contributor though – any volunteers e.g. students as a SoC-Project ;-)

Support for externalizing Strings

One of the plan items we’ve worked on for the “Eclipse 4.1 Application Platform” was to add Native Language Support (NLS) and a major part of this is to make our application aware of internationalized text values. The process is quite similar to the one used in plugin.xml.

Instead of entering the real value you put it into OSGi-Bundles NLS-Properties-File (by default located in OSGI-INF/l10n/bundle.properties).
The model editor which is going be released as part of M6 is going to provide tooling to internationalize your application with only a few clicks which is once more similar to how you externalized texts using the plugin.xml-Editor provided by PDE.

  • Bring up context menu
  • Review provided externalization information
  • Improved C&P Support

    Copy and Paste support was not implemented appropriately. It allowed you only to copy and paste inside the left tree area. Since some days this is fixed and you can also use C&P in the form-fields (currently only text fields on the right hand side)

    Improved Search support for contributed classes

    The dialog to search for contributed classes (the classes handed over to the DI-Container to create instances) has been improved to allow wildcard searchs as part of the package name.

    Bundle-Dependency management in wizards

    The class creation wizards have been improved so that if they add Java code which requires your bundle to have more dependencies they add them automatically to your MANIFEST.MF so that the generated code can compile.

    Runtime Contribution Inspection

    This is the feature I’m just working on and I think we’ll be a great way for you to debug the application written on the “Eclipse 4.x Application Platform” because it provides you a deep look in the runtime environment of your application.

    The first thing provided as part of this tooling effort is to inspect you the instance of contributed classes making up the parts contents including extra informations for stuff provided by the DI-Container. At the moment only field informations are provided but others – most important Method Injections and @Execute-methods – will follow soon.

    Another plan item is to provide informations about the IEclipseContext an Application Model Element is located in so that you can better understand why you’ve got which value injected.

Enhanced RCP: Reuse of WritableList in JFace-Viewers

Just because I’ve been running into this today. I thought it might be interesting to others as well. The following is a shortened version of a code I’ve been using in an RCP-Application.

public class CreateItemsHandler {
   @Execute
   public void execute(IObservableList companies, 
                      @Named(IServiceConstants.ACTIVE_SHELL) Shell shell) {
       DialogImpl i = new DialogImpl(shell,companies);
       i.open();
   }

   class DialogImpl extends TitleAreaDialog {
      private final IObservableList companies;

      // ....
      public Composite createDialogArea(Composite parent) {
        // ....
        TreeViewer v; 
        // ....
        v.setContentProvider(
          new ObservableListTreeContentProvider(new ObservableFactoryImpl(),
          new StructureAdvisorImpl())
        );
        v.setInput(companies);
      }
   }; 
}

Does someone spot the error? I guess not because from it is not visible at all without looking into the implementation of ObservableListTreeContentProvider. The problem in there is that when the viewer is disposed it disposes also the IObservableList passed into it so when the handler is executed the second time you’ll run into problems.

The solution to the problem though is quite simple by making the input call look like this:

 v.setInput(new DecoratingObservableList(companies,false));

This way your original IObservableList is not disposed but only the decorator.

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