Matthias Schoettle

Software Developer

Category: Frameworks (page 1 of 2)

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.

Deploying Angular application on Apache server

If you need to deploy an Angular application on a server running Apache, and are making use of routing for navigation, you can’t just upload the built application onto the server and be done with it. As soon as you navigate to another path, the Apache server will try to look for that resource on the server and most likely will give you an 404 Not Found error.

You need to rewrite all the URLs to the index.html so that the Angular application can take care of it. Your server needs to support mod_rewrite for that. If that is the case, you can upload a file .htaccess with the following content to your directory where the Angular application resides in.

RewriteEngine on
RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !^/assets/(.+)$
RewriteRule ^(.+)$ index.html?path=$1 [L]

This will rewrite all requests to the index.html file and append any extra path to it. Unless, the request is for an existing file or directory on the server, then it will not rewrite it. This is necessary for the additional resources that will need to be loaded, such as CSS and JS files and images.

In order to get the necessary feedback when a resource is requested that does not exist, the third condition excludes to rewrite any request located inside the assets folder.

MT4j with Processing 2

In our TouchCORE project at the Software Engineering Lab at McGill University, we use MT4j (Multitouch for Java) for the multitouch-enabled user interface.

For a long time, we had the problem that we couldn’t run it on OSX using Java 7 and newer, because it is based on an older version of Processing (1.x), which in turn uses an old version of JOGL (Java Binding for the OpenGL API). That version is only compatible with the Java supplied by Apple. As we know, they stopped support with Java 6.

We grudgingly lived with this state for a long time, which meant that OSX users needed to download and use an old version of Java. Now, with the new release of Eclipse Mars, support for Java 6 was dropped, i.e., Java 7 or greater is required. We wanted to update to Mars, and since we use some Eclipse plugins, such as the Eclipse Modeling Framework (EMF), we needed to use at least Java 7.

This finally pushed us to update MT4j ourselves. With the help of the UltraCom project who did the same but added a ton of other stuff. Based on their commits we managed to update it on our copy (which already had some minor improvements) of MT4j’s last official release v0.98.

We put it up on GitHub so hopefully other people can benefit from it: https://github.com/mschoettle/mt4j

How to use OCL when running EMF standalone with Eclipse Mars

I previously explained on how to use OCL when running EMF in standalone (not as an Eclipse application). This method works until Eclipse Luna. With Eclipse Mars, OCL was heavily updated again. For instance, it was promoted out of the examples space.

The good thing is it seems to be much easier to initialize it now. Add a dependency to org.eclipse.ocl.xtext.completeocl and use the following code:

PivotStandaloneSetup.doSetup();
CompleteOCLStandaloneSetup.doSetup();

Modifying the “New Child” sub-menu items in EMF

I was just looking into a way to adjust the text of the items in the “New Child” (the same applies to “New Sibling” as well) sub-menu of the generated editor with EMF. By default the items just show the type name of the element to create. Depending on your meta-model it might be necessary to add some more information in order to be able to see which feature the new element gets added (or set) to.

The available items are depending on the current selected element. The collectNewChildDescriptors(Collection<Object>, Object) method is called on the item provider of this element. The actions for those menu items are created inside your ActionBarContributor class (see generateCreateChildActions(Collection<?>, ISelection)), which is located in the editor project. The text for this action is determined by CreateChildCommand.getText(), which in turn calls getCreateChildText(Object, Object, Object, Collection<?>) of the corresponding item provider. The default case is implemented in ItemProviderAdapter.

There seem to be two approaches, depending on what your goal is.

General approach: If you want to change the text independent from the element, meaning for all elements, you should change the _UI_CreateChild_text key in plugin.properties. By default this is {0}. This refers to the type name of the child. Also available are the feature text ({1}) and the type name of the owner ({2}).

Case-specific approach: If you just need to adjust it for one element, simply overwrite getCreateChildText(Object, Object, Object, Collection<?>) in the corresponding item provider and adjust it to your needs. Here is an example, that simply adds the features’ text in front of the default child text:

@Override
public String getCreateChildText(Object owner, Object feature,
        Object child, Collection<?> selection) {
    StringBuffer result = new StringBuffer();

    result.append(getFeatureText(feature));
    result.append(" ");
    result.append(super.getCreateChildText(owner, feature, child, selection));

    return result.toString();
}
« Older posts

© 2020 Matthias Schoettle

Theme by Anders NorenUp ↑