Package org.jboss.soa.esb.actions.soap

Source Code of org.jboss.soa.esb.actions.soap.SOAPProcessor

/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA  02110-1301, USA.
*
* (C) 2005-2006, JBoss Inc.
*/
package org.jboss.soa.esb.actions.soap;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;

import org.jboss.internal.soa.esb.publish.Publish;
import org.jboss.internal.soa.esb.util.JBossDeployerUtil;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.actions.AbstractActionPipelineProcessor;
import org.jboss.soa.esb.actions.ActionProcessingException;
import org.jboss.soa.esb.actions.ActionUtils;
import org.jboss.soa.esb.actions.soap.adapter.JBossWSFactory;
import org.jboss.soa.esb.actions.soap.adapter.SOAPProcessorFactory;
import org.jboss.soa.esb.actions.soap.adapter.SOAPProcessorHttpServletRequest;
import org.jboss.soa.esb.actions.soap.adapter.SOAPProcessorHttpServletResponse;
import org.jboss.soa.esb.helpers.ConfigTree;
import org.jboss.soa.esb.listeners.message.MessageDeliverException;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.message.MessagePayloadProxy;
import org.jboss.soa.esb.message.Properties;
import org.jboss.soa.esb.message.ResponseHeader;
import org.jboss.soa.esb.message.ResponseStatus;
import org.jboss.soa.esb.message.body.content.BytesBody;
import org.jboss.wsf.spi.deployment.Deployment;
import org.jboss.wsf.spi.deployment.Endpoint;
import org.jboss.wsf.spi.invocation.EndpointAssociation;
import org.jboss.wsf.spi.invocation.RequestHandler;

/**
* JBoss Webservices SOAP Processor.
* <p/>
* This action supports invocation of a JBossWS hosted webservice endpoint through any JBossESB hosted
* listener.  This means the ESB can be used to expose Webservice endpoints for Services that don't
* already expose a Webservice endpoint.  You can do this by writing a thin Service Wrapper Webservice
* (e.g. a JSR 181 implementation) that wraps calls to the target Service (that doesn't have a Webservice endpoint),
* exposing that Service via endpoints (listeners) running on the ESB.  This also means that these Services
* are invocable over any transport channel supported by the ESB (http, ftp, jms etc).
*
* <h3>"ESB Message Aware" Webservice Endpoints</h3>
* Note that Webservice endpoints exposed via this action have direct access to the current
* JBossESB {@link org.jboss.soa.esb.message.Message} instance used to invoke this action's
* {@link #process(org.jboss.soa.esb.message.Message)} method.  It can access
* the current {@link org.jboss.soa.esb.message.Message} instance via the {@link #getMessage()} method
* and can change the {@link org.jboss.soa.esb.message.Message} instance via the
* {@link #setMessage(org.jboss.soa.esb.message.Message)} method.  This means that Webservice endpoints
* exposed via this action are "ESB Message Aware".
*
* <h3>Webservice Endpoint Deployment</h3>
* Any JBossWS Webservice endpoint can be exposed via ESB listeners using this action.  That includes endpoints that are deployed
* from inside (i.e. the Webservice .war is bundled inside the .esb) and outside (e.g. standalone Webservice .war deployments,
* Webservice .war deployments bundled inside a .ear) a .esb deployment.
*
* <div style="margin-left: 20">
* <h4>JAXB Introductions</h4>
* The native JBossWS SOAP stack uses JAXB to bind to and from SOAP.  This typically means that an unannotated typeset
* could not be used to build a JSR 181 endpoint on JBossWS.  To overcome this we use a JBossESB and JBossWS feature
* called "JAXB Introductions" which basically means you can define an XML configuration to "Introduce" the JAXB Annotations.
* For more on this, see the section on this action in the Message Action Guide.
* </div>
*
* <h3>Action Configuration</h3>
* The &lt;action ... /&gt; configuration for this action is very straightforward.  The action requires only one
* mandatory property value, which is the "jbossws-endpoint" property.  This property names the JBossWS endpoint
* that the SOAPProcessor is exposing (invoking).
*
* <pre>
* &lt;action name="ShippingProcessor" class="org.jboss.soa.esb.actions.soap.SOAPProcessor"&gt;
*     &lt;property name="<b>jbossws-endpoint</b>" value="<b>ABI_Shipping</b>"/&gt;
*     &lt;property name="<b>rewrite-endpoint-url</b>" value="true/false"/&gt; &lt;-- Optional. Default "true". --&gt;
* &lt;/action&gt;
* </pre>
*
* The optional "rewrite-endpoint-url" property is there to support load balancing on HTTP endpoints,
* in which case the Webservice endpoint container will have been configured to set the HTTP(S) endpoint address
* in the WSDL to that of the Load Balancer.  The "rewrite-endpoint-url" property can be used to turn off HTTP endpoint
* address rewriting in situations such as this.  It has no effect for non-HTTP protocols.
*
* <h3>Quickstarts</h3>
* A number of quickstarts that demonstrate how to use this action are available in the JBossESB
* distribution (samples/quickstarts).  See the "webservice_jbossws_adapter_01" and "webservice_bpel"
* quickstarts.
*
* @author <a href="mailto:tom.fennelly@jboss.com">tom.fennelly@jboss.com</a>
* @author <a href="mageshbk@jboss.com">Magesh Kumar B</a>
*/
@Publish(JBossWSWebserviceContractPublisher.class)
public class SOAPProcessor extends AbstractActionPipelineProcessor {

