使用JAX-WS跟踪XML请求/响应

是否有简单的方法(也就是不使用代理)访问使用JAX-WS参考实现发布的Web服务的原始请求/响应XML(包含在JDK 1.5及更高版本中)? 能够通过代码做到这一点是我需要做的。 只要它通过聪明的日志configurationlogging到文件将是很好,但足够。

我知道其他更复杂和完整的框架可能会这样做,但我想尽可能简单,axis,cxf等都会增加我想避免的大量开销。

谢谢!

以下选项可以将所有通信logging到控制台(从技术上讲,您只需要其中一个,但取决于您使用的库,因此将所有四个设置为更安全的选项)。 你可以像例子中那样在代码中设置它,或者像Upendra写的那样使用-D或者作为环境variables来设置命令行参数。

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

请参阅问题发生错误时 , 使用JAX-WS跟踪XML请求/响应的详细信息。

这里是原始代码的解决scheme(感谢stjohnroe和Shamik):

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

SOAPLoggingHandler的位置(从链接示例中剥离):

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

在启动tomcat之前,在Linux环境下设置JAVA_OPTS如下。 然后启动Tomcat。 您将在catalina.out文件中看到请求和响应。

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

如其他答案中所述,以编程方式进行这种操作有多种方式,但它们是非常有创意的机制。 但是,如果您知道您正在使用JAX-WS RI(又名“Metro”),则可以在configuration级别执行此操作。 请参阅此处了解如何执行此操作的说明 。 没有必要乱用你的应用程序。

//这个解决scheme提供了一种方法,以编程方式将处理程序添加到Web服务客户端,而不使用XMLconfiguration

//请参阅完整文档: http : //docs.oracle.com/cd/E17904_01//web.1111/e13734/handlers.htm#i222476

//创build实现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) { } } 

//以编程方式添加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); 

设置以下系统属性,这将启用xml日志logging。 你可以在java或configuration文件中设置它。

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

控制台日志:

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

您需要实现javax.xml.ws.handler.LogicalHandler,然后需要在处理程序configuration文件中引用此处理程序,该文件又由服务端点(接口或实现)中的@HandlerChain注释引用。 然后,您可以通过system.out或者processMessage实现中的logging器输出消息。

看到

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

我发布了一个新的答案,因为我没有足够的信誉来评论Antonio提供的(请参阅: https : //stackoverflow.com/a/1957777 )。

如果您希望将SOAP消息打印在文件中(例如通过Log4j),则可以使用:

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

请注意,在某些情况下,方法调用writeTo()可能不像预期的那样运行(请参阅: https ://community.oracle.com/thread/1123104?tstart =0或https://www.java.net/node / 691073 ),因此下面的代码将做的伎俩:

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

SOAPHandler注入到端点接口。 我们可以跟踪SOAP请求和响应

编程方式实现SOAPHandler

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

装饰性地通过添加@HandlerChain(file = "handlers.xml")注释到您的端点接口。

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

你可以尝试把一个ServletFilter放在webservice的前面,并检查请求和响应去/从服务返回。

虽然你特别没有要求代理,但有时候我发现tcptrace足以查看连接上发生了什么。 这是一个简单的工具,没有安装,它显示数据stream,也可以写入文件。

运行时你可以简单地执行

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

因为dump是在类中定义的公共variables,如下所示

 public static boolean dump; 

我是否正确理解您要更改/访问原始XML消息?

如果是这样的话,那么你(或者是五岁以后,下一个人)可能会想看看JAXWS提供者接口。 客户端对应完成使用“调度”类。 无论如何,你不必添加处理程序或拦截器。 当然,你仍然可以。 缺点是这样,你完全负责构buildSOAPMessage,但它很容易,如果这就是你想要的(就像我做的那样),这是完美的。

这里是一个服务器端的例子(有点笨拙,只是为了试验) –

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

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

或者你可以使用一个Endpoint类。 希望有帮助。

哦,如果你想,你不需要处理标题和东西,如果你改变服务模式为PAYLOAD(你只会得到肥皂身体)。

一种方法是不使用你的代码,而是使用networking数据包嗅探器,如Etheral或WireShark,它可以捕获HTTP数据包的XML消息作为有效载荷,并且可以将它们logging到文件等等。

但更复杂的方法是编写自己的消息处理程序。 你可以在这里看看。

其实。 如果您查看HttpClientTransport的来源,您会注意到它也正在将消息写入java.util.logging.Logger。 这意味着您可以在日志中看到这些消息。

例如,如果您正在使用Log4J2,则只需执行以下操作:

  • 将JUL-Log4J2桥添加到您的课程path中
  • 为com.sun.xml.internal.ws.transport.http.client包设置TRACE级别。
  • 将-Djava.util.logging.manager = org.apache.logging.log4j.jul.LogManager系统属性添加到您的applicaton start命令行

完成这些步骤之后,您将开始在日志中看到SOAP消息。

这里列出的指导您使用SOAPHandler是完全正确的。 这种方法的好处是可以与任何JAX-WS实现一起工作,因为SOAPHandler是JAX-WS规范的一部分。 但是,SOAPHandler的问题在于,它隐式地试图在内存中表示整个XML消息。 这可能会导致巨大的内存使用量。 JAX-WS的各种实现已经为此添加了自己的解决方法。 如果您处理大量请求或大量响应,则需要查看其中一种专有方法。

既然您问到“包含在JDK 1.5或更高版本中的那个”,我将回答关于正式名称为JAX-WS RI(又名Metro)的内容,这是JDK包含的内容。

JAX-WS RI有一个针对内存使用非常有效的具体解决scheme。

请参阅https://javaee.github.io/metro/doc/user-guide/ch02.html#efficient-handlers-in-jax-ws-ri 。 不幸的是,这个链接现在被破坏,但是你可以在WayBack Machine上find它。 我将给出下面的亮点:

2007年,Metro地区的人们引入了一个额外的处理器typesMessageHandler<MessageHandlerContext> ,这是Metro专有的。 它比SOAPHandler<SOAPMessageContext>有效得多,因为它不会尝试执行内存中的DOM表示。

以下是原始博客文章中的重要文字:

的MessageHandler:

利用JAX-WS规范提供的可扩展的Handler框架和RI中更好的Message抽象,我们引入了一个名为MessageHandler的新处理程序来扩展您的Web Service应用程序。 MessageHandler类似于SOAPHandler,只是它的实现可以访问MessageHandlerContext (MessageContext的扩展)。 通过MessageHandlerContext可以访问消息并使用消息API处理消息。 正如我放入博客的标题,这个处理程序让你工作在消息,它提供了有效的方式来访问/处理消息不只是一个基于DOM的消息。 处理程序的编程模型是相同的,消息处理程序可以与标准的逻辑和SOAP处理程序混合使用。 我已经在JAX-WS RI 2.1.3中添加了一个示例,其中显示了使用MessageHandlerlogging消息,这里是示例代码片段:

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

(从2007年博客文章结束引用)

不用说你的自定义处理程序,在这个例子中, LoggingHandler ,需要被添加到你的处理链,以产生任何效果。 这与添加其他Handler相同,因此您可以在此页面上查找其他答案以了解如何执行此操作。

你可以在Metro GitHub仓库中find一个完整的例子 。