Spring Integration - Open Source ESB Vergleich Teil 7

Spring Integration ist ein leichtgewichtiges Framework für die Entwicklung von Integrationslösungen. Es erweitert das vom Spring Framework bekannte Programmiermodell um Abstraktionen für die Integration von Systemen und Anwendungen. Über eine XML oder Java basierte problembezogene Sprache können Adapter und Enterprise Integration Patterns verwendet werden.

1. Entwicklung

Spring Integration enthält keinen graphischen Editor für die Entwicklung von Integrationslösungen. Eine Integration wird mit Quellcode beschrieben und in einer Entwicklungsumgebung z.B. mit Eclipse entwickelt.

Für die Entwicklung stehen dem Entwickler drei problembezogene Sprachen zur Verfügung, die beliebig kombiniert werden können. Eine problembezogene Sprache oder in Englisch Domain specific Language (DSL) erlaubt die Beschreibung eines Problems auf einer höheren Abstraktionsebene als dies mit Allzwecksprachen wie z.B. Java möglich wäre. Der Vorteil einer DSL liegt in einer höheren Produktivität und einer besseren Lesbarkeit bei speziellen Aufgaben wie z.B. der Integration.

Die alte, XML basierte DSL ist unübersichtlich und umständlich, da eine Integrationslösung aus vielen verstreuten Spring Beans besteht, deren Verbindungen man mühsam im Projekt zusammensuchen muss. Der Vorteil der XML DSL ist die Möglichkeit Änderungen ohne Kompilieren durchzuführen zu können. Dank Maven und Buildautomation ist das heute oft kein Kriterium mehr.

Die Java basierte Konfiguration entspricht im Wesentlichen der XML DSL nur die Notation erfolgt über Java Annotation anstatt über XML. Die Java Konfiguration ist wie die XML Konfiguration meiner Meinung nach zu unübersichtlich.

Seit der Version 5.0.0 gibt es für Spring Integration eine neue auf Java 8 basierende DSL. Zu den Highlights gehören eine vom Compiler prüfbare Konfiguration der Adapter sowie die Verwendung von Lambdas für Ausdrücke. Die Verwendung der Spring Expression Language (SpEL) in String-Ausdrücken ist größtenteils nicht mehr notwendig. Die Vorteile sind Autovervollständigung in der Entwicklungsumgebung sowie weniger Fehler zur Laufzeit.

Das folgende Listing zeigt einen Flow für die Verarbeitung von Bestellungen in der neuen Java 8 DSL. Ein File-Adapter „pollt“ ein Verzeichnis nach neuen Dateien. Gelesene Dateien werden über einen JSONPath Ausdruck in einzelnen Positionen (Items) gesplittet. Danach wird über eine Transformation mit einem Lambda Ausdruck der Preis pro Position ermittelt. Ein Aggregator fasst anschließend Items eines Lieferanten zu einer neuen Nachricht zusammen, bevor diese über einen Router an andere Flows weitergeleitet werden.

@Bean
public IntegrationFlow order() {
    return IntegrationFlows.from(
            Files.inboundAdapter(new File("in"))
        )
        .split("#jsonPath(payload, '$.items')")
        .transform(Transformers.toJson())
        .transform(Transformers.fromJson(Item.class))
        .<Item,Item>transform(item -> {
            item.setPriceTotal(item.getPrice() * item.getQuantity());
            return item;
        })
        .aggregate(a -> a
            .groupTimeout(1000)
            .expireGroupsUponTimeout(true)
            .sendPartialResultOnExpiry(true)
            .correlationStrategy(s -> ((Item)s.getPayload()).getVendor())
            .outputProcessor(p ->
                MessageBuilder.withPayload(
                    p.getMessages().stream()
                        .map( m -> m.getPayload())
                        .toArray()
                )
                .build()
            )
        )
        .route("headers['vendor']")
        .get();
}
            
Listing 1: Integration Flow in der Java 8 Lambda DSL

Von allen Frameworks und ESBs, die ich mir in dieser Reihe angeschaut habe, macht Spring Integration dank der Java 8 DSL momentan auf mich den modernsten Eindruck.

