简体   繁体   English

使用AXIS2创建的ADB-stub在客户端获取原始XML SOAP响应

[英]Getting raw XML SOAP-response on client side using ADB-stubs created by AXIS2

I access a SOAP service using ADB-stubs created by AXIS2. 我使用AXIS2创建的ADB-stub访问SOAP服务。 I would like to log the raw XML response of any Axis Fault, that is returned by the service. 我想记录服务返回的任何Axis Fault的原始XML响应。 I can catch those errors as "ServiceError". 我可以将这些错误视为“ServiceError”。 However, I do not find a way to retreive the raw XML (see example below). 但是,我找不到一种方法来检索原始XML(参见下面的示例)。

I found a way to access the raw XML request / response for regular processing, using getOMElement (see example below). 我找到了一种使用getOMElement访问原始XML请求/响应以进行常规处理的方法(参见下面的示例)。 However, this does not work for faults. 但是,这不适用于故障。

How do I get the raw XML fault using ADB stubs? 如何使用ADB存根获取原始XML错误?

Example Java code: 示例Java代码:

    public void testRequest(String URL) throws AxisFault {
        MyServiceStub myservice = new MyServiceStub(URL);
        MyRequest req = new MyRequest();
        try {
            TypeMyFunctionResponse response = myservice.myFunction(req);

            // logging full soap response
            System.out.println("SOAP Response: "
                    + response.getOMElement(null,
                            OMAbstractFactory.getOMFactory())
                            .toStringWithConsume());
        } catch (RemoteException e) {
            //...
        } catch (ServiceError e) {
            // how to get the raw xml?
        }
    }

Example fault response, that I would like to fetch and log: 示例故障响应,我想获取并记录:

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
    <soapenv:Body>
        <soapenv:Fault>
            <soapenv:Code>
                <soapenv:Value>soapenv:Receiver</soapenv:Value>
            </soapenv:Code>
            <soapenv:Reason>
                <soapenv:Text xml:lang="en-US">service error</soapenv:Text>
            </soapenv:Reason>
            <soapenv:Detail>
                <ns1:error xmlns:ns1="http://www.somehost.com/webservices/someservice">
                    <ns1:code>500</ns1:code>
                    <ns1:messageText>some fault message</ns1:messageText>
                </ns1:error>
            </soapenv:Detail>
        </soapenv:Fault>
    </soapenv:Body>
</soapenv:Envelope>

Below is what you probably are looking for, yourStub is what you generated via wsdl2java and use below lines after you make your request. 以下是您可能正在寻找的内容, yourStub是您通过wsdl2java生成的内容,并在您提出请求后使用以下行。 The message is set to lastOperation and sends it when you make the actual call: 消息设置为lastOperation并在您进行实际调用时发送:

request = yourStub._getServiceClient().getLastOperationContext().getMessageContext("Out")
              .getEnvelope().toString());

response = yourStub._getServiceClient().getLastOperationContext().getMessageContext("In")
              .getEnvelope().toString());

Hope that was helpful. 希望这很有帮助。

While this question is already answered well, I needed to do this earlier and had trouble finding a suitable answer that fit my constraints so I'm adding my own for posterity. 虽然这个问题已经得到了很好的回答,但我需要提前做到这一点,并且无法找到适合我的约束的合适答案,因此我为后代添加了自己的答案。

I needed to do this with Axis 2 version 1.4.1 for a recent project running JDK 1.4, which from what I'd read was not supported by the JAX-WS stubs. 对于最近运行JDK 1.4的项目,我需要使用Axis 2版本1.4.1执行此操作,而JAX-WS存根不支持我所阅读的内容。 I ended up keeping the ADB stubs while capturing the input by wrapping SoapBuilder with my own builder class, copying the input stream and passing the copy to the SoapBuilder: 我最后通过使用我自己的构建器类包装SoapBuilder来捕获输入时保留ADB存根,复制输入流并将副本传递给SoapBuilder:

public class SOAPBuilderWrapper implements Builder {
    private String lastResponse;

    private SOAPBuilder builder = new SOAPBuilder();

