Reputation: 2055
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
.
Upvotes: 6
Views: 21306
Reputation: 4712
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.
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;
}
}
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;
}
}
package foo.bar;
import javax.jws.WebService;
@WebService
public class InternalActServiceImpl {
public Echo echo(Echo val) {
return val;
}
}
<?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();
}
}
Upvotes: 16
Reputation: 7722
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.
Upvotes: 2