如何通过CXF中的Interceptor将SoapFault转换为SoapMessage?

时间:2011-11-09 14:28:41

标签: java web-services jax-ws cxf

我通过SpringCXF创建并配置了网络服务。见下面的bean:

<?xml version="1.0" encoding="UTF-8"?>
<beans <!-- ommited -->>
    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

    <bean id="internalActService" class="package.InternalActServiceImpl" />

    <jaxws:endpoint implementor="#internalActService" address="/InternalActService">
        <jaxws:properties>
            <entry key="schema-validation-enabled" value="true" />
        </jaxws:properties>

        <jaxws:outFaultInterceptors>
            <bean class="package.InternalActServiceFaultOutInterceptor" />
        </jaxws:outFaultInterceptors>
    </jaxws:endpoint>
</beans>

您可以看到我在我的网络服务中添加了架构验证。但是当请求与架构不对应时,CXF会抛出SoapFault。 我想发送给客户SoapMessage而不是SoapFault,这就是我添加outFaultInterceptors的原因。

我的问题是如何将SoapFaul t转换为SoapMessage?我做了几次尝试,但我不知道如何实现outFaultInterceptor

2 个答案:

答案 0 :(得分:16)

可能你忘了设置拦截器阶段及其在拦截器链中的顺序。

尝试这样的事情:

package org.foo.bar;

import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.interceptor.AttachmentOutInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.interceptor.StaxOutInterceptor;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageContentsList;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;

import java.util.Arrays;
import java.util.List;

public class InternalActServiceFaultOutInterceptor extends AbstractSoapInterceptor {

    public InternalActServiceFaultOutInterceptor() {
        super(Phase.PRE_STREAM);
        addBefore(Arrays.asList(StaxOutInterceptor.class.getName(), AttachmentOutInterceptor.class.getName()));
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        Exception exception = message.getContent(Exception.class);
        if(exception != null) {
            message.getExchange().put(Exception.class, null);

            for(Class<?> contentFormat : message.getContentFormats()) {
                message.setContent(contentFormat, null);
            }

            message.setContent(List.class, new MessageContentsList(createSoapMessage(RegisterDocumentResponse.class)));
        }
    }

    protected <T> T createSoapMessage(Class<T> messageType) {
        // create your message
        return null;
    }

}

- 编辑 -

这是一个适合我的单元测试。能够将POJO发送到输出端有点棘手。我想如果自己构建DOM会更简单。

拦截

package foo.bar;

import java.util.Arrays;
import java.util.List;

import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.interceptor.InterceptorChain;
import org.apache.cxf.interceptor.OutgoingChainInterceptor;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageContentsList;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.service.model.BindingMessageInfo;
import org.apache.cxf.service.model.BindingOperationInfo;
import org.apache.cxf.service.model.MessageInfo;
import org.apache.cxf.service.model.OperationInfo;
import org.apache.cxf.service.model.ServiceModelUtil;
import org.apache.cxf.ws.policy.PolicyOutInterceptor;

public class InternalActServiceFaultOutInterceptor extends AbstractSoapInterceptor {

    public InternalActServiceFaultOutInterceptor() {
        super(Phase.SETUP);
        addBefore(Arrays.asList(PolicyOutInterceptor.class.getName()));
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        Exchange exchange = message.getExchange();

        resetOrigInterceptorChain(message);
        resetFault(exchange);

        Message outMessage = createOutMessage(exchange);

        InterceptorChain chain = prepareNewInterceptorChain(exchange);
        chain.doIntercept(outMessage);
    }

    private InterceptorChain prepareNewInterceptorChain(Exchange exchange) {
        Message message = exchange.getOutMessage();
        bind(message);

        InterceptorChain chain = OutgoingChainInterceptor.getOutInterceptorChain(exchange);
        message.setInterceptorChain(chain);

        return chain;
    }

    private Message createOutMessage(Exchange exchange) {
        Endpoint ep = exchange.get(Endpoint.class);

        Message outMessage = ep.getBinding().createMessage();
        outMessage.setExchange(exchange);
        outMessage.setContent(List.class, new MessageContentsList(createSoapMessage()));

        exchange.setOutMessage(outMessage);
        return outMessage;
    }

