Introduction aux EIP avec Spring Intégration et Apache Camel

La dernière soirée du Paris JUG qui s’est tenue le 8 octobre dernier avait pour thème l’intégration dans le monde des applications Java. Nous saisissons donc par l’intermédiaire de ce billet l’occasion de faire (re)découvrir les principaux patterns d’intégration (les EIP) ainsi que deux des principaux Framework open source Java du marché Spring Integration et Apache Camel.

Pourquoi le besoin d’intégration ?

Très peu d’applications vivent en isolation.
Un système d’information est un ensemble de systèmes connectés qui ont parfois besoin de coopérer entre eux afin de pouvoir fournir une fonctionnalité à l’utilisateur final. On parle d’intégration lorsque l’on rend possible la connexion entre plusieurs systèmes hétérogènes afin qu’ils puissent échanger données et services pour former une chaîne de traitement.

L’intégration est un challenge qui prend sa source dans le besoin de fournir des solutions standards à des problématiques récurrentes. L’intégration doit aussi faire face à des contraintes qui peuvent êtres liées à la sécurité, aux infrastructures réseaux (fiabilité, vitesse), à l’évolution des architectures et des besoins métiers (gestion du changement), et à l’augmentation constante des volumes de données.

Historiquement on note l’émergence de quatre principaux styles d’intégration

  • L’échange de fichiers, apparût dans les années 70, mais qui est encore très utilisé (par des outils comme Hadoop avec HDFS notamment), consiste à transmettre les informations entre applications par l’intermédiaire de fichiers de données. L’avantage de cette solution est qu’elle permet un découplage total entre les deux applications (à la fois physique, logique et temporel). En revanche elle introduit des problématiques de synchronisation pour garantir la cohérence des données, ex : les temps d’écriture pour les gros volumes..
  • Shared Database, apparut dans les années 80, consiste à utiliser une base de données commune comme moyen d’échange entre les applications consommatrices et émettrices. Ce mode à l’avantage d’assurer la cohérence des données grâce à l’aspect transactionnel de l’accès en base. L’inconvénient d’une telle solution est qu’elle requiert une représentation communes des données entre les applications (même si cet argument est moins valable avec les bases actuelles schemaless). L’autre désavantage est la contention lié à la concentration des accès en lecture et écriture sur une même base.
  • RPC, dans les années 90, l’apparition du style RPC qui permet des échanges sur le mode service à l’avantage d’être proche des langages orientés objets. Cependant, bien que découplé physiquement les deux systèmes doivent être actifs pour que la communication se fasse, il y a un couplage temporel.
  • Asynchronous Messaging, dans ce mode les systèmes s’échangent des données par l’intermédiaire de messages véhiculés au travers de channel. Les producteurs déposent les messages dans le channel,  les consommateurs eux utilisent le channel pour lire les messages qui y sont stockés, en les dépilant à leur propre rythme. Envoyer des messages n’impose pas que les deux systèmes soient actifs en même temps, par ailleurs c’est le channel qui garanti la transmission des messages. Ainsi le channel permet un couplage faible entre les systèmes par l’intermédiaire d’un mode de communication totalement asynchrone.

Les EIP

Vous l’aurez compris, le style d’intégration Asynchronous Messaging est celui qui offre, de part ses caractéristiques, la plus grande flexibilité. Il est donc naturel qu’aient émergés un certain nombre de modèles (patterns) pour normaliser les différents concepts d’intégration autour du messaging. Les EIP, Enterprise Integration Pattern, sont apparus il y a près de 10 ans dans le livre éponyme de Gregor Hohpe et Bobby Woolf (ISBN 0321200683) mais restent aujourd’hui largement valables. Les EIP sont un ensemble de patterns qui vont permettre à l’architecte ou au développeur de mettre en œuvre et d’implémenter des solutions d’intégration rapidement et efficacement indépendamment de la technologie utilisée.

Outre le fait d’être un catalogue bien fourni de patterns (65 au total), les EIP sont aussi un moyen d’apporter une nomenclature et de formaliser graphiquement des concepts liés au monde du messaging au moyen de notations visuelles, apportant ainsi un langage commun.

Les EIP sont organisés autour des concepts suivants :

  • Channels, il s’agit du canal de communication (virtuel ou non) qui relie le producteur au consommateur afin qu’ils puissent échanger des messages
  • Messages, le message est la représentation des données échangées généralement composé d’un header et d’un payload
  • Pipes and Filters, représentent un pipeline, un Pipeline est une notion qui matérialise une chaîne visant à diviser un traitement en sous tâches indépendantes (Filters) reliées entre elles par des channels (Pipes).
  • Routage, représente la logique d’acheminement des messages vers leur(s) destination(s)
  • Transformation, est une fonction qui manipule un message pour transformer le format du message en fonction des besoin des applications consommatrices
  • Endpoints, grâce aux endpoints les applications ont la capacité de s’interfacer avec le monde du messaging
  • System Management, englobe les concepts liés au monitoring et à l’analyse de traffic

