Pages

jeudi 12 septembre 2013

Intercepter les messages SOAP avec Spring WS

Spring-ws est un module de Spring facilitant la mise en place d’un client ou d’un serveur de webservices. J’ai expliqué dans un précédent article comment tester vos clients ou serveurs via le module spring-ws-test.

Dans ce nouvel article sur spring-ws nous allons voir comment intercepter les messages sortant ou entrant. Cette opération est possible au niveau du client ou du serveur. Le principe étant le même, je ne m’attarderai que sur le client.


Tracer les messages dans les logs

Le framework propose cette fonctionnalité dans ses options de log. Au niveau de log4j vous devez ajouter la définition des loggers suivants

    <logger name="org.springframework.ws.client.MessageTracing.sent" additivity="false">
        <level value="TRACE" />
        <appender-ref ref="FILE_SOAP_REG" />
    </logger>
    <logger name="org.springframework.ws.client.MessageTracing.received" additivity="false">
        <level value="TRACE" />
        <appender-ref ref="FILE_SOAP_REG" />
    </logger>

Cet ajout affiche les requêtes envoyées et reçues dans l’appender portant le nom FILE_SOAP_REG


Faire un traitement métier avant l’envoi, à la réception d’une réponse ou d’une fault

Pour pouvoir intervenir avant l’envoi ou après la réponse d’un webservice, nous devons mettre en place un interceptor qui respetera le contrat



Il existe plusieurs implémentations dans Spring de ces interfaces pour gérer des problématiques diverses comme les logs, la sécurité des appels, la validation des données envoyées.... Pour vous montrer un exemple simple nous allons écrire un Interceptor spécifique qui tracera les éléments dans la log applicative

public class MonMessageInterceptor implements ClientInterceptor {

    private static final Logger LOG = Logger.getLogger(MonMessageInterceptor.class);

    @Override
    public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
        LOG.debug(messageContext.getRequest());
        //Renvoie true pour continuer le traitement
        return true;
    }

    @Override
    public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
        LOG.info(String.format("Message reçu [%s] pour la demande [%s]",
                convertWebServiceMessageToString(messageContext.getRequest()),
                convertWebServiceMessageToString(messageContext.getResponse())));
        //Renvoie true pour continuer le traitement        
        return true;
    }

    @Override
    public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
        LOG.error(String.format("Message d'erreur [%s] lors de l'execution de la requete [%s]",
                convertWebServiceMessageToString(messageContext.getRequest()),
                convertWebServiceMessageToString(messageContext.getResponse())));
        //Renvoie true pour continuer le traitement        
        return true;
    }

    /**
     * Cette méthode permet de transformer un message SOAP en String pour l'écrire en base de données
     * @param message
     * @return
     */
    protected String convertWebServiceMessageToString(WebServiceMessage message){
        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            message.writeTo(os);
            return os.toString();
        }
        catch (IOException e) {
            LOG.error("Impossible de lire le message SOAP pour le tracer", e);
            return "Impossible de récuperer le payload " + e.getMessage();
        }
    }
}

Une fois l’interceptor créé il ne reste plus qu’à le déclarer dans le contexte Spring

<bean id="webServiceTemplatePublication" class="org.springframework.ws.client.core.WebServiceTemplate">
        <constructor-arg ref="messageFactory" />
        <property name="marshaller" ref="jaxb2Marshaller" />
        <property name="unmarshaller" ref="jaxb2Marshaller" />
        <property name="messageSender" ref="httpSender" />
        <property name="checkConnectionForFault" value="false" />
        <property name="interceptors">
            <list>
                <ref bean="monMessagInterceptor" />
            </list>
        </property>
</bean>
<bean id="monMessagInterceptor" class="com.javamind.MonMessageInterceptor"/>

Modifier le message avant l’appel à un webservice

Si votre besoin est de modifier le message avant l’envoi de la requête au webservice distant, vous ne devez pas passer par un interceptor mais par un org.springframework.ws.client.core.WebServiceMessageCallback


Imaginons un webservice proposant plusieurs opérations SOAP et dont l’opération dépend du contexte. Nous pouvons ajouter un WebServiceMessageCallback dans les méthodes du webServiceTemplate de spring. Par exemple

PartieJoueurDto reponse = (PartieJoueurDto) webServiceTemplate
       .marshalSendAndReceive(
               joueurDto,
               new WebServiceMessageCallback() { 
                     public void doWithMessage(WebServiceMessage message) {
                        if (message instanceof SoapMessage) {
                              SoapMessage soapMessage = (SoapMessage) message;
                              String soapActionWebService = getOperationSoap(context);
                              soapMessage.setSoapAction(soapActionWebService );


                              //On ajoute la soap action dans le header
                              soapMessage.getSoapHeader().addAttribute(QNameUtils
                                           .parseQNameString(WebServiceHeaders.SOAP_ACTION), soapActionWebService);
                        }
                     }
               });



Conclusion

Voici deux manières d’agir sur les messages envoyés ou reçus par un webservice. Pour plus d’informations je vous laisse lire la documentation officielle de spring webservice

1 commentaire:

  1. Tank you! This helped me a lot. Now my Webservice client is printing the SOAP request and response on the log file.

    RépondreSupprimer

Remarque : Seul un membre de ce blog est autorisé à enregistrer un commentaire.