Grund für die Entwicklung
Das Mapping von Edifact auf JSON und umgekehrt benötigt eine Dependency auf den EdiGenerator. Diese wird in der B2B über den Validation Content geliefert. Wir haben dort somit keine Kontrolle über die vorliegende Version und es kann so zu Versionskonflikten kommen. Daher haben wir uns entschieden hier einen Microservice zu implementieren, der die Abhängigkeit selbstverwaltet ausserhalb des B2B Kontextes nutzen kann.
Aufbau
Der Microservice ist eine eigenständige SpringBoot Applikation, die einen Post Endpunkt pro Konvertierungrichtung anbietet. Streng genommen wäre hier die GET Methode angebrachter, diese übermittelt ihre Daten aber nicht im Request Body, der bei einer Größe von potentiell mehreren Megabytes die bessere Option darstellt. Alternativ wäre eine Datenübermittlung im Rahmen eines File Uploads. Dieser Weg wurde aber hier nicht gewählt.
Unterstützte Formate und Sprache
Ab der Formatumstellung im Oktober 2022 wird das edi-json Mapping von allen EDIFACT Formatversionen des deutschen Strommarktes (definiert durch den BDEW) unterstützt. Die edi-json Datenstruktur liegt dabei ab den Formatversionen zur Umstellung Oktober 2022 in deutscher Sprache vor. Die Benennungen der Felder erfolgen dabei direkt anhand der Spezifikationen des BDEWs aus dem MIG Dokumenten, sodass die edi-json hiermit fachlich leicht lesbar werden.
Für EDIFACT Formatversionen vor der Formatumstellung im Oktober 2022 wird nur das Mapping ausgewählter wichtiger Formate unterstützt, nämlich: UTILMD, MSCONS, ORDERS, ORDRSP, IFTSTA, APERAK. Zudem liegt hier das edi-json in der Regel nur in englischer Sprache vor.
Sollte das edi-json Mapping auch von Gas oder anderen EDIFACT Formaten benötigt werden, so kann dies beauftragt werden.
Datenverarbeitung
Die übermittelten Daten werden synchron konvertiert und die Resultate im Responsebody übertragen.
Konfiguration des Microservice
Der Microservice wird über die Datei application.yml
konfiguriert. Relevante Settings dabei
- server.port - Der Port unter den der Service erreichbar sein soll
- andere aus SpringBoot bekannte parameter wie logging etc.
Versionsinfo
Die aktuelle Version des Microservice kann mit Hilfe des Endpunkts /actuator/info abgerufen werden.
Artefakt
Der edi-json.converter
steht (NLI intern im Nexus) im PrivateDL (mit berechtigtem Zugang) oder als Docker Image docker-nob-erp.next-level-apps.com/edi-json.converter:${EDIC_VER} bezogen werden.
Release
Ein EdiJson-Converter Release ist meistens unabhängig von einem B2B Release, d.h. weder erfolgt er geplant zeitgleich mit einem B2B Release, noch müssen B2B und EdiJson-Converter Versionen aufeinander abgestimmt werden. Die Release Notes zum EdiJson-Converter finden sich im hier.
Deployment (als Docker Container)
Beispiel Eintrag in docker-compose.yml zum Start einer edi-json.converter Instanz.
services:
edic01:
image: docker-nob-erp.next-level-apps.com/edi-json.converter:${EDIC_VER}
hostname: edic01
container_name: edic01
restart: always
command: "-Xmx4g"
ports:
- "8090:8080"
# management server port
- "9001:9001"
# debug
# - "5005:5005"
environment:
- TZ=Europe/Berlin
- JAVA_OPTS= -XX:+ExitOnOutOfMemoryError
# debug
# - JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n
volumes:
- ./logs_container:/logs
- /etc/localtime:/etc/localtime:ro
Verwendung zusammen mit der B2B
Schnell-Customizing
Es folgt ein minimales Customizing. Dieses stellt die Edi-Json Grundfunktionalitäten zur Verfügung, die in den folgenden Abschnitten genauer erläutert werden.
globalProperties:
EDIJSON_CONVERTER_URL: "http://edi-json-converter:8080"
services:
"ProcessTrigger":
name: EdiJsonMeta an ProcessTrigger
class: com.nextlevel.services.outbound.http.ProcessTriggerService
type: REST
direction: OUT
properties:
URL: "http://sxp-processtrigger-sim:8080"
MONITOR_OUTPUT: true
"EdiJsonInbound":
name: EdiJson Receiver
type: REST
channel: OUT_SEP
direction: IN
actions:
"SXP-ProcessTrigger #730":
id: 730
class: org.b2bbp.runtime.actions.internal.SetPropertyAction
description: 'Ruft den Service auf, der das EdiJson-Meta an den SXP-ProcessTrigger übergibt.'
properties:
B3P_USED_SERVICE_ID:
value: "ProcessTrigger"
techMon: true
"EdiJson-2-Edifact #731":
id: 731
class: com.nextlevel.edifactjson.mapping.EdiJson2EdifactMappingAction
description: 'Action, die das EdiJson in eine Edifact konvertiert.'
properties:
URL:
value: "http://edi-json-converter:8080"
techMon: true
channels:
-
id: IN_SXP
direction: IN
actions:
"ValidatorAction": true
"Contrl erzeugen": true
"Aperak erzeugen": true
"Übergabe an SXP-ProcessTrigger #730": true
"ErrorMailHandler": false
-
id: OUT_SXP
direction: OUT
actions:
"EdiJson-2-Edifact #731": true
"IndexingService Volltext": true
"Msg versenden (Mail/AS2)": true
"ErrorMailHandler": false
Einleitung
Die folgenden zwei Schaubilder zeigen schematisch wie der edi-json.converter Microservice zusammen mit der B2B und einem angeschlossenen Backend (z.B. der SXP Process Engine) konfiguriert werden kann, welches über EdiJsons (mit der B2B) kommuniziert. Dabei sind die folgenden zwei Kommunikationsrichtungen zu beachten:
Markt -> Backend (Edifact -> JSON)
Innerhalb der B2B findet zunächst eine normale Nachrichtenverarbeitung statt. Hier findet keine Push-Kommunikation statt. Es wird nur ein GET Endpoint angeboten, der es erlaubt über die MessageId einer verabeiteten Nachricht diese im JSON-Format abzurufen.
Eine Push-Kommunikation kann jedoch separat über den ProcessTrigger realisiert werden: Am Ende der Verarbeitung wird der ProcessTriggerService im Channel aufgerufen, welches ein MetaDaten-JSON ans Backend-System sendet. Dieses enthält u.a. die MessageId.
Nach Abschluss der Verarbeitung kann die JSON-Representation der verarbeiteten Edifact über den folgenden B2B REST Endpoint (tatsächliche Adresse über Swagger einsehbar) erhalten werden.
GET [b2b]/api/edifactjson/v1/edifact/{messageid}
Dabei wird zunächst die Ursprungsnachricht anhand der messageId aus der Datenbank geladen. Wurden einige Vorgänge der Nachricht per APERAK abgelehnt, so wird nur die “gekürzte” EDIFACT der APERAK-validen Vorgänge geladen. Ist die verarbeitete Ursprungsnachricht keine Edifact, sondern ein EdiJson, so wird dieses direkt zurückgegeben. Im Fall einer Edifact spricht die B2B mit dieser dann den edi-json.converter Microservice an, um diese in ein edi-json zu mappen.
Dieser REST Endpunkt unterscheidet dabei die folgenden Fehlerklassen für den Fall, dass bei der Anfrage kein edi-json erfolgreich zurückgegeben werden kann:
Fehlerfall | HTTP Status | Fehlerklasse (Response-Body -> details) |
---|---|---|
Autorisierung an B2B fehlgeschlagen | 403 | ACCESS_DENIED - Access is denied |
MessageId aus Abfrage ist in B2B DB nicht vorhanden oder vorhandener aber hat keine Ursprungsnachricht (BASE_MESSAGE) z.B. Job-Eintrag | 404 | MESSAGE_ID_NOT_FOUND - MessageId not found |
Ursprungsnachricht (BASE_MESSAGE) zu MessageId ist weder eine Edifact oder ein edi-json | 404 | WRONG_MESSAGE_FORMAT - The message is neither an edifact nor an edi-json |
Edifact zur der Nachricht konnte nicht erfolgreich vom edi-json.converter gemappt werden | 500 | EDIFACT_CONVERSION_FAILURE - Edifact to edi-json conversion failed |
edi-json.converter ist (vom B2B) nicht erreichbar | 500 | CONVERTER_EXCEPTION - Edi-json converter is unavailable or could not handle the requested message |
edi-json.converter ist in B2B nicht konfiguriert | 500 | CONVERTER_NOT_CUSTOMIZED - This service is inoperational because the global property EDIJSON_CONVERTER_URL could not be read |
B2B nicht erreichbar | 500 | kein B2B Fehlercode Body |
B2B DB Fehler/nicht erreichbar | 503 | B2B_DATABASE_UNAVAILABLE - B2B database is not available |
Seine Base URL muss dazu in der folgenden GlobalProperty hinterlegt sein.
EDIJSON_CONVERTER_URL = http://<converter_server>:<converter_port>
Der HATEOAS-Wrapper der B2B bei der JSON Convertierung hat die folgende Struktur:
{
"data": "edi-json",
"links": []
}
Es können nur nicht archivierte Nachrichten, bei denen das Attribut B3P_BASE_MESSAGE
eine Edifact Nachricht enthält abgerufen werden.
Backend -> Markt (JSON -> Edifact)
Die Edifacts werden im definierten JSON Format (gemäß edi-json-model, inklusive Hateoas-Wrapper) mit UTF-8 Encoding per POST an den Endpunkt (tatsächliche Adresse über Swagger einsehbar) gesendet.
POST [b2b]/api/edifactjson/v1/queue/{priority}
Wird keine Priorität angegeben nimmt die B2B als default-Wert LOW an. Dies entspricht dem üblichen Default-Wert. In der B2B muss dazu ein passiver Pseudo-Inbound-Service angelegt werden, der die Nachrichtenverabeitung mit einem bestimmten Channel in Queue leitet.
Die ID des Services kann abweichend vom Defaultwert in der folgenden GlobalProperty definiert werden, welche nach Neustart aktiv wird:
INBOUND_EDIJSON_SERVICE = My_Json_Inbound_Service (Default: EdiJsonInbound)
Noch vor der Queue durchläuft das JSON die Formaterkennung, welche aus den unterstützten JSON Formate in der HATEOAS-Wrapper Struktur die entsprechenden Daten auslesen kann. (Im Formatobjekt ist das Attribut UtilRef
nicht befüllt.)
Nachdem die Nachricht in der Queue abgelegt wurde wird der Request beendet und die Id der neuen Nachricht im ResponseBody zurück geschickt.
Die Verarbeitung der Nachricht hat zu diesem Zeitpunkt noch nicht begonnen.
Die zurückgegebene ID hat die folgende Form:
<B2BMessageId>_<B2BQueueId>
Die Konvertierung findet hier innerhalb eines Outbound-Channels statt, in welchem die Action EdiJson2EdifactMappingAction konfiguriert werden muss.
Debuggen
- in der docker-compose.yml die Zeilen unter # debug aktivieren
- bei
ports
- bei
environment
- bei
- Debugger konfigurieren
- Datei liegt unter: .runConfigurations/localhost_5005.run.xml
- diese als Debug-Konfiguration verwenden
- (IntelliJ Ultimate: Datei im Viewer anschauen, oberhalb des Codes Hinweis “Open Run/Debug Configurations” anklicken
- Breakpoints setzen
- Container starten
- Debugger in IDE starten
- Rest-Calls durchführen (z. B. mit rest-api.http in IntelliJ)
- Programmlauf stoppt an Breakpoints