Notre but ici n’est pas de dresser l’inventaire complet des patterns, le livre le fait très bien, mais plutôt de donner un aperçu des différents concepts mis en œuvre et des solutions apportées par ces patterns.
Le livre dresse par exemple les problématiques d’intégration et donne le pattern permettant de répondre à cette problématique, en voiçi quelques exemples:

  • Channel
    • Comment s’assurer qu’un seul consommateur consomme le message produit ? Point to point channel
    • Comment s’assurer qu’un producteur puisse diffuser un événement à tous les consommateurs intéressés? Publish-subscribe channel
  • Router
    • Comment acheminer un message à une liste de destinataires spécifiés dynamiquement? Recipient List
    • Comment traiter un message contenant plusieurs éléments, dont chacun peut être traité d’une manière différente? Splitter
    • Comment combiner les messages individuels mais liés de sorte qu’ils puissent être traitées comme un tout? Aggregator
  • Transform
    • Comment convertir des messages sémantiquement équivalents, mais arrivant dans un format différent? Normalizer
  • Endpoint
    • Comment séparer l’accès au système de messagerie du reste de l’application? Messaging Gateway

Pour un aperçu plus complet, on pourra se référer au site des auteurs [1]

Il est important de savoir que les différents concepts se combinent entre eux pour former des chaînes de traitement, résultat d’un assemblage de briques EIP. Dans le cas particulier d’un comparateur de prix, le principe consiste à interroger plusieurs vendeurs simultanément pour fournir une vision agrégée des différentes offres de prix. Un site comparateur de prix pourrait donc être vu comme l’assemblage de composants EIPs suivant :

eip_comparator

Les frameworks Java

Maintenant que les bases sont posées, intéressons-nous aux outils permettant de mettre en oeuvre ces patterns. Cette partie est consacrée à la découverte des frameworks Java Spring Integration et Apache Camel. Ces frameworks partagent les caractéristiques suivantes :

  • Ils sont open source (Apache-2.0 Licence), et sont soutenus par une large communauté[2]
  • Les deux produits ont des sociétés offrant un support commercial
  • Ils supportent la plupart des EIP, fournissant un grand nombre de composants « sur étagère » endpoint, channel, filter, etc
  • Ils supportent un grand nombre de protocoles parmi
    • FTP/SFTP
    • WebServices (SOAP and REST)
    • TCP/UDP
    • JMS
  • Ils sont non invasifs, pas d’installation logicielle, se limitent à une librairie, ne requièrent pas de conteneur applicatif ni serveur
  • Ils supportent différents usages, inter et intra application
  • Ils peuvent être utilisés comme des petits ESB, embarquables et légers, car ils peuvent fournir des services de routage, transformation, mediation, monitoring, orchestration etc.
  • Ils supportent JMX pour le management et le monitoring

 

Spring Integration

  • C’est la brique d’intégration de l’écosystème Spring, date de 2007
  • Fourni un grand nombre d’adapter vers les systèmes externes, RMI, JPA, http, FILE, FTP, SOAP, JMS, etc
  • Spring s’appuie sur un modèle XML déclaratif à travers un namespace, (bien qu’une DSL Scala existe aussi)

<int:channel id="inputChannel'/>

  • Profite du modèle Spring, possibilité d’injecter des dépendances, support des transactions et de la securité
  • Chaque composant est un bean qu’il est possible d’accéder au travers de l’application context,

inputChannel =  cxt.getBean("inputChannel", MessageChannel.class);

  • Il y a un support IDE permettant une représentation graphique, Spring Tool Suite(STS), IntelliJ IDEA

 

Apache Camel

  • Framework Java apparu en 2007
  • Camel dispose d’un grand nombre de DSL : Java, XML, Groovy, Scala, etc..
  • Camel propose une bonne intégration avec Spring, support transactionnel, configuration XML (via un namespace dédié),
  • Camel dispose un très grand nombre de connecteurs vers les systèmes externes (une centaine) dont certains très spécifiques tels que Akka, Lucene, AWS
  • Camel utilise la notion de route pour matérialiser un traitement entre 1 ou plusieurs endpoints.
  • Les routes sont matérialisée par un point d’entrée (from) et une sortie (to)
  • Les routes peuvent utiliser des processors qui sont les composants réalisant les traitements
  • Les endpoints sont représentés par des URIs [3] basées sur le format suivant:

scheme:adresse[?options]

 

Exemple d’implémentation avec Spring Integration

Considérons le cas de l’implémentation d’un traitement simple réalisant la conversion d’une chaîne de caractères en majuscules lue depuis l’entrée standard[4]. Ce traitement peut être matérialisé sous forme d’un assemblage de composants EIP : un canal d’entrée, un composant de transformation et un canal de sortie. L’exemple ci-dessous est une illustration de l’implémentation avec Spring Intégration.

