Trace SOAP request/responses with JAX-WS on the client side - java

I'm using JAX-WS reference implementation (2.1.7) and I want to trace SOAP request/responses on the client side. Actually, what I need is to examine some Http headers when I receive the response.
Following these previous questions ( Tracing XML request/responses with JAX-WS and Java JAX-WS web-service client: how log request & response xml? ), I've created my own handler to log when I send a request and receive a response:
public class SHandler implements SOAPHandler<SOAPMessageContext>
{
private static final Logger log = Logger.getLogger(SHandler.class);
#Nullable
#Override
public Set<QName> getHeaders()
{
log.debug(">>>>>>>>>>> GetHeaders");
return null;
}
#Override
public boolean handleMessage(SOAPMessageContext soapMessageContext)
{
log.debug(">>>>>>>>>>> HandleMessage");
return true;
}
#Override
public boolean handleFault(SOAPMessageContext soapMessageContext)
{
log.debug(">>>>>>>>>>> HandleFault");
return true;
}
#Override
public void close(MessageContext messageContext)
{
log.debug(">>>>>>>>>>> Close");
}
}
and I add the handler to the handler chain during the service initialisation:
#WebServiceClient(name = "MyService", targetNamespace = "http://www.whatever.com/", wsdlLocation = "file:/path/to/wsdl")
public class MyService extends Service
{
public MyService(URL wsdlLocation) {
super(...);
initializeBinding();
}
#WebEndpoint(name = "MyOperation")
public MyPort getMyPort() {
return super.getPort(new QName("http://www.whatever.com/", "MyPort"), MyPort.class);
}
private void initializeBinding() {
MyPort port = getMyPort();
BindingProvider bindingProvider = ((BindingProvider) port);
List handlerChain = bindingProvider.getBinding().getHandlerChain();
handlerChain.add(new SHandler());
bindingProvider.getBinding().setHandlerChain(handlerChain);
}
...
}
The problem is that this doesn't work at all on the client side. I don't see any logs and my handler is never executed when I send a request and receive a response.
Notice that there is no specific WSDL related to this issue because I work on an MDA platform that generates client/server artifacts from any WSDL. In addition, I cannot do this at configuration level as all is generated, so I can only do it programmatically (I've been googling this and all the solutions that I find are either the one in the original post or using the handler-chain.xml configuration file).
Am I missing something? Is there any other way of doing this?
Thanks in advance.

If you only want to look at the SOAP messages run with
-Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true
VM argument.

Why not use #HandlerChain(file = "....") annotation?
From my pov, you can not mix constructor- and annotation-based configurations as on-deploy webservice initialization and creating new instance of your service class are performed in absolutely different contexts.

there are 2 tools that you can use to help with this:
soapui
Eclipse tcp/ip monitor
Both tools offer a proxy mode, which intercepts, logs and forwards requests and responses.

Related

Post request response using Camel Netty4 - HTTP operation failed invoking

I'm new to Camel and am trying to get a response from a Netty4 route using a POST request. I'd like to send a JSON and return a string extracted from the body.
My rest setup is as follows:
public class Server extends RouteBuilder {
#Override
public void configure() {
String listenAddress = "0.0.0.0";
int listenPort = 8080;
restConfiguration()
.component("netty4-http")
.scheme("http")
.host(listenAddress)
.dataFormatProperty("prettyPrint", "true")
.bindingMode(RestBindingMode.auto)
.port(listenPort);
rest("/")
.post()
.consumes("application/json; charset=UTF-8")
.to("direct:post");
}
}
Within my Camel route I'd like to send the message back using:
#Component
public class RestRoute extends RouteBuilder {
#Autowired
CamelContext context;
#Override
public void configure() {
from("direct:post")
.log("New Request")
.streamCaching()
.setHeader(Exchange.HTTP_METHOD,constant(org.apache.camel.component.http4.HttpMethods.POST))
.setBody().jsonpath("$.Text") // extract text from JSON
.to("http4://0.0.0.0:8080?bridgeEndpoint=true");
However I get the following error: org.apache.camel.http.common.HttpOperationFailedException: HTTP operation failed invoking http://0.0.0.0:8080 with statusCode: 500
I'd appreciate some help!
Oh you should not send the message back, this happens automatic when the routing ends, then the message at that point is used as the response message for the rest.
So remove
.to("http4://0.0.0.0:8080?bridgeEndpoint=true");

Retrieve a list of entities from a Restfull service (inside client app)

Im new to Rest web services and say Ive created this web service using Netbeans
#Path("browse")
#Stateless
public class ArticleBrowseResource {
#EJB
private ArticleSearcherLocal ejbRef;
#GET
#Produces(MediaType.APPLICATION_XML)
public List<Article> browse(#DefaultValue("") #QueryParam("username") String username,#QueryParam("sd") String sd) {
// convert sd string to date
List<Article> articles = ejbRef.search(username, date);
return articles;
}
}
where Article is an entity which is anotated with #XmlRootElement
Now how am I supossed to retreive this list of articles in my client which for simplicity lets just say it is a java standard application? In SOAP web services I know that these objects are automatically generated but not in Rest.
This is the client class generated for this service by Netbeans
public class ArticleBrowseClient {
private WebResource webResource;
private Client client;
private static final String BASE_URI = "http://localhost:8080/cityblog/rest";
public ArticleBrowseClient() {
com.sun.jersey.api.client.config.ClientConfig config = new com.sun.jersey.api.client.config.DefaultClientConfig();
client = Client.create(config);
webResource = client.resource(BASE_URI).path("browse");
}
public <T> T browse(Class<T> responseType, String username, String sd) throws UniformInterfaceException {
WebResource resource = webResource;
if (username != null) {
resource = resource.queryParam("username", username);
}
if (sd != null) {
resource = resource.queryParam("sd", sd);
}
return resource.accept(javax.ws.rs.core.MediaType.APPLICATION_XML).get(responseType);
}
public void close() {
client.destroy();
}
}
What is the best and simplest way to resolve this issue?
Any help is appreciated and
thx in advance
Please try fewer code generation and more understanding of what you are actually doing. On the server, you generate a XML message with help of JAXB. On the client side, you can consume this XML with a programming language and library you like. Just use tools like curl to see what is going actually over "the wire". Your generated client site looks fully reasonable. You just need your Article class from the server side on the client side. The generated code uses Jersey which can read XML messages per JAXB per default. So just drop your server side Article class in your client side classpath and use it. But please also have a look at the wire level protocol to understand the portability of your REST API.

Secure a java embedded web service with javax.jws without application server

I wrote the following code to implement a Java web service that communicates with an application written in another language on the same host:
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
#WebService(name = "MyWebService")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
public class MyWebService {
#WebMethod(operationName = "methodName", action = "urn:#methodName")
#WebResult(name = "result", partName = "output")
public String methodName(#WebParam(name = "param1", partName = "input") String param1,
#WebParam(name = "param2", partName = "input") String param2){
// ...do something
return "You called this service with params: " + param1 + "," + param2;
}
Since requirements are not to use an application server to expose the web service I instantiated the service from another class as follows:
Endpoint endpoint = Endpoint.create(new MyWebService());
URL url = new URL("http://localhost:7777/MyWebService");
endpoint.publish(url.toString());
Questions:
1) Which is the simplest way to secure this service with username and password considering the architecture of this project?
Any code sample would be greatly appreciated.
2) I made some research and found the use of SOAPHandler and I think it would work for me.
In the case of using the SOAPHandler class how do I add headers to the message to require authentication from the client?
Thank you in advance
thanks so much for the response that's the direction I'm following too but
when I check any of the headers for example:
SOAPHeader header = soapContext.getMessage().getSOAPPart().getEnvelope().getHeader();
Iterator<SOAPElement> iterator = header.getAllAttributes();
I get a nullpointer exception...any ideas?
I did a working program. Just to add to what you already found out, following is a way to use handler
Endpoint endpoint = Endpoint.create(new MyWebService());
Binding binding = endpoint.getBinding();
List<Handler> handlerChain = new ArrayList<Handler>(1);
handlerChain.add(new MyHandler());
binding.setHandlerChain(handlerChain);
URL url = new URL("http://localhost:7777/MyWebService");
endpoint.publish(url.toString());
MyHandler is class extending Handler interface. Alternately, you can use #HandlerChain annotation which will need an xml configuration file for handlers. Configure this for incoming messages only
public class MyHandler implements SOAPHandler{
#Override
public Set<?> getHeaders() {
// TODO Auto-generated method stub
return null;
}
#Override
public void close(MessageContext context) {
// TODO Auto-generated method stub
}
#Override
public boolean handleFault(MessageContext context) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean handleMessage(MessageContext context) {
System.out.println("Hehehe the handler");
SOAPMessageContext soapContext = (SOAPMessageContext)context;
try {
SOAPHeader header = soapContext.getMessage().getSOAPPart().getEnvelope().getHeader();
//Check there if the required data (username/password) is present in header or not and return true/false accordingly.
} catch (SOAPException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
}
From the client side also, if your client is using JAB-WS, you will have to use client handlers. Following is a typical JAX-WS client invocation example
Dispatch<Source> dispatch = … create a Dispatch<Source>
dispatch.getBinding().setHandlerChain(chain)
Source request = … create a Source object
Source response = dispatch.invoke(request);
Here the handler in chain will add header to outgoing request. Configure this for Outgoing messages only.
What you did is fair enough.
Concerning the authentication you can just expose a method for passing user name and password as login credentials.
Once the user has provided the correct credentials the user has been authenticated.
Note: Now you must maintain session data and make sure that an incoming request is from an authenticated user. The Endpoint just deploys internally a lightweight http server. You must design you web service implementation to keep "state" among requests.
You have 2 more options.
Do the authentication at the SOAP level. I would not really recomend
it. But if you do, note that the Endpoint does not deploy a
WSDL. So you must communicate exactly to the client connecting,
the SOAP header you expect. It is possible though to write a WSDL by
yourself and "attach" it to the Endpoint.
Do the authentication at the http request level. I.e. add a token or
cookie to the http request. To be honest I do not remember if this
is easy using the Endpoint

override http status with cxf

I'm using CXF for web services.
Because of some client restrictions, I need all web faults to return code 200 instead of 500.
I tried to use interceptors, depends on the phase I was able to either override the status and then the response is empty or the response is full with the fault but then the status is not overridden.
Any ideas how to do that?
Using interceptors, what would be the right phase?
I registered the interceptor like this:
#org.apache.cxf.interceptor.OutFaultInterceptors(interceptors = { "com.my.prod.core.service.itercept.HttpStatusInterceptor" })
and this is the interceptor:
public class HttpStatusInterceptor extends AbstractSoapInterceptor {
public HttpStatusInterceptor(){
super(Phase.POST_STREAM_ENDING);
}
#Override public void handleMessage(org.apache.cxf.binding.soap.SoapMessage msg) throws org.apache.cxf.interceptor.Fault{
msg.put(SoapMessage.RESPONSE_CODE, "200");
}}
Can you try
msg.put(SoapMessage.RESPONSE_CODE, 200);
so it ends up as and Integer object instead of a String. I think it's expecting the integer.

Tracing XML request/responses with JAX-WS

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!
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.
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);
}
}
}
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"
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) {
}
}
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
--------------------------------------
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.
// 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);
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());
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
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.
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>--------------------
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.
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;
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).
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");
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.
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.
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()
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>
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.

Categories

Resources