Трассировка XML-запросов / ответов с помощью JAX-WS


есть ли простой способ (он же: не используя прокси) получить доступ к необработанному запросу / ответу XML для веб-сервиса, опубликованного с помощью JAX-WS reference implementation (тот, который включен в JDK 1.5 и лучше) ? Возможность сделать это с помощью кода-это то, что мне нужно сделать. Просто зарегистрировав его в файле с помощью умных конфигураций ведения журнала, было бы неплохо, но достаточно.

Я знаю, что существуют другие более сложные и полные структуры, которые могут это сделать, но я хотел бы сохранить это так же просто, как возможно и axis, cxf и т. д. Все добавляют значительные накладные расходы, которых я хочу избежать.

спасибо!

15 136

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

вы можете попробовать поставить 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 в своих журналах.