Chrisport
Chrisport

Reputation: 3146

Camel return simple SoapFault without CXF/Spring-ws

I created a proxy-camel which accepts SOAP (over HTTP) and RESTful requests and forwards them to the correct web service. The Camel is unaware of message-structure, it doesn't know the WSDL or anything, it just knows if it is SOAP or not according to a http header. There is no CXF endpoint.

Further it does some Processing. Exception can occur inside there, for example when a service is not found or the url is invalid. Is there an easy way to return a valid SOAPFault directly from this camel? I tried to write a simple processor which is called onException. It looks like this:

.choice().when().header("SOAP").processRef(ExceptionToSoapProcessor())

The Processor that should transform any Exception into a SOAPFault looks like this

@Override
public void process(Exchange exchange) throws Exception {
    Exception exception = (Exception) exchange.getProperty(Exchange.EXCEPTION_CAUGHT);
    Integer responseCode = (Integer) exchange.getOut().getHeader(Exchange.HTTP_RESPONSE_CODE);

    QName qName = SoapFault.FAULT_CODE_SERVER;
    if (responseCode != null && responseCode < 500) {
        qName = SoapFault.FAULT_CODE_CLIENT;
    }

    SoapFault fault = new SoapFault(exception.getMessage(), qName);
    Message outMessage = exchange.getOut();
    outMessage.setHeader(Message.RESPONSE_CODE, 500);
    outMessage.setFault(true);
    outMessage.setBody(fault);

    exchange.setException(null);
    exchange.removeProperty(Exchange.EXCEPTION_CAUGHT);
    exchange.setProperty(Exchange.EXCEPTION_HANDLED, true);
}

But now I don't understand how I will marshal it, the response looks like this:

org.apache.cxf.binding.soap.SoapFault: Unauthorized

("Unauthorized" is the actual message)

PS: I used the dataformat SOAP before, but as mentioned, I don't have any ServiceInterface in this Camel.

Upvotes: 4

Views: 3309

Answers (1)

Ralf
Ralf

Reputation: 6853

I would move the handling of the error scenario to an onException() block. That way you can "declare" some of the behavior, like marking the exception as handled. IMHO makes it a little cleaner.

Just returning the SOAP fault would not result in a valid SOAP response. You have to build the complete message structure. I don't think there is a type converter for SOAP messages to a text stream, so you have to marshal the SOAP response yourself.

This is the code I am using to do the job:

<onException>
    <exception>java.lang.Exception</exception>
    <handled>
        <constant>true</constant>
    </handled>
    <bean beanType="some.package.WSHelper" method="createSOAPFaultServerError" />
</onException>


public static String createSOAPFaultServerError(final Exception cause) {
    String result = null;
    LOG.error("Creating SOAP fault, hiding original cause from client:", cause);
    try {
        SOAPMessage message = MessageFactory.newInstance().createMessage();
        SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
        SOAPBody body = message.getSOAPBody();
        SOAPFault fault = body.addFault();
        fault.setFaultCode("Server");
        fault.setFaultString("Unexpected server error.");
        Detail detail = fault.addDetail();
        Name entryName = envelope.createName("message");
        DetailEntry entry = detail.addDetailEntry(entryName);
        entry.addTextNode("The server is not able to complete the request. Internal error.");

        result = soapMessage2String(message);
    } catch (Exception e) {
        LOG.error("Error creating SOAP Fault message", e);
    }

    return result;
}

private static String soapMessage2String(final SOAPMessage message) throws SOAPException, IOException {
    String result = null;

    ByteArrayOutputStream outStream = new ByteArrayOutputStream();
    message.writeTo(outStream);
    result = new String(outStream.toByteArray(), StandardCharsets.UTF_8);

    return result;
}

HTH

Upvotes: 6

Related Questions