Les classloaders des modules dans Axis2: Les ressources


J’ai expliqué le genre de choses intéressantes que l’on peut faire avec les modules dans la pile Apache Axis2 dans l’article sur le reroutage d’appels de services. J’ai eu besoin aujourd’hui d’aller un peu plus loin dans l’utilisation de ces modules et comme d’habitude, en utilisant ce genre de choses, je partage mes aventures… On va voir rapidement ici comment se comportent les ClassLoaders dans le cas d’Axis2 et des modules.

Et alors?

Je suis en train de développer un module qui reroute les appels de service. L’adresse du service à appeler est donc mis à jour lors de la traversée du module en question. Pour faire bête, je me suis dit que j’aller spécifier un jeu d’adresses dans un bon vieux fichier de propriétés et que j’allais mettre ce fichier dans le module. OK, c’est bien, ça marche (heureusement…), je peux charger mon fichier dans le Handler du module et utiliser les adresses qui y sont définies.

Ce qui m’embête, c’est que ce fichier est packagé dans l’archive du module et donc non modifiable à souhait… C’est là que le Classloader d’Axis2 entre en piste. Si je mets un fichier avec le même nom dans les ressources de mon code client, celui ci est pris en compte en priorité lors de l’appel par le module de MonModule.class.getResource(« /foo.properties »); Je peux donc cette fois ci livrer un module générique qui inclus des valeurs par défaut et les clients qui utilisent ce module peuvent ‘overrider’ les valeurs par défaut dans leur propre fichier de configuration.

Encore une fois, merci Axis2.

L’acheminement de messages dans Petals ESB : #2 Le routage


Intro

Dans l’article précédent sur l’acheminement de messages dans Petals ESB, j’introduisais les notions, aujourd’hui nous allons regarder en détails la couche de routage du bus. Cette couche est largement appelée Router par les développeurs du Bus, et le sera donc dans cette article et dans la suite de la série.

L’archi simplifiée

L’architecture du Router utilise la notion très répandue et  fort utile de modules ie le Router invoque séquentiellement une liste de modules pour élire les services à appeller. A la fin de la traversée on se retrouve avec toutes les informations nécessaires pour appeler les services. On peut modifier cette liste en ajoutant ou supprimant des modules par configuration. Un schéma simplifié donne un Router avec cette tête :

Plus de détails sur l’utilisation des modules dans Petals et leur injection dans un vieil article (à non pas si vieux…).

L’implémentation

L’implémentation de base de Petals ESB est composée de 2 modules en émission :

  1. Le module qui interroge le registre de services et retourne une liste de point d’accès (endpoint dans le jargon service) en fonction des informations contenues dans le message d’entrée.
  2. Le module de résolution de la couche de transport. En fonction de la localisation des endpoints trouvés par le module précédent, un contexte est créé pour définir la couche de transport à utiliser (cette couche de transport sera détaillée dans le prochain article de la série).

On peut imaginer un grand nombre d’utilisations de ces modules. Personnellement, je les utilise à outrance par exemple pour :

  • Ajouter des informations de timestamp sur les messages
  • Notifier une couche de monitoring que des messages sont échangés entre consommateurs et fournisseurs de service
  • Logger les appels
  • Mettre à jour le choix de la couche de transport avec ma propre implémentation du transport de messages inter container

Outro

Vous en savez un peu plus sur le routage dans le bus de service Petals. il faut vraiment retenir que cette couche est vraiment extensible et customisable sans grand effort, en effet pas besoin de recompiler le coeur de Petals pour ajouter des fonctionnalités pour la résolution des endpoints. Dans le prochain article de la série, nous regarderons de plus prêt comment sont échangés les messages entre les instances de Petals.

Axis2 : Rerouter vos appels de services sans modifier vote code client


Imaginons que vous ayez à votre disposition un client de Web service et que pour une raison (qui doit être bonne je l’accorde), vous avez besoin de rerouter vos appels de service vers un nouveau point d’accès. Le problème est que vous n’avez pas le droit de modifier le code client (ou que vous ne l’avez pas), que vous ne voulez pas regénérer ce code client à partir du WSDL du nouveau service a appeler (qui est exactement le même hormis l’adresse du endpoint)… Heureusement, votre client utilise la pile Web service Apache Axis2, et les développeurs d’Axis2 ont gentiment pensé à introduire des modules dans leur architecture. Une bonne intorduction aux modules est disponible dans le guide d’architecture d’Axis2.

Nous allons donc développer un module qui change dynamiquement l’URL de l’endpoint à appeler (on utilisera bien sûr Maven2 pour nous aider à créer et packager le projet…).

1. Créer le projet

Utilisons Maven2 pour créer un projet :

mvn archetype:generate

et je réponds bêtement a Maven quand il me pose des questions sur le groupId, l’artifactId, etc… Une fois créé, je génère le projet Eclipse associé et je l’importe (Eclipse fait aussi parti de mes grand amis) :

