Skip to content

Repository files navigation

!!! This repository is discontinued. This project has moved to eclipse-emfcloud and will be maintained there !!!

Model Server

Build

To build the model server as standalone JAR and execute all component tests execute the following maven goal in the root directory:

mvn clean install

Code Coverage

The latest code coverage can be found here: com.eclipsesource.modelserver.codecoverage/jacoco/index.html.

The code coverage report is generated with JaCoCo and is integrated in the Maven build. In the package com.eclispesource.modelserver.codecoverage all code coverages are aggregated into one report.

When executing the Maven build locally, the detailed results are computed and can be investigated in more detail.

Run

Execute from IDE

To run the example model server within an IDE, run the main method of ExampleServerLauncher.java as a Java Application, located in the module com.eclipsesource.modelserver.example.

Execute Standalone JAR

To run the model server standalone JAR, run this command in your terminal:

cd examples/com.eclipsesource.modelserver.example/target/
java -jar com.eclipsesource.modelserver.example-X.X.X-SNAPSHOT-standalone.jar

Usage

usage: java -jar com.eclipsesource.modelserver.example-X.X.X-SNAPSHOT-standalone.jar
[-e] [-h] [-p <arg>] [-r <arg>]
options:
-e,--errorsOnly Only log errors
-h,--help Display usage information about ModelServer
-p,--port <arg> Set server port, otherwise default port 8081 is used
-r,--root <arg> Set workspace root

Model Server API

HTTP Endpoints

If the model server is up and running, you can access the model server API via http://localhost:8081/api/v1/*.

The following table shows the current HTTP endpoints:

CategoryDescriptionHTTP methodPathInput
ModelsGet all available models in the workspaceGET/models-
Get modelGET/modelsquery parameter: [?modeluri=...[&format=...]]
Create new modelPOST/modelsquery parameter: ?modeluri=...[&format=...]
application/json
Update modelPATCH/modelsquery parameter: ?modeluri=...[&format=...]
application/json
Delete modelDELETE/modelsquery parameter: ?modeluri=...
SaveGET/savequery parameter: ?modeluri=...
Execute commandsPATCH/editquery parameter: ?modeluri=...
Get all available model URIs in the workspaceGET/modeluris-
JSON schemaGet JSON schema of a modelGET/schemaquery parameter: ?modeluri=...
Server actionsPing serverGET/server/ping-
Update server configurationPUT/server/configureapplication/json

  • The query parameter ?modeluri= accepts files in the loaded workspace as well as absolute file paths.
  • Parameters in brackets [] are optional.

WebSocket Endpoints

Subscriptions are implemented via websockets ws://localhost:8081/api/v1/*.

The following table shows the current WS endpoints:

DescriptionPathInputReturns
Subscribe to model changes/subscribequery parameter: ?modeluri=...[&format=...]sessionId

Java Client API

The model server project features a Java-based client API that eases integration with the model server. The interface declaration looks as follows

publicinterfaceModelServerClientApiV1<A> {
CompletableFuture<Response<String>> get(StringmodelUri);
CompletableFuture<Response<A>> get(StringmodelUri, Stringformat);
CompletableFuture<Response<List<String>>> getAll();
CompletableFuture<Response<Boolean>> delete(StringmodelUri);
CompletableFuture<Response<String>> update(StringmodelUri, StringupdatedModel);
CompletableFuture<Response<A>> update(StringmodelUri, AupdatedModel, Stringformat);
CompletableFuture<Response<Boolean>> save(StringmodelUri);
CompletableFuture<Response<String>> getSchema(StringmodelUri);
CompletableFuture<Response<Boolean>> configure(ServerConfigurationconfiguration);
CompletableFuture<Response<Boolean>> ping();
CompletableFuture<Response<Boolean>> edit(StringmodelUri, Commandcommand);
CompletableFuture<Response<Boolean>> edit(StringmodelUri, Commandcommand, Stringformat);
CompletableFuture<Response<Boolean>> edit(StringmodelUri, CCommandcommand, Stringformat);
voidsubscribe(StringmodelUri, SubscriptionListenersubscriptionListener, Stringformat);
booleanunsubscribe(StringmodelUri);
EditingContextedit();
booleanclose(EditingContexteditingContext);
}

