How to print SOAP XML response - java

I am using below system properties:
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");
This is printing:
---[HTTP response - https://XXXXXXXXXXXXXXXXX/v1.0?wsdl - 200]---
null: HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Language: en-US
Content-Type: text/xml; charset=utf-8
Date: Tue, 09 Jan 2018 12:23:42 GMT
Keep-Alive: timeout=30, max=100
Transfer-Encoding: chunked
X-Frame-Options: SAMEORIGIN
X-Powered-By: Servlet/3.0
<?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><sch:InquiryResponse xmlns:sch="http://XXXXXXXXXX/1.0"><sch:InquiryResponseHeader><sch:ClientID>XXXX</sch:ClientID><sch:ProductCode>PCS</sch:ProductCode><sch:SuccessCode>0</sch:SuccessCode><sch:Date>XX-XX-XXXX</sch:Date><sch:Time>17:53:28</sch:Time></sch:InquiryResponseHeader><sch:InquiryRequestInfo><sch:InquiryPurpose>05</sch:InquiryPurpose><sch:FirstName>Ajay</sch:FirstName><sch:LastName>XXXX</sch:LastName><sch:AddrLine1>XXXX</sch:AddrLine1><sch:State>MH</sch:State><sch:Postal>411014</sch:Postal><sch:DOB>1987-06-21</sch:DOB><sch:Id>XXXX</sch:Id><sch:MobilePhone>XXXX</sch:MobilePhone></sch:InquiryRequestInfo><sch:ReportData><sch:Error><sch:ErrorCode>E0021</sch:ErrorCode><sch:ErrorMsg>User ID does not exist for the given customer.</sch:ErrorMsg></sch:Error></sch:ReportData></sch:InquiryResponse></soapenv:Body></soapenv:Envelope>
How can I get XML data from above printing data?
I want to store that XML in a variable for further use.
Thanks.

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

Related

STOMP connection using java and java.net.socket?

I've set up an Stomp endpoint in Java using Spring boot like this:
#Configuration
#EnableWebSocketMessageBroker
public class ChatConfig implements WebSocketMessageBrokerConfigurer
{
#Override
public void registerStompEndpoints(StompEndpointRegistry registry)
{
registry.addEndpoint("/ws").setAllowedOriginPatterns("*"); // connection point
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry)
{
registry.setApplicationDestinationPrefixes("/app"); // sender point
registry.enableSimpleBroker("/user"); // listener point
}
}
I'm trying to connect to this STOMP server(via LAN) using java.net.socket. I've read through STOMP specification here Stomp specification. What I've written so far in client side:
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("my computer Ipv4 address", 8080);
String frame = "CONNECT\n"
+ "accept-version:1.2\n\n"
+ "host:localhost\0";
OutputStream os = socket.getOutputStream();
os.write(frame.getBytes(StandardCharsets.UTF_8));
InputStream is = socket.getInputStream();
String answer = new String(is.readAllBytes());
System.out.print(answer);
}
It returns this html code:
HTTP/1.1 400
Content-Type: text/html;charset=utf-8
Content-Language: en
Content-Length: 435
Date: Thu, 16 Feb 2023 10:15:41 GMT
Connection: close
<!doctype html><html lang="en"><head><title>HTTP Status 400 – Bad Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 – Bad Request</h1></body></html>
Server returns this:
Invalid character found in method name [CONNECT0x0aaccept-version:1.20x0a0x0ahost:192.168.1.1760x00...]. HTTP method names must be token
I'm quite a noob in network programming. There's a library for this, but I want to learn more about STOMP. So, I hope you guys can help me out with this.

Camel loosing HTTP headers on response to original caller