    private static ThreadLocal<Message> messageTL = new ThreadLocal<Message>();
    private String jbossws_endpoint;
    private String jbossws_context;
    private MessagePayloadProxy payloadProxy;
    private boolean httpResponseStatusEnabled;

    /**
     * Public constructor.
     * @param config Configuration.
     * @throws ConfigurationException "jbossws-endpoint" not specified.
     */
    public SOAPProcessor(ConfigTree config) throws ConfigurationException {
        jbossws_endpoint = config.getRequiredAttribute(WebServiceUtils.JBOSSWS_ENDPOINT);
        jbossws_context = config.getAttribute(WebServiceUtils.JBOSSWS_CONTEXT);
        payloadProxy = new MessagePayloadProxy(config,
                                               new String[] {BytesBody.BYTES_LOCATION, ActionUtils.POST_ACTION_DATA},
                                               new String[] {ActionUtils.POST_ACTION_DATA});
        httpResponseStatusEnabled = ResponseStatus.isHttpEnabled(config);
    }

    /**
     * Process the SOAP message.
     * <p/>
     * Invokes the JBossWS endpoint and writes the SOAP response back into the message payload.
     * @param message The ESB Aware (normalized) SOAP request message.
     * @return The SOAP response message.
     * @throws ActionProcessingException
     */
    public Message process(Message message) throws ActionProcessingException {
        Endpoint endpoint = WebServiceUtils.getServiceEndpoint(jbossws_endpoint, jbossws_context);
        byte[] soapMessage;

        if(endpoint == null) {
            throw new ActionProcessingException("Unknown Service Endpoint '" + jbossws_endpoint + "'.");
        }

        soapMessage = getSOAPMessagePayload(message);
        try {
            messageTL.set(message);

            RequestHandler requestHandler = endpoint.getRequestHandler();

            final Map<String, List<String>> headers = new HashMap<String, List<String>>() ;
            final Properties properties = message.getProperties() ;
            final String[] names = properties.getNames() ;
            for(final String name: names)
            {
                final Object value = properties.getProperty(name) ;
                if (value != null)
                {
                    String normalisedName = name.toLowerCase() ;

                    if ("content-type".equals(normalisedName))
                    {
                        if ("application/octet-stream".equals(value))
                        {
                            continue;
                        }
                        else
                        {
                            // CXF needs it to be case sensitive
                            normalisedName = "Content-Type";
                        }
                    }

                    final List<String> values = headers.get(normalisedName) ;
                    if (values == null)
                    {
                        final List<String> newValues = new ArrayList<String>() ;
                        newValues.add(value.toString()) ;
                        headers.put(normalisedName, newValues) ;
                    }
                    else
                    {
                        values.add(value.toString()) ;
                    }
                }
            }

            //CXF throws NPE in handler if content-length not set
            List<String> newValues = new ArrayList<String>();
            newValues.add(String.valueOf(soapMessage.length));
            headers.put("content-length", newValues);

            final String endpointAddress = endpoint.getAddress() ;
            String path = null ;
            if (endpointAddress != null)
            {
                try
                {
                    path = new URI(endpointAddress).getPath() ;
                }
                catch (final URISyntaxException urise) {} //ignore
            }
            if (path == null)
            {
                path = getHeaderValue(headers, "path") ;
            }

            final SOAPProcessorHttpServletRequest servletRequest = new SOAPProcessorHttpServletRequest(path, soapMessage, headers) ;
            final SOAPProcessorHttpServletResponse servletResponse = new SOAPProcessorHttpServletResponse() ;
            final ServletContext servletContext = SOAPProcessorFactory.getFactory().createServletContext(endpoint) ;

            EndpointAssociation.setEndpoint(endpoint);
            final ClassLoader old = Thread.currentThread().getContextClassLoader();
            try
            {
                initialiseContextClassLoader(endpoint);
                requestHandler.handleHttpRequest(endpoint, servletRequest, servletResponse, servletContext) ;
            }
            finally
            {
                Thread.currentThread().setContextClassLoader(old);
                EndpointAssociation.removeEndpoint();
            }
           
            // This may have been changed, make sure we get the current version.
            message = messageTL.get();
            final Properties responseProperties = message.getProperties() ;
            final String contentType = servletResponse.getContentType() ;
            final Map<String, List<String>> responseHeaders = servletResponse.getHeaders() ;
            // We deal with Content-Type below
            // HTTP Headers *should* be case-insensitive but not with JBR
            responseHeaders.remove("content-type") ;
          
            for(Map.Entry<String, List<String>> header: responseHeaders.entrySet())
            {
                // We can only deal with the first value in the list.
              // JBESB-2511
              new ResponseHeader(header.getKey(), header.getValue().get(0)).setPropertyNameThis(properties);
            }
            // JBESB-2761
            if (httpResponseStatusEnabled) {
              ResponseStatus.setHttpProperties(properties, servletResponse.getStatus(), servletResponse.getStatusMessage());
            }
           
            final byte[] responseData = servletResponse.getContent() ;
            if(contentType != null) {
                responseProperties.setProperty("Content-Type", new ResponseHeader("Content-Type", contentType));
            } else {
                responseProperties.setProperty("Content-Type", new ResponseHeader("Content-Type", "text/xml"));
            }
           
            if ((contentType != null) && contentType.startsWith("multipart/")) {
                payloadProxy.setPayload(message, responseData) ;
            } else {
                final String charset = servletResponse.getCharset() ;
                if (charset == null) {
                    payloadProxy.setPayload(message, new String(responseData)) ;
                } else {
                    payloadProxy.setPayload(message, new String(responseData, charset)) ;
                }
            }
        } catch (Exception ex) {
            throw new ActionProcessingException("Cannot process SOAP request", ex);
        } finally {
            messageTL.remove();
        }

        return message;
    }