REST API Example

// You can customize the underlying okhttp instance by passing it in as a 1st parameter ModelServerClientclient = newModelServerClient("http://localhost:8081/api/v1/");
// perform simple GETclient.get("SuperBrewer3000.json")
.thenAccept(response -> System.out.println("GET: " + response.body()));
// perform same GET, but expect an EObjectclient.get("SuperBrewer3000.json", "xmi")
.thenAccept(response -> System.out.println("GET: " + response.body()));
// perform GET ALLclient.getAll()
.thenAccept(response -> System.out.println("GET ALL: " + response.body()));
// perform PATCH updateclient.update("SuperBrewer3000.json", "{ <payload> }")
.thenAccept(response -> System.out.println(response.body()));
// perform PATCH update with XMI formatclient.update("SuperBrewer3000.json", brewingUnit_EObject, "xmi")
.thenAccept(response -> {
client.get("SuperBrewer3000.json").thenAccept(resp -> {
System.out.println(client.decode(resp.body(), "xmi"));
});
});
}

Executing Commands

To perform changes on the model, clients may issue PATCH requests to update the model state incrementally in the server. These updates are broadcast to subscribers as incremental updates (see below).

Consider the following JSON payload for a PATCH request to add change the name of the workflow in the example Super Brewer 3000 model and to add another task to it:

{
"eClass": "http://www.eclipsesource.com/schema/2019/modelserver/command#//CompoundCommand",
"type": "compound",
"commands": [
{
"eClass": "http://www.eclipsesource.com/schema/2019/modelserver/command#//Command",
"type": "set",
"owner": {
"eClass":"http://www.eclipsesource.com/modelserver/example/coffeemodel#//AutomaticTask",
"$ref":"SuperBrewer3000.json#//@workflows.0"
},
"feature": "name",
"dataValues": [ "Auto Brew" ]
},
{
"eClass": "http://www.eclipsesource.com/schema/2019/modelserver/command#//Command",
"type": "add",
"owner": {
"eClass":"http://www.eclipsesource.com/modelserver/example/coffeemodel#//AutomaticTask",
"$ref":"SuperBrewer3000.json#//@workflows.0"
},
"feature": "nodes",
"objectValues": [
{
"eClass":"http://www.eclipsesource.com/modelserver/example/coffeemodel#//AutomaticTask",
"$ref":"//@commands.1/@objectsToAdd.0"
}
],
"objectsToAdd": [
{
"eClass":"http://www.eclipsesource.com/modelserver/example/coffeemodel#//AutomaticTask",
"name":"Brew"
}
],
"indices": [ 1 ]
}
]
}

This is a JSON representation of an EMF CompoundCommand containing two commands, a SetCommand that changes the name of the first workflow in the model, and an AddCommand that adds a new AutomaticTask to that workflow. The SetCommand does not require any index because the name feature is single-valued. The AddCommand here explicitly adds an position 1, but this can also be omitted to simply append to the end of the list. Notice how each command indicates the owner object in the model to which the change is applied using a cross-document reference. And in the case of the AddCommand, the object to be added does not yet exist in the model, so it must be included in the payload of the command, itself. Thus it is contained in the objectsToAdd property and indicate via an in-document reference in the objectValues property. Other commands, such as the RemoveCommand, would indicate objects in the objectValues property that already exist in thee model (to be removed in that case), and so those would be cross-document references and the objectsToAdd is unused.

To execute this command, issue a PATCH request to the edit endpoint like:

 PATCH http://localhost:8081/api/v1/edit?modeluri=SuperBrewer3000.json
Content-type: application/json
{ "data" : <payload> }

Subscriptions Example

