Отслеживание XML-запроса/ответов с помощью JAX-WS

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

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

Спасибо!

Ответ 1

Вот решение в сыром коде (вместе взятое благодаря соджонро и шамику):

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

Ответ 2

Следующие опции позволяют регистрировать все соединения с консолью (технически вам нужен только один из них, но это зависит от библиотек, которые вы используете, поэтому установка всех четырех параметров безопаснее). Вы можете установить его в коде, как в примере, или как параметр командной строки, используя -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");
System.setProperty("com.sun.xml.internal.ws.transport.http.HttpAdapter.dumpTreshold", "999999");

Подробнее см. вопрос Отслеживание XML-запроса/ответов с помощью JAX-WS при возникновении ошибки.

Ответ 3

Перед запуском tomcat установите JAVA_OPTS, как показано ниже в Linux envs. Затем запустите Tomcat. Вы увидите запрос и ответ в файле catalina.out.

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

Ответ 4

Установите следующие системные свойства, это включит ведение журнала 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");
        System.setProperty("com.sun.xml.internal.ws.transport.http.HttpAdapter.dumpTreshold", "999999");
    }

журналы консоли:

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

Ответ 5

Существуют различные способы сделать это программно, как описано в других ответах, но они довольно инвазивные механизмы. Однако, если вы знаете, что используете JAX-WS RI (ака "Metro" ), вы можете сделать это на уровне конфигурации. См. здесь инструкции о том, как это сделать. Не нужно путаться с вашим приложением.

Ответ 6

//Это решение предоставляет способ программно добавить обработчик к веб-службе clien без конфигурации XML

//См. полный документ здесь: 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);

Ответ 7

Введите 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) {
    }

}

Ответ 8

Я отправляю новый ответ, так как у меня недостаточно репутации, чтобы прокомментировать тот, который предоставлен Antonio (см.: fooobar.com/questions/48963/...).

Если вы хотите, чтобы сообщение 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());

Ответ 9

Вам нужно реализовать javax.xml.ws.handler.LogicalHandler, тогда этот обработчик должен быть указан в файле конфигурации обработчика, который, в свою очередь, ссылается на аннотацию @HandlerChain в конечной точке службы (интерфейс или реализация), Затем вы можете вывести сообщение через system.out или регистратор в своей реализации 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

Ответ 10

Вы можете попробовать поставить ServletFilter перед веб-сервисом и проверить запрос и ответ, отправляемые/возвращаемые из службы.

Хотя вы специально не запрашивали прокси-сервер, иногда я нахожу tcptrace, чтобы узнать, что происходит в соединении. Это простой инструмент, без установки, он показывает потоки данных и может также записывать в файл.

Ответ 11

В время выполнения вы можете просто выполнить

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

поскольку dump - это открытый var, определенный в классе следующим образом

public static boolean dump;

Ответ 12

Я правильно понимаю, что вы хотите изменить/получить доступ к необработанному XML-сообщению?

Если это так, вы (или, так как это пять лет, следующий парень), возможно, захотите взглянуть на интерфейс провайдера, который является частью JAXWS. Клиент-копия выполняется с использованием класса "Отправка". Во всяком случае, вам не нужно добавлять обработчиков или перехватчиков. Вы все еще МОЖЕТ, конечно. Недостатком является то, что вы ПОЛНОСТЬЮ ответственны за создание 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);
}
}

Или вы можете использовать для него класс Endpoint. Надеюсь, что это было полезно.

И о, если вы хотите, чтобы вам не нужно иметь дело с заголовками и т.д., если вы измените режим обслуживания на PAYLOAD (вы получите только тело мыла).

Ответ 13

Ниже перечислены ответы, которые помогут вам использовать 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. Я расскажу ниже:

The Metro people еще в 2007 году представил дополнительный тип обработчика MessageHandler<MessageHandlerContext>, который является собственностью Metro. Он намного эффективнее, чем SOAPHandler<SOAPMessageContext>, поскольку он не пытается делать представление DOM в памяти.

Вот ключевой текст из оригинальной статьи блога:

MessageHandler:

Использование расширяемой структуры Handler, предоставляемой JAX-WS Спецификация и лучшая абстракция сообщения в RI, мы ввели новый обработчик с именем MessageHandler для расширения веб-службы Приложения. MessageHandler аналогичен SOAPHandler, за исключением того, что его реализация получает доступ к MessageHandlerContext ( расширение MessageContext). Через MessageHandlerContext можно получить доступ к сообщению и обработать его с помощью API сообщений. Когда я вставляю название блога, этот обработчик позволяет вам работать с Message, который обеспечивает эффективные способы доступа/обработки сообщения не только DOM основанное сообщение. Модель программирования обработчиков такая же, и Обработчики сообщений могут быть смешаны со стандартными логическими и SOAP-обработчиками. Я добавил образец в 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.

Ответ 14

с помощью файлов конфигурации logback.xml вы можете сделать:

<logger name="com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe" level="trace" additivity="false">
    <appender-ref ref="STDOUT"/>
</logger>

Это запишет запрос и ответ следующим образом (в зависимости от вашей конфигурации для вывода журнала):

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

Ответ 15

Один из способов сделать это - не использовать ваш код, а использовать снифферы сетевого пакета, такие как Etheral или WireShark, которые могут захватывать HTTP-пакет с XML-сообщением в качестве полезной нагрузки, и вы можете вести их запись в файл или так.

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

Ответ 16

На самом деле. Если вы посмотрите на источники HttpClientTransport, вы заметите, что он также пишет сообщения в java.util.logging.Logger. Это означает, что вы также можете видеть эти сообщения в своих журналах.

Например, если вы используете Log4J2, все, что вам нужно сделать, это следующее:

  • добавьте мост JUL-Log4J2 в свой путь к классу.
  • установите уровень TRACE для пакета com.sun.xml.internal.ws.transport.http.client.
  • добавить -Djava.util.logging.manager = org.apache.logging.log4j.jul.LogManager системное свойство в вашу командную строку запуска приложения

После этих шагов вы начинаете видеть сообщения SOAP в своих журналах.

Ответ 17

Я пытался найти какую-то библиотеку фреймворков для регистрации запроса и ответа мыла веб-службы в течение пары дней. Код ниже исправил проблему для меня:

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