简体   繁体   中英

How to transform SoapFault to SoapMessage via Interceptor in CXF?

I have web-service created and configured via Spring and CXF . See beans below:

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

As can you see I added schema validation to my web service. But CXF throws SoapFault when request is not corresponding with schema. I want to send to the client SoapMessage instead of SoapFault , that's why I added outFaultInterceptors .

My question is how to transform SoapFaul t to SoapMessage ? I've made few tries but I don't know how to implement outFaultInterceptor .

Probably you forgot to setup interceptor phase and its order in the interceptor chain.

Try something like this:

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

}

-EDIT-

Here is a unit test that works for me. It's a little bit tricky to be able to send POJOs to the output. I suppose it can be much more simpler if constructing DOM by yourself.

Interceptor

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

}

Request/Response 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>

Unit Test

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

}

Your interceptor should implement

org.apache.cxf.interceptor.Interceptor

The handleFault of handleMessage method will be called. The parameter is both cases will be an instance of

org.apache.cxf.message.Message

You can call on that

removeContent()

or

setContent()

to replace the message.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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