it-swarm.com.de

Verfolgung von XML-Anfragen/-Antworten mit JAX-WS

Gibt es einen einfachen Weg (alias: keinen Proxy), um Zugriff auf die unformatierte Request/Response-XML für einen Webservice zu erhalten, der mit der JAX-WS-Referenzimplementierung veröffentlicht wurde (die in JDK 1.5 und höher enthalten ist)? Das mache ich über Code. Was ich tun muss, ist einfach nett.

Ich weiß, dass andere komplexere und umfassendere Frameworks existieren, die das tun könnten, aber ich möchte es so einfach wie möglich halten und Achse, Cxf usw. alle einen erheblichen Overhead hinzufügen, den ich vermeiden möchte.

Vielen Dank!

145
Antonio

Hier ist die Lösung in rohem Code (zusammengestellt dank stjohnroe und Shamik):

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

Wo ist SOAPLoggingHandler (aus verknüpften Beispielen gerissen):

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

Mit den folgenden Optionen können Sie die gesamte Kommunikation auf der Konsole protokollieren (technisch gesehen benötigen Sie nur eine davon, aber das hängt von den verwendeten Bibliotheken ab. Daher ist das Festlegen aller vier Optionen eine sicherere Option). Sie können es wie im Beispiel im Code oder als Befehlszeilenparameter mit -D oder als Umgebungsvariable wie von Upendra angegeben festlegen.

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");

Siehe Frage XML-Anfrage/-Antworten mit JAX-WS verfolgen, wenn ein Fehler auftritt für Details.

238
Mr. Napik

Legen Sie vor dem Starten von Tomcat Java_OPTS wie folgt in Linux-Umgebungen fest. Starten Sie dann Tomcat. Sie werden die Anfrage und Antwort in der catalina.out-Datei sehen.

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

Legen Sie die folgenden Systemeigenschaften fest. Dadurch wird die XML-Protokollierung aktiviert. Sie können es in Java oder in der Konfigurationsdatei einstellen.

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");
    }

konsolenprotokolle:

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

Es gibt verschiedene Möglichkeiten, dies programmgesteuert zu tun, wie in den anderen Antworten beschrieben, aber es handelt sich dabei um ziemlich invasive Mechanismen. Wenn Sie jedoch wissen, dass Sie den JAX-WS RI (auch "Metro") verwenden, können Sie dies auf der Konfigurationsebene tun. Hier finden Sie Anweisungen wie das geht. Sie müssen sich nicht mit Ihrer Anwendung herumschlagen.

9
skaffman

// Diese Lösung bietet eine Möglichkeit, programmgesteuert einen Handler zum Webserviceclient ohne XML-Konfiguration hinzuzufügen

// Das vollständige Dokument finden Sie hier: http://docs.Oracle.com/cd/E17904_01//web.1111/e13734/handlers.htm#i222476

// Neue Klasse erstellen, die SOAPHandler implementiert

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) {
}
}

// Fügen Sie Ihren LogMessageHandler programmgesteuert hinzu

   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

Fügen Sie SOAPHandler in die Endpunktschnittstelle ein. Wir können die SOAP - Anforderung und -Antwort verfolgen

Implementieren von SOAPHandler mit 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);

Deklarativ durch Hinzufügen einer @HandlerChain(file = "handlers.xml")-Anmerkung zu Ihrer Endpunktschnittstelle.

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

Ich poste eine neue Antwort, da ich nicht genug Ruf habe, um die von Antonio abgegebene Antwort zu kommentieren (siehe: https://stackoverflow.com/a/1957777 ).

Wenn Sie die SOAP - Nachricht in einer Datei drucken möchten (z. B. über Log4j), können Sie Folgendes verwenden:

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());

Bitte beachten Sie, dass sich der Methodenaufruf writeTo () unter Umständen nicht wie erwartet verhält (siehe: https://community.Oracle.com/thread/1123104?tstart=0 oder https: // www .Java.net/node/691073 ), daher wird der folgende Code den Trick ausführen:

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

Sie müssen einen javax.xml.ws.handler.LogicalHandler implementieren. Dieser Handler muss dann in einer Handler-Konfigurationsdatei referenziert werden, auf die wiederum eine @HandlerChain-Annotation in Ihrem Serviceendpunkt (Schnittstelle oder Implementierung) verweist. Sie können die Nachricht dann entweder über system.out oder über einen Logger in Ihrer processMessage-Implementierung ausgeben. 

Sehen 

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

In Laufzeit Sie können einfach ausführen

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

as dump ist eine öffentliche Variable, die in der Klasse wie folgt definiert ist

public static boolean dump;
1
jozh

Sie könnten versuchen, eine ServletFilter vor den Webservice zu stellen und die Anforderung und Antwort zu prüfen, die an den Dienst gesendet/von diesem zurückgegeben wird.

Obwohl Sie nicht ausdrücklich nach einem Proxy gefragt haben, reicht es manchmal aus, tcptrace zu sehen, was bei einer Verbindung vor sich geht. Es ist ein einfaches Tool, keine Installation, es zeigt die Datenströme an und kann auch in eine Datei schreiben.

1
rsp

mit den Konfigurationsdateien von logback.xml können Sie Folgendes tun: 

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

Dadurch werden die Anfrage und die Antwort wie folgt protokolliert (abhängig von Ihrer Konfiguration für die Protokollausgabe):

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

Die hier aufgeführten Antworten, in denen Sie SOAPHandler verwenden, sind vollständig korrekt. Der Vorteil dieses Ansatzes ist, dass er mit jeder JAX-WS-Implementierung funktioniert, da SOAPHandler Teil der JAX-WS-Spezifikation ist. Das Problem mit SOAPHandler besteht jedoch darin, dass implizit versucht wird, die gesamte XML-Nachricht im Speicher darzustellen. Dies kann zu einer enormen Speicherauslastung führen. Verschiedene Implementierungen von JAX-WS haben hierfür eigene Problemumgehungen hinzugefügt. Wenn Sie mit großen Anfragen oder umfangreichen Antworten arbeiten, müssen Sie einen der proprietären Ansätze untersuchen.

Da Sie nach "dem in JDK 1.5 oder besser enthaltenen" fragen, werde ich auf das antworten, was als JAX-WS RI (alias Metro) bekannt ist, was im JDK enthalten ist.

JAX-WS RI bietet hierfür eine spezifische Lösung, die hinsichtlich der Speichernutzung sehr effizient ist.

Siehe https://javaee.github.io/metro/doc/user-guide/ch02.html#efficient-handlers-in-jax-ws-ri . Leider ist der Link jetzt defekt, aber Sie können ihn auf WayBack Machine finden. Ich gebe die Highlights unten:

Die Metro-Leute im Jahr 2007 eingeführt einen zusätzlichen Handlertyp, MessageHandler<MessageHandlerContext>, der von Metro proprietär ist. Es ist weitaus effizienter als SOAPHandler<SOAPMessageContext>, da keine DOM-Darstellung im Arbeitsspeicher versucht wird.

Hier ist der entscheidende Text aus dem ursprünglichen Blogartikel:

MessageHandler:

Verwendung des erweiterbaren Handler-Frameworks von JAX-WS Spezifikation und die bessere Message-Abstraktion in RI haben wir eingeführt ein neuer Handler namens MessageHandler, um Ihren Web-Service zu erweitern Anwendungen. MessageHandler ähnelt SOAPHandler mit der Ausnahme, dass Implementierungen davon erhalten Zugriff auf MessageHandlerContext (eine Erweiterung von MessageContext ). Durch MessageHandlerContext kann man Greifen Sie auf die Nachricht zu und verarbeiten Sie sie mithilfe der Nachrichten-API. Wie ich in Im Titel des Blogs können Sie mit diesem Handler an Message arbeiten, die bietet effiziente Möglichkeiten, auf die Nachricht und nicht nur auf ein DOM zuzugreifen. /. basierte Nachricht. Das Programmiermodell der Handler ist das gleiche und das Message-Handler können mit Standard-Logical- und SOAP -Handlern gemischt werden . Ich habe in JAX-WS RI 2.1.3 ein Beispiel hinzugefügt, das die Verwendung von .__ zeigt. MessageHandler zum Protokollieren von Nachrichten. Hier ist ein Ausschnitt aus dem Beispiel:

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;
    }
}

(Schlusszitat aus dem Blogeintrag 2007)

Es ist unnötig zu erwähnen, dass Ihr benutzerdefinierter Handler, LoggingHandler im Beispiel, zu Ihrer Handler-Kette hinzugefügt werden muss, um einen Effekt zu erzielen. Dies ist dasselbe wie das Hinzufügen einer beliebigen anderen Handler, sodass Sie in anderen Antworten auf dieser Seite nachschlagen können, wie Sie dies tun.

Ein vollständiges Beispiel finden Sie im Metro GitHub Repo .

1
peterh

verstehe ich, dass Sie die rohe XML-Nachricht ändern möchten oder auf sie zugreifen möchten?

Wenn ja, möchten Sie (oder, da dies fünf Jahre alt ist, der nächste Typ), einen Blick auf die Provider-Benutzeroberfläche werfen, die Bestandteil von JAXWS ist. Das Client-Gegenstück wird mit der Klasse "Dispatch" ausgeführt. Sie müssen jedoch keine Handler oder Interceptor hinzufügen. Du kannst natürlich immer noch. Der Nachteil ist auf diese Weise, Sie sind VOLLSTÄNDIG für das Erstellen der SOAPMessage verantwortlich, aber es ist einfach, und wenn Sie möchten (wie ich), ist dies perfekt.

Hier ist ein Beispiel für die Serverseite (etwas ungeschickt, es war nur zum Experimentieren) -

@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;}


   }
}

Sie veröffentlichen es wie ein 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);
}
}

Oder Sie können eine Endpoint-Klasse dafür verwenden. Hoffe, das war hilfreich.

Und wenn Sie möchten, müssen Sie sich nicht mit Kopfzeilen und Dingen beschäftigen, wenn Sie den Servicemodus auf PAYLOAD ändern (Sie erhalten nur den Soap Body).

1

Ich hatte einige Tage lang versucht, eine Framework-Bibliothek zu finden, um die Anfrage und Antwort des Webdienstes zu protokollieren. Der folgende Code hat das Problem für mich behoben:

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

Eine Möglichkeit ist, nicht Ihren Code zu verwenden, sondern Netzwerk-Paket-Sniffer wie Etheral oder WireShark, die das HTTP-Paket mit der XML-Nachricht als Nutzlast erfassen und Sie in einer Datei oder so weiter protokollieren können.

Ein ausgefeilterer Ansatz besteht jedoch darin, Ihre eigenen Meldungshandler zu schreiben. Sie können es anschauen hier

0
Shamik

Tatsächlich. Wenn Sie sich die Quellen von HttpClientTransport ansehen, werden Sie feststellen, dass auch Nachrichten in Java.util.logging.Logger geschrieben werden. Das bedeutet, dass Sie diese Meldungen auch in Ihren Protokollen sehen können.

Wenn Sie beispielsweise Log4J2 verwenden, müssen Sie Folgendes tun:

  • fügen Sie JUL-Log4J2-Bridge Ihrem Klassenpfad hinzu
  • legen Sie die TRACE-Stufe für das Paket com.Sun.xml.internal.ws.transport.http.client fest.
  • fügen Sie -Djava.util.logging.manager = org.Apache.logging.log4j.jul.LogManager-Systemeigenschaft zur Startbefehlszeile der Anwendung hinzu

Nach diesen Schritten werden SOAP -Nachrichten in Ihren Protokollen angezeigt.

0
glory1