Matthias Schoettle

Software Developer

Month: December 2019

Making EMF models serialized in XMI available in JSON with emfjson

This summer we had two interns in our team who started creating a web application for our TouchCORE tool. I have wanted this for a long time. Not only does it allow you to model in your browser, you can also do this collaboratively! For now class diagrams are supported but more supported will be added in the future (for example for sequence diagrams and feature models).

Re-creating the complete application right away was not feasible for this project. Also, we are all about reuse and I had envisioned in the architecture to reuse the backend parts when we build another user interface. Therefore, the goal was to keep our current backend based on the Eclipse Modeling Framework (EMF) and making it available through an API to the web application. Fortunately, there is already support for EMF and JSON by emfjson. The main use case however is to replace the XML/XMI serialization/deserialization with JSON.

There is a way to keep XMI on the backend and making it available through JSON. This allows to keep the XMI serialization which is still being used by the existing application. It requires to use unique IDs when serializing, otherwise you need to manage your own ID mapping (see below).

The emfjson-jackson setup is as follows:

ObjectMapper mapper = new ObjectMapper();
// Optional
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        
module = new EMFModule();
module.configure(EMFModule.Feature.OPTION_USE_ID, true);
// Optional
module.configure(EMFModule.Feature.OPTION_SERIALIZE_TYPE, true);

module.setIdentityInfo(new EcoreIdentityInfo("_id"));
mapper.registerModule(module);

The use of IDs along with the EcoreIdentityInfo ensures that the IDs used by EMF during serialization are used. Now, assuming you loaded an existing model from a resource for any EObject of that model you can get the JSON by calling:

mapper.writeValueAsString(eObject)

To do the reverse, you can identify an object by its ID. To retrieve the EObject for a given ID EMF of course has a handy utility method. For this, you need the Resource of your model.

resource.getEObject(id)

To get the ID of an object you can use resource.getURIFragment(eObject).

If you have cross-references in your models to other models, the URI you get is of the form “anotherModel.ext#id”. You can use the ResourceSet to get the resource of the other model or the cross-referenced object using the resource set’s getResource(URI, boolean) and getEObject(URI, boolean) methods.

You can also maintain your own ID mapping by passing a custom ValueWriter as a second parameter to EcoreIdentityInfo, for example:

module.setIdentityInfo(new EcoreIdentityInfo("_id",
    new ValueWriter<EObject, Object>() {
        @Override
        public Object writeValue(EObject eObj, SerializerProvider context) {
            // TODO: return ID of object
        }
    }
));

Furthermore, you need to handle references between objects specifically by customizing the reference serializer:

module.setReferenceSerializer(new JsonSerializer<EObject>() {
    @Override
    public void serialize(EObject eObject, JsonGenerator generator, SerializerProvider serializer) throws IOException {
        generator.writeString(/* TODO: Get the ID of eObject */);
    }
});

There is one caveat though. That is when using EMF notifications on the backend to notify the frontend that something changed. If an object is deleted, EMF does not give you an ID for this deleted object anymore. That’s why we in the end used the custom ID solution.

As I researched some links for this post I noticed that there is now a whole Eclipse project to make EMF available in the cloud (EMF.cloud). It would be interesting to evaluate whether these technologies (Sprotty as the web-diagramming framework and GLSP as the graphical language server protocol) are more suitable instead of a custom-baked solution. Currently we use Spring Boot on the backend with web sockets and Angular with mxgraph on the frontend.

Migrate (Legacy) Thunderbird add-on to MailExtension for Thunderbird 68

Like with Firefox, Thunderbird is also changing add-ons to WebExtensions (called MailExtension there). Thunderbird 68 introduced this requirement. Users on older versions still are not offered the latest version through the automatic updater so it stayed below my radar.

Recently, some users of small Toggle Headers extension reached out to me asking whether I intend to update it for Thunderbird 68 and later. I finally got a chance to migrate it and it was fairly easy in my case.

The easiest way is to convert it to a MailExtension with legacy support that allows to keep the old XUL stuff. The main changes I had to do were:

  1. Replace install.rdf with manifest.json: This is really straightforward given the documentation
  2. Add a legacy key to the manifest. Some examples showed "legacy": true but that did not work. Instead, you need to specify:
    "legacy": {
         "type": "xul" 
    }
  3. In the chrome.manifest I overlaid mailWindowOverlay.xul. This had to be changed to messenger.xul. See the note on overlaying.

With this, the extension can be packaged up (now with a nice little ant build script that automates this) and uploaded to Thunderbird’s add-ons site. Luckily, there it passed the review with no complaints and version 2.0 is now available.

I think it should be possible to accomplish this without any old XUL stuff. There is a MailExtension API documentation that outlines commands. Since users mainly use the add-on for the convenient key shortcut (H) this seems feasible. But that’s for another day.

© 2020 Matthias Schoettle

Theme by Anders NorenUp ↑