If you want to be notified about any changes happening on a certain model, you can subscribe with a SubscriptionListener and define a format for the responses, which is "xmi" in this example.

ModelServerClientclient = newModelServerClient("http://localhost:8081/api/v1/");
StringsubscriptionId = "SuperBrewer3000.json";
client.subscribe(subscriptionId, newSubscriptionListener() {
@OverridepublicvoidonOpen(Response<String> response) {
System.out.println("Connected: " + response.getMessage());
}
@OverridepublicvoidonMessage(Stringresponse) {
System.out.println("Message received: " + response);
}
@OverridepublicvoidonClosing(intcode, @NotNullStringreason) {
System.out.println("Closing: Code " + code);
}
@OverridepublicvoidonFailure(Throwablet) {
System.out.println("Failed: ");
t.printStackTrace();
}
@OverridepublicvoidonClosed(intcode, @NotNullStringreason) {
System.out.println("Connection closed: Reason " + reason);
}
@OverridepublicvoidonFailure(Throwablet, Response<String> response) {
System.out.println("Failed: " + response);
}
@OverridepublicvoidonNotification(ModelServerNotificationnotification) {
System.out.println("Notification: " + notification);
}
}, "xmi");
client.unsubscribe(subscriptionId);

The kind of message received depends on the operation. For an update call (PUT request on the model), the message is the new content of the model. For an incremental update applied by a PATCH request with an edit command (see above), the message is the command that was executed. This command can then be executed in the client application to effect the same change as occurred in the server:

ModelServerClientclient = newModelServerClient("http://localhost:8081/api/v1/");
StringsubscriptionId = "SuperBrewer3000.json&format=json";
client.subscribe(subscriptionId, newJsonToEObjectSubscriptionListener() {
privatefinalCommandCodeccodec = newDefaultCommandCodec();
publicvoidonIncrementalUpdate(EObjectmessage) {
CCommandpayload = (CCommand) message;
EditingDomaineditingDomain = newEditingDomain() { ... };
try {
Commandcommand = codec.decode(editingDomain, payload);
CommandStackstack = editingDomain.getCommandStack();
if (command.canExecute()) {
stack.execute(command);
} else {
System.err.println("Cannot execute command: " + command);
}
} catch (DecodingExceptione) {
System.err.println("Cannot decode incremental update: " + e.getMessage());
}
}
}, "json");

Contributing

All involved code must adhere to the provided codestyle and checkstyle settings.

Eclipse IDE Setup

Requirements

  • Please make sure your Eclipse workspace uses a JRE of Java 9 or higher.
  • Install the Eclipse Checkstyle Plug-in via its update site https://checkstyle.org/eclipse-cs/#!/install.

Configure Checkstyle

To configure Checkstyle for the new project right click on the project, choose Checkstyle > Configure project(s) from blueprint... and select com.eclipsesource.modelserver.common as blueprint project. Run Checkstyle > Check Code with Checkstyle to make sure Checkstyle is activated correctly.

Import Existing Projects

Import all maven projects via File > Import... > Existing Maven Projects > Root directory: $REPO_LOCATION.

Please also import the codestyle project via File > Import... > Existing Projects into Workspace > Root directory: $REPO_LOCATION/releng > com.eclipsesource.modelserver.codestyle.

Create New Project

When a new project is needed, please stick to the following instructions to guarantee your code will be conform to the existing code conventions.

Project-Specific settings

Upon project creation the settings file org.eclipse.resources.prefs is created automatically and usually needs no further adjustment. Please copy and replace (if applicable) the following preferences files from com.eclipsesource.modelserver.common before you start coding:

  • org.eclipse.jdt.core.prefs
  • org.eclipse.jdt.launching.prefs
  • org.eclipse.jdt.ui.prefs
  • org.eclipse.m2e.core.prefs

Commit Changes

Please make sure to include the .settings folder as well as the .checkstyle settings file to the repository in your initial commit.

About

Server for synchronizing (EMF-based) models in a single-user scenario

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages