216

Is there an easy way (aka: not using a proxy) to get access to the raw request/response XML for a webservice published with JAX-WS reference implementation (the one included in JDK 1.5 and better) ? Being able to do that via code is what I need to do. Just having it logged to a file by clever logging configurations would be nice but enough.

I know that other more complex and complete frameworks exist that might do that, but I would like to keep it as simple as possible and axis, cxf, etc all add considerable overhead that I want to avoid.

Thanks!

2
  • 6
    Just a note: JAX-WS is a standard, which CXF implements. Commented Dec 22, 2009 at 11:07
  • Setting Java system properties and environment variables see:<br> stackoverflow.com/questions/7054972/… Commented Oct 25, 2012 at 13:17

20 Answers 20

360

Following options enable logging of all communication to the console (technically, you only need one of these, but that depends on the libraries you use, so setting all four is safer option). You can set it in the code like in example, or as command line parameter using -D or as environment variable as Upendra wrote.

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"); System.setProperty("com.sun.xml.internal.ws.transport.http.HttpAdapter.dumpTreshold", "999999"); 

See question Tracing XML request/responses with JAX-WS when error occurs for details.

Sign up to request clarification or add additional context in comments.

10 Comments

Thanks, this is the best answer I have found to this problem
This does NOT work for me, when the CLIENT runs in Tomcat. Only the -D stuff works. I believe this is due to the classLoader-structure in Tomcat?
System.setProperty("com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true"); is the right onefor JAX-WS 2.2 RI bundled in JDK7 and used by default
for this work in tomcat you need to add these commands to JAVA_OPTS in catalina.sh e.g. on the first line add: JAVA_OPTS=" -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true -Dcom.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true -Dcom.sun.xml.ws.transport.http.HttpAdapter.dump=true -Dcom.sun.xml.internal.ws.transport.http.HttpAdapter.dump=true" after that you can check catalina.out and the output from this will show there.
Also add System.setProperty("com.sun.xml.internal.ws.transport.http.HttpAdapter.dumpTreshold", "999999"); to not have the request and response output truncated
|
102

Here is the solution in raw code (put together thanks to stjohnroe and Shamik):

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

Where SOAPLoggingHandler is (ripped from linked examples):

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

7 Comments

See link if you still don't see response/request xml with the above code: stackoverflow.com/questions/2808544/…
This relies on the existence of a SOAPMessage object, so it will fail (just print an exception, but not the trace) if you receive malformed response from the server. Check my answer, if you need a trace even when thigns go wrong.
In the snippet at top: regarding the last line ep.publish(publishURL);: what is publishURL (In my code, the wsdl url is included in the service itself; i do not have any url outside. What do I miss?)
If you would like to publish it on all interface then the publishUrl is something like this (hltp = http) : "hltp://0.0.0.0:8080/standalone/service" . In this particular case, you can access the service at "hltp://127.0.0.1:8080/standalone/service/yourService" where "yourService" is the wsdl port location defined in the wsdl.
@Mr.Napik: But this way you can still provide your own logging facility, which is nice when you using a logging framework.
|
60

Before starting tomcat, set JAVA_OPTS as below in Linux envs. Then start Tomcat. You will see the request and response in the catalina.out file.

export JAVA_OPTS="$JAVA_OPTS -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true" 

5 Comments

Brilliant. This is the best answer IMHO.
For some reason, for me it is : -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true
For some reason, this worked on only one of my 3 web services (I have 3 JAX-WS web services in my Tomcat web-app). Any idea why it wouldn't work on all 3?
Worked fine for me to see why my test failed (Set the option in the 'run configutration' of my test as 'VM argument').
You sire just exploeded the internet with the best answer ever
21

Inject SOAPHandler to endpoint interface. we can trace the SOAP request and response

Implementing SOAPHandler with 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); 

Declarative by adding @HandlerChain(file = "handlers.xml") annotation to your endpoint interface.

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

3 Comments

