Modelserver
Server for synchronizing (EMF-based) models in a single-user scenario
Install / Use
/learn @eclipsesource/ModelserverREADME
!!! 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:
|Category|Description|HTTP method|Path|Input
|-|-|:-:|-|-
|Models|Get all available models in the workspace|GET|/models| -
| |Get model|GET|/models|query parameter: [?modeluri=...[&format=...]]
| |Create new model|POST|/models|query parameter: ?modeluri=...[&format=...] <br> application/json
| |Update model|PATCH|/models|query parameter: ?modeluri=...[&format=...] <br> application/json
| |Delete model|DELETE|/models|query parameter: ?modeluri=...
| |Save|GET|/save|query parameter: ?modeluri=...
| |Execute commands|PATCH|/edit|query parameter: ?modeluri=...
| |Get all available model URIs in the workspace|GET|/modeluris| -
|JSON schema |Get JSON schema of a model|GET|/schema|query parameter: ?modeluri=...
|Server actions|Ping server|GET|/server/ping| -
| |Update server configuration|PUT|/server/configure|application/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:
|Description|Path|Input|Returns
|-|-|-|-
|Subscribe to model changes|/subscribe|query 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
public interface ModelServerClientApiV1<A> {
CompletableFuture<Response<String>> get(String modelUri);
CompletableFuture<Response<A>> get(String modelUri, String format);
CompletableFuture<Response<List<String>>> getAll();
CompletableFuture<Response<Boolean>> delete(String modelUri);
CompletableFuture<Response<String>> update(String modelUri, String updatedModel);
CompletableFuture<Response<A>> update(String modelUri, A updatedModel, String format);
CompletableFuture<Response<Boolean>> save(String modelUri);
CompletableFuture<Response<String>> getSchema(String modelUri);
CompletableFuture<Response<Boolean>> configure(ServerConfiguration configuration);
CompletableFuture<Response<Boolean>> ping();
CompletableFuture<Response<Boolean>> edit(String modelUri, Command command);
CompletableFuture<Response<Boolean>> edit(String modelUri, Command command, String format);
CompletableFuture<Response<Boolean>> edit(String modelUri, CCommand command, String format);
void subscribe(String modelUri, SubscriptionListener subscriptionListener, String format);
boolean unsubscribe(String modelUri);
EditingContext edit();
boolean close(EditingContext editingContext);
}
REST API Example
// You can customize the underlying okhttp instance by passing it in as a 1st parameter
ModelServerClient client = new ModelServerClient("http://localhost:8081/api/v1/");
// perform simple GET
client.get("SuperBrewer3000.json")
.thenAccept(response -> System.out.println("GET: " + response.body()));
// perform same GET, but expect an EObject
client.get("SuperBrewer3000.json", "xmi")
.thenAccept(response -> System.out.println("GET: " + response.body()));
// perform GET ALL
client.getAll()
.thenAccept(response -> System.out.println("GET ALL: " + response.body()));
// perform PATCH update
client.update("SuperBrewer3000.json", "{ <payload> }")
.thenAccept(response -> System.out.println(response.body()));
// perform PATCH update with XMI format
client.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.
ModelServerClient client = new ModelServerClient("http://localhost:8081/api/v1/");
String subscriptionId = "SuperBrewer3000.json";
client.subscribe(subscriptionId, new SubscriptionListener() {
@Override
public void onOpen(Response<String> response) {
System.out.println("Connected: " + response.getMessage());
}
@Override
public void onMessage(String response) {
System.out.println("Message received: " + response);
}
@Override
public void onClosing(int code, @NotNull String reason) {
System.out.println("Closing: Code " + code);
}
@Override
public void onFailure(Throwable t) {
System.out.println("Failed: ");
t.printStackTrace();
}
@Override
public void onClosed(int code, @NotNull String reason) {
System.out.println("Connection closed: Reason " + reason);
}
@Override
public void onF