I've created a route to allow me to forward a REST call. Everything is going well, except I cannot modify the HTTP headers of the response (actually I can't even get them untouched on the response).
// My processor
private void remplacerLiensDansHeader(final Exchange exchange, final String rootUrlPivotJoram, final String rootUrlRemplacement) {
// That is OK, I get the values just fine
ArrayList<String> oldLinks = exchange.getIn().getHeader(HEADER_LINK, ArrayList.class);
// This is also OK
final List<String> newLinks = anciensLiens.stream().map(lien -> lien.replace(rootUrlPivotJoram, rootUrlRemplacement)).collect(toList());
// No error, but apparently doesn't work
exchange.getMessage().setHeader(HEADER_LINK, newLinks);
exchange.getMessage().setHeader("test", "test");
}
// Route configuration
#Override
public void configure() throws Exception {
this.from(RestRouteBuilder.endPoint(createProducerJoramConfiguration))
.setExchangePattern(InOut)
.removeHeader(Exchange.HTTP_URI)
.toD(createProducerJoramConfiguration.getUrlDestination())
.setHeader("test", () -> "test") // that doesn't work either
.process(createProducerJoramConfiguration.getProcessor()); // this is the processor with the code above
}
This is the response I get (note that the response code is 200 and I think it's weird as the original is 201)
curl -v -XPost --user "xxx:yyyy" http://localhost:10015/zzzz/webservices/xxxxx
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 10015 (#0)
* Server auth using Basic with user 'xxx'
> Post /zzzzz/webservices/eeee HTTP/1.1
> Host: localhost:10015
> Authorization: Basic pppppppppppppppppp==
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 200
< Date: Tue, 31 Aug 2021 11:17:49 GMT
< Content-Type: application/octet-stream
< Content-Length: 0
<
* Connection #0 to host localhost left intact
Two things I've noticed:
if I add a body in the processor, then the body is present in the response,
if I remove the processor, the headers present in the "original response" are not present either.
I don't know what headers you exactly lose, but be aware that the Camel HTTP component has a default header filter (as lots of Camel components have).
If you don't specify your own HeaderFilterStrategy, the default HttpHeaderFilterStrategy is used.
This default filters the following headers:
content-length
content-type
host
cache-control
connection
date
pragma
trailer
transfer-encoding
upgrade
via
warning
Camel*
org.apache.camel*
With this filter, Camel wants to avoid that old HTTP headers are still present on outgoing requests (probably with wrong data).
The filtering of Camel headers is just to remove Camel specific stuff that is not relevant for HTTP.
Actually, the problem was the cxfrs component.
We manage to find an answer here : see : Response to REST client from Camel CXFRS route?
Here is the final solution.
Thanks to everyone that looked or answer, I hope that'll help someone else.
public class ModificationHeaderLinkProcessor implements Processor {
private static final String HEADER_LINK = "Link";
#Override
public void process(final Exchange exchange) {
List<String> newLinks= getNewLinks(exchange, oldUrl, newUrl);
ResponseBuilder builder = createHttpResponse(exchange);
builder.header(HEADER_LINK, newLinks);
exchange.getIn().setBody(builder.build());
}
private List<String> getNewLinks(final Exchange exchange, final String oldUrl, final String newUrl) {
ArrayList<String> oldLinks= exchange.getIn().getHeader(HEADER_LINK, ArrayList.class);
return oldLinks.stream().map(link-> link.replace(oldUrl, newUrl)).collect(toList());
}
private ResponseBuilder createHttpResponse(final Exchange exchange) {
ResponseBuilder builder = Response.status(getHttpStatusCode(exchange))
.entity(exchange.getIn().getBody());
clearUselessHeader(exchange);
exchange.getIn().getHeaders().forEach(builder::header);
return builder;
}
private void clearUselessHeader(final Exchange exchange) {
exchange.getIn().removeHeader(HEADER_LINK);
exchange.getIn().removeHeaders("Camel*");
}
private Integer getHttpStatusCode(final Exchange exchange) {
return exchange.getIn().getHeader(exchange.HTTP_RESPONSE_CODE, Integer.class);
}
private final String getPropertiesValue(CamelContext camelContext, String key) {
return camelContext.getPropertiesComponent().resolveProperty(key).orElseThrow();
}
}

Jersey declarative linking doesn't work

I try to make a restfull web service with Jersey. I would like to give a link in the response of that request :
GET /mac/ws/gtm HTTP/1.1
Host: localhost:8080
Accept: application/json
Cache-Control: no-cache
I want the response to be :
HTTP/1.1 200 OK
link: </dossiers>;rel=dossiers
{
"message": "Hello"
}
But the response is :
HTTP/1.1 200 OK
{
"message": "Hello"
}
The link is not produce !
Look my Gtm Resource :
#Component
#Path("/gtm")
public class GTmRessource
{
#GET
#Produces(MediaType.APPLICATION_JSON)
public GTm getJson()
{
GTm gtm = new GTm();
return gtm;
}
}
And my Gtm entity
#XmlRootElement()
#Link(value = #Ref(value = "/dossiers", method = "get"), rel = "dossiers")
public class GTm
{
String message = "Hello";
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
}
What's wrong ?
Thanks for help.
By
See Declarative Hyperlinking: Configuration
You need to add the LinkFilter either programmatically:
resourceConfig.getContainerResponseFilters().add(LinkFilter.class);
or through web.xml
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.sun.jersey.server.linking.LinkFilter</param-value>
</init-param>
C:\>curl -i http://localhost:8080/gtm
HTTP/1.1 200 OK
Content-Type: application/json
Link: </dossiers>;rel=dossiers
Date: Thu, 04 Dec 2014 12:38:06 GMT
Transfer-Encoding: chunked
{"message":"Hello"}

soap response encoding '?' characters in all strings instead of russian .Net proxy, Java server(?)

I generate proxy classes with wsdl.exe to request web-services, that are probably build at java platform. The problem is with encoding of response. I get '?' instead of russian letters.(for example '????26' instead of 'АН26')
I also use soapUI and everything works well there. I am not experienced at configuring .Net clients. So how I could determine and configure proper encoding for response. I already played with app.config as next:
I attach headers information here. I don't wee encoding info at responce headers...
request headers:
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: "urn:#DCSSci_ListFlight_5"
Content-Length: 641
Host: 109.73.1.66:23022
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
response headers:
HTTP/1.1 200 OK
Date: Thu, 06 Sep 2012 03:47:52 GMT
Server: Apache/2.2.10 (Linux/SUSE)
200 OKX-FidelXML-Version: 2.0
Content-length: 15464
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: text/xml
Solution:
public class TraceExtension : SoapExtension
{
Stream oldStream;
Stream newStream;
public override Stream ChainStream(Stream stream)
{
oldStream = stream;
newStream = new MemoryStream();
return newStream;
}
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return null;
}
public override object GetInitializer(Type WebServiceType)
{
return null;
}
public override void Initialize(object initializer)
{
}
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
newStream.Position = 0;
Copy(newStream, oldStream);
break;
case SoapMessageStage.BeforeDeserialize:
message.ContentType = "application/soap+xml; utf-8";
Copy(oldStream, newStream);
newStream.Position = 0;
break;
case SoapMessageStage.AfterDeserialize:
break;
}
}
void Copy(Stream from, Stream to)
{
TextReader reader = new StreamReader(from, System.Text.Encoding.GetEncoding("utf-8"));
TextWriter writer = new StreamWriter(to, System.Text.Encoding.GetEncoding("utf-8"));
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
}
[AttributeUsage(AttributeTargets.Method)]
public class TraceExtensionAttribute : SoapExtensionAttribute
{
private int priority;
public override Type ExtensionType
{
get { return typeof(TraceExtension); }
}
public override int Priority
{
get { return priority; }
set { priority = value; }
}
}
And than just add
[TraceExtension()]
attribute for proxy invoke method
You can override GetWebResponse of your proxy and change the encoding
public class YourProxyClass : SoapHttpClientProtocol
{
protected override WebResponse GetWebResponse(WebRequest request)
{
var response = base.GetWebResponse(request);
response.Headers["Content-Type"] = "text/xml; charset=utf-8"; //<==
return response;
}
}

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