    private void resetFault(Exchange exchange) {
        exchange.put(Exception.class, null);
    }

    private void resetOrigInterceptorChain(SoapMessage message) {
        InterceptorChain chain = message.getInterceptorChain();
        for(Interceptor<?> interceptor : chain) {
            chain.remove(interceptor);
        }
        chain.reset();
    }

    private void bind(Message message) {
        Exchange exchange = message.getExchange();
        BindingOperationInfo bop = unwrap(message.getExchange().getBindingOperationInfo());

        message.put(MessageInfo.class, bop.getOperationInfo().getOutput());
        message.put(BindingMessageInfo.class, bop.getOutput());

        bop = unwrap(ServiceModelUtil.getOperationForWrapperElement(exchange, bop.getName(), false));

        exchange.put(BindingOperationInfo.class, bop);
        if (bop != null) {
            exchange.put(BindingOperationInfo.class, bop);
            exchange.put(OperationInfo.class, bop.getOperationInfo());
        }
    }

    private BindingOperationInfo unwrap(BindingOperationInfo bop) {
        while(bop.getUnwrappedOperation() != null) {
            bop = bop.getUnwrappedOperation();
            return bop;
        }
        return bop;
    }

    protected Echo createSoapMessage() {
        Echo e = new Echo();
        e.setValue("Bye World!");
        return e;
    }

}

请求/响应POJO

package foo.bar;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "EchoType")
public class Echo {
    private String value;
    public String getValue() {
        return value;
    }
    public void setValue(String value) {
        this.value = value;
    }
}

的WebService

package foo.bar;

import javax.jws.WebService;

@WebService
public class InternalActServiceImpl {
    public Echo echo(Echo val) {
        return val;
    }
}

Spring Context:CxfInterceptorTest-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
">

    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-http-jetty.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-jaxws.xml" />

    <bean id="internalActService" class="foo.bar.InternalActServiceImpl" />

    <jaxws:endpoint implementor="#internalActService" address="http://localhost:9080/InternalActService">
        <jaxws:properties>
            <entry key="schema-validation-enabled" value="true" />
        </jaxws:properties>
        <jaxws:outFaultInterceptors>
            <bean class="foo.bar.InternalActServiceFaultOutInterceptor" />
        </jaxws:outFaultInterceptors>
    </jaxws:endpoint>

单元测试

package foo.bar;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class CxfInterceptorTest {

    private static final String REQ =
            "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:bar=\"http://bar.foo/\">\r\n" +
            "   <soapenv:Header/>\r\n" +
            "   <soapenv:Body>\r\n" +
            "      <bar:echo>\r\n" +
            "         <arg0>\r\n" +
            "            <value1>Hello World</value1>\r\n" +
            "         </arg0>\r\n" +
            "      </bar:echo>\r\n" +
            "   </soapenv:Body>\r\n" +
            "</soapenv:Envelope>";

    @Test
    public void validate() throws Exception {
        String s = call();
        Assert.assertTrue(s.contains("Bye World!"));
    }

    private String call() throws Exception {
        URL url = new URL("http://localhost:9080/InternalActService");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

        conn.setDoOutput(true);
        conn.setInstanceFollowRedirects(true);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "text/xml;charset=UTF-8");
        conn.setRequestProperty("SOAPAction", "");

        OutputStream os = conn.getOutputStream();
        os.write(REQ.getBytes());
        os.flush();
        os.close();

        final int buffSize = 1024;
        byte[] buff = new byte[1024];
        InputStream is = conn.getInputStream();

        StringBuilder builder = new StringBuilder(buffSize);
        for(int readBytes = -1; (readBytes = is.read(buff, 0, buffSize)) != -1; ) {
            builder.append(new String(buff, 0, readBytes));
        }

        is.close();

        return builder.toString();
    }

}

答案 1 :(得分:2)

你的拦截器应该实现

org.apache.cxf.interceptor.Interceptor

将调用handleMessage方法的handleFault。参数是两种情况都是

的实例

org.apache.cxf.message.Message

你可以打电话

removeContent()

setContent()

替换消息。