Bei Hypoport haben wir seit Jahren einen selbst entwickelten ESB im Einsatz. In unserem Team haben wir wenig KnowHow diesen zu nutzen und anzupassen. Die Anbindung und Erweiterung von externen Schnittstellen fällt daher nicht leicht. Erschwerend kommt hinzu, dass die Integration Flows nicht durch unser Continuous Delivery Skript automatisiert ausgerollt werden können. Wir standen vor der Entscheidung uns intensiv in diesen ESB einzuarbeiten oder es mit einer anderen Integrationslösung zu versuchen. Wir entschieden uns für letzteres. Es gibt mittlerweile eine Fülle an leistungsstarken Lösungen, die aktiv weiter entwickelt werden, große Communities und gute Dokumentation besitzen. Die Auswahl fiel schnell auf Spring Integration. In unserer Anwendung verwenden wir bereits diverse Spring Module und sind damit vertraut The Spring Way zu entwickeln. Die Zeit fehlte um eine ESB Evaluierung und eine Abstimmung mit anderen Teams durch zu führen. Unser Team hatte nur vereinzeltes und eher oberflächliches ESB KnowHow. Die Notwendigkeit eines eigenen Application-Server mit seinen eigenen Techniken und Technologien für Erzeugung und Deployment von Software-Artefakten hätte unsere derzeitige Systemlandschaft unnötig komplex macht.


Spring Integration

Spring Integration ist non-invasive und leichtgewichtig. Es passt sich nahtlos in die Spring Module Familie ein. Dies und die sehr gute Referenz-Dokumentation vereinfacht die Entwicklung und lässt einen schnell produktiv sein. Vor allem die Unterstützung der Enterprise Integration Patterns lassen einen schnell seine Integration Flows Lego-artig zusammen stellen. Im Folgenden ein Beispiel.

Dieses Bild stellt schematisch einen gewöhnlichen Flow da. Bei Punkt 1 (Channel Adapter) wird eine Nachricht auf einen Channel gepackt. In unserem Fall ist dies ein JAXB Object, welches durch Punkt 2 in ein XML Dokument umgewandelt wird (Marshalling Transformer). Anschließend (Punkt 3) wird dieses XML in das Format des Kunden transformiert (XSLT Transformer). Ist das entstandene XML Schemakonform (Punkt 4 / Validating Filter), wird es mit Dingen wie der SoapAction angereichert (Punkt 5b / Content Enricher) und schlussendlich an die WebService-Schnittstelle des Kunden übergeben (Punkt 6 / Outbound Gateway). Punkt 5a deutet den Fehlerfall an und wird hier nicht näher betrachtet. Ebenso wird nicht näher die Behandlung der WebService-Antwort beschrieben.

Die für solch einen Flow notwendigen Klassen sind überschaubar. Mit Spring Integration wird es noch einfacher.

Punkt 1 – Channel Adapter

 eoOjectChannel.send(MessageBuilder.withPayload(eoEmmaNl).build());

In diesem Beispiel kommt kein Inbound Gateway zum Einsatz. Vielmehr legen wir aus unserem Applikationscode eine Message (den JAXB annotierten Objektbaum) auf den eoOjectChannel.

Punkt 2 – Marshalling Transformer

<si:channel id="eoObjectChannel"/>

<si-xml:marshalling-transformer id="eoMarshallerTransformer"
    marshaller="eoMarshaller"
    input-channel="eoObjectChannel"
    output-channel="eoXmlChannel"
    result-transformer="resultToStringTransformer"/>

<si:channel id="eoXmlChannel"/>

<oxm:jaxb2-marshaller id="eoMarshaller" contextPath="de.hypoport.emma.nl.export.facade"/>
<bean id="resultToStringTransformer"
    class="org.springframework.integration.xml.transformer.ResultToStringTransformer"/>

In Zeile 3 wird ein XML Marshalling Transformer definiert und mit dem Input- und Output-Channel verbunden. Das Marshalling wird in diesem Fall durch den in Zeile 8 definierten JAXB Marshaller durchgeführt.

Punkt 3 – XSLT Transformer

<si-xml:xslt-transformer id="xsltTransformer"
    xsl-resource="classpath:de/hypoport/emma/nl/staterfa/emmaToStater.xslt"
    input-channel="eoXmlChannel"
    output-channel="elfXmlChannel"/>

<si:channel id="elfXmlChannel"/>

Die Transformation unseres Export XML in das Format des Kunden ist ebenfalls trivial. Neben dem Verketten der Channels geben wir in Zeile 2 an welche XSLT zu verwenden ist.

Punkt 4 – Validating Filter

<si-xml:validating-filter id="elfSchemaValidator"
    input-channel="elfXmlChannel"
    output-channel="validElfXmlChannel"
    discard-channel="invalidElfXmlChannel"
    schema-location="classpath:elf.xsd" />

Nun wollen wir unserem Kunden nur Schema-konforme Requests schicken. Einfach wieder die richtigen Channels zusammen stecken und noch angeben, welche XSD zu verwenden ist (Zeile 5).

Punkt 5a

Wie oben bereits erwähnt ist die Fehlerbehandlung hier nicht abgebildet. Die fehlerhaften Nachrichten landen auf dem discard-channel (siehe Punkt 4).

Punkt 5b/6 – Content Enricher/Outbound Gateway

<si:chain
    input-channel="elfXmlChannel"
    output-channel="replyChannel">
    <si-ws:header-enricher>
        <si-ws:soap-action value="http://ExternalWebService/ElfService"/>
    </si-ws:header-enricher>
    <si-ws:outbound-gateway id="elfService"
        uri="https://dev.service.com/elfService.asmx"
        message-sender="messageSender">
    </si-ws:outbound-gateway>
</si:chain>

<bean id="messageSender" class="org.springframework.ws.transport.http.HttpsUrlConnectionMessageSender">
    <property name="trustManagers" ref="trustManagers"/>
</bean>

Das jetzt valide XML soll als nächstes einem externen WebService übergeben werden. Hierzu wird die Message um die zu verwendende SoapAction ergänzt (Zeile 5). In Zeile 8 wird der Endpunkt definiert. In Zeile 9 und 13 wird der MessageSender konfiguriert. In diesem Fall erfolgt die Kommunikation gesichert über HTTPS. Beim Definieren der zu verwendenden Zertifikate muss derzeit das erste Mal etwas Code selbst geschrieben werden. Sobald das Ticket SWS-731 umgesetzt ist, sollte diese Konfiguration ebenso einfach sein, wie der Rest.

Fazit

Das Beispiel hat verdeutlicht wie leicht ein Integration-Flow mit Spring Integration umgesetzt werden kann. Der dabei selbst zu schreiben Code ist minimal. Aufgrund der Unterstützung der Enterprise Integration Patterns ist die Konfiguration einfach, lesbar und verständlich. Die bisherigen Erfahrungen sind sehr gut und wir planen weitere Integration-Flows mit Spring Integration umzusetzen.

In einem weiteren Artikel plane ich das Thema Testen und die Strukturierung von Konfigurationen zu behandeln.