    private static final int BUFFER_SIZE = 8192;

    public OMElement processDocument(InputStream inputStream,
            String contentType, MessageContext messageContext) throws AxisFault {
        ByteArrayOutputStream copiedStream = new ByteArrayOutputStream();
        try {
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead = inputStream.read(buffer);
            while (bytesRead > -1) {
                copiedStream.write(buffer, 0, bytesRead);
                bytesRead = inputStream.read(buffer);
            }
            lastResponse = copiedStream.toString();

        } catch (IOException e) {
            throw new AxisFault("Can't read from input stream", e);
        }
        return builder.processDocument(
                new ByteArrayInputStream(copiedStream.toByteArray()),
                contentType, messageContext);
    }

    public String getLastResponse() {
        return lastResponse;
    }
}

For various reasons, configuring using axis2.xml was problematic, so the wrapper was added programmatically with the following: 由于各种原因,使用axis2.xml进行配置存在问题,因此以编程方式添加了包装器,其中包含以下内容:

SoapBuilderWrapper responseCaptor = new SoapBuilderWrapper();
AxisConfiguration axisConfig = stub._getServiceClient().getAxisConfiguration();
axisConfig.addMessageBuilder("application/soap+xml", responseCaptor);
axisConfig.addMessageBuilder("text/xml", responseCaptor);

This allows responses to be retrieved using responseCaptor.getLastResponse() after the service is invoked. 这允许在调用服务后使用responseCaptor.getLastResponse()检索响应。

Related to Ducane's reply: 与Ducane的回复有关:

response = yourStub._getServiceClient().getLastOperationContext().getMessageContext("In")
          .getEnvelope().toString());

fails with a com.ctc.wstx.exc.WstxIOException exception and the message: Attempted read on closed stream . com.ctc.wstx.exc.WstxIOException异常和消息失败: 在关闭的流上尝试读取

As suggested by joergl I changed my ADB-stubs to JAX-WS-ones using a "SOAPHandler" to log requests, responses and faults following the description here: http://www.mkyong.com/webservices/jax-ws/jax-ws-soap-handler-in-client-side/ 正如joergl所建议的,我使用“SOAPHandler”将我的ADB-stubs更改为JAX-WS-one,以记录请求,响应和故障,如下所示: http ://www.mkyong.com/webservices/jax-ws/jax -ws皂处理程序功能于客户端/

My handler looks like this for logging the nicely formated XML using log4j: 我的处理程序看起来像这样使用log4j记录格式良好的XML:

public class RequestResponseHandler  implements SOAPHandler<SOAPMessageContext> {

    private static Logger log = Logger.getLogger(RequestResponseHandler.class);
    private Transformer transformer = null;
    private DocumentBuilderFactory docBuilderFactory = null;
    private DocumentBuilder docBuilder = null;

    public RequestResponseHandler() {
        try {
            transformer = TransformerFactory.newInstance().newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "5");
            docBuilderFactory = DocumentBuilderFactory.newInstance();
            docBuilder = docBuilderFactory.newDocumentBuilder();
        } catch (TransformerConfigurationException
                | TransformerFactoryConfigurationError
                | ParserConfigurationException e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public void close(MessageContext arg0) {
    }

    @Override
    public boolean handleFault(SOAPMessageContext messageContext) {
        log(messageContext);
        return true;
    }

    @Override
    public boolean handleMessage(SOAPMessageContext messageContext) {
        log(messageContext);
        return true;
    }

    private void log(SOAPMessageContext messageContext) {
        String xml = "";
        SOAPMessage msg = messageContext.getMessage();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            msg.writeTo(out);
            xml = out.toString("UTF-8");
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }       

        String direction = "";
        Boolean outbound = (Boolean) messageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); 
        if (outbound) { 
            direction += "Request: \n"; 
        } else { 
            direction += "Response: \n";
        } 

        log.info(direction + getXMLprettyPrinted(xml));     
    }

    @Override
    public Set<QName> getHeaders() {
        return Collections.emptySet();
    }


    public String getXMLprettyPrinted(String xml) {

        if (transformer == null || docBuilder == null)
            return xml;

        InputSource ipXML = new InputSource(new StringReader(xml));
        Document doc;

        try {
            doc = docBuilder.parse(ipXML);
            StringWriter stringWriter = new StringWriter();
            StreamResult streamResult = new StreamResult(stringWriter);
            DOMSource domSource = new DOMSource(doc);
            transformer.transform(domSource, streamResult);
            return stringWriter.toString();
        } catch (SAXException | IOException | TransformerException e) {
            log.error(e.getMessage(), e);
            return xml;
        }
    }
}

In addition I wanted to reuse the raw XML in my application code. 另外,我想在我的应用程序代码中重用原始XML。 So I had to transfer this data from the SOAPHandler back to my client code. 所以我不得不将这些数据从SOAPHandler传回我的客户端代码。 How to this was not too obvious. 怎么做不太明显。 More about this problem can be found in this article: How to send additional fields to soap handler along with soapMessage? 有关此问题的更多信息,请参阅以下文章: 如何将其他字段与soapMessage一起发送到soap处理程序?

For Axis2, those who do not have luxury of changing implementation/ or do not want to use JAS-WS for xyz reasons, 对于Axis2,那些没有改变实现/或者不想因为xyz原因而使用JAS-WS的人,

Found @Ducane's useful 发现@Ducane很有用

 request = >yourStub._getServiceClient().getLastOperationContext().getMessageContext("Out") .getEnvelope().toString()); response = >yourStub._getServiceClient().getLastOperationContext().getMessageContext("In") .getEnvelope().toString()); 

As suggested in @dayer's answer 正如@ dayer的答案所示

 response = >yourStub._getServiceClient().getLastOperationContext().getMessageContext("In") .getEnvelope().toString()); 

fails with a com.ctc.wstx.exc.WstxIOException exception and the message: >Attempted read on closed stream. 失败并出现com.ctc.wstx.exc.WstxIOException异常并且消息:>尝试在已关闭的流上读取。

Not sure what is issue with "In" Message Lable, 不确定“In”Message Lable的问题是什么,

But while searching, found following JIRA ticket https://issues.apache.org/jira/browse/AXIS2-5469 which points to https://issues.apache.org/jira/browse/AXIS2-5202 And in discussion found one of the WA to solve this issue using following code, I am able to listen Response message for the soapRequest. 但在搜索时,发现以下JIRA票据https://issues.apache.org/jira/browse/AXIS2-5469指向https://issues.apache.org/jira/browse/AXIS2-5202并在讨论中找到一个使用以下代码解决此问题的WA,我能够侦听soapRequest的响应消息。

stub._getServiceClient().getAxisService().addMessageContextListener(
new MessageContextListener() {
    public void attachServiceContextEvent(ServiceContext sc,
        MessageContext mc) {}
    public void attachEnvelopeEvent(MessageContext mc) {
        try
        { mc.getEnvelope().cloneOMElement().serialize(System.out); }
        catch (XMLStreamException e) {}
    }
});

As here MessageContextListner is Argument-Defined Anonymous Inner Classes it will have access to all enclosing variables, So i just defined a string class variable as latestSoapResponse and stored response for further use. 因为MessageContextListner是Argument-Defined Anonymous Inner Classes,它可以访问所有封闭变量,所以我只是将字符串类变量定义为latestSoapResponse并存储响应以供进一步使用。

ByteArrayOutputStream baos = new ByteArrayOutputStream();
mc.getEnvelope().cloneOMElement().serialize(baos); 
latestSoapResponse=baos.toString();

Note that you need to add listener before generating soap request. 请注意,您需要在生成soap请求之前添加侦听器。 and Request MessageContext will only be available once you generate soap request. 和Request MessageContext仅在您生成soap请求后才可用。

Also those who are just want raw soap request response for debugging purpose may see answer from @Sanker, here to enable Apache commons logging using JVM arguments. 那些只是想要调试目的的原始SOAP请求响应的人可以从@Sanker看到答案, 这里是为了使用JVM参数启用Apache commons日志记录。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM