Publié par

Il y a 11 années -

Temps de lecture 7 minutes

Introduction à Spring Integration

Comme nous l’évoquions dans le billet « Spring Integration – L’avènement des ‘lightweight ESB’ ? », SpringSource a annoncé fin 2007 le lancement de Spring Integration, une implémentation légère des Enterprise Integration Patterns. Le projet est aujourd’hui dans sa phase finale (la version courante est 1.0.0.M4). Mark Fisher apporte les dernières touches à Spring Integration : bugfix, refactoring, configuration, documentation, samples, …

L’occasion d’une introduction à cette plate-forme de messaging.

Objectifs et principes

Spring Integration vise à simplifier le développement de solutions d’intégration d’entreprise et à faciliter l’adoption par les utilisateurs de Spring des principes EDA (Event Driven Architecture). Pour ce faire, les objectifs de Spring Integration sont :

  • De fournir un modèle simple pour implémenter des solutions complexes d’intégration d’entreprise.
  • De faciliter la mise en place de modèles d’échanges asynchrones orientés messages au sein d’une application basée sur Spring.

D’autre part, Spring Integration (comme toutes les briques du portfolio Spring) suit les principes suivants :

  • Couplage lâche entre composants pour faciliter la modularité et la testabilité.
  • Respect de la séparation des préoccupations (separation of concerns) entre la logique métier et la logique d’intégration.
  • Points d’extension abstraits mais clairement définis afin de promouvoir la réutilisation et la portabilité.

Les composants

Le système de messaging proposé par Spring Integration suit le modèle « pipes-and-filters« . Dans ce modèle, les filtres (filters) représentent n’importe quel composant capable de produire et / ou de consommer des messages. Les tubes (pipes) sont quant à eux responsables du transport des messages entre les filtres. Dans ce modèle, les composants sont ainsi faiblement couplés entre eux.

Les messages

Le composant central de Spring Integration est bien évidemment le message. Un message est un wrapper générique qui emballe n’importe quel objet java et un ensemble de méta-données (utilisées par le framework pour prendre en charge cet objet).

Un message est donc constitué :

  • D’un identifiant unique.
  • D’un en-tête (header) qui regroupe ses méta-données : Time stamp, date d’expiration, identifiant de corrélation, adresse de retour, …, couples arbitraires de clés / valeur.
  • De sa charge utile (payload) qui peut donc être n’importe quel objet java.

Les messages sont définis par l’interface Message<T>. L’implémentation par défaut est fournie par la classe GenericMessage<T>. Deux implémentations spécialisées sont proposées : StringMessage (message dont le payload est une String) et ErrorMessage (message dont le payload est un Throwable).

Les sources et les targets

L’interfaçage entre le système de messaging et le monde extérieur se fait par le biais de sources et de targets.

En entrée, l’interface Source<T> définit un modèle d’adaptateur pour la conversion d’Object en Message<T>. Ainsi, les implémentations de l’interface Source<T> permettent l’ingestion de messages dans le système.

Spring Integration propose un ensemble d’implémentations parmi lesquelles : ByteStreamSource, FileSource, JmsSource ou encore FtpSource

En sortie, l’interface Target<T> définit un modèle d’adaptateur pour la conversion de Message<T> en Object. Ainsi, les implémentations de l’interface Target<T> permettent l’envoi de messages vers l’extérieur.

Spring Integration propose un ensemble d’implémentations parmi lesquelles : ByteStreamTarget, FileTarget, JmsTarget ou encore MailTarget

Les Message Channels

Au sein du système de messaging, les messages transitent au travers de channels. Les channels représentent la partie « pipes » du modèle « pipes-and-filters« .

Les channels sont définis par l’interface MessageChannel. Ils peuvent (entre autres) être définis dans un fichier de configuration de contexte Spring. Par exemple :




Les Message Endpoints

La partie « filters » du modèle « pipes-and-filters » est quant à elle représentée par les endpoints.
Les endpoints sont le point d’accostage entre le code applicatif et l’infrastructure de messaging. Ils prennent en charge cette connexion de manière non invasive au travers d’une configuration déclarative. L’objectif est ici d’isoler le code applicatif de l’infrastructure qui le supporte (« separtion of concerns »).
On distingue trois types de endpoints:

Les endpoints sources
Un endpoint source est un adaptateur qui permet de connecter une source vers un channel. Il est responsable de la réception des messages en provenance d’une source et de leur transmission à un channel. La réception des messages depuis la source par le endpoint est contrôlée par scheduling.

