All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mule.module.cxf.CxfInboundMessageProcessor Maven / Gradle / Ivy

There is a newer version: 3.9.0
Show newest version
/*
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */
package org.mule.module.cxf;

import static org.mule.module.cxf.HttpRequestPropertyManager.getBasePath;
import static org.mule.module.cxf.HttpRequestPropertyManager.getRequestPath;
import static org.mule.module.cxf.HttpRequestPropertyManager.getScheme;
import org.mule.DefaultMuleEvent;
import org.mule.NonBlockingVoidMuleEvent;
import org.mule.OptimizedRequestContext;
import org.mule.VoidMuleEvent;
import org.mule.api.DefaultMuleException;
import org.mule.api.ExceptionPayload;
import org.mule.api.MessagingException;
import org.mule.api.MuleEvent;
import org.mule.api.MuleException;
import org.mule.api.MuleMessage;
import org.mule.api.NonBlockingSupported;
import org.mule.api.endpoint.EndpointNotFoundException;
import org.mule.api.lifecycle.InitialisationException;
import org.mule.api.lifecycle.Lifecycle;
import org.mule.api.transformer.TransformerException;
import org.mule.api.transport.OutputHandler;
import org.mule.api.transport.ReplyToHandler;
import org.mule.config.i18n.MessageFactory;
import org.mule.message.DefaultExceptionPayload;
import org.mule.module.cxf.support.DelegatingOutputStream;
import org.mule.module.xml.stax.StaxSource;
import org.mule.processor.AbstractInterceptingMessageProcessor;
import org.mule.transformer.types.DataTypeFactory;
import org.mule.transport.http.HttpConnector;
import org.mule.transport.http.HttpConstants;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.Bus;
import org.apache.cxf.binding.soap.SoapBindingConstants;
import org.apache.cxf.binding.soap.jms.interceptor.SoapJMSConstants;
import org.apache.cxf.continuations.SuspendedInvocationException;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.interceptor.StaxInEndingInterceptor;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.ExchangeImpl;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageImpl;
import org.apache.cxf.service.model.EndpointInfo;
import org.apache.cxf.staxutils.StaxUtils;
import org.apache.cxf.transport.DestinationFactory;
import org.apache.cxf.transport.DestinationFactoryManager;
import org.apache.cxf.transport.local.LocalConduit;
import org.apache.cxf.transports.http.QueryHandler;
import org.apache.cxf.wsdl.http.AddressType;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

/**
 * The CxfInboundMessageProcessor performs inbound CXF processing, sending an event
 * through the CXF service, then on to the next MessageProcessor. This processor gets
 * built by a MessageProcessorBuilder which is responsible for configuring it and the
 * {@link Server} that it dispatches to.
 */
public class CxfInboundMessageProcessor extends AbstractInterceptingMessageProcessor implements Lifecycle, NonBlockingSupported
{

    public static final String JMS_TRANSPORT = "jms";

    /**
     * logger used by this class
     */
    protected transient Log logger = LogFactory.getLog(getClass());

    protected Bus bus;

    // manager to the component
    protected String transportClass;

    protected Server server;

    private boolean proxy;

    private QueryHandler wsdlQueryHandler;

    @Override
    public void initialise() throws InitialisationException
    {
        if (bus == null)
        {
            throw new InitialisationException(
                MessageFactory.createStaticMessage("No CXF bus instance, this component has not been initialized properly."),
                this);
        }
    }

    @Override
    public void stop() throws MuleException
    {
        if (server != null)
        {
            server.stop();
        }
    }

    @Override
    public void start() throws MuleException
    {
        // Start the CXF Server
        if (server != null)
        {
            server.start();
        }
    }

    @Override
    public void dispose()
    {
        // nothing to do
    }

