Qu’est-ce que BPEL ?

BPEL (Business Process Execution Language) est un langage XML pour orchestrer des services web. Il permet de définir des processus métier qui coordonnent plusieurs services web en un workflow complexe.

Concepts clés

Orchestration : Coordination centralisée de services multiples

  • Un processus BPEL agit comme chef d’orchestre
  • Il appelle différents services web
  • Il gère le flux de données entre eux
  • Il implémente la logique métier

Structure d’un processus BPEL

<process name="NomDuProcessus"
         targetNamespace="http://example.com/process"
         xmlns="http://docs.oasis-open.org/wsbpel/2.0/process/executable">
    
    <!-- 1. Déclaration des partenaires -->
    <partnerLinks>
        <!-- Services externes et clients -->
    </partnerLinks>
    
    <!-- 2. Déclaration des variables -->
    <variables>
        <!-- Données manipulées par le processus -->
    </variables>
    
    <!-- 3. Corps du processus -->
    <sequence>
        <!-- Activités du workflow -->
    </sequence>
    
</process>

Définit les services externes avec lesquels le processus interagit.

<partnerLinks>
    
    <!-- Le client qui invoque le processus -->
    <partnerLink name="client"
                 partnerLinkType="tns:ClientPLT"
                 myRole="orderProcessor"/>
    
    <!-- Service externe : vérification de crédit -->
    <partnerLink name="creditChecker"
                 partnerLinkType="tns:CreditPLT"
                 partnerRole="creditService"/>
    
    <!-- Service externe : gestion des stocks -->
    <partnerLink name="inventoryService"
                 partnerLinkType="tns:InventoryPLT"
                 partnerRole="inventoryProvider"/>
    
    <!-- Service externe : expédition -->
    <partnerLink name="shippingService"
                 partnerLinkType="tns:ShippingPLT"
                 partnerRole="shipper"/>
    
</partnerLinks>

Attributs :

  • name : Nom du partenaire (utilisé dans les activités)
  • partnerLinkType : Type de la relation (défini dans WSDL)
  • myRole : Rôle du processus (quand il reçoit des messages)
  • partnerRole : Rôle du partenaire (quand le processus l’appelle)

2. Variables

Stocke les données échangées et manipulées.

<variables>
    
    <!-- Variable basée sur un type de message WSDL -->
    <variable name="orderRequest" 
              messageType="ord:OrderRequestMessage"/>
    
    <variable name="orderResponse" 
              messageType="ord:OrderResponseMessage"/>
    
    <!-- Variable basée sur un type XSD -->
    <variable name="creditScore" 
              type="xsd:int"/>
    
    <variable name="totalAmount" 
              type="xsd:decimal"/>
    
    <!-- Variable basée sur un élément XML -->
    <variable name="customerInfo" 
              element="ord:Customer"/>
    
    <variable name="approved" 
              type="xsd:boolean"/>
    
</variables>

Types de variables :

  • messageType : Message WSDL complet
  • type : Type simple XSD (string, int, boolean, etc.)
  • element : Élément XML complexe

3. Activités BPEL

receive : Réception d’un message

Attend un message entrant (début du processus ou message asynchrone).

<!-- Réception initiale qui démarre le processus -->
<receive name="receiveOrder"
         partnerLink="client"
         operation="submitOrder"
         portType="ord:OrderPortType"
         variable="orderRequest"
         createInstance="yes"/>

Attributs :

  • createInstance="yes" : Crée une nouvelle instance du processus
  • variable : Stocke le message reçu

invoke : Appel d’un service externe

Invoque une opération sur un service externe.

<!-- Appel synchrone (request-response) -->
<invoke name="checkCredit"
        partnerLink="creditChecker"
        operation="checkCreditScore"
        portType="cred:CreditPortType"
        inputVariable="creditRequest"
        outputVariable="creditResponse"/>
 
<!-- Appel asynchrone (one-way) -->
<invoke name="sendNotification"
        partnerLink="notificationService"
        operation="notify"
        portType="notif:NotificationPortType"
        inputVariable="notificationMessage"/>

reply : Envoi d’une réponse

Répond au client (finalise une interaction synchrone).

<reply name="sendOrderConfirmation"
       partnerLink="client"
       operation="submitOrder"
       portType="ord:OrderPortType"
       variable="orderResponse"/>

assign : Manipulation de données

Copie et transforme des données entre variables.

<assign name="prepareRequest">
    
    <!-- Copie simple -->
    <copy>
        <from variable="orderRequest" part="customerId"/>
        <to variable="creditRequest" part="customerId"/>
    </copy>
    
    <!-- Copie avec expression XPath -->
    <copy>
        <from>$orderRequest/order/totalAmount</from>
        <to variable="totalAmount"/>
    </copy>
    
    <!-- Assignation d'une valeur littérale -->
    <copy>
        <from>
            <literal>PENDING</literal>
        </from>
        <to variable="orderStatus"/>
    </copy>
    
    <!-- Expression de calcul -->
    <copy>
        <from>$orderRequest/order/quantity * $orderRequest/order/unitPrice</from>
        <to variable="totalAmount"/>
    </copy>
    
</assign>

sequence : Exécution séquentielle

Exécute les activités les unes après les autres.

<sequence name="processOrder">
    
    <receive name="receiveOrder" .../>
    
    <assign name="extractData" .../>
    
    <invoke name="checkCredit" .../>
    
    <invoke name="reserveInventory" .../>
    
    <invoke name="scheduleShipping" .../>
    
    <reply name="confirmOrder" .../>
    
</sequence>

flow : Exécution parallèle

Exécute plusieurs activités en parallèle.

<flow name="parallelChecks">
    
    <!-- Ces trois appels s'exécutent simultanément -->
    <invoke name="checkCredit" 
            partnerLink="creditChecker" .../>
    
    <invoke name="checkInventory" 
            partnerLink="inventoryService" .../>
    
    <invoke name="calculateShipping" 
            partnerLink="shippingService" .../>
    
</flow>

Avec liens de dépendance :

<flow name="parallelWithDependencies">
    
    <links>
        <link name="creditChecked"/>
        <link name="inventoryChecked"/>
    </links>
    
    <invoke name="checkCredit" ...>
        <sources>
            <source linkName="creditChecked"/>
        </sources>
    </invoke>
    
    <invoke name="checkInventory" ...>
        <sources>
            <source linkName="inventoryChecked"/>
        </sources>
    </invoke>
    
    <!-- Attend que crédit ET inventaire soient vérifiés -->
    <invoke name="processPayment" ...>
        <targets>
            <target linkName="creditChecked"/>
            <target linkName="inventoryChecked"/>
        </targets>
    </invoke>
    
</flow>

if : Branchement conditionnel

Exécution conditionnelle basée sur une condition.

<if name="checkApproval">
    
    <!-- Condition -->
    <condition>$creditScore &gt; 700</condition>
    
    <!-- Si vrai -->
    <sequence>
        <invoke name="approveOrder" .../>
        <assign name="setApproved">
            <copy>
                <from>true()</from>
                <to variable="approved"/>
            </copy>
        </assign>
    </sequence>
    
    <!-- Sinon si -->
    <elseif>
        <condition>$creditScore &gt; 600 and $orderAmount &lt; 1000</condition>
        <invoke name="requestManualApproval" .../>
    </elseif>
    
    <!-- Sinon -->
    <else>
        <sequence>
            <invoke name="rejectOrder" .../>
            <assign name="setRejected">
                <copy>
                    <from>false()</from>
                    <to variable="approved"/>
                </copy>
            </assign>
        </sequence>
    </else>
    
</if>

Opérateurs de comparaison (en XML) :

  • &lt; pour <
  • &gt; pour >
  • &lt;= pour
  • &gt;= pour >=
  • = pour ==
  • != pour !=

while : Boucle

Répète des activités tant qu’une condition est vraie.

