Programming

JAX-WS로 XML 요청 / 응답 추적

procodes 2020. 6. 2. 21:57
반응형

JAX-WS로 XML 요청 / 응답 추적


JAX-WS 참조 구현 (JDK 1.5 이상에 포함 된)으로 게시 된 웹 서비스에 대한 원시 요청 / 응답 XML에 액세스하는 쉬운 방법 (일명 프록시 사용 안 함)이 있습니까? 코드를 통해이를 수행 할 수 있어야합니다. 영리한 로깅 구성으로 파일에 기록하는 것만으로도 충분합니다.

나는 그렇게 할 수있는 더 복잡하고 완전한 다른 프레임 워크가 존재한다는 것을 알고 있지만 가능한 한 간단하게 유지하고 싶습니다. 축, cxf 등은 모두 피하고 싶은 상당한 오버 헤드를 추가합니다.

감사!


다음 옵션을 사용하면 콘솔에 대한 모든 통신을 로깅 할 수 있습니다 (기술적으로는이 중 하나만 필요하지만 사용하는 라이브러리에 따라 다르므로 4 개를 모두 설정하는 것이 더 안전한 옵션입니다). 예를 들어 코드에서 또는 -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");

자세한 내용은 오류 발생시 JAX-WS로 XML 요청 / 응답 추적 질문 을 참조하십시오.


다음은 원시 코드의 솔루션입니다 (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_OPTSLinux 환경에서 아래와 같이 설정 하십시오. 그런 다음 Tomcat을 시작하십시오. catalina.out파일 에 요청 및 응답이 표시 됩니다.

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

다음 시스템 속성을 설정하면 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
--------------------------------------

다른 답변에서 설명한 것처럼 프로그래밍 방식 으로이 작업을 수행하는 다양한 방법이 있지만 상당히 침습적 인 메커니즘입니다. 그러나 JAX-WS RI (일명 "Metro")를 사용중인 경우 구성 레벨에서이를 수행 할 수 있습니다. 이 작업을 수행하는 방법에 대한 지침은 여기를 참조하십시오 . 응용 프로그램을 망칠 필요가 없습니다.


//이 솔루션은 XML 설정없이 웹 서비스 clien에 핸들러를 프로그래밍 방식으로 추가하는 방법을 제공합니다.

// 여기에서 전체 문서를 참조하십시오 : 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);

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

}

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

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


ServletFilter웹 서비스 앞에 넣고 서비스에서 / 요청으로 이동하는 요청 및 응답을 검사 하려고 시도 할 수 있습니다.

프록시를 요청하지 않았지만 tcptrace 가 연결에서 발생하는 일을 볼 수있을 때가 있습니다. 간단한 도구이며 설치가 필요 없으며 데이터 스트림을 표시하고 파일에도 쓸 수 있습니다.


에서 런타임 간단히 실행할 수 있습니다

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

덤프는 다음과 같이 클래스에 정의 된 속성 public var이다

public static boolean dump;

원시 XML 메시지를 변경 / 액세스하려는 것으로 이해하고 있습니까?

그렇다면, 귀하 (또는이 사람이 5 세이므로 다음 사람)는 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);
}
}

또는 Endpoint 클래스를 사용할 수 있습니다. 도움이 되었기를 바랍니다.

그리고 헤더와 물건을 다룰 필요가없는 경우 서비스 모드를 PAYLOAD로 변경하면 비누 바디 만 얻습니다.


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

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.


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

참고URL : https://stackoverflow.com/questions/1945618/tracing-xml-request-responses-with-jax-ws

반응형