    private void initialiseContextClassLoader(final Endpoint endpoint)
        throws ActionProcessingException
    {
        if (JBossDeployerUtil.isWebMetaDataPresent())
        {
            final ClassLoader tccl = JBossWSFactory.getFactory().getClassLoader(endpoint) ;
            if (tccl != null)
            {
                Thread.currentThread().setContextClassLoader(tccl);
                return ;
            }
            throw new ActionProcessingException("Could not locate ENC ClassLoader for service endpoint '" + jbossws_endpoint + "'.");
        }
    }

    private String getHeaderValue(final Map<String, List<String>> headers,
            final String header)
    {
        final List<String> values = headers.get(header) ;
        if (values != null)
        {
            return values.get(0) ;
        }
        return null ;
    }

    private byte[] getSOAPMessagePayload(Message message) throws ActionProcessingException {
        byte[] soapMessage;
        Object messagePayload;

        try {
            messagePayload = payloadProxy.getPayload(message);
        } catch (MessageDeliverException e) {
            throw new ActionProcessingException(e);
        }

        if(messagePayload instanceof byte[]) {
            soapMessage = (byte[])messagePayload;
        } else if(messagePayload instanceof String) {
            try {
                soapMessage = ((String)messagePayload).getBytes("UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new ActionProcessingException("Unable to decode SOAP message payload.", e);
            }
        } else {
            throw new ActionProcessingException("Unable to decode SOAP message payload.  Must be either a byte[] or java.lang.String.");
        }
        return soapMessage;
    }

    /**
     * Set the {@link org.jboss.soa.esb.message.Message} instance for this invocation context.
     * <p/>
     * This allows message aware Webservice endpoints modify the {@link org.jboss.soa.esb.message.Message}
     * instance for the current Action Processing Pipeline.
     *
     * @param message The new message instance.
     */
    public static void setMessage(final Message message) {
        messageTL.set(message);
    }

    /**
     * Get the {@link org.jboss.soa.esb.message.Message} instance for this invocation context.
     * 
     * @return The message instance.
     * @deprecated
     */
    public static Message getMessage() {
        Message message = messageTL.get();
        if (message == null)
            throw new RuntimeException("ESB message not found!");
        return message;
    }

    /**
     * Removes {@link org.jboss.soa.esb.message.Message} instance for this invocation context.
     * 
     */
    public static void removeMessage() {
        messageTL.remove();
    }
}
TOP

Related Classes of org.jboss.soa.esb.actions.soap.SOAPProcessor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.