mvn eclipse:eclipse

Ill faut modifier le POM pour dire a Maven que l’on est en train de créer un projet qui n’est pas une librarie Java mais un module Axis2. Ceci est possible en spécifiant le type de projet dans le POM et en déclarant que l’on veut utiliser le plugin MAR fournit par Axis2 :


<project ...>

...

<groupId>foo.bar</groupId>
<artifactId>reroute</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>mar</packaging>

...

<dependencies>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-kernel</artifactId>
<version>1.5.1</version>
</dependency>
</dependencies>

...

<build>
<plugins>
<plugin>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-mar-maven-plugin</artifactId>
<version>1.5.1</version>
<extensions>true</extensions>
<configuration>
<moduleXmlFile>src/main/META-INF/module.xml</moduleXmlFile>
<includeDependencies>false</includeDependencies>
</configuration>
</plugin>
</plugins>
</build>
...
</project>

2. Business code

Créons maintenant notre handler (le module est en fait un handler Axis2 packagé dans une archive avec un descripteur de module). Pour faire simple, je vais étendre la classe AbstractHandler pour créer mon Handler de reroutage et remplacer le ‘target EPR’ par le nouveau (bien sûr on peut faire quelque chose de beaucoup plus évolué, on va dire qu’ici tout part vers un seul service) :

package net.chamerling.blog.axis2module.reroute;

import org.apache.axis2.AxisFault;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.handlers.AbstractHandler;
import org.apache.axis2.util.LoggingControl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author chamerling
 *
 */
public class ReRouteHandler extends AbstractHandler {
	private static final Log log = LogFactory.getLog(AbstractHandler.class);

	private static final String FINAL_EPR_PREFIX = "http://localhost:8082/petals/proxy/";

	/**
	 * {@inheritDoc}
	 */
	public InvocationResponse invoke(MessageContext messageContext)
			throws AxisFault {
		EndpointReference toEPR = messageContext.getOptions().getTo();
		String finalEPR = FINAL_EPR_PREFIX + toEPR.getAddress();

		if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled()) {
			log.debug("Reroute Handler for message : "
					+ messageContext.getMessageID());
			log.debug("Initial ToEPR is " + toEPR.getAddress()
					+ " and is changed to " + finalEPR);
		}

		toEPR.setAddress(finalEPR);
		return InvocationResponse.CONTINUE;
	}
}
<pre>

Avec Axis2 c’est ‘facile’, tout est dans le message context! Je viens de remplacer le service à appeler par le mien en modifiant le champ ‘address’ du ‘EndpointReference’.

3. Packager le module

Pour créer un module avec le Handler que l’on vient de définir, il faut passer par l’écriture du fichier de description du module, le ‘module.xml’. Ici c’est simple :

<module name="reroute">
	<OutFlow>
		<handler name="ReRouteHandler" class="net.chamerling.blog.axis2module.reroute.ReRouteHandler">
			<order phase="reroutePhase" />
		</handler>
	</OutFlow>
</module>
<pre>

Mon module s’appellera donc ‘reroute’ et sera appelé que lors de la phase d’émission de message ‘OutFlow’. Plus d’informations sur ce qui est possible avec ce fichier sur le site d’Axis2.

4. Configurer Axis2

Il y a plusieurs façcons de dire à Axis2 d’utiliser le module que l’on vient de développer. En se basant sur la contrainte de départ, je n’ai pas le droit de modifier mes services donc je ne peux pas modifier le descripteur de service ‘service.xml’. Heureusement, j’ai accès au serveur et je peux modifier le fichier de configuration d’Axis2 ‘axis2.xml’ :

...
<!-- Engage the reroute module -->
<module ref="reroute" />
...
<phaseOrder type="OutFlow">
	<!--      user can add his own phases to this area  -->
	<phase name="soapmonitorPhase" />
	<phase name="OperationOutPhase" />
	<!--system predefined phase-->
	<!--these phase will run irrespective of the service-->
	<phase name="PolicyDetermination" />
	<phase name="reroutePhase" />
	<phase name="MessageOut" />
	<phase name="Security" />
</phaseOrder>
<pre>

Je viens d’engager mon module pour tous les services et je l’ai aussi ajouté dans la collection de phases qui est appelé lors de l’émission d’un message. A chaque fois qu’un message est envoyé vers un service, on passe donc dans le module que l’on vient de développer. Je viens donc de modifier les services invoqués sans modifier le code client! Je reviendrais bientôt sur l’utilisation de ce genre de choses dans un vrai cas d’usage avec du REST, du SOAP, des PROXYs, de l’ESB distribué sur le Web (bien sûr avec Petals ESB), etc…

Resources

Le code de ce ‘tutoriel’ est bien sûr disponible sur mon Google Code sous http://code.google.com/p/chamerling/source/browse/#svn/trunk/blog/reroute-axis2.