2. Enterprise Integration Patterns

Spring Integration orientiert sich in der Architektur und in den Bezeichnungen für Muster an der Bibel für Nachrichtenbasierte Integration, dem Enterprise Integration Patterns Buch von Gregor Hohpe und Bobby Woolf?

Die wichtigsten Enterprise Integration Patterns wie z.B. Router, Transformer, Filter, Splitter und Aggregator stehen dem Entwickler für seine Projekte zur Verfügung. Für die Anwendung der Muster können selbstverständlich Java 8 Lambdas verwendet werden.

3. Adaptoren

Die Auswahl an Protokoll- und Systemadapter ist für Spring Integration noch begrenzt. Außer Adaptoren für:

  • HTTP
  • Mail ( SMTP, IMAP, POP3)
  • Datei
  • FTP, SFTP
  • AMQP Messaging z.B. mit dem Qpid oder RabbitMQ Broker
  • Java Messaging Service JMS

sind kaum Konnektoren verfügbar. Die kleine Anzahl von Adaptern ist nicht ganz so kritisch, da das Spring Framework von sich aus eine große Anzahl von Protokollen und Technologien unterstützt. Für den Fall, dass es weder einen Adapter noch eine Unterstützung vom Spring Framework gibt, kann mit ein paar Zeilen Java Code alles angebunden werden, was mit Java angesprochen werden kann.

4. Transformation und Mapping

Wie bei Apache Camel gibt es bei Spring Integration kein graphisches Werkzeug für das Mapping oder die Transformation von Nachrichten. Anstatt einer speziellen Mapping Technologie bietet Spring Integration über die mehrfach überladene transform-Methode eine Reihe von Möglichkeiten:


Abbildung 1: Überladene transform-Methoden

Selbstverständlich kann der transform-Methode ein Java 8 Lambda Ausdruck übergeben werden. Durch die Angabe von Typinformationen kann im Ausdruck die Payload ohne Typecasting verwendet werden. Im folgenden Beispiel enthält die Payload, ein Objekt der Klasse Address, welches in einen String umgewandelt wird:

.<Address,String>transform( address ->
    String.format("Ciao %s %s", address.getTitle(), address.getFirstname())
)
Listing 2: Transformation mit Lambda Ausdruck

Damit dieser Lambda Ausdruck verwendet werden kann, muss die Payload als Java Objekt vorliegen. Für die Umwandlung von JSON und XML Dokumenten in Java Objekte kann das Data-Binding verwendet werden, für das es bereits fertige Transformer gibt. In der ersten Zeile im Code Ausschnitt unten wird mit der Transformation von JSON in ein Objekt der Klasse Address die Voraussetzung für den Aufruf des Lambda Ausdruckes geschaffen:

.transform(Transformers.fromJson(Address.class))
.<Address,String>transform( address ->
    String.format("Ciao %s %s",
        address.getTitle(),
        address.getFirstname())
)
        
Listing 3: JSON nach Java Objekt Transformation

Mit Hilfe des Data-Bindings können zahlreiche Konvertierungen elegant mit Lambda-Ausdrücken realisiert werden. Weitere Alternativen für die Transformation sind XPath und JSONPath, die über die Spring Expression Language zur Verfügung stehen. Genauso wie Apache Camel kann auch Spring Integration mit anderen Mapping Tools wie zum Beispiel Smooks oder Dozer kombiniert werden.

5. Deployment

Für Spring Integration gibt es keinen speziellen Server. Das ist auch nicht nötig, da Spring Integration mit anderen Deployments kombiniert werden kann. Wer möchte kann Spring Integration beispielsweise standalone in einer Java VM oder in einer Web Anwendung auf einem Application Server betreiben. Eine elegante Möglichkeit ist das Deployment als Spring Boot Anwendung. Wer möchte kann mit oder ohne Spring Boot seine Flows auch noch in Docker Container packen und als Microservice in der Cloud betreiben.

5.1 Spring Boot

Spring Boot unterstützt Spring Integration mit einem speziellen Starter. Nach dem Hinzufügen der folgenden Abhängigkeit in die Maven pom.xml Datei können Flows definiert werden.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-integration</artifactId>
</dependency>
Listing 4: Maven pom.xml mit Spring Integration

