Трассировка XML-запросов / ответов с помощью JAX-WS
есть ли простой способ (он же: не используя прокси) получить доступ к необработанному запросу / ответу XML для веб-сервиса, опубликованного с помощью JAX-WS reference implementation (тот, который включен в JDK 1.5 и лучше) ? Возможность сделать это с помощью кода-это то, что мне нужно сделать. Просто зарегистрировав его в файле с помощью умных конфигураций ведения журнала, было бы неплохо, но достаточно.
Я знаю, что существуют другие более сложные и полные структуры, которые могут это сделать, но я хотел бы сохранить это так же просто, как возможно и axis, cxf и т. д. Все добавляют значительные накладные расходы, которых я хочу избежать.
спасибо!
15 ответов:
следующие параметры позволяют регистрировать все сообщения на консоль (технически вам нужен только один из них, но это зависит от используемых библиотек, поэтому установка всех четырех является более безопасным вариантом). Вы можете установить его в коде, как в Примере, или в качестве параметра командной строки с помощью-D или в качестве переменной среды, как написал Upendra.
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");
вопрос трассировка XML-запросов / ответов с помощью JAX-WS при возникновении ошибки для сведения.
вот решение в сыром коде (вместе взятые благодаря 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, установите
JAVA_OPTS
как показано ниже в Linux envs. Затем запустите Tomcat. Вы увидите запрос и ответ в .export JAVA_OPTS="$JAVA_OPTS -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true"
существуют различные способы сделать это программно, как описано в других ответах, но они довольно инвазивные механизмы. Однако, если вы знаете, что используете JAX-WS RI (он же "Metro"), то вы можете сделать это на уровне конфигурации. смотрите здесь инструкции о том, как это сделать. Не нужно возиться с вашим приложением.
// это решение предоставляет способ программно добавить обработчик в веб-службу clien без XML config
/ / Смотрите полный документ здесь:http://docs.oracle.com/cd/E17904_01//web.1111/e13734/handlers.htm#i222476
/ / создать новый класс, который реализует 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. Вы можете установить его в java или конфигурационном файле.
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 --------------------------------------
Inject
SOAPHandler
интерфейс конечной точки. мы можем отследить запрос SOAP и ответреализация SOAPHandler с 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);
декларативный добавлять
@HandlerChain(file = "handlers.xml")
аннотация к интерфейсу конечной точки.обработчики.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) { } }
я публикую новый ответ, так как у меня недостаточно репутации, чтобы прокомментировать тот, который предоставлен Антонио (см.: 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());
вам нужно реализовать javax.xml.ws.handler.LogicalHandler, этот обработчик затем должен быть указан в файле конфигурации обработчика, на который, в свою очередь, ссылается аннотация @HandlerChain в конечной точке службы (интерфейс или реализация). Затем вы можете либо вывести сообщение через систему.выход или регистратор в вашей реализации processMessage.
посмотреть
http://java.sun.com/mailers/techtips/enterprise/2006/TechTips_June06.html
вы можете попробовать поставить
ServletFilter
перед веб-сервисом и проверить запрос и ответ, идущий / возвращенный из службы.хотя вы специально не просили прокси, иногда я нахожу tcptrace достаточно, чтобы увидеть, что происходит на связи. Это простой инструмент, без установки, он показывает потоки данных и может писать в файл тоже.
на runtime вы можете просто выполнить
com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump = true
как свалка является общедоступным var, определенным в классе следующим образом
public static boolean dump;
правильно ли я понимаю, что вы хотите изменить/получить доступ к необработанному XML-сообщению?
Если это так, вы (или так как это пять лет, следующий парень), возможно, захотите взглянуть на интерфейс поставщика, который является частью JAXWS. Клиентский аналог выполняется с использованием класса "Dispatch". В любом случае, вам не нужно добавлять обработчики или перехватчики. Вы все еще можете, конечно. Недостатком этого способа является то, что вы полностью ответственны за создание SOAPMessage, но его легко, и если это то, что вы хотите(как и я) это прекрасно.
вот пример для серверной части (немного неуклюжий, это было просто для экспериментов) -
@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); } }
или вы можете использовать класс конечной точки для этого. Надеюсь, что это было полезно.
и о, если вы хотите, вам не нужно иметь дело с заголовками и прочее, если вы измените режим службы на полезную нагрузку(вы получите только тело Soap).
ответы, перечисленные здесь, которые помогут вам использовать
SOAPHandler
полностью правильно. Преимущество этого подхода заключается в том, что он будет работать с любой реализацией JAX-WS, поскольку SOAPHandler является частью спецификации JAX-WS. Однако проблема с SOAPHandler заключается в том, что он неявно пытается представить все XML-сообщение в памяти. Это может привести к огромному использованию памяти. Различные реализации JAX-WS добавили свои собственные обходные пути для этого. Если вы работаете с большими запросами или большими ответами, затем вам нужно изучить один из проприетарных подходов.поскольку вы спрашиваете о "том, что включено в JDK 1.5 или лучше", я отвечу относительно того, что официально известно как JAX-WS RI (aka Metro), который входит в JDK.
JAX-WS RI имеет специальное решение для этого, которое является очень эффективным с точки зрения использования памяти.
см https://javaee.github.io/metro/doc/user-guide/ch02.html#efficient-handlers-in-jax-ws-ri. К сожалению, эта ссылка теперь сломана, но вы можете найти ее на WayBack Machine. Я приведу основные моменты ниже:
люди метро еще в 2007 году ввел дополнительный тип обработчика,
MessageHandler<MessageHandlerContext>
, который является собственностью Metro. Это гораздо более эффективно, чемSOAPHandler<SOAPMessageContext>
поскольку он не пытается сделать представление DOM в памяти.вот ключевой текст из оригинальной статьи блог:
MessageHandler:
использование расширяемая структура обработчика, предоставляемая JAX-WS Спецификация и лучшая абстракция сообщения в RI, мы ввели новый обработчик называется
MessageHandler
для расширения вашего веб-сервиса приложения. MessageHandler похож на SOAPHandler, за исключением того, что реализации этого получает доступ кMessageHandlerContext
(an расширение MessageContext). Через MessageHandlerContext можно получите доступ к сообщению и обработайте его с помощью API сообщений. Как я положил в название блога, этот обработчик позволяет работать на сообщение, которое обеспечивает эффективные способы доступа / обработки сообщения не только DOM основанное сообщение. Программная модель обработчиков такая же и Обработчики сообщений могут быть смешаны со стандартными логическими и мыльными обработчиками. Я добавил образец в JAX-WS RI 2.1.3, показывающий использование MessageHandler для регистрации сообщений и вот фрагмент из примера: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 repo.
один из способов сделать это-не использовать ваш код, а использовать сетевые пакетные снифферы, такие как Etheral или WireShark, которые могут захватывать HTTP-пакет с сообщением XML в качестве полезной нагрузки, и вы можете продолжать записывать их в файл или около того.
но более сложный подход заключается в написании собственных обработчиков сообщений. Вы можете посмотреть на него здесь.
на самом деле. Если вы посмотрите на источники HttpClientTransport, вы заметите, что он также пишет сообщения на java.утиль.лесозаготовительный.Лесоруб. Это означает, что вы также можете видеть эти сообщения в своих журналах.
например, если вы используете Log4J2 все, что вам нужно сделать, это следующее:
- добавьте мост JUL-Log4J2 в свой путь к классу
- установите уровень трассировки для пакета com.sun.xml.internal.ws.transport.http.client.
- добавить - Джава.утиль.лесозаготовительный.менеджер=организация.апаш.лесозаготовительный.к log4j.июля.Системное свойство LogManager для вашего приложения запуск командной строки
после этих шагов вы начинаете видеть сообщения SOAP в своих журналах.