soapcxf

Implement SOAP "Gateway" with Apache CXF?


I have a service that uses requires SenderVouches authentication, which works fine for CXF-based Java applications. But now we need to integrate an .NET-based application that does not support SenderVouches in it's SOAP/WS-Security Stack.

I looked around a little bit an found this article explaining the underlying theory of the WS-Trust underlying the SenderVouches: https://www.xml.com/pub/a/ws/2003/06/24/ws-trust.html

In there I found this diagram which could be a solution to the problem we're facing, using a SOAP "Gateway". Does Apache CXF has support for implementing the Gateway part?

WS-Trust scenario components

I searched around, but most results are unrelated to implementing a SOAP Gateway.


Solution

  • I ended up using a combination of Membrane API Gateway and CXF:

    package com.example.wsproxy.membrane;
    
    import com.example.wsproxy.configmodel.ServiceProperties;
    import com.predic8.membrane.core.exchange.Exchange;
    import com.predic8.membrane.core.http.Response;
    import com.predic8.membrane.core.interceptor.AbstractInterceptor;
    import com.predic8.membrane.core.interceptor.Outcome;
    import lombok.Getter;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.cxf.jaxws.DispatchImpl;
    
    import javax.xml.soap.MessageFactory;
    import javax.xml.soap.SOAPException;
    import javax.xml.transform.Transformer;
    import javax.xml.transform.TransformerException;
    import javax.xml.transform.TransformerFactory;
    import javax.xml.transform.stream.StreamResult;
    import javax.xml.transform.stream.StreamSource;
    import javax.xml.ws.Dispatch;
    import javax.xml.ws.Service;
    import java.io.IOException;
    import java.io.PipedInputStream;
    import java.io.PipedOutputStream;
    
    @Slf4j
    public class MembraneCxfInterceptor extends AbstractInterceptor {
    
        protected final Dispatch<StreamSource> dispatch;
        protected final MessageFactory messageFactory;
    
        public MembraneCxfInterceptor(ServiceProperties serviceProperties) throws SOAPException {
            dispatch = setupDispatch(serviceProperties);
            messageFactory = MessageFactory.newInstance();
        }
    
        private Dispatch<StreamSource> setupDispatch(ServiceProperties serviceProperties){
    
            log.debug("Setting up dispatcher for {}", serviceProperties.getServiceName());
    
            Service service = Service.create(serviceProperties.getWsdlLocation(), serviceProperties.getServiceName());
            Dispatch<StreamSource> d = service.createDispatch(service.getPorts().next(), StreamSource.class, Service.Mode.MESSAGE);
    
            log.debug("Warming up CXF...");
    
            // warming up CXF
            ((DispatchImpl<StreamSource>) d).getClient().getConduitSelector().getEndpoint();
    
            return d;
        }
    
        @Override
        public Outcome handleRequest(Exchange exc) throws Exception {
    
            StreamSource request = new StreamSource(exc.getRequest().getBodyAsStream());
            StreamSource response = dispatch.invoke(request);
    
            @SuppressWarnings("squid:S2095") // closed by membrane
            PipedInputStream pis = new PipedInputStream();
            @SuppressWarnings("squid:S2095") // closed in finally block
            PipedOutputStream pos = new PipedOutputStream(pis);
    
            new Thread(() -> {
                try {
    
                    TransformerFactory tf = TransformerFactory.newInstance();
                    Transformer t = tf.newTransformer();
    
                    // TODO: Faults?
                    t.transform(response, new StreamResult(pos));
                } catch (TransformerException e) {
                    throw new CxfInterceptorTransformerException(e);
                } finally {
                    try {
                        pos.close();
                    } catch (IOException e) {
                        log.error("error closing piped output stream!", e);
                    }
                }
            }).start();
    
            exc.setResponse(Response.ok().body(pis, true).build());
    
            return Outcome.RETURN;
        }
    
        static class CxfInterceptorTransformerException extends RuntimeException {
            public CxfInterceptorTransformerException(TransformerException e){
                super(e);
            }
        }
    }
    
    package com.example.wsproxy.configmodel;
    
    import lombok.Data;
    
    import javax.xml.namespace.QName;
    import java.net.URL;
    
    @Data
    public class ServiceProperties {
        private URL wsdlLocation;
        private QName serviceName;
    }