I am following this exactly. I am printing out the message after making modifications to the header, however i am not seeing those changes. It appears as though the message doesn't change until after it leaves the handleMessage method
If I call out to print message twice, the second time has the update. Very odd
Works for me on JBoss 7.3. Txs
19

Set the following system properties, this will enabled xml logging. You can set it in java or configuration file.

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"); System.setProperty("com.sun.xml.internal.ws.transport.http.HttpAdapter.dumpTreshold", "999999"); } 

console logs:

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

Comments

12

There are various ways of doing this programmatically, as described in the other answers, but they're quite invasive mechanisms. However, if you know that you're using the JAX-WS RI (aka "Metro"), then you can do this at the configuration level. See here for instructions on how to do this. No need to mess about with your application.

5 Comments

Metro = JAX-WS RI + WSIT (i.e. JAX-WS RI != Metro)
@Pau: Fixed. You know, instead of voting me down for that, you could've put a bit of effort in and suggested an alternative link.
If I had found one, be sure I would have put it. Don't take it as personal. removed down vote ;)
Link is broken again (what's up with java.net???). I think this is the new link: metro.java.net/nonav/1.2/guide/Logging.html
Looks like they've since moved to GitHub Pages; I've updated the answer with the current link.
9

// This solution provides a way programatically add a handler to the web service clien w/o the XML config

// See full doc here: http://docs.oracle.com/cd/E17904_01//web.1111/e13734/handlers.htm#i222476

// Create new class that implements 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) { } } 

// Programatically add your 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); 

Comments

5

I am posting a new answer, as I do not have enough reputation to comment on the one provided by Antonio (see: https://stackoverflow.com/a/1957777).

In case you want the SOAP message to be printed in a file (e.g. via Log4j), you may use:

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

Please note that under certain circumstances, the method call writeTo() may not behave as expected (see: https://community.oracle.com/thread/1123104?tstart=0 or https://www.java.net/node/691073), therefore the following code will do the trick:

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

Comments

4

The answers listed here which guide you to use SOAPHandler are fully correct. The benefit of that approach is that it will work with any JAX-WS implementation, as SOAPHandler is part of the JAX-WS specification. However, the problem with SOAPHandler is that it implicitly attempts to represent the whole XML message in memory. This can lead to huge memory usage. Various implementations of JAX-WS have added their own workarounds for this. If you work with large requests or large responses, then you need to look into one of the proprietary approaches.

Since you ask about "the one included in JDK 1.5 or better" I'll answer with respect to what is formally known as JAX-WS RI (aka Metro) which is what is included with the JDK.

JAX-WS RI has a specific solution for this which is very efficient in terms of memory usage.

See https://javaee.github.io/metro/doc/user-guide/ch02.html#efficient-handlers-in-jax-ws-ri. Unfortunately that link is now broken but you can find it on WayBack Machine. I'll give the highlights below:

The Metro folks back in 2007 introduced an additional handler type, MessageHandler<MessageHandlerContext>, which is proprietary to Metro. It is far more efficient than SOAPHandler<SOAPMessageContext> as it doesn't try to do in-memory DOM representation.

Here's the crucial text from the original blog article:

MessageHandler:

Utilizing the extensible Handler framework provided by JAX-WS Specification and the better Message abstraction in RI, we introduced a new handler called MessageHandler to extend your Web Service applications. MessageHandler is similar to SOAPHandler, except that implementations of it gets access to MessageHandlerContext (an extension of MessageContext). Through MessageHandlerContext one can access the Message and process it using the Message API. As I put in the title of the blog, this handler lets you work on Message, which provides efficient ways to access/process the message not just a DOM based message. The programming model of the handlers is same and the Message handlers can be mixed with standard Logical and SOAP handlers. I have added a sample in JAX-WS RI 2.1.3 showing the use of MessageHandler to log messages and here is a snippet from the sample:

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

(end quote from 2007 blog post)

Needless to say your custom Handler, LoggingHandler in the example, needs to be added to your Handler Chain to have any effect. This is the same as adding any other Handler, so you can look in the other answers on this page for how to do that.

You can find a full example in the Metro GitHub repo.

1 Comment

Seems even MesageHandler deals with parsed XML response, meaning if you get corrupted response you won't be able to enter the handler. With pipe logger (like com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe) you have a chance to see broken responses.
2

You need to implement a javax.xml.ws.handler.LogicalHandler, this handler then needs to be referenced in a handler configuration file, which in turn is referenced by an @HandlerChain annotation in your service endpoint (interface or implementation). You can then either output the message via system.out or a logger in your processMessage implementation.

See

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

Comments

2

with logback.xml configuration files, you can do :

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

That will log the request and the response like this (depending on your configuration for the log output) :

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 Comment

I'm trying to find a way, how to log this in logback, but this doesn't worked for me (I tried all combinations of system props, but logs are still logged as standard output). Your logger is trace and logs are debug. Can you post whole logback-spring.xml please?
1

You could try to put a ServletFilter in front of the webservice and inspect request and response going to / returned from the service.

Although you specifically did not ask for a proxy, sometimes I find tcptrace is enough to see what goes on on a connection. It's a simple tool, no install, it does show the data streams and can write to file too.

Comments

1

am I correct in understanding that you want to change/access the raw XML message?

If so, you (or since this is five years old, the next guy) might want to have a look at the Provider interface that is part of the JAXWS. The client counterpart is done using the "Dispatch" class. Anyway, you don't have to add handlers or interceptors. You still CAN, of course. The downside is this way, you are COMPLETELY responsible for building the SOAPMessage, but its easy, and if that's what you want(like I did) this is perfect.

Here is an example for the server side(bit clumsy, it was just for experimenting)-

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

You publish it like you would an 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); } } 