La configuration se fait déclarativement dans un fichier XML dans lequel nous définissons:

  • une gateway qui est un bean encapsulant l’API Spring Integration pour masquer la logique de communication (entrée/sortie)
  • un service activator, qui est un type de endpoint permettant de connecter n’importe quel bean Spring à un channel afin qu’il puisse jouer le rôle d’un service.
  • un channel d’entrée, requestChannel
  • un channel de sortie, replyChannel

<int:gateway id="gateway"
    default-request-channel="requestChannel"
    default-reply-channel="replyChannel"
    service-interface="com.sgcib.test.StringConversionService">
    <int:method name="convertToUpperCase" />
</int:gateway>

<int:service-activator id="serviceActivator"
    input-channel="requestChannel"
    output-channel="replyChannel"
    expression="payload.toUpperCase()" />

<int:channel id="replyChannel" />
<int:channel id="requestChannel" />

Suit, la déclaration de l’interface qui sera utilisée par la Gateway. Aucune implémentation n’est requise dans cet exemple car le service activator effectue la conversion de la String lui-même (via l’attribut expression). Dans un exemple plus « réel » on ferait par exemple probablement référence à un autre bean afin d’appeler un service métier.

public interface StringConversionService {
    String convertToUpperCase(String in);
}

Enfin, le main qui permet d’injecter l’entrée standard dans la Gateway et d’en afficher le résultat.

public static void main(String[] args) throws IOException {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
    StringConversionService converter = ctx.getBean("gateway", StringConversionService.class);
    Scanner scanner = new Scanner(System.in);
    System.out.println("Enter any String:");
    String in = scanner.nextLine();
    System.out.println(converter.convertToUpperCase(in));
}

Enter something:
hello

> HELLO

 

Même exemple avec la DSL Java d’Apache Camel

public class CamelTest extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("stream:in?promptMessage=Enter something: ")
        .transform(
                ExpressionBuilder.simpleExpression("${body.toUpperCase()}"))
        .to("stream:out");
    }

    public static void main(String[] args) throws Exception {
        org.apache.camel.spring.Main main = new Main();
        main.enableHangupSupport();
        main.addRouteBuilder(new CamelTest());
        main.run();
    }
}

Enter something:
hello

> HELLO

Cette exemple produit le même résultat. Dans le cas de Camel nous avons décrit le traitement avec la DSL Java bien que la déclaration XML ci-dessous soit équivalente:


<route>
    <from uri="stream:in?promptMessage=Enter something: " />
        <transform>
            <simple>${body.toUpperCase()}</simple>
        </transform>
    <to uri="stream:out" />
</route>

On voit ici que Camel nous permet de profiter de sa grande variété de composant, nous offrant un composant stream prêt à l’emploi.

Conclusion

Le tableau suivant récapitule les principales caractéristiques de ces deux frameworks:

Spring Integration

Apache Camel

Date de mise sur le marché

2007

2007

Configuration

Principalement déclaratif via XML
+ Spring Expression Langage (SPEL)

Nombreuses DSL (java, xml, scala, groovy)

Complexité

Simple quand on vient du monde Spring
Déclaration XML peut être complexe

DSL fluide qui apporte concision
et clarté proche du langage naturel

Connecteurs

Assez nombreux

Très nombreux (une centaine)

Outillage (IDE)

Spring Tools Suite

Fuse IDE

Testing

Rien de spécifique

Plusieurs modules dédiés (dont camel-test)

Doc

Doc en ligne + livres

Doc en ligne + livres

Support commercial

Springsource

Progress Software

 
Spring Integration et Apache Camel sont deux implémentations différentes mais permettant de répondre à un même besoin.

Ces deux outils partagent la caractéristique d’être :

  • des implémentations open source matures des patterns EIP en Java
  • des librairies légères et non invasive

En revanche, il se distinguent sur certains aspects :

Spring Integration

Camel

Est une extension de Spring Framework donc profite d’un écosystème riche

Propose une grande variété de DSL, et de connecteurs

S’intègre facilement avec d’autre projets Spring, comme Spring Batch

Sa logique d’intégration ne dépend pas de la nature des Endpoints

A une courbe d’apprentissage très rapide pour ceux ayant une connaissance préalable de Spring

Son outillage est plus complet, notamment pour la testabilité des Endpoints (mocking)

 
Même si ces deux outils ont une approche différente ils répondent tous les deux aux problématiques d’intégration et sont de bonne qualité. Spring Integration est une approche intéressante pour les applications déjà basée sur Spring qui souhaiteraient ajouter une couche d’intégration. Dans le cas contraire, on aimera utiliser Camel car il propose plus de fonctionnalités et est manipulable par son API fluide (DSL), apportant ainsi l’avantage de la validation syntaxique à la compilation et l’autocompletion.

Références

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *