Mapping WSDL JBI


Cet article fait suite à une question récurrente des utilisateurs de Petals ESB : « Pourquoi j’ai une erreur au déploiement de mon service??? ». Je vais ici tenter de donner une réponse partielle à cette question car il peut y avoir beaucoup de raisons à une erreur de déploiement. La plus courante venant souvent de la configuration. Rien que bien technique ni compliqué dans cet article, juste un mini tutoriel pour mieux comprendre la philosophie Petals – JBI.

La spécification JBI est basée sur l’état de l’art des Web services et donc utilise WSDL pour la description des services ‘hostés’ ou liés à l’implémentation JBI. Petals ESB implémente (et étend – mais ça sera peut être l’objet d’un autre article sur le pourquoi du comment…) la spécification JBI depuis sa première version. Petals ESB est même certifié compatible JBI par SUN (RIP) après le passage du TCK JBI (Outil de test de compatibilité fournit par SUN). Bref, là n’est pas la question… Les descripteurs JBI servent à beaucoup de choses dans une implémentation JBI mais nous allons particulièrement regarder comment nous nous en servons dans l’exposition de services.

Un service exposé dans le bus fournit donc sa description via un WSDL associé. Ce service est activé dans le bus après le déploiement de ce que l’on appelle communément une ‘SA’ (Service Assembly) qui contient des ‘SU’ (Service Unit). Pour exposer un service dans le bus afin qu’il soit accessible à tout les consommateurs, la SU doit être en mode ‘provide’ ie fournisseur. Elle doit aussi définir le Endpoint, le Service et l’Interface qu’elle veut exposer. Prenons l’exemple du bon vieux service HelloWorld définit par le WSDL suivant :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:definitions name="HelloServiceImplService" targetNamespace="http://sample.petals.ow.org/" xmlns:ns2="http://schemas.xmlsoap.org/wsdl/" xmlns:ns3="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:ns9="http://www.w3.org/ns/wsdl/http" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ns5="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:ns6="http://schemas.xmlsoap.org/wsdl/http/"
	xmlns:ns10="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:ns7="http://www.w3.org/ns/wsdl" xmlns:ns8="http://www.w3.org/ns/wsdl/soap">
	<ns2:types>
		<xs:schema elementFormDefault="unqualified" attributeFormDefault="unqualified" targetNamespace="http://sample.petals.ow.org/">
			<xs:element name="sayHello" type="tns:sayHello" xmlns:tns="http://sample.petals.ow.org/" />
			<xs:element name="sayHelloResponse" type="tns:sayHelloResponse" xmlns:tns="http://sample.petals.ow.org/" />
			<xs:complexType name="sayHello">
				<xs:sequence>
					<xs:element name="arg0" minOccurs="0" type="xs:string" />
				</xs:sequence>
			</xs:complexType>
			<xs:complexType name="sayHelloResponse">
				<xs:sequence>
					<xs:element name="return" minOccurs="0" type="xs:string" />
				</xs:sequence>
			</xs:complexType>
		</xs:schema>
	</ns2:types>
	<ns2:message name="sayHelloResponse">
		<ns2:part element="tns:sayHelloResponse" name="parameters" xmlns:tns="http://sample.petals.ow.org/" />
	</ns2:message>
	<ns2:message name="sayHello">
		<ns2:part element="tns:sayHello" name="parameters" xmlns:tns="http://sample.petals.ow.org/" />
	</ns2:message>
	<ns2:portType name="HelloService">
		<ns2:operation name="sayHello">
			<ns2:input message="tns:sayHello" name="sayHello" xmlns:tns="http://sample.petals.ow.org/" />
			<ns2:output message="tns:sayHelloResponse" name="sayHelloResponse" xmlns:tns="http://sample.petals.ow.org/" />
		</ns2:operation>
	</ns2:portType>
	<ns2:binding type="tns:HelloService" name="HelloServiceImplServiceSoapBinding" xmlns:tns="http://sample.petals.ow.org/">
		<ns3:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
		<ns2:operation name="sayHello">
			<ns3:operation style="document" soapAction="" />
			<ns2:input name="sayHello">
				<ns3:body use="literal" />
			</ns2:input>
			<ns2:output name="sayHelloResponse">
				<ns3:body use="literal" />
			</ns2:output>
		</ns2:operation>
	</ns2:binding>
	<ns2:service name="HelloServiceImplService">
		<ns2:port binding="tns:HelloServiceImplServiceSoapBinding" name="HelloServiceImplPort" xmlns:tns="http://sample.petals.ow.org/">
			<ns3:address location="http://localhost:9999/sample/HelloService" />
		</ns2:port>
	</ns2:service>
</ns2:definitions>
<pre>

Son descripteur JBI associé sera donc (lié a Petals via le composant Web service dans cet exemple) :

<?xml version="1.0" encoding="UTF-8"?>
<jbi:jbi version="1.0"
        xmlns:generatedNs="http://sample.petals.ow.org/"
        xmlns:jbi="http://java.sun.com/xml/ns/jbi"
	xmlns:petalsCDK="http://petals.ow2.org/components/extensions/version-5"
	xmlns:soap="http://petals.ow2.org/components/soap/version-4"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

	<jbi:services binding-component="true">
		<jbi:provides interface-name="generatedNs:HelloService"
			service-name="generatedNs:HelloServiceImplService"
			endpoint-name="HelloServiceImplPort">

			<!-- CDK specific elements -->
			<petalsCDK:timeout>60000</petalsCDK:timeout>
			<petalsCDK:wsdl>Service.wsdl</petalsCDK:wsdl>

			<!-- Component specific elements -->
			<soap:address>http://localhost:9999/sample/HelloService</soap:address>
			<soap:soap-version>11</soap:soap-version>
			<soap:add-root>false</soap:add-root>
			<soap:chunked-mode>false</soap:chunked-mode>
			<soap:cleanup-transport>true</soap:cleanup-transport>
			<soap:mode>SOAP</soap:mode>
		</jbi:provides>
	</jbi:services>
</jbi:jbi>

Où on respecte bien que :

  1. JBI ‘endpoint-name‘ = Valeur de l’attribut ‘name’ du Port dans le WSDL
  2. JBI ‘service-name‘ = Valeur de l’attribut ‘name’ du Service dans le WSDL (avec JBI Service NameSpace = targetNamespace du WSDL)
  3. JBI ‘interface-name‘ = Valeur de l’attribut ‘name’ du PortType dans le WSDL (avec JBI Interface NameSpace = targetNamespace du WSDL)

Petals ESB v3.x ne vous insultera plus lors du déploiement de vos services. Une solution simple pour ne pas se faire insulter, est d’utiliser le Studio Petals qui facilite grandement la vie des développeurs!

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.

Outils Petals ESB : Librarie pour binder des services Web


Dans un précédent article, je décrivais comment ‘proxifier’ un Web service dans Petals ESB avec un plugin Maven maison. Ce plugin Maven utilise une librairie Java que j’ai développé pour lier et exposer des Web services dans/avec Petals ESB.

Le ‘problème’ actuel de Petals ESB est qu’il est pleinement basé sur la spécification JBI, cela veut dire que actuellement, si on veut exposer un service JBI en tant que Web service, il faut créer un fichier de configuration (le fameux JBI.xml), le packager dans une unité de service (Service Unit – SU), et packager cette SU dans un assemblage de services (Service Assembly – SA). Bien sur des outils comme le Petals Studio sont la pour aider les développeurs à intégrer leurs services, moi je me place plus dans la peau du ‘Core Developper’ et je pour le moment je n’ai pas eu l’utilité d’outils graphiques pour configurer Petals.

Dans une de mes taches actuelles, je suis en train d’étendre Petals pour le faire communiquer sur Internet et je développe ce que j’appelle l’API de Monitoring & Management (M&M API) qui est exposée (pour le moment seulement) en Web service. La partie Management de l’API doit permettre à un administrateur de la plateforme, de ‘binder’ des services (Web, REST, …) sans se soucier de la partie JBI.
L’API fournit donc une opération du style bind(URI wsdl). Coté serveur, j’utilise :

  1. Une librairie maison de génération de SA
  2. Je déploie localement cette SA pour ‘binder’ un Web service au bus.
  3. J’expose ce service de management avec la feature expliquée dans ce post

Un exemple d’utilisation de la librairie est donné dans le listing suivant (binder au bus, le Service Web Version de Axis2) :


package org.ow2.petals.tools.generator.jbi;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import org.ow2.petals.tools.generator.jbi.api.JBIGenerationException;
import org.ow2.petals.tools.generator.jbi.ws2jbi.WS2Jbi;

public class BindAxis2Version {

public static void main(String[] args) throws JBIGenerationException, URISyntaxException {

String wsdl = "http://localhost:8080/axis2/services/Version?wsdl";
Map<String, String> map = new HashMap<String, String>();

// let's say that we want to generate SA for SOAP component 4.0
map.put(org.ow2.petals.tools.generator.commons.Constants.COMPONENT_VERSION, "4.0");

URI wsdlURI = new URI(wsdl);

File f = new WS2Jbi(wsdlURI, map).generate();

System.out.println("Service Assembly is available at " + f.getAbsolutePath());

}
}

Pour utiliser WS2Jbi, utilisez dans votre projet Maven, la dépendance :


<dependency>

<groupId>org.ow2.petals.sandbox</groupId>

<artifactId>petals-ws2jbi</artifactId>

<version>1.0-SNAPSHOT</version>

</dependency>

Celle ci est normalement disponible est mise a jour régulièrement sur le repository Maven d’OW2 http://maven.ow2.org/maven2-snapshot/. Les sources sont disponibles dans ma Sandbox Petals.

Petals Master 1.0 is out


Petals Master v1.0, the first official version of the Petals governance tool has been finally released on OW2 today. Here is the quick release note from the Team :

Petals Master 1.0, includes a lot of improvements for users, as well as optimisations, and nice new features :

  • Categorize your services, endpoints and enterprises with UDDI based categories or add your own categorization value sets
  • Community capabilities : Share ratings, tags and comments on registered services and endpoints with all users
  • Share additional information about services by uploading related documents. Supported related document types are: DOC, PDF, XML, HTML, XLS, PPT, TXT, RTF, ODT, ODS, ODP
  • Identify your Enterprises with UDDI based identification system (Thomas Register, Dun and Bradstreet (DUNS)), or add your own identification system)
  • Search registered entities with a UDDI V2/V3 compliant Inquiry WS API
  • Do all what you can do with Petals Master UI with the Petals Master specific WS API
  • SLA Management is disabled, but will be back soon, with lots of improvements: SLA enforcement, service contract creation and management based on information collected on execution environments
  • Integration with new Petals ESB v3.0 Camelia edition: Govern services and endpoints deployed in Petals ESB by just providing Petals ESB specific WS address and click « synchronize ». That’s all !

As mentioned, this tool is integrated with Petals ESB v3.x, so now you can govern services from your Entreprise Service Bus.
There are also several new tools which are connected to Petals ESB, I think that more news will come soon…

EasyWSDL 1.2 is out


OW2 EasyWSDL WSDL manipulation library has just been released. This release fix many bugs and introduces some new tooling such as java2wsdl (create WSDL from Java interfaces) and xsd2xml (create XML from XSD definition).

EasyWSDL is easy since you can manipulate both WSDL 1.1 and WSDL 2.0 with the same object model.

// Read a WSDL 1.1 or 2.0
WSDLReader reader = WSDLFactory.newInstance().newWSDLReader();
Description desc = reader.readWSDL(new URI("http://file/path/document.wsdl"));

// Write a WSDL 1.1 or 2.0 (depend of desc version)
Document doc = WSDLFactory.newInstance().newWSDLWriter().getDocument(desc);

// Create a WSDL 1.1 or 2.0
Description desc11 = WSDLFactory.newInstance().newDescription(WSDLVersionConstants.WSDL11);
Description desc20 = WSDLFactory.newInstance().newDescription(WSDLVersionConstants.WSDL20);

Once the Description object is loaded from the WSDL document, you can maniulate all parts of the service description. Let’s look at endpoints :

// Endpoints take place in services.
// Select a service
Service service = desc.getServices().get(0);

// List endpoints
List endpoints = service.getEndpoints();

// Read specific endpoint
Endpoint specificEndpoint = service.getEndpoint("endpointName");

// Add endpoint to service
service.addEndpoint(specificEndpoint);

// Remove a specific enpoint
service.removeEndpoint("endpointName");

// Create endpoint
Endpoint createdEndpoint = service.createEndpoint();
service.addEndpoint(createdEndpoint);

Easy!