    @Override
    public MuleEvent process(MuleEvent event) throws MuleException
    {
        // if http request
        String requestPath = parseHttpRequestProperty(getRequestPath(event.getMessage()));
        try
        {
            if (requestPath.indexOf('?') > -1)
            {
                return generateWSDLOrXSD(event, requestPath);
            }
            else
            {
                return sendToDestination(event);
            }
        }
        catch (IOException e)
        {
            throw new DefaultMuleException(e);
        }
    }

    private String parseHttpRequestProperty(String request)
    {
        String uriBase = "";

        if (!(request.contains("?wsdl")) && (!(request.contains("?xsd"))))
        {
            int qIdx = request.indexOf('?');
            if (qIdx > -1)
            {
                uriBase = request.substring(0, qIdx);
            }
        }
        else
        {
            uriBase = request;
        }

        return uriBase;
    }

    protected MuleEvent generateWSDLOrXSD(MuleEvent event, String req)
        throws EndpointNotFoundException, IOException
    {
        // TODO: Is there a way to make this not so ugly?
        String ctxUri = event.getMessage().getInboundProperty(HttpConnector.HTTP_CONTEXT_PATH_PROPERTY);
        String wsdlUri = getUri(event);
        String serviceUri = wsdlUri.substring(0, wsdlUri.indexOf('?'));

        EndpointInfo ei = getServer().getEndpoint().getEndpointInfo();

        if (serviceUri != null)
        {
            ei.setAddress(serviceUri);

            if (ei.getExtensor(AddressType.class) != null)
            {
                ei.getExtensor(AddressType.class).setLocation(serviceUri);
            }
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        String ct = null;

        if (wsdlQueryHandler.isRecognizedQuery(wsdlUri, ctxUri, ei))
        {
            ct = wsdlQueryHandler.getResponseContentType(wsdlUri, ctxUri);
            wsdlQueryHandler.writeResponse(wsdlUri, ctxUri, ei, out);
            out.flush();
        }

        String msg;
        if (ct == null)
        {
            ct = "text/plain";
            msg = "No query handler found for URL.";
        }
        else
        {
            msg = out.toString();
        }

        event.getMessage().setPayload(msg);
        event.getMessage().setOutboundProperty(HttpConstants.HEADER_CONTENT_TYPE, ct);
        return event;
    }

    private String getUri(MuleEvent event)
    {
        String scheme = getScheme(event);
        String host = event.getMessage().getInboundProperty("Host");
        String ctx = getRequestPath(event.getMessage());

        return scheme + "://" + host + ctx;
    }

    protected MuleEvent sendToDestination(MuleEvent event) throws MuleException, IOException
    {
        try
        {
            final Exchange exchange = new ExchangeImpl();

            final MuleEvent originalEvent = event;
            if (event.isAllowNonBlocking())
            {
                final ReplyToHandler originalReplyToHandler = event.getReplyToHandler();

                event = new DefaultMuleEvent(event, new ReplyToHandler()
                {
                    @Override
                    public void processReplyTo(MuleEvent responseEvent, MuleMessage returnMessage, Object replyTo) throws MuleException
                    {
                        try
                        {
                            // CXF execution chain was suspended, so we need to resume it.
                            // The MuleInvoker component will be recalled, by using the CxfConstants.NON_BLOCKING_RESPONSE flag we force using the received response event instead of re-invoke the flow
                            exchange.put(CxfConstants.MULE_EVENT, responseEvent);
                            exchange.put(CxfConstants.NON_BLOCKING_RESPONSE, true);
                            exchange.getInMessage().getInterceptorChain().resume();

                            // Process the response
                            responseEvent = (MuleEvent) exchange.get(CxfConstants.MULE_EVENT);
                            responseEvent = processResponse(originalEvent, exchange, responseEvent);

                            // Continue the non blocking execution
                            originalReplyToHandler.processReplyTo(responseEvent, responseEvent.getMessage(), replyTo);
                        }
                        catch (Exception e)
                        {
                            ExceptionPayload exceptionPayload = new DefaultExceptionPayload(e);
                            responseEvent.getMessage().setExceptionPayload(exceptionPayload);
                            returnMessage.setOutboundProperty(HttpConnector.HTTP_STATUS_PROPERTY, 500);
                            responseEvent.setMessage(returnMessage);
                            processExceptionReplyTo(new MessagingException(responseEvent, e), replyTo);
                        }
                    }

                    @Override
                    public void processExceptionReplyTo(MessagingException exception, Object replyTo)
                    {
                        originalReplyToHandler.processExceptionReplyTo(exception, replyTo);
                    }
                });
                // Update RequestContext ThreadLocal for backwards compatibility
                OptimizedRequestContext.unsafeSetEvent(event);
            }

            MuleEvent responseEvent = sendThroughCxf(event, exchange);

            if (responseEvent == null || !responseEvent.equals(NonBlockingVoidMuleEvent.getInstance()))
            {
                return processResponse(event, exchange, responseEvent);
            }

            return responseEvent;
        }
        catch (MuleException e)
        {
            logger.warn("Could not dispatch message to CXF!", e);
            throw e;
        }
    }

    private MuleEvent sendThroughCxf(MuleEvent event, Exchange exchange) throws TransformerException, IOException
    {
        final MessageImpl m = new MessageImpl();
        m.setExchange(exchange);
        final MuleMessage muleReqMsg = event.getMessage();
        String method = muleReqMsg.getInboundProperty(HttpConnector.HTTP_METHOD_PROPERTY);

        String ct = muleReqMsg.getInboundProperty(HttpConstants.HEADER_CONTENT_TYPE);
        if (ct != null)
        {
            m.put(Message.CONTENT_TYPE, ct);
        }

        String path = muleReqMsg.getInboundProperty(HttpConnector.HTTP_REQUEST_PATH_PROPERTY);
        if (path == null)
        {
            path = "";
        }

        if (method != null)
        {
            m.put(Message.HTTP_REQUEST_METHOD, method);
            m.put(Message.PATH_INFO, path);
            String basePath = getBasePath(muleReqMsg);
            m.put(Message.BASE_PATH, basePath);

            method = method.toUpperCase();
        }

        if (!"GET".equals(method))
        {
            Object payload = event.getMessage().getPayload();

            setPayload(event, m, payload);
        }

        // TODO: Not sure if this is 100% correct - DBD
        String soapAction = getSoapAction(event.getMessage());
        m.put(SoapConstants.SOAP_ACTION_PROPERTY_CAPS, soapAction);

        // For MULE-6829
        if (shouldSoapActionHeader())
        {
            // Add protocol headers with the soap action so that the SoapActionInInterceptor can find them if it is soap v1.1
            Map> protocolHeaders = new HashMap>();
            if (soapAction != null && !soapAction.isEmpty())
            {
                List soapActions = new ArrayList();
                // An HTTP client MUST use [SOAPAction] header field when issuing a SOAP HTTP Request.
                // The header field value of empty string ("") means that the intent of the SOAP message is provided by the HTTP Request-URI.
                // No value means that there is no indication of the intent of the message.
                soapActions.add(soapAction);
                protocolHeaders.put(SoapBindingConstants.SOAP_ACTION, soapActions);
            }

            String eventRequestUri = event.getMessageSourceURI().toString();
            if (eventRequestUri.startsWith(JMS_TRANSPORT))
            {
                String contentType = muleReqMsg.getInboundProperty(SoapJMSConstants.CONTENTTYPE_FIELD);
                if (contentType == null)
                {
                    contentType = "text/xml";
                }
                protocolHeaders.put(SoapJMSConstants.CONTENTTYPE_FIELD, Collections.singletonList(contentType));

                String requestUri = muleReqMsg.getInboundProperty(SoapJMSConstants.REQUESTURI_FIELD);
                if (requestUri == null)
                {
                    requestUri = eventRequestUri;
                }
                protocolHeaders.put(SoapJMSConstants.REQUESTURI_FIELD, Collections.singletonList(requestUri));
            }

            m.put(Message.PROTOCOL_HEADERS, protocolHeaders);
        }

        org.apache.cxf.transport.Destination d;

        if (server != null)
        {
            d = server.getDestination();
        }
        else
        {
            String serviceUri = getUri(event);

            DestinationFactoryManager dfm = bus.getExtension(DestinationFactoryManager.class);
            DestinationFactory df = dfm.getDestinationFactoryForUri(serviceUri);

            EndpointInfo ei = new EndpointInfo();
            ei.setAddress(serviceUri);
            d = df.getDestination(ei);
        }

        // Set up a listener for the response
        m.put(LocalConduit.DIRECT_DISPATCH, Boolean.TRUE);
        m.setDestination(d);

        // mule will close the stream so don't let cxf, otherwise cxf will close it too early
        exchange.put(StaxInEndingInterceptor.STAX_IN_NOCLOSE, Boolean.TRUE);
        exchange.setInMessage(m);

        // if there is a fault, then we need an event in here because we won't
        // have a responseEvent from the MuleInvoker
        exchange.put(CxfConstants.MULE_EVENT, event);

        // invoke the actual web service up until right before we serialize the
        // response
        try
        {
            d.getMessageObserver().onMessage(m);
        }
        catch (SuspendedInvocationException e)
        {
            MuleEvent responseEvent = (MuleEvent) exchange.get(CxfConstants.MULE_EVENT);

            if (responseEvent == null || !responseEvent.equals(NonBlockingVoidMuleEvent.getInstance()))
            {
                throw e;
            }
        }

        // get the response event
        return (MuleEvent) exchange.get(CxfConstants.MULE_EVENT);
    }

    private MuleEvent processResponse(MuleEvent event, Exchange exchange, MuleEvent responseEvent)
    {
        // If there isn't one, there was probably a fault, so use the original event
        if (responseEvent == null || VoidMuleEvent.getInstance().equals(responseEvent) || !event.getExchangePattern().hasResponse())
        {
            return null;
        }

        MuleMessage muleResMsg = responseEvent.getMessage();
        muleResMsg.setPayload(getResponseOutputHandler(exchange));

        // Handle a fault if there is one.
        Message faultMsg = exchange.getOutFaultMessage();
        if (faultMsg != null)
        {
            Exception ex = faultMsg.getContent(Exception.class);
            if (ex != null)
            {
                ExceptionPayload exceptionPayload = new DefaultExceptionPayload(ex);
                event.getMessage().setExceptionPayload(exceptionPayload);
                muleResMsg.setOutboundProperty(HttpConnector.HTTP_STATUS_PROPERTY, 500);
            }
        }

        return responseEvent;
    }

    protected boolean shouldSoapActionHeader()
    {
        // Only add soap headers if we can validate the bindings. if not, cxf will throw a fault in SoapActionInInterceptor
        // we cannot validate the bindings if we're using mule's pass-through invoke proxy service 
        boolean isGenericProxy = "http://support.cxf.module.mule.org/"
                .equals(getServer().getEndpoint().getService().getName().getNamespaceURI()) && 
                "ProxyService".equals(getServer().getEndpoint().getService().getName().getLocalPart());
        return !isGenericProxy;
    }

    @Override
    public MuleEvent processNext(MuleEvent event) throws MuleException
    {
        return super.processNext(event);
    }

    @Deprecated
    protected OutputHandler getRessponseOutputHandler(final MessageImpl m)
    {
        return getResponseOutputHandler(m);
    }
    protected OutputHandler getResponseOutputHandler(final MessageImpl m)
    {
        return getResponseOutputHandler(m.getExchange());
    }

    protected OutputHandler getResponseOutputHandler(final Exchange exchange)
    {
        OutputHandler outputHandler = new OutputHandler()
        {
            @Override
            public void write(MuleEvent event, OutputStream out) throws IOException
            {
                Message outFaultMessage = exchange.getOutFaultMessage();
                Message outMessage = exchange.getOutMessage();

                Message contentMsg = null;
                if (outFaultMessage != null && outFaultMessage.getContent(OutputStream.class) != null)
                {
                    contentMsg = outFaultMessage;
                }
                else if (outMessage != null)
                {
                    contentMsg = outMessage;
                }

                if (contentMsg == null)
                {
                    return;
                }

                DelegatingOutputStream delegate = contentMsg.getContent(DelegatingOutputStream.class);
                if (delegate.getOutputStream() instanceof ByteArrayOutputStream)
                {
                    out.write(((ByteArrayOutputStream) delegate.getOutputStream()).toByteArray());
                }
                delegate.setOutputStream(out);

                out.flush();

                contentMsg.getInterceptorChain().resume();
            }

        };
        return outputHandler;
    }

    private void setPayload(MuleEvent ctx, final MessageImpl m, Object payload) throws TransformerException
    {
        if (payload instanceof InputStream)
        {
            m.put(Message.ENCODING, ctx.getEncoding());
            m.setContent(InputStream.class, payload);
        }
        else if (payload instanceof Reader)
        {
            m.setContent(XMLStreamReader.class, StaxUtils.createXMLStreamReader((Reader) payload));
        }
        else if (payload instanceof byte[])
        {
            m.setContent(InputStream.class, new ByteArrayInputStream((byte[]) payload));
        }
        else if (payload instanceof StaxSource)
        {
            m.setContent(XMLStreamReader.class, ((StaxSource) payload).getXMLStreamReader());
        }
        else if (payload instanceof Source)
        {
            m.setContent(XMLStreamReader.class, StaxUtils.createXMLStreamReader((Source) payload));
        }
        else if (payload instanceof XMLStreamReader)
        {
            m.setContent(XMLStreamReader.class, payload);
        }
        else if (payload instanceof Document)
        {
            DOMSource source = new DOMSource((Node) payload);
            m.setContent(XMLStreamReader.class, StaxUtils.createXMLStreamReader(source));
        }
        else
        {
            InputStream is = ctx.transformMessage(DataTypeFactory.create(InputStream.class));
            m.put(Message.ENCODING, ctx.getEncoding());
            m.setContent(InputStream.class, is);
        }
    }

    /**
     * Gets the stream representation of the current message.
     * 
     * @param context the event context
     * @return The inputstream for the current message
     * @throws MuleException
     */
    protected InputStream getMessageStream(MuleEvent context) throws MuleException
    {
        InputStream is;
        Object eventMsgPayload = context.getMessage().getPayload();

        if (eventMsgPayload instanceof InputStream)
        {
            is = (InputStream) eventMsgPayload;
        }
        else
        {
            is = context.transformMessage(DataTypeFactory.create(InputStream.class));
        }
        return is;
    }

    protected String getSoapAction(MuleMessage message)
    {
        String action = message.getInboundProperty(SoapConstants.SOAP_ACTION_PROPERTY);

        if (action != null && action.startsWith("\"") && action.endsWith("\"") && action.length() >= 2)
        {
            action = action.substring(1, action.length() - 1);
        }

        return action;
    }

    public Bus getBus()
    {
        return bus;
    }

    public void setBus(Bus bus)
    {
        this.bus = bus;
    }

    public Server getServer()
    {
        return server;
    }

    public void setServer(Server server)
    {
        this.server = server;
    }

    public void setProxy(boolean proxy)
    {
        this.proxy = proxy;
    }

    public boolean isProxy()
    {
        return proxy;
    }

    public void setWSDLQueryHandler(QueryHandler wsdlQueryHandler)
    {
        this.wsdlQueryHandler = wsdlQueryHandler;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy