1. OpenAPI-Spezifikation

Die OpenAPI-Spezifikation ist ein Standard zur Beschreibung von REST-APIs.

Die REST-API des FVA Simulation Hub ist vollständig nach OpenAPI v3.0.3 (siehe spec.openapis.org/oas/v3.0.3) spezifiziert. Die Spezifikationsdatei befindet sich im Installationsverzeichnis unter docs/schema/openapi.yaml.

Mit dem OpenAPI-Generator (siehe openapi-generator.tech/) besteht die Möglichkeit, Quellcode aus der OpenAPI-Spezifikation zu generieren. Dabei werden bereits viele verschiedene Programmiersprachen unterstützt.

2. Java-Bibliothek

Kommt für die Entwicklung von Client-Anwendungen die Programmiersprache Java zum Einsatz, dann kann als Basis die bereitgestellte Java-Bibliothek verwendet werden.

Die Java-Bibliothek befindet sich im Installationsverzeichnis unter docs/libs/java. Es sind alle Modell-Klassen enthalten, die auch im FVA Simulation Hub für den Austausch der Objekte zwischen Client und Server verwendet werden.

3. Beispiele

Im Installationsverzeichnis unter docs/client-examples sind Beispiele für Client-Anwendungen hinterlegt.

4. Tutorial

Um die Beispiele in diesem Kapitel möglichst übersichtlich zu halten, erfolgt die Angabe für eine deaktivierte Authentifizierung. Alle notwendigen Informationen können im Abschnitt "Authentifizierung" nachgeschlagen werden.

4.1. Authentifizierung

Die Authentifizierung gegenüber der Anwendung ist nur möglich, wenn der Konfigurationsparamter app.security.api.enable mit true gesetzt wurde. Andernfalls sind die Endpunkte /api/2.0/auth/* deaktiviert.

Für die Authentifizierung werden JWT (JSON Web Token) verwendet. Detailierte Informationen über Aufbau und Ablauf können unter jwt.io/introduction nachgelesen werden.

Login

Die Anmeldung findet über den Endpunkt POST /api/2.0/auth/login statt. Im Body der Anfrage werden Benutzername und Kennwort angegeben.

GET /api/2.0/auth/login HTTP/1.1
Host: example.com
Content-Type: application/json

{
  "username": "user1", (1)
  "password": "password1" (2)
}
1 Benutzernamen werden über den Konfigurationsparameter app.security.users[0].username definiert.
2 Kennwörter werden über den Konfigurationsparameter app.security.users[0].password definiert.

Die Antwort enthält den Access-Token für weitere Anfragen:

HTTP/1.1 200
Content-Type: application/json
Date: Mon, 02 May 2022 12:26:00 GMT

{
  "accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyMiIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9hcGkvMS4wL2F1dGgvbG9naW4iLCJleHAiOjE2NTEzNDQzMjcsImlhdCI6MTY1MTM0MzcyN30.3gYLlU4wfY53DXd8SMMUA7fxS5gZQhGwW0WD3h3HpeE" (1)
}
1 Der Token für weitere Anfragen.

Anfragen

Bei jeder Anfrage muss der Access-Token im Authorization-Header angegeben werden.

GET /actuator/info HTTP/1.1
Host: example.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyMiIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9hcGkvMS4wL2F1dGgvbG9naW4iLCJleHAiOjE2NTEzNDQzMjcsImlhdCI6MTY1MTM0MzcyN30.3gYLlU4wfY53DXd8SMMUA7fxS5gZQhGwW0WD3h3HpeE (1)
1 Im Authorization-Header muss der Token mit dem vorangestelltem Schlüsselwort Bearer angegeben werden.

JWT werden aus Sicherheitsgründen mit einer Gültigkeitsdauer versehen (Konfiguration über Parameter app.security.accessToken.expiration). Sollte ein Token abgelaufen sein, liefern die Anfragen mit dem Authorization-Header den HTTP-Statuscode 401 (Unauthorized). In diesem Fall muss ein erneuter Login erfolgen und dann kann die Anfrage erneut gesendet werden.

4.2. Dateien als Base64

Mit der REST-API des FVA Simulation Hub werden Dateien mittels Base64 übermittelt. Dabei muss Byte-Strom der Datei Base64-kodiert angegeben werden.

Für die Programmiersprache Java sehen die Codezeilen für die Kodierung und Dekodierung wie folgt aus:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;

public static String encode(Path pathToFile) throws IOException
{
    byte[] fileContent = Files.readAllBytes(pathToFile);
    byte[] contentEncodedBytes = Base64.getEncoder().encode(fileContent);
    return new String(contentEncodedBytes);
}

public static void decode(String base64Content, Path pathToFile) throws IOException
{
    byte[] content = Base64.getDecoder().decode(base64Content);
    Files.write(pathToFile, content);
}

4.3. Simulate Work

Um den grundlegenden Ablauf zur Bearbeitung von Tasks kennenzulernen, werden zunächst die Simulate-Work-Endpunkte empfohlen. Dabei wird ein Task erstellt, der Arbeit simuliert, indem eine FVA-Workbench-Instanz für einen bestimmten Zeitraum blockiert.

Erstellung des Task

Der Task wird über den Endpunkt POST /api/2.0/workbench/task/simulate-work/create erstellt. Im Body der Anfrage wird die Dauer des simulierten Jobs in Sekunden angegeben.

POST /api/2.0/workbench/task/simulate-work/create HTTP/1.1
Host: example.com
Content-Type: application/json

{
  "duration": 10 (1)
}
1 Für 10 Sekunden soll eine FVA-Workbench-Instanz simulieren, dass sie arbeitet.

In der Antwort erhält man u. A. die ID des erstellten Task und kann damit dessen Status überwachen.

HTTP/1.1 201
Content-Type: application/json
Date: Mon, 02 May 2022 12:26:00 GMT

{
  "status": "WAITING",
  "id": "b431a18d-8e92-46d9-a9a5-b456dbc898e3", (1)
  "requestedDatetime": "2022-03-30T13:20:48.940547"
}
1 Die ID des Task für weitere Anfragen.

Status prüfen und Ergebnisse holen

Die Überwachung des Tasks erfolgt über den Endpunkt GET /api/2.0/workbench/task/simulate-work/{id}/result. Unter Angabe der ID erhält man damit den aktuellern Status und nach Fertigstellung auch die Ergebnisse.

GET /api/2.0/workbench/task/simulate-work/b431a18d-8e92-46d9-a9a5-b456dbc898e3/result HTTP/1.1 (1)
Host: example.com
1 Die ID des Tasks wird im Pfad der URL angegeben.

Die obere Anfrage liefert sofort eine Antwort mit dem aktuellen Status.

Es kann aber auch der Query-Parameter timeout angegeben werden. Damit wird die maximale Zeit in Sekunden angegeben, die auf die Beendigung des Tasks gewartet werden soll. Ist der Task bereits beendet, kommt die Antwort sofort.

GET /api/2.0/workbench/task/simulate-work/b431a18d-8e92-46d9-a9a5-b456dbc898e3/result?timeout=60 HTTP/1.1 (1)
Host: example.com
1 Bis zu 60 Sekunden soll auf die Beendigung des Tasks gewartet werden.

Befindet sich der Task noch in der Bearbeitung, könnte die Antwort wie folgt aussehen:

HTTP/1.1 201
Content-Type: application/json
Date: Mon, 02 May 2022 12:26:00 GMT

{
  "status": "RUNNING", (1)
  "id": "b431a18d-8e92-46d9-a9a5-b456dbc898e3",
  "requestedDatetime": "2022-03-30T13:20:48.940547", (2)
  "startedDatetime": "2022-03-30T13:25:48.4796426" (3)
}
1 Der Status RUNNING gibt an, dass der Task gerade von einer FVA-Workbench-Instanz bearbeitet wird.
2 Mit requestedDatetime wird der Zeitpunkt angegeben, zu dem die Anfrage zur Erstellung des Tasks bei der Anwendung eingegangen ist.
3 Mit startedDatetime wird der Zeitpunkt angegeben, zu dem der Task die Warteschlange verlassen hat und zur Bearbeitung von einer FVA-Workbench-Instanz übernommen wurde.

Bei einem erfolgreich beendeten Task sieht die Antwort wie folgt aus:

HTTP/1.1 200
Content-Type: application/json
Date: Mon, 02 May 2022 12:26:00 GMT

{
  "status": "FINISHED", (1)
  "id": "b431a18d-8e92-46d9-a9a5-b456dbc898e3",
  "executingWorkbench": (2)
  {
    "id": "foo",
    "version": "7.1.0",
    "revision": "12345"
  },
  "requestedDatetime": "2022-03-30T13:20:48.940547",
  "startedDatetime": "2022-03-30T13:25:48.4796426",
  "endedDatetime": "2022-03-30T13:25:58.6424796", (3)
  "message": "received Workbench after 0 seconds, then waited for 10 seconds" (4)
}
1 Der Status FINISHED gibt an, dass der Task erfolgreich bearbeitet wurde.
2 Unter executingWorkbench erfolgen Angaben zur FVA-Workbench, mit der der Task bearbeitet wurde.
3 Mit endedDatetime wird der Zeitpunkt angegeben, zu dem die Bearbeitung des Task abgeschlossen wurde.
4 Mit message wird das Endpunkt-spezifische Ergebnis angegeben.
Nachdem der Result-Endpunkt einmal mit einem abschließendem Status (CANCELED, FINISHED und FAILED) aufgerufen wurde, wird der Task innderhalb der Anwendung gelöscht. Alle weiteren Anfrage mit der ID liefern dann nur noch den HTTP-Statuscode 404 (Not Found).

4.4. Execute Batch Job

Mit den Execute-Batch-Job-Endpunkten können Batch-Jobs für die FVA-Workbench über den FVA Simulation Hub ausgeführt werden. Alle gewöhnlichen Funktionen der Batch-Jobs stehen auch hier zur Verfügung.

Erstellung des Task

Der Task wird über den Endpunkt POST /api/2.0/workbench/task/execute-batch-job/create erstellt. Im Body der Anfrage erfolgen Angaben zum FVA-Workbench-Modell und den auszuführenden Scripts.

{
  "inputModel":
  {
    "type": "WBPZ", (1)
    "content": "UEsDBAoAAAAAAMNwGFUAAAAAAAAAA..." (2)
  },
  "inputScripts": [
    {
      "content": "cnVuQ2FsY01ldGhvZCgnMDAxX1NZU1RFTV9DQUxDVUxBVElPTicsIDEpOw==" (3)
    }
  ],
  "additionalFiles": [ (4)
    {
      "filename": "some-file.txt",
      "content": "cnVuQ2FsY01ldGhvZCgnMDAxX1NZU1RFTV9DQUxDVUxBVElPTicsIDEpOw=="
    }
  ]
}
1 Die Angabe des Formats zum FVA-Workbench-Modell.
2 Der Base64-kodierte Inhalt der FVA-Workbench-Modelldatei (Achtung: Hier nur verkürzte Angabe).
3 Der Base64-kodierte Inhalt des auszuführenden Scripts.
4 Mit additionalFiles können weitere Dateien übergeben werden, die für die Bearbeitung notwendig werden könnten (z. B. Reportvorlagen).

In der Antwort erhält man u. A. die ID des erstellten Task und kann damit dessen Status überwachen.

HTTP/1.1 201
Content-Type: application/json
Date: Mon, 02 May 2022 12:26:00 GMT

{
  "status": "WAITING",
  "id": "b431a18d-8e92-46d9-a9a5-b456dbc898e3",
  "requestedDatetime": "2022-03-30T13:20:48.940547"
}

Scripting

Innerhalb des Scripting stehen die folgenden Variablen zur Verfügung:

  • WorkingDirectory: Das Ausführungs-Verzeichnis. In diesem Verzeichnis werden u. A. alle zusätzlich hinterlegten Dateien abgelegt (siehe Eigenschaft additionalFiles im Request).

  • OutputDirectory: Das Ausgabe-Verzeichnis. In diesem Verzeichnis können Dateien hinterlegt werden. Alle Dateien in diesem Verzeichnis werden Bestandteil des Ergebnisses zum zugehörigen Task. Die Verwendung von Unterverzeichnissen ist zulässig.

Ein Beispiel für ein übergebenes Script könnte wie folgt aussehen:

const status = runCalcMethod('001_SYSTEM_CALCULATION', 1); (1)
if (status === 1) { (2)
    println('Calculation was successful');
    const templateFile = WorkingDirectory + '/000_System_report.wbrep'; (3)
    const outputFile = OutputDirectory + '/report.html'; (4)
    const options = {compactView: false,
                        language: "de",
                        completeTree: false,
                        notifications: true,
                        navigationBar: true};
    generateReport(templateFile, outputFile, options); (5)
    exportModel(OutputDirectory + '/test.rexs'); (6)
} else {
    exportNotifications(OutputDirectory + '/notifications.xml'); (7)
    throw 'Calculation was NOT successful'; (8)
}
1 Ausführung der Gesamtsystemberechnung.
2 Prüfen, ob die Berechnung erfolgreich durchgeführt wurde.
3 Variable mit Pfad zur übergebenen Reportvorlage deklarieren.
4 Variable mit Pfad zur Report-Datei im Ausgabe-Verzeichnis deklarieren.
5 Report mit definierten Parametern und Optionen erstellen.
6 REXS-Datei ins Ausgabe-Verzeichnis exportieren.
7 Meldungen nach Fehlschlag der Berechnung in das Ausgabe-Verzeichnis exportieren.
8 Ausführung des Scripts fehlschlagen lassen, damit der batchExecutionStatus korrekt gesetzt werden kann.

Status prüfen und Ergebnisse holen

Die Überwachung des Tasks erfolgt über den Endpunkt GET /api/2.0/workbench/task/execute-batch-job/{id}/result. Unter Angabe der ID erhält man damit den aktuellern Status und nach Fertigstellung auch die Ergebnisse.

GET /api/2.0/workbench/task/execute-batch-job/b431a18d-8e92-46d9-a9a5-b456dbc898e3/result?timeout=60 HTTP/1.1 (1)
Host: example.com
1 Die ID des Tasks wird im Pfad der URL angegeben. Zudem soll bis zu 60 Sekunden auf die Beendigung des Tasks gewartet werden.

Bei einem erfolgreich beendeten Task sieht die Antwort wie folgt aus:

HTTP/1.1 200
Content-Type: application/json
Date: Mon, 02 May 2022 12:26:00 GMT

{
  "status": "FINISHED", (1)
  "id": "b431a18d-8e92-46d9-a9a5-b456dbc898e3",
  "batchExecutionStatus": "OK", (2)
  "executingWorkbench": (3)
  {
    "id": "foo",
    "version": "7.1.0",
    "revision": "12345"
  },
  "requestedDatetime": "2022-03-30T13:20:48.940547", (4)
  "startedDatetime": "2022-03-30T13:25:48.4796426", (5)
  "endedDatetime": "2022-03-30T13:26:25.6424796", (6)
  "outputFiles": [ (7)
    {
      "directory": "path/to/file/in/output/directory",
      "filename": "protocol.log",
      "content": "UEsDBAoAAAAAAKlbVlIAAAAAAAAAAAAAAAALACQAcmVzdW..."
    }
  ]
}
1 Der Status FINISHED gibt an, dass der Task erfolgreich bearbeitet wurde.
2 Der batchExecutionStatus gibt an, ob die Scripts erfolgreich ausgeführt werden konnten.
3 Unter executingWorkbench erfolgen Angaben zur FVA-Workbench, mit der der Task bearbeitet wurde.
4 Mit requestedDatetime wird der Zeitpunkt angegeben, zu dem die Anfrage zur Erstellung des Tasks bei der Anwendung eingegangen ist.
5 Mit startedDatetime wird der Zeitpunkt angegeben, zu dem der Task die Warteschlange verlassen hat und zur Bearbeitung von einer FVA-Workbench-Instanz übernommen wurde.
6 Mit endedDatetime wird der Zeitpunkt angegeben, zu dem die Bearbeitung des Task abgeschlossen wurde.
7 Mit den outputFiles werden alle Dateien zurückgegeben, die während der Ausführung des Scripts im Ausgabe-Verzeichnis hinterlegt wurden. Standardmäßig ist immer die Datei protocol.log enthalten.
Nachdem der Result-Endpunkt einmal mit einem abschließendem Status (CANCELED, FINISHED und FAILED) aufgerufen wurde, wird der Task innderhalb der Anwendung gelöscht. Alle weiteren Anfrage mit der ID liefern dann nur noch den HTTP-Statuscode 404 (Not Found).

4.5. Execute Batch Job mit großen Modelldateien

Bei Batch-Jobs mit großen Modelldateien gibt es aus Sicht der Performance folgende Einschränkungen:

  • Die Modelldatei muss clientseitig zu Base64 kodiert werden.

  • Die Modelldatei muss Base64-kodiert übertragen werden (bis zu 33% mehr Datengröße).

  • Die Modelldatei muss serverseitig aus Base64 dekodiert werden.

  • Die Modelldatei muss bei jedem neu erstellten Tasks vollständig übertragen werden.

Um diese Probleme zu lösen, bietet der FVA Simulation Hub einen integrierten Storage an. Damit werden die Modelldateien vor der Erstellung des Tasks übertragen und können dann sogar mehrfach verwendet werden.

Upload der Modelldatei

Der Upload der Modelldatei erfolgt über den Endpunkt POST /api/2.0/storage/input/upload. Im Body der Anfrage muss der Dateiinhalt als multipart/form-data angegeben werden.

GET /api/2.0/storage/input/upload?overwrite=true HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="cylindrical-gear_model.wbpz"

... contents of file goes here ...
------WebKitFormBoundary7MA4YWxkTrZu0gW--

Der Status 204 in der Antwort gibt an, dass der Upload erfolgreich war.

HTTP/1.1 204
Content-Type: application/json
Date: Mon, 02 May 2022 12:26:00 GMT

Ein Quellcode-Beipiel für den Upload einer Modelldatei mit RestTemplate, dem HTTP-Client von Spring, sieht wie folgt aus:

import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import de.fva.simulationhub.api.workbench.task.ExecuteBatchJobRequest.WorkbenchModelTypeEnum;

public class Application
{
    public static void main(String[] args)
    {
        RestTemplate restTemplate = new RestTemplate();

        // Headers
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        // Body
        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        Path pathToFile = Paths.get("path/to/cylindrical-gear_model.wbpz");
        body.add("file", new FileSystemResource(pathToFile)); (1)

        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);

        // URL
        String url = "http://localhost:8080/api/2.0/storage/input/upload";

        // Query parameters
        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(url)
            .queryParam("overwrite", true);

        URI uri = builder.build().toUri();

        // Submit request
        ResponseEntity<Void> response = restTemplate.postForEntity(uri, requestEntity, Void.class);
        System.out.println("Response code: " + response.getStatusCode());
    }
}
1 An dieser Stelle wird eine Implementierung des Interface org.springframework.core.io.Resource erwartet. Soll hier ein abweichender Dateiname verwendet werden, dann muss die Methode getFilename():String überschrieben werden.

Erstellung des Task

Der Task wird über den Endpunkt POST /api/2.0/workbench/task/execute-batch-job/create erstellt. Im Body der Anfrage wird jetzt eine Referenz zur FVA-Workbench-Modelldatei im Storage angegeben.

{
  "inputModel": {
    "type": "WBPZ",
    "storageName": "cylindrical-gear_model.wbpz" (1)
  },
  "inputScripts": [
    {
      "content": "cnVuQ2FsY01ldGhvZCgnMDAxX1NZU1RFTV9DQUxDVUxBVElPTicsIDEpOw=="
    }
  ]
}
1 Mit dem Parameter storageName wird der eindeutige Name der Modelldatei im Storage angegeben.

In der Antwort erhält man unverändert u. A. die ID des erstellten Task und kann damit dessen Status überwachen.

HTTP/1.1 201
Content-Type: application/json
Date: Mon, 02 May 2022 12:26:00 GMT

{
  "status": "WAITING",
  "id": "b431a18d-8e92-46d9-a9a5-b456dbc898e3",
  "requestedDatetime": "2022-03-30T13:20:48.940547"
}

Status der Datei prüfen

Mit dem Endpunkt GET /api/2.0/storage/input/{name} kann der Status der Datei geprüft werden. So kann geprüft werden, ob eine bestimmte Datei überhaupt im Storage hinterlegt ist, bevor sie erneut hochgeladen wird.

GET /api/2.0/storage/input/cylindrical-gear_model.wbpz HTTP/1.1 (1)
Host: example.com
1 Der Name der Modelldatei werden im Pfad der URL angegeben.

Bei einer Modelldatei, die im Storage vorhanden ist, sieht die Antwort wie folgt aus:

HTTP/1.1 200
Content-Type: application/json
Date: Mon, 02 May 2022 12:26:00 GMT

{
  "exists": true
}