Thursday, February 9, 2017

GEF4 + 1 = GEF 5

It's been roughly eight months since we graduated GEF4 1.0.0 as part of the GEF 4.0.0 (Neon) release, and we have since not kept still. As its always hard to get a debut right, we first concentrated on some bug fixes and shipped GEF4 1.1.0 as part of the GEF 4.1.0 (Neon.1) bugfix release in September. Since then, we have concentrated on the Oxygen stream.

In order to emphasize that through its graduation, the GEF4 new code base has now officially superseded the former production components, we thought it important to adopt our wording accordingly. That is, we now refer to the original code base as GEF-Legacy, while we use GEF to refer to the new code base alone.


We reflected this when migrating our org.eclipse.gef and org.eclipse.gef4 Git repositories to respective GEF-Legacy and GEF GitHub repositories, and we further adopted the namespace of all GEF(4) bundles from org.eclipse.gef4.* to org.eclipse.gef.* (the namespace of the former production components was preserved, and as there is no overlap, they can still be installed in parallel). The Oxygen release is accordingly referred to as the GEF 5.0.0 (Oxygen) release: GEF4 + 1 = GEF 5.

While the renaming alone would already have enforced a major release because of semantic versioning constraints, we also had the strong intention to do more than mere 'cosmetics'. Let me point out just two things that have concerned us so far on our way to Oxygen.

Merger of MVC and MVC.FX

When we started working on the GEF model-view-controller framework (MVC) component, we took much care of separating generic, rendering-toolkit independent concepts (MVC) from JavaFX-specific aspects (MVC.FX). The motivation behind was to enable a potential reuse of generic concepts also for other rendering toolkits than JavaFX. As it turned out, this was not a wise decision, which is why we have merged MVC and MVC.FX, and the GEF model-view-controller framework is now dedicated to JavaFX alone. Why?

First and foremost, because it lead to overcomplicated code that turned out to be a hurdle for adopters. The root visual type parameter, which we had to introduce in order to be able to reason about rendering-related aspects already inside MVC, may be quoted as a good evidence. It provoked that most types within MVC needed to be parameterized, thus leading to a lot of overhead. For instance, to query the current viewer selection, the following had to be done: 

SelectionModel<VR> selectionModel = viewer.getAdapter(new TypeToken<SelectionModel<VR>>(){}
  .where(new TypeParameter<VR>() {}, Types.<VR>argumentOf(viewer.getClass())));
  
SelectionModel<VR> selectionModel = getSelectionModel();
List<IContentPart<VR, ? extends VR>> currentSelection
  new ArrayList<>(selectionModel.getSelectionUnmodifiable());

When applying GEF in customer projects we further learned that client code does indeed need to access concrete, visualization-specific parts more often than we had thought. Many type casts were required to access JavaFX-specifics like the viewer's canvas: 

((FXViewer) viewer).getCanvas().setStyle(FXViewer.FOCUSED_STYLE);

Further, the separation of generic and JavaFX-specific aspects lead to overcomplicated implementations especially in those parts of the client code that are related to interaction handling (behaviors and policies).

It was also unwise because we became aware that supporting multiple rich-client rendering technologies with a single code base does not seem to be as desirable as we initially thought. As far as Java-based rich-client applications, and especially Eclipse, are concerned, JavaFX seems to be the single, obvious choice: it is feature-complete, open-source, hardware accelerated, supported on all major platforms, and can (quite) easily be integrated with SWT. There is no real need for an alternative.

And for browser-based applications, a Java-based client framework does not seem to be a valid choice anyway. Here, a framework that makes use of web languages and technologies alone is the only viable option. While there already is a whole zoo of such technologies around, I have the impression that the same flexibility, we currently have reached with GEF for the rich client, is not yet available. We will thus (have to) do something, but that's a different story.

Java 9 Compatibility

While the new JavaFX-based code base that we have developed through GEF4 has made it feasible to use GEF for building standalone Java richt-client applications, the Eclipse platform is still our major target platform. We thus rely on the ability to integrate JavaFX with SWT, and we need to be able to do this in an OSGi environment.

In general, integrating JavaFX with SWT is realized through javafx.embed.swt.FXCanvas, an integration that is directly shipped as part of JavaFX in its javafx-swt.jar. FXCanvas subclasses org.eclipse.swt.widgets.Canvas and embeds a JavaFX scene, whose contents is rendered as an image and to which (nearly) all SWT events are forwarded. The ability to use this integration in an OSGi-environment is provided through an Equinox framework extension that is delivered by e(fx)clipse. It ensures that the javafx-swt.jar is available on the classpath of all OSGi bundles that require it. The combination of both thus enables us to use JavaFX within an Eclipse editor or view. 

However, while e(fx)clipse works quite nicely out of the box, the FXCanvas implementation provided with Java 8 has some problems. Horizontal mouse scroll events and gesture events are, for instance, not forwarded from SWT to the embedded JavaFX scene, image cursors cannot be set, focus cannot be properly traversed, etc. We have thus been providing an FXCanvasEx implementation as well as some helper classes as part of the GEF FX component, that incorporates workarounds for the following issues:
  • JDK-8088862: Provide possibility to traverse focus out of FX scene.
  • JDK-8161282: FXCanvas does not forward horizontal mouse scroll events to the embedded scene.
  • JDK-8143596: Forward touch gestures to FXCanvas embedded scene.
  • JDK-8088147: Image cursors not supported.
  • JDK-8159227: KeyEvent.doit should be forwarded to KeyEvent.consumed().
  • JDK-8161587: FXCanvas does not update SWT display.
While that works fine for Java 8, some of the workarounds require access to JDK-internals, which makes them inoperable for Java 9, where such access is now prevented by the newly introduced modularity system.

The only option we saw was to contribute fixes for at least the affected workarounds directly to JavaFX 9, so workarounds within our code base would no longer be needed. That was immanently clear to us when the JIGSAW-fog finally started to lift. Consequently, I contacted the OpenJFX team and became a contributor. And while Oracle has recently had some bad press regarding its open-source engagement, let me emphasize that the OpenJFX team, in particular Kevin Rushforth, made me feel very welcome and greatly supported me in contributing.

As the most invasive fixes will thus be covered by FXCanvas in Java 9 directly, I could guard the related workarounds within our FXCanvasEx to only be effective in Java 8. As a result, users of FXCanvasEx can now transparently rely on identical functionality on Java 8 and Java 9. And with Tom Schindl having added support for Java 9 to e(fx)clipse just a few weeks ago (see bug 482428 for details), running GEF on Java 9 has (finally) become feasible. You can try it yourself:
The 'GEF MVC Logo Example' and the 'GEF Zest Graph Example' views (located in the 'Other' category) can now be opened:



















In case, as an adopter, you already want to try developing bundles against a JavaSE-9 execution environment (BREE), you will have to install 'e(fx)clipse - IDE - PDE' 3.0.0 from http://download.eclipse.org/efxclipse/updates-nightly/site in addition (which will 'inject' javafx-swt.jar to your bundle dependencies), and you will have to update the 'Eclipse Java Developer Tools' to the Java 9 preview available at http://download.eclipse.org/eclipse/updates/4.7-Y-builds.

Give it a try, all early feedback will be more than welcome!