web-dev-qa-db-fra.com

Traçage des requêtes/réponses XML avec JAX-WS

Existe-t-il un moyen simple (autrement dit, de ne pas utiliser de proxy) pour accéder au XML de requête/réponse brut pour un service Web publié avec une implémentation de référence JAX-WS (celle incluse dans JDK version 1.5 et supérieure)? faire cela via le code est ce que je dois faire .. .. Il suffit de le noter dans un fichier à l'aide de configurations de journalisation intelligentes, mais suffisant.

Je sais qu’il existe d’autres cadres plus complexes et plus complets pour le faire, mais j’aimerais le garder le plus simple possible. Axe, cxf, etc. ajoutent une surcharge considérable que je souhaite éviter.

Merci!

145
Antonio

Voici la solution en code brut (mis en place grâce à stjohnroe et Shamik):

Endpoint ep = Endpoint.create(new WebserviceImpl());
List<Handler> handlerChain = ep.getBinding().getHandlerChain();
handlerChain.add(new SOAPLoggingHandler());
ep.getBinding().setHandlerChain(handlerChain);
ep.publish(publishURL);

Où SOAPLoggingHandler est (extrait des exemples liés):

package com.myfirm.util.logging.ws;

import Java.io.PrintStream;
import Java.util.Map;
import Java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

/*
 * This simple SOAPHandler will output the contents of incoming
 * and outgoing messages.
 */
public class SOAPLoggingHandler implements SOAPHandler<SOAPMessageContext> {

    // change this to redirect output if desired
    private static PrintStream out = System.out;

    public Set<QName> getHeaders() {
        return null;
    }

    public boolean handleMessage(SOAPMessageContext smc) {
        logToSystemOut(smc);
        return true;
    }

    public boolean handleFault(SOAPMessageContext smc) {
        logToSystemOut(smc);
        return true;
    }

    // nothing to clean up
    public void close(MessageContext messageContext) {
    }

    /*
     * Check the MESSAGE_OUTBOUND_PROPERTY in the context
     * to see if this is an outgoing or incoming message.
     * Write a brief message to the print stream and
     * output the message. The writeTo() method can throw
     * SOAPException or IOException
     */
    private void logToSystemOut(SOAPMessageContext smc) {
        Boolean outboundProperty = (Boolean)
            smc.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);

        if (outboundProperty.booleanValue()) {
            out.println("\nOutbound message:");
        } else {
            out.println("\nInbound message:");
        }

        SOAPMessage message = smc.getMessage();
        try {
            message.writeTo(out);
            out.println("");   // just to add a newline
        } catch (Exception e) {
            out.println("Exception in handler: " + e);
        }
    }
}
75
Antonio

Les options suivantes permettent d’enregistrer toutes les communications sur la console (techniquement, vous n’en avez besoin que d’une seule, mais cela dépend des bibliothèques que vous utilisez. Il est donc plus sûr de les configurer toutes les quatre). Vous pouvez le définir dans le code, comme dans l'exemple, ou en tant que paramètre de ligne de commande à l'aide de -D ou en tant que variable d'environnement définie par Upendra.

System.setProperty("com.Sun.xml.ws.transport.http.client.HttpTransportPipe.dump", "true");
System.setProperty("com.Sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true");
System.setProperty("com.Sun.xml.ws.transport.http.HttpAdapter.dump", "true");
System.setProperty("com.Sun.xml.internal.ws.transport.http.HttpAdapter.dump", "true");

Voir la question Traçage des requêtes/réponses XML avec JAX-WS en cas d'erreur pour plus de détails.

238
Mr. Napik

Avant de démarrer Tomcat, définissez Java_OPTS comme ci-dessous dans les envs Linux. Ensuite, démarrez Tomcat. Vous verrez la demande et la réponse dans le fichier catalina.out.

export Java_OPTS="$Java_OPTS -Dcom.Sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true"
51
Upendra

Définissez les propriétés système suivantes, cela activera la journalisation XML. Vous pouvez le définir en Java ou dans un fichier de configuration.

static{
        System.setProperty("com.Sun.xml.ws.transport.http.client.HttpTransportPipe.dump", "true");
        System.setProperty("com.Sun.xml.ws.transport.http.HttpAdapter.dump", "true");
        System.setProperty("com.Sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true");
        System.setProperty("com.Sun.xml.internal.ws.transport.http.HttpAdapter.dump", "true");
    }

journaux de la console:

INFO: Outbound Message
---------------------------
ID: 1
Address: http://localhost:7001/arm-war/castService
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml
Headers: {Accept=[*/*], SOAPAction=[""]}
Payload: xml
--------------------------------------
INFO: Inbound Message
----------------------------
ID: 1
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml; charset=UTF-8
Headers: {content-type=[text/xml; charset=UTF-8], Date=[Fri, 20 Jan 2017 11:30:48 GMT], transfer-encoding=[chunked]}
Payload: xml
--------------------------------------
10
ravi

Il existe différentes manières de procéder par programme, comme décrit dans les autres réponses, mais ce sont des mécanismes assez invasifs. Toutefois, si vous savez que vous utilisez le RI JAX-WS (alias "Metro"), vous pouvez le faire au niveau de la configuration. Voir ici pour les instructions sur la façon de procéder. Pas besoin de déconner avec votre application.

9
skaffman

// Cette solution offre un moyen d'ajouter par programme un gestionnaire à la clien de service Web sans la configuration XML.

// Voir la doc complète ici: http://docs.Oracle.com/cd/E17904_01//web.1111/e13734/handlers.htm#i222476

// Créer une nouvelle classe qui implémente SOAPHandler

public class LogMessageHandler implements SOAPHandler<SOAPMessageContext> {

@Override
public Set<QName> getHeaders() {
    return Collections.EMPTY_SET;
}

@Override
public boolean handleMessage(SOAPMessageContext context) {
    SOAPMessage msg = context.getMessage(); //Line 1
    try {
        msg.writeTo(System.out);  //Line 3
    } catch (Exception ex) {
        Logger.getLogger(LogMessageHandler.class.getName()).log(Level.SEVERE, null, ex);
    } 
    return true;
}

@Override
public boolean handleFault(SOAPMessageContext context) {
    return true;
}

@Override
public void close(MessageContext context) {
}
}

// Ajouter par programme votre LogMessageHandler

   com.csd.Service service = null;
    URL url = new URL("https://service.demo.com/ResService.svc?wsdl");

    service = new com.csd.Service(url);

    com.csd.IService port = service.getBasicHttpBindingIService();
    BindingProvider bindingProvider = (BindingProvider)port;
    Binding binding = bindingProvider.getBinding();
    List<Handler> handlerChain = binding.getHandlerChain();
    handlerChain.add(new LogMessageHandler());
    binding.setHandlerChain(handlerChain);
8
TriMix

Injectez SOAPHandler à l'interface du noeud final. nous pouvons suivre la demande et la réponse SOAP

Implémentation de SOAPHandler avec Programmatic  

ServerImplService service = new ServerImplService();
Server port = imgService.getServerImplPort();
/**********for tracing xml inbound and outbound******************************/
Binding binding = ((BindingProvider)port).getBinding();
List<Handler> handlerChain = binding.getHandlerChain();
handlerChain.add(new SOAPLoggingHandler());
binding.setHandlerChain(handlerChain);

Déclaratif en ajoutant l'annotation @HandlerChain(file = "handlers.xml") à votre interface de noeud final.

handlers.xml

<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://Java.Sun.com/xml/ns/javaee">
    <handler-chain>
        <handler>
            <handler-class>SOAPLoggingHandler</handler-class>
        </handler>
    </handler-chain>
</handler-chains>

SOAPLoggingHandler.Java 

/*
 * This simple SOAPHandler will output the contents of incoming
 * and outgoing messages.
 */


public class SOAPLoggingHandler implements SOAPHandler<SOAPMessageContext> {
    public Set<QName> getHeaders() {
        return null;
    }

    public boolean handleMessage(SOAPMessageContext context) {
        Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (isRequest) {
            System.out.println("is Request");
        } else {
            System.out.println("is Response");
        }
        SOAPMessage message = context.getMessage();
        try {
            SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
            SOAPHeader header = envelope.getHeader();
            message.writeTo(System.out);
        } catch (SOAPException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return true;
    }

    public boolean handleFault(SOAPMessageContext smc) {
        return true;
    }

    // nothing to clean up
    public void close(MessageContext messageContext) {
    }

}
6
Premraj

Je poste une nouvelle réponse car je n'ai pas assez de réputation pour commenter celle fournie par Antonio (voir: https://stackoverflow.com/a/1957777 ).

Si vous souhaitez que le message SOAP soit imprimé dans un fichier (par exemple via Log4j), vous pouvez utiliser:

OutputStream os = new ByteArrayOutputStream();
javax.xml.soap.SOAPMessage soapMsg = context.getMessage();
soapMsg.writeTo(os);
Logger LOG = Logger.getLogger(SOAPLoggingHandler.class); // Assuming SOAPLoggingHandler is the class name
LOG.info(os.toString());

Notez que dans certaines circonstances, la méthode call writeTo () peut ne pas se comporter comme prévu (voir: https://community.Oracle.com/thread/1123104?tstart=0 ou https: // www .Java.net/node/691073 ), le code suivant fera donc l'affaire:

javax.xml.soap.SOAPMessage soapMsg = context.getMessage();
com.Sun.xml.ws.api.message.Message msg = new com.Sun.xml.ws.message.saaj.SAAJMessage(soapMsg);
com.Sun.xml.ws.api.message.Packet packet = new com.Sun.xml.ws.api.message.Packet(msg);
Logger LOG = Logger.getLogger(SOAPLoggingHandler.class); // Assuming SOAPLoggingHandler is the class name
LOG.info(packet.toString());
4
Alex

Vous devez implémenter javax.xml.ws.handler.LogicalHandler. Ce gestionnaire doit ensuite être référencé dans un fichier de configuration de gestionnaire, lui-même référencé par une annotation @HandlerChain dans le point de terminaison de votre service (interface ou implémentation) .Vous pouvez ensuite générer le message via system.out ou un consignateur dans votre implémentation processMessage. 

Voir 

http://publib.boulder.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/twbs_jaxwshandler.html

http://Java.Sun.com/mailers/techtips/enterprise/2006/TechTips_June06.html

2
stjohnroe

Dans runtime vous pouvez simplement exécuter

com.Sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump = true

comme dump est une variable publique définie dans la classe comme suit

public static boolean dump;
1
jozh

Vous pouvez essayer de placer une ServletFilter devant le service Web et d’inspecter la demande et la réponse renvoyées au service.

Bien que vous n'ayez spécifiquement pas demandé de proxy, je trouve parfois que tcptrace est suffisant pour voir ce qui se passe sur une connexion. C'est un outil simple, sans installation, il montre les flux de données et peut aussi écrire dans un fichier.

1
rsp

avec les fichiers de configuration logback.xml, vous pouvez faire: 

<logger name="com.Sun.xml.internal.ws.transport.http.client.HttpTransportPipe" level="trace" additivity="false">
    <appender-ref ref="STDOUT"/>
</logger>

Cela enregistrera la demande et la réponse comme ceci (selon votre configuration pour la sortie du journal):

09:50:23.266 [qtp1068445309-21] DEBUG c.s.x.i.w.t.h.c.HttpTransportPipe - ---[HTTP request - http://xyz:8081/xyz.svc]---
Accept: application/soap+xml, multipart/related
Content-Type: application/soap+xml; charset=utf-8;action="http://xyz.Web.Services/IServiceBase/GetAccessTicket"
User-Agent: JAX-WS RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e
<?xml version="1.0" ?><S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope">[CONTENT REMOVED]</S:Envelope>--------------------

09:50:23.312 [qtp1068445309-21] DEBUG c.s.x.i.w.t.h.c.HttpTransportPipe - ---[HTTP response - http://xyz:8081/xyz.svc - 200]---
null: HTTP/1.1 200 OK
Content-Length: 792
Content-Type: application/soap+xml; charset=utf-8
Date: Tue, 12 Feb 2019 14:50:23 GMT
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">[CONTENT REMOVED]</s:Envelope>--------------------
1
Christian Goudreau

Les réponses répertoriées ici qui vous guident à utiliser SOAPHandler sont tout à fait correctes. L'avantage de cette approche est qu'elle fonctionnera avec n'importe quelle implémentation JAX-WS, car SOAPHandler fait partie de la spécification JAX-WS. Cependant, le problème avec SOAPHandler est qu’il tente implicitement de représenter l’ensemble du message XML en mémoire. Cela peut entraîner une utilisation énorme de la mémoire. Diverses implémentations de JAX-WS ont ajouté leurs propres solutions de contournement. Si vous travaillez avec des demandes volumineuses ou des réponses volumineuses, vous devez vous pencher sur l'une des approches propriétaires.

Puisque vous posez des questions sur "celui inclus dans JDK version 1.5 ou supérieure", je vais répondre à ce qui est officiellement appelé JAX-WS RI (aka Metro), qui est inclus dans le JDK.

JAX-WS RI propose une solution spécifique très efficace en termes d'utilisation de la mémoire.

Voir https://javaee.github.io/metro/doc/user-guide/ch02.html#efficient-handlers-in-jax-ws-ri . Malheureusement, ce lien est maintenant brisé mais vous pouvez le trouver sur WayBack Machine. Je vais donner les points forts ci-dessous:

En 2007, les employés de Metro ont introduit un type de gestionnaire supplémentaire, MessageHandler<MessageHandlerContext>, propre à Metro. Il est beaucoup plus efficace que SOAPHandler<SOAPMessageContext> car il n'essaie pas de représenter le DOM en mémoire.

Voici le texte crucial de l'article de blog original:

MessageHandler:

Utilisation de la structure de gestionnaire extensible fournie par JAX-WS Spécification et meilleure abstraction de message dans RI, nous avons introduit un nouveau gestionnaire appelé MessageHandler pour étendre votre service Web applications. MessageHandler est similaire à SOAPHandler, sauf que ses implémentations ont accès à MessageHandlerContext (une extension de MessageContext). Grâce à MessageHandlerContext, on peut accédez au message et traitez-le à l'aide de l'API Message. Comme je l'ai mis en titre du blog, ce gestionnaire vous permet de travailler sur Message, qui fournit des moyens efficaces pour accéder/traiter le message et pas seulement un DOM message basé. Le modèle de programmation des gestionnaires est le même et le Les gestionnaires de messages peuvent être mélangés avec les gestionnaires logiques standard et SOAP . J'ai ajouté un exemple dans JAX-WS RI 2.1.3 illustrant l'utilisation de MessageHandler pour enregistrer les messages et voici un extrait de l'exemple:

public class LoggingHandler implements MessageHandler<MessageHandlerContext> {
    public boolean handleMessage(MessageHandlerContext mhc) {
        Message m = mhc.getMessage().copy();
        XMLStreamWriter writer = XMLStreamWriterFactory.create(System.out);
        try {
            m.writeTo(writer);
        } catch (XMLStreamException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    public boolean handleFault(MessageHandlerContext mhc) {
        ..... 
        return true;
    }

    public void close(MessageContext messageContext) {    }

    public Set getHeaders() {
        return null;
    }
}

(fin citation du billet de blog 2007)

Inutile de dire que votre gestionnaire personnalisé, LoggingHandler dans l'exemple, doit être ajouté à votre chaîne de gestionnaires pour avoir un effet quelconque. Cela revient à ajouter une autre variable Handler. Vous pouvez donc regarder dans les autres réponses de cette page pour savoir comment procéder.

Vous pouvez trouver un exemple complet dans le repo Metro GitHub .

1
peterh

ai-je raison de comprendre que vous souhaitez modifier/accéder au message XML brut?

Si tel est le cas, vous (ou depuis l'âge de cinq ans, le prochain type) voudrez peut-être jeter un coup d'œil à l'interface du fournisseur qui fait partie du JAXWS. La contrepartie client est réalisée à l'aide de la classe "Dispatch". Quoi qu'il en soit, vous n'avez pas à ajouter de gestionnaires ou d'intercepteurs. Vous pouvez toujours, bien sûr. L’inconvénient est que vous êtes entièrement responsable de la création du message SOAP, mais c’est facile, et si c’est ce que vous voulez (comme je l’ai fait), c’est parfait.

Voici un exemple pour le côté serveur (peu maladroit, c'était juste pour expérimenter) -

@WebServiceProvider(portName="Provider1Port",serviceName="Provider1",targetNamespace = "http://localhost:8123/SoapContext/SoapPort1")
@ServiceMode(value=Service.Mode.MESSAGE)
public class Provider1 implements Provider<SOAPMessage>
{
  public Provider1()
  {
  }

  public SOAPMessage invoke(SOAPMessage request)
  { try{


        File log= new File("/home/aneeshb/practiceinapachecxf/log.txt");//creates file object
        FileWriter fw=new FileWriter(log);//creates filewriter and actually creates file on disk

            fw.write("Provider has been invoked");
            fw.write("This is the request"+request.getSOAPBody().getTextContent());

      MessageFactory mf = MessageFactory.newInstance();
      SOAPFactory sf = SOAPFactory.newInstance();

      SOAPMessage response = mf.createMessage();
      SOAPBody respBody = response.getSOAPBody();
      Name bodyName = sf.createName("Provider1Insertedmainbody");
      respBody.addBodyElement(bodyName);
      SOAPElement respContent = respBody.addChildElement("provider1");
      respContent.setValue("123.00");
      response.saveChanges();
      fw.write("This is the response"+response.getSOAPBody().getTextContent());
      fw.close();
      return response;}catch(Exception e){return request;}


   }
}

Vous le publiez comme un SEI,

public class ServerJSFB {

    protected ServerJSFB() throws Exception {
        System.out.println("Starting Server");
        System.out.println("Starting SoapService1");

        Object implementor = new Provider1();//create implementor
        String address = "http://localhost:8123/SoapContext/SoapPort1";

        JaxWsServerFactoryBean svrFactory = new JaxWsServerFactoryBean();//create serverfactorybean

        svrFactory.setAddress(address);
        svrFactory.setServiceBean(implementor);

        svrFactory.create();//create the server. equivalent to publishing the endpoint
        System.out.println("Starting SoapService1");
  }

public static void main(String args[]) throws Exception {
    new ServerJSFB();
    System.out.println("Server ready...");

    Thread.sleep(10 * 60 * 1000);
    System.out.println("Server exiting");
    System.exit(0);
}
}

Ou vous pouvez utiliser une classe de point de terminaison pour cela ..___.

Et oh, si vous voulez, vous n'avez pas besoin de traiter les en-têtes, si vous changez le mode de service en PAYLOAD (vous n'obtiendrez que le corps de savon).

1
Aneesh Barthakur

J'avais essayé de trouver une bibliothèque de structure pour enregistrer la demande de savon du service Web et sa réponse pendant quelques jours. Le code ci-dessous corrige le problème pour moi:

System.setProperty("com.Sun.xml.ws.transport.http.client.HttpTransportPipe.dump", "true");
        System.setProperty("com.Sun.xml.ws.transport.http.HttpAdapter.dump", "true");
        System.setProperty("com.Sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true");
        System.setProperty("com.Sun.xml.internal.ws.transport.http.HttpAdapter.dump", "true");
0
JChanthamontry

Une solution consiste à ne pas utiliser votre code, mais à utiliser des renifleurs de paquets réseau tels qu'Etheral ou WireShark, qui peuvent capturer le paquet HTTP avec le message XML comme charge utile et vous pouvez continuer à les enregistrer dans un fichier.

Mais une approche plus sophistiquée consiste à écrire vos propres gestionnaires de messages. Vous pouvez y jeter un coup d'oeil ici

0
Shamik

Réellement. Si vous examinez les sources de HttpClientTransport, vous remarquerez qu'il écrit également des messages dans Java.util.logging.Logger. Ce qui signifie que vous pouvez également voir ces messages dans vos journaux.

Par exemple, si vous utilisez Log4J2, tout ce que vous devez faire est le suivant:

  • ajouter un pont JUL-Log4J2 dans votre chemin de classe
  • définir le niveau TRACE pour le package com.Sun.xml.internal.ws.transport.http.client.
  • add -Djava.util.logging.manager = org.Apache.logging.log4j.jul.LogManager propriété système à votre ligne de commande de l'application

Après ces étapes, vous commencez à voir les messages SOAP dans vos journaux.

0
glory1