Les endpoints targets
À l’inverse, un endpoint target est un adpatateur qui permet de connecter un channel vers une target. Il est responsable de la réception des messages depuis un channel et de leur envoi vers une target. De la même façon, la réception des messages en provenance du channel est contrôlée par scheduling.

Les Handler endpoints
Les handler endpoints permettent quant à eux les interactions de type request / reply. Ils jouent ainsi le rôle de « Service Activator ».

Les endpoints peuvent (entre autres) être définis par annotation. Par exemple :

@MessageEndpoint(input="inputChannel", output="outputChannel")
public class MyHandlerEndpoint {

    @Handler
    public String handle(Message input) {
        // Invocation d'un service applicatif ...
        Message output;
        // Construction du message de retour ...
        return output;
    }
}

Notons qu’il n’est pas obligatoire de travailler avec des Message<T> (cette façon de faire simplifie juste l’accès au header). Le endpoint précédant, pourrait également s’écrire :

@MessageEndpoint(input="inputChannel", output="outputChannel")
public class MyHandlerEndpoint {

    @Handler
    public String handle(Foo input) {
        // Invocation d'un service applicatif ...
        Foo output;
        // Construction du message de retour ...
        return output;
    }
}

Ici, l’extraction du payload du message (de type Foo) est prise en charge par Spring Integration.

Les Handlers

Alors que les sources et les targets proposent un modèle d’adapteur unidirectionnel, les handlers fournissent un modèle d’adapteur bi-directionnel, offrant ainsi la possibilité d’implémenter des scenarii de type request / reply.

Les handlers sont définis par l’interface MessageHandler. La prise en charge des messages se fait par invocation d’une méthode arbitraire sur un POJO.
Parmi les implémentations fournies par Spring Intégration, nous retiendrons :

Le Message Router Handler
Ce handler implémente l’ Integration Patterns Message Router. Il permet ainsi de router un message en provenance d’un channel d’entrée vers un ou plusieurs channels de sortie.

La méthode de handling peut être configurée (entre autres) par annotation de différentes façons :

// Route le message reçu vers le MessageChannel retourné.
@Router
public MessageChannel route(Message message) {...}
// Route le message reçu vers l'ensemble des MessageChannel retournés (broadcast).
@Router
public List route(Message message) {...}
// Route le message reçu vers le channel portant le nom retourné.
@Router
public String route(Foo payload) {...}
// Route le message reçu vers l'ensemble des channels dont les noms sont retournés (broadcast).
@Router
public List route(Foo payload) {...} 

Le Message Splitter Handler
Ce handler implémente l’ Integration Patterns Splitter. Il permet ainsi de découper le message reçu en plusieurs messages.

La méthode de handling peut être configurée (entre autres) par annotation de différentes façons :

// Reçoit un payload de type Order et le découpe en message dont le payload et de type OrderItem.
@Splitter(channel="output")
List extractItems(Order order) {
	return order.getItems()
} 



@Splitter(channel="output")
public List> split(Message input) {...}

Les messages résultants se voient affecter un identifiant de corrélation (automatiquement ou explicitement) qui permettra de les agréger.

Le Message Aggregator Handler
Ce handler va de pair avec le Splitter. Il implémente l’ Integration Patterns Aggregator. Il permet ainsi de recomposer un message à partir de plusieurs messages.

La méthode de handling peut être configurée (entre autres) par annotation de différentes façons :

@Aggregator(defaultReplyChannel="output")
public Message aggregate(Collection> input){
	...
}

La barrière d’agrégation se base sur une CompletionStrategy pour déterminer quand un groupe de message est complet pour agrégation. La stratégie par défaut se base sur un nombre de messages.

Le Message Bus

Les aspects runtime de Spring Integration sont assurés par le Message Bus.

Le Message Bus est une extension logique de l’Application Context Spring pour le domaine du messaging. Il joue le rôle de registre pour les Message Channels et les Message Endpoints.
Il prend notamment en charge :

  • Le scheduling des différents pollers.
  • La création des pools de threads.
  • La gestion du cycle de vie de l’ensemble des composants du messaging.

Pour bien commencer

Et rendez-vous prochainement sur notre blog pour un premier sample.

Publié par

Commentaire

Laisser un commentaire

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

Nous recrutons

Être un Xebian, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.