Das folgende Listing zeigt eine Spring Boot Anwendung mit einen einfachen Flow, der HTTP Anfragen an den Pfad /articles/{id} entgegennimmt. Ein Mapping auf den Pfad wird von Spring Integration automatisch vorgenommen.

@SpringBootApplication
public class ArticleApplication {

    public static void main(String[] args) {
        SpringApplication.run(ArticleApplication.class, args);
    }

    @Bean
    public IntegrationFlow flow() {
        return IntegrationFlows
        .from( Http.inboundGateway("/articles/{id}")
            .payloadExpression("#pathVariables.id")
        )
        .transform(p -> "Artikel: " + p)
        .get();
    }
}
        
Listing 5: Spring Boot Microservice mit Integration Flow

Spring Boot nimmt die über Spring Integration bereitgestellten Endpunkte auch in die Actuator Endpunkte für das Monitoring mit auf. Die Abbildung unten zeigt in den Statistiken auch die Aufrufe an den /article/{id} Endpunkt.


Abbildung 2: Spring Boot Metrics Actuator

6. Monitoring

In Verbindung mit Spring Boot lässt sich eine Sprint Integration Anwendung komfortabel überwachen. Dann ist auch eine Einbindung in die Spring Boot Admin Konsole möglich.

7. Performanz und Speicherverbrauch

Der Footprint von Spring Integration Anwendungen ist wie bei Apache Camel vergleichsweise niedrig. Spring Integration selbst benötigt nur wenige Megabytes. Einen größeren Einfluss auf den Verbrauch der Ressourcen haben meist die verwendeten Muster wie z.B. Transformer oder Aggregator.

8. Dokumentation

Außer einer kurzen Referenz gibt es nicht viel Dokumentation zur Java 8 DSL. Zum jetzigen Zeitpunkt (Stand Mai 2017) ist Spring Integration in der Version 5 noch nicht final und bei einem Milestone kann keiner eine umfassende Dokumentation erwarten. Mit Hilfe der Javadoc Beschreibung und der „alten“ Dokumentation zur XML Sprache ist es dennoch möglich zu recht zu kommen. Es ist zu erwarten, dass in den nächsten Monaten die Dokumentation noch ausgebaut wird.

9. Fazit

Spring Integration ist kein Werkzeug für Nicht-Programmierer. Für das Schreiben von Integration Flows sind solide Java Kenntnisse erforderlich. Für die neue Java 8 DSL sollte der Entwickler neben den Lambdas auch die Anwendung der Generics beherrschen. Wer Code nicht scheut bekommt mit Spring Integration ein produktives Werkzeug und kann mit ausdrucksstarken Integrationflows komplexe Integrationslösungen entwickeln.

9.1. Spring Integration oder Apache Camel?

Das ist hier die große Frage. Wer mit dem knapp ein Dutzent Adaptoren für die gängigsten Protokolle wie HTTP und AMQP von Spring Integration nicht auskommt, wird bei den ca. 200 Adaptoren für Apache Camel bestimmt fündig. Die Adaptoren von Camel bieten auch mehr Optionen und Funktionen als die von Spring Integration. Gerade wenn in Spring Integration benötigte Adaptoren fehlen, dann fällt die Entscheidung für Apache Camel leicht. Wer keine Unterstützung für exotischen Protokolle benötigt sollte vor einer Entscheidung unbedingt einen Blick auf die neue Java 8 DSL von Spring Integration werfen.

Seit der Version 2.18.0 bietet auch Apache Camel experimentelle Lambda und Spring Boot Unterstützung. Spring Integration hat momentan noch bei der Java 8 DSL die Nase vorn. Die nächsten Monate werden zeigen, ob der Vorsprung bei der Java 8 DSL Spring Integration hilft Marktanteile zu erobern.

10. Quellen

  1. Spring Integration Java DSL @github
  2. Spring Integration Java DSL Reference
  3. Spring Integration Java DSL: Line by line tutorial
    by Artem Bilan @artem_bilan