Der Json Translator Service kann beliebige JSON-Daten auf beliebige andere JSON-Strukturen mit Hilfe des Frameworks Jolt abbilden.

Hintergrund

Um beispielsweise eine interne AMQP API an eine externe AMQP API anzubinden, ist es notwendig, die Datenstrukturen der beiden APIs aufeinander abzubilden. Dazu wird das Framework Jolt verwendet, welches eine JSON-Struktur in eine andere JSON-Struktur abbilden kann. Die Abbildung wird dabei durch eine Konfigurationsdatei definiert, die die Abbildungsregeln enthält.

Der Json Translator Service ist ein eigenständiger SpringBoot Microservice.

Konfiguration

Der Service kann mit oder ohne Docker installiert werden. Über den Helm Chart ist auch ein Deployment in Kubernetes möglich. Der Service wird über die Datei application.properties konfiguriert. Relevante Settings dabei

Server Port

Mit diesem Parameter kann der Port der Applikation gesetzt werden. Der Default ist 8080.

server.port=8080

RabbitMQ Configuration

Um den Message Broker verwenden zu können, muss er konfiguriert werden.

rabbitmq.host=localhost
rabbitmq.port=5672
rabbitmq.username=guest
rabbitmq.password=guest

Json Translator Consumer

Der JSON-Translator unterstützt bis zu vier verschiedene JSON-Consumer. Jeder Consumer arbeitet unabhängig und verwendet eine eigene Spezifikationsdatei für die Zuordnung. Die Anwendung konsumiert JSON-Daten aus den Queues jsonTranslatorConsumerExchangeName(1-4). Der Name der Queue setzt sich aus dem ExchangeName und der Group <jsonTranslatorConsumerExchangeName(1-4>.<jsonTranslatorConsumerGroup(1-4)>. Der ExchangeType kann über den Parameter jsonTranslatorConsumerExchangeType(1-4) konfiguriert werden. Wenn für die Queue Routing eingerichtet ist, muss der Wert, nachdem geroutet werden soll, an der jsonTranslatorConsumerGroup angegeben werden. Ein konfiguriertes Routing am Producer ist hierfür notwendig.

Konfiguration für alle vier Consumer:

spring.cloud.function.definition= consumeEvent1;consumeEvent2;consumeEvent3;consumeEvent4

jsonTranslatorConsumerExchangeName1=json.translator.consumer.1
jsonTranslatorConsumerGroup1=default
jsonTranslatorConsumerExchangeType1=direct

jsonTranslatorConsumerExchangeName2=json.translator.consumer.2
jsonTranslatorConsumerGroup2=default
jsonTranslatorConsumerExchangeType2=direct

jsonTranslatorConsumerExchangeName3=json.translator.consumer.3
jsonTranslatorConsumerGroup3=default
jsonTranslatorConsumerExchangeType3=direct

jsonTranslatorConsumerExchangeName4=json.translator.consumer.4
jsonTranslatorConsumerGroup4=default
jsonTranslatorConsumerExchangeType4=direct

Die Eigenschaft spring.cloud.function.definition definiert, welche Consumer aktiv sind.

Soll nur durch einen bestimmten routingKey in diese Queue geroutet werden, so kann dies über den bindingRoutingKey angegeben werden. Im Beispiel für Consumer 1 und Consumer 2.

spring.cloud.stream.rabbit.bindings.consumeEvent1-in-0.consumer.bindingRoutingKey=9900000000001
spring.cloud.stream.rabbit.bindings.consumeEvent2-in-0.consumer.bindingRoutingKey=9900000000002

Soll anhand von mehreren Routingkeys in die Queue geroutet werden, so können mehrere angegeben werden. In diesem Beispiel kommasepariert.

spring.cloud.stream.rabbit.bindings.consumeEvent1-in-0.consumer.bindingRoutingKey=9900000000001,9900000000002
spring.cloud.stream.rabbit.bindings.consumeEvent1-in-0.consumer.bindingRoutingKeyDelimiter=,
spring.cloud.stream.rabbit.bindings.consumeEvent2-in-0.consumer.bindingRoutingKey=9900000000001,9900000000002,9900000000003,9900000000004
spring.cloud.stream.rabbit.bindings.consumeEvent2-in-0.consumer.bindingRoutingKeyDelimiter=,

Max Concurrency - Parallelität

Es ist möglich für dein Consumer die Eigenschaft max-concurrency zu setzen. Diese definiert die maximale Anzahl von gleichzeitigen Verarbeitungsthreads, die für einen Consumer gestartet werden können. Der Default liegt bei 50.

jsonTranslatorConsumerMaxConcurrency1=50
jsonTranslatorConsumerMaxConcurrency2=50
jsonTranslatorConsumerMaxConcurrency3=50
jsonTranslatorConsumerMaxConcurrency4=50

Json Translator Producer

Nachdem die JSON-Daten auf eine andere JSON-Struktur abgebildet wurden, sendet der Service die abgebildeten JSON-Daten an die entsprechende Queue. Jeder der vier Consumer hat einen spezifischen Producer.

Der ExchangeName kann über <jsonTranslatorProducerExchangeName(1-4)> konfiguriert werden. Der ExchangeType kann über den Parameter jsonTranslatorProducerExchangeType(1-4) konfiguriert werden. Der RoutingKey kann über den Parameter jsonTranslatorRoutingkeyExpression(1-4) konfiguriert werden. Stellen Sie sicher, dass der Routing-Schlüssel in Anführungszeichen steht, da er statisch ist. Bei dynamischen Routingkeys, wie beispielsweise beim Routing nach tenant, muss diese Funktion nach dem Muster konfiguriert werden, wie es im nachfolgendem Beispiel unter jsonTranslatorRoutingkeyExpression3 angegeben ist.

jsonTranslatorProducerExchangeName1=json.translator.producer.1
jsonTranslatorRoutingkeyExpression1='default'
jsonTranslatorProducerExchangeType1=direct

jsonTranslatorProducerExchangeName2=json.translator.producer.2
jsonTranslatorRoutingkeyExpression2='delivery'
jsonTranslatorProducerExchangeType2=direct

jsonTranslatorProducerExchangeName3=json.translator.producer.3
jsonTranslatorRoutingkeyExpression3=headers.tenant
jsonTranslatorProducerExchangeType3=direct

jsonTranslatorProducerExchangeName4=json.translator.producer.4
jsonTranslatorRoutingkeyExpression4='default'
jsonTranslatorProducerExchangeType4=direct

ACHTUNG: die Producer-Queue wird nicht vom Service selbst erstellt. Es muss vor Verwendung sicher gestellt werden, dass die Queue vorhanden ist, damit keine Nachrichten verloren gehen.

Json Translator Mapping

Die Mapping Regeln können als Konfigurationsdatei bereitgestellt werden. Der Pfad zur Mapping Datei kann über den Parameter jsonSpecPath konfiguriert werden. Der Default Pfad ist /var/lib/config/spec<1-4>.json und muss nur gesetzt werden, wenn ein anderer Pfad benötigt wird. Es kann für jeden Consumer, eine Konfigurationsdatei genutzt werden.

jsonSpecPath1=/var/lib/config/spec1.json
jsonSpecPath2=/var/lib/config/spec2.json
jsonSpecPath3=/var/lib/config/spec3.json
jsonSpecPath4=/var/lib/config/spec4.json

Release Download

Der Json Translator Service kann als Docker Image docker-nob-erp.next-level-apps.com/aep/json-translator-service:${TRANSLATOR_VER} bezogen werden oder über das PrivateDL (mit berechtigtem Zugang) als Artefakt heruntergeladen werden.

Deployment

Json-Translator Helm Chart

Der Helm Chart ermöglicht ein direktes Deployment in Kubernetes.

Um eine neue Transformation im Helm Chart anzulegen, müssen zum einen bestimmte Umgebungsvariablen definiert sein, zum anderen muss die Jolt Spezifikation in den Container gemounted werden. Im folgenden Beispiel wurde eine ConfigMap mit der Jolt Spezifikation angelegt.

kind: ConfigMap
apiVersion: v1
metadata:
  name: json-translator-service-configmap
data:
  usermessage_request_spec.json: |-

In folgenden Beispiel werden Nachrichten aus dem usermessage.request topic gelesen, transformiert und anschließend in den topic b2b.edifact geschrieben.

env:
- name: JSONSPECPATH1
  value: "/var/lib/config/usermessage_request_spec.json"
- name: JSONTRANSLATORCONSUMEREXCHANGENAME1
  value: "usermessage.request"
- name: JSONTRANSLATORCONSUMERGROUP1
  value: "mygroup"
- name: JSONTRANSLATORCONSUMEREXCHANGETYPE1
  value: "topic"
- name: JSONTRANSLATORCONSUMERMAXCONCURRENCY1
  value: "50"
- name: JSONTRANSLATORPRODUCEREXCHANGENAME1
  value: "b2b.edifact"
- name: JSONTRANSLATORPRODUCEREXCHANGETYPE1
  value: "topic"
extraVolumes:
- name: json-translator-service-volume
  configMap:
	name: json-translator-service-configmap
extraVolumeMounts:
- name: json-translator-service-volume
  mountPath: "/var/lib/config/usermessage_request_spec.json"
  subPath: "usermessage_request_spec.json"

Weitere Transformationen können analog dem obigen Muster angelegt werden.

Docker Container

Beispiel Eintrag in docker-compose.yml zum Start einer Json Translator Service Instanz. In diesem Fall muss die application.properties und die Mapping Datei im Verzeichnis ./conf/json-translator-service/ liegen.

services:
  json-translator-service:
    image: docker-nob-erp.next-level-apps.com/aep/json-translator-service:${TRANSLATOR_VER}
    hostname: json-translator-service
    container_name: json-translator-service
    restart: always
    environment:
      - SPRING_CONFIG_ADDITIONAL-LOCATION=/var/lib/config/application.properties
      - TZ=${TIME_ZONE}
      - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD}
    volumes:
      - ./conf/json-translator-service/:/var/lib/config

Start ohne Docker

Das Artefakt des Json Translator Service kann einfach mit dem Befehl

java -jar json-translator-service.jar –spring.config.additional-location=classpath:./json-translator-service.properties –server.port=8096

im Verzeichnis des Artefakts ausgeführt werden.

Hinweis: Der edi-json.converter läuft mit Java 17, welches hierbei verwendet werden muss.

Der ausgeführte Service kann dabei durch ein application.properties File, welches neben der jar gelegt wird, konfiguriert werden. Die Default Einstellungen aus dem Artefakt sehen wie folgt aus:

spring.application=json-translator-service

#==== RabbitMQ
rabbitmq.host=host
rabbitmq.port=port
rabbitmq.username=user
rabbitmq.password=pw

spring.cloud.function.definition= consumeEvent1;consumeEvent2;consumeEvent3;consumeEvent4

#Consumer variables
jsonTranslatorConsumerExchangeName1=json.translator.consumer.1
jsonTranslatorConsumerGroup1=default
jsonTranslatorConsumerExchangeType1=direct

jsonTranslatorConsumerExchangeName2=json.translator.consumer.2
jsonTranslatorConsumerGroup2=default
jsonTranslatorConsumerExchangeType2=direct

jsonTranslatorConsumerExchangeName3=json.translator.consumer.3
jsonTranslatorConsumerGroup3=default
jsonTranslatorConsumerExchangeType3=direct

jsonTranslatorConsumerExchangeName4=json.translator.consumer.4
jsonTranslatorConsumerGroup4=default
jsonTranslatorConsumerExchangeType4=direct

#Producer variables
jsonTranslatorProducerExchangeName1=json.translator.producer.1
jsonTranslatorRoutingkeyExpression1='default'
jsonTranslatorHeaderName1=tenant
jsonTranslatorProducerExchangeType1=direct

jsonTranslatorProducerExchangeName2=json.translator.producer.2
jsonTranslatorRoutingkeyExpression2='default'
jsonTranslatorHeaderName2=tenant
jsonTranslatorProducerExchangeType2=direct

jsonTranslatorProducerExchangeName3=json.translator.producer.3
jsonTranslatorRoutingkeyExpression3='default'
jsonTranslatorHeaderName3=tenant
jsonTranslatorProducerExchangeType3=direct

jsonTranslatorProducerExchangeName4=json.translator.producer.4
jsonTranslatorRoutingkeyExpression4='default'
jsonTranslatorHeaderName4=tenant
jsonTranslatorProducerExchangeType4=direct

#Path to the json specification file
jsonSpecPath1=/var/lib/config/spec1.json
jsonSpecPath2=/var/lib/config/spec2.json
jsonSpecPath3=/var/lib/config/spec3.json
jsonSpecPath4=/var/lib/config/spec4.json

Die minimal Konfiguration wäre lediglich das Eintragen der Werte für die Anbindung der AMQP:

#==== RabbitMQ
rabbitmq.host=host
rabbitmq.port=port
rabbitmq.username=user
rabbitmq.password=pw

Andere Propertys hätten bei der minimal Konfiguration die oben gezeigten default Werte.

Zugriff über externen Client mit eingeschränkten Berechtigungen

Soll ein externer Zugriff mit eingeschränkter Berechtigung erfolgen kann ein entsprechender RabbitMQ User angelegt werden. Diesem User werden im Anschluss die Berechtigungen für die einzelnen Exchanges und Queues über entsprechende RegEx Ausdrücke erteilt.

Im obigen Beispiel würde man dem User die drei folgenden Berechtigungen erteilen.

configuration: ^$
write: ^(usermessage\.request|b2b\.edifact)$
read: ^(usermessage\.request\.default|b2b\.edifact\.default)$

Der Eintrag configuration enthält ein leeren Ausdruck, der User darf entsprechend keine Objekte in RabbitMq neu anlegen oder löschen. Der zweite Ausdruck erteilt Schreibrechte auf die zwei Exchanges usermessage.request und b2b.edifact. Auf die zwei Queues usermessage.request.default und b2b.edifact.default hat der User Leserechte. Über diesen User kann entsprechend auf alle anderen Exchanges und Queues nicht zugegriffen werden.

Um den neuen User zu verwenden, muss der Client den User entsprechend über

rabbitmq.username=newuser
rabbitmq.password=pw

setzen.

View Me   Edit Me