<while name="retryLoop">
    
    <condition>$retryCount &lt; 3 and $success = false()</condition>
    
    <sequence>
        <invoke name="attemptOperation" .../>
        
        <assign>
            <copy>
                <from>$retryCount + 1</from>
                <to variable="retryCount"/>
            </copy>
        </assign>
        
        <wait>
            <for>'PT10S'</for> <!-- Attendre 10 secondes -->
        </wait>
    </sequence>
    
</while>

forEach : Itération

Itère sur une collection.

<forEach name="processItems" counterName="itemIndex">
    
    <!-- De 1 à nombre d'items -->
    <startCounterValue>1</startCounterValue>
    <finalCounterValue>count($orderRequest/items/item)</finalCounterValue>
    
    <scope>
        <sequence>
            <assign>
                <copy>
                    <from>$orderRequest/items/item[$itemIndex]</from>
                    <to variable="currentItem"/>
                </copy>
            </assign>
            
            <invoke name="processItem" 
                    inputVariable="currentItem" .../>
        </sequence>
    </scope>
    
</forEach>

Avec exécution parallèle :

<forEach name="processItemsParallel" 
         counterName="itemIndex"
         parallel="yes">
    <!-- Traite tous les items en parallèle -->
</forEach>

pick : Attente d’événements

Attend plusieurs événements possibles (premier arrivé).

<pick name="waitForResponse">
    
    <!-- Événement 1 : Réception d'une approbation -->
    <onMessage partnerLink="approver"
               operation="approve"
               portType="app:ApprovalPortType"
               variable="approvalResponse">
        <sequence>
            <assign>
                <copy>
                    <from>true()</from>
                    <to variable="approved"/>
                </copy>
            </assign>
        </sequence>
    </onMessage>
    
    <!-- Événement 2 : Réception d'un rejet -->
    <onMessage partnerLink="approver"
               operation="reject"
               portType="app:ApprovalPortType"
               variable="rejectionResponse">
        <sequence>
            <assign>
                <copy>
                    <from>false()</from>
                    <to variable="approved"/>
                </copy>
            </assign>
        </sequence>
    </onMessage>
    
    <!-- Événement 3 : Timeout -->
    <onAlarm>
        <for>'PT1H'</for> <!-- 1 heure -->
        <sequence>
            <assign>
                <copy>
                    <from>'TIMEOUT'</from>
                    <to variable="status"/>
                </copy>
            </assign>
        </sequence>
    </onAlarm>
    
</pick>

wait : Temporisation

Attend un délai ou jusqu’à une date.

<!-- Attendre 30 secondes -->
<wait name="delay30sec">
    <for>'PT30S'</for>
</wait>
 
<!-- Attendre 2 heures -->
<wait name="delay2hours">
    <for>'PT2H'</for>
</wait>
 
<!-- Attendre jusqu'à une date spécifique -->
<wait name="waitUntilDate">
    <until>$scheduledDate</until>
</wait>

Format de durée ISO 8601 :

  • PT30S : 30 secondes
  • PT5M : 5 minutes
  • PT2H : 2 heures
  • P1D : 1 jour

scope : Portée avec gestion d’erreurs

Définit une portée avec variables locales et gestionnaires d’erreurs.

<scope name="protectedOperation">
    
    <!-- Variables locales au scope -->
    <variables>
        <variable name="localData" type="xsd:string"/>
    </variables>
    
    <!-- Activité principale -->
    <sequence>
        <invoke name="riskyOperation" .../>
    </sequence>
    
    <!-- Gestionnaire d'erreurs -->
    <faultHandlers>
        <catch faultName="tns:CreditCheckFault">
            <sequence>
                <invoke name="notifyError" .../>
                <reply name="sendErrorResponse" .../>
            </sequence>
        </catch>
        
        <catchAll>
            <invoke name="logUnknownError" .../>
        </catchAll>
    </faultHandlers>
    
    <!-- Gestionnaire de compensation (annulation) -->
    <compensationHandler>
        <invoke name="cancelReservation" .../>
    </compensationHandler>
    
</scope>

throw : Lever une erreur

<throw name="throwCreditError" 
       faultName="tns:InsufficientCreditFault"
       faultVariable="creditErrorData"/>