Or you can use an Endpoint class for it. Hope that has been helpful.

And oh, if you want you needn't deal with headers and stuff, if you change the service mode to PAYLOAD(You'll only get the Soap Body).

Comments

1

Actually. If you look into sources of HttpClientTransport you will notice that it is also writing messages into java.util.logging.Logger. Which means you can see those messages in your logs too.

For example if you are using Log4J2 all you need to do is the following:

  • add JUL-Log4J2 bridge into your class path
  • set TRACE level for com.sun.xml.internal.ws.transport.http.client package.
  • add -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager system property to your applicaton start command line

After these steps you start seeing SOAP messages in your logs.

Comments

1

I had been trying to find some framework library to log the web service soap request and response for a couple days. The code below fixed the issue for me:

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

Comments

1

There are a couple of answers using SoapHandlers in this thread. You should know that SoapHandlers modify the message if writeTo(out) is called.

Calling SOAPMessage's writeTo(out) method automatically calls saveChanges() method also. As a result all attached MTOM/XOP binary data in a message is lost.

I am not sure why this is happening, but it seems to be a documented feature.

In addition, this method marks the point at which the data from all constituent AttachmentPart objects are pulled into the message.

https://docs.oracle.com/javase/7/docs/api/javax/xml/soap/SOAPMessage.html#saveChanges()

Comments

0

One way to do is not using your code but use network packet sniffers like Etheral or WireShark which can capture the HTTP packet with the XML message as payload to it and you can keep logging them to a file or so.

But more sophisticated approach is to write your own message handlers. You can have a look at it here.

Comments

0

In runtime you could simply execute

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

as dump is a public var defined in the class as follows

public static boolean dump; 

1 Comment

For me work with com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump = true;
0

If you happen to run a IBM Liberty app server, just add ibm-ws-bnd.xml into WEB-INF directory.

<?xml version="1.0" encoding="UTF-8"?> <webservices-bnd xmlns="http://websphere.ibm.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-ws-bnd_1_0.xsd" version="1.0"> <webservice-endpoint-properties enableLoggingInOutInterceptor="true" /> </webservices-bnd> 

Comments

0

Solution for Glassfish/Payara

Add the following entries to the logger settings (log level FINER):

  • com.sun.xml.ws.transport.http.client.HttpTransportPipe
  • com.sun.xml.ws.transport.http.HttpAdapter

Found here.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.