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

org.jibx.ws.http.servlet.WsServletDelegate Maven / Gradle / Ivy

/*
 * Copyright (c) 2007-2009, Sosnoski Software Associates Limited. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
 * disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 * following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of
 * JiBX nor the names of its contributors may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.jibx.ws.http.servlet;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.ParseException;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jibx.runtime.IXMLReader;
import org.jibx.runtime.IXMLWriter;
import org.jibx.runtime.impl.InByteBuffer;
import org.jibx.runtime.impl.OutByteBuffer;
import org.jibx.ws.WsConfigurationException;
import org.jibx.ws.WsException;
import org.jibx.ws.codec.CodecDirectory;
import org.jibx.ws.codec.CodecPool;
import org.jibx.ws.codec.MediaType;
import org.jibx.ws.codec.XmlCodec;
import org.jibx.ws.io.XmlOptions;
import org.jibx.ws.server.MediaTypeMapper;
import org.jibx.ws.server.Service;
import org.jibx.ws.transport.InConnection;
import org.jibx.ws.transport.OutConnectionBase;
import org.jibx.ws.transport.OutServerConnection;
import org.jibx.ws.transport.StreamBufferInPool;
import org.jibx.ws.transport.StreamBufferOutPool;
import org.jibx.ws.transport.interceptor.InputStreamInterceptor;
import org.jibx.ws.transport.interceptor.OutputStreamInterceptor;
import org.jibx.ws.wsdl.WsdlProvider;

/**
 * Web service request handler servlet. The configuration information for this servlet is obtained from one or more
 * service definition files located with the WEB-INF directory of the web application. The particular service definition
 * files handled by an instance of this servlet are configured as initialization parameters for the servlet. If the
 * servlet is invoked with any path information in the request the path information is used to identify the particular
 * service being requested. As a special case, the request parameter "?WSDL" is recognized as a request for the WSDL
 * service description.
 * 
 * @author Dennis M. Sosnoski
 */
public final class WsServletDelegate
{
    // TODO: make this a configuration parameter
    private static final int BUFFER_SIZE = 8192;

    /** The key of the character set parameter, which can be used as a parameter to the CONTENT_TYPE header field. */  
    private static final String CHARSET_KEY = "charset";
    
    private static final Log logger = LogFactory.getLog(WsServletDelegate.class);

    /** Pool of codecs used for input and output. Access to this pool must be synchronized on the pool object. */
    private static final CodecPool s_codecPool = new CodecPool();

    /** Input byte buffer pool. Access to this pool must be synchronized on the {@link #s_codecPool} object. */
    private static final StreamBufferInPool s_inBufferCache = new StreamBufferInPool(BUFFER_SIZE);

    /** Output byte buffer pool. Access to this pool must be synchronized on the {@link #s_codecPool} object. */
    private static final StreamBufferOutPool s_outBufferCache = new StreamBufferOutPool(BUFFER_SIZE);

    /** Maps the incoming request to a service. */
    private ServiceMapper m_serviceMapper;
    
    /**
     * Sets the {@link ServiceMapper} which will determine which service to call based on the incoming request.
     * 
     * @param mapper the service mapper
     */
    void setServiceMapper(ServiceMapper mapper) {
        m_serviceMapper = mapper;
    }

    /**
     * POST request handler. This processes the incoming request message and generates the response.
     * 
     * @param req servlet request information
     * @param rsp servlet response information
     * @exception ServletException on message content or operational error
     * @exception IOException on error reading or writing
     */
    public void doPost(HttpServletRequest req, HttpServletResponse rsp) throws ServletException, IOException {
        logger.debug("Entered WsServletDelegate.doPost()");
        Service serv = null;
        XmlCodec incodec = null;
        XmlCodec outcodec = null;
        InByteBuffer inbuff = null;
        OutByteBuffer outbuff = null;
        try {
            // make sure we have a service instance
            serv = m_serviceMapper.getServiceInstance(req);
            if (serv == null) {
                rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
            } else {
                MediaType intype = getMediaType(req.getContentType(), serv.getMediaTypeMapper());
                MediaType outtype = getAcceptableMediaType(req.getHeader("Accept"), intype);

                synchronized (s_codecPool) {
                    // allocated codec(s) and buffers for the input and output
                    incodec = s_codecPool.getCodec(intype);
                    if (intype.equals(outtype)) {
                        outcodec = incodec;
                    } else {
                        outcodec = s_codecPool.getCodec(outtype);
                    }
                    inbuff = (InByteBuffer) s_inBufferCache.getInstance();
                    outbuff = (OutByteBuffer) s_outBufferCache.getInstance();
                }

                // pass the processing on to the service
                InboundConnection inconn = new InboundConnection(req, incodec, inbuff);
                OutboundConnection outconn = new OutboundConnection(rsp, req.getCharacterEncoding(), 
                    serv.getXmlOptions(), outcodec, outbuff);
                HttpServletOptions options = (HttpServletOptions) serv.getTransportOptions(HttpServletOptions.class);
                if (options != null) {
                    if (options.getInputStreamInterceptor() != null) {
                        inconn.setInterceptor(options.getInputStreamInterceptor());
                    } 
                    if (options.getOutputStreamInterceptor() != null) {
                        outconn.setInterceptor(options.getOutputStreamInterceptor());
                    }
                }
                serv.processRequest(inconn, outconn);
            }

        } catch (WsException e) {
            logger.error("Error processing request", e);
            rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
        } finally {
            synchronized (s_codecPool) {

                // release all resources acquired for processing request
                if (serv != null) {
                    serv.releaseInstance();
                }
                if (incodec != null) {
                    s_codecPool.releaseCodec(incodec);
                }
                if (outcodec != null && outcodec != incodec) {
                    s_codecPool.releaseCodec(outcodec);
                }
                if (inbuff != null) {
                    s_inBufferCache.endUsage(inbuff);
                }
                if (outbuff != null) {
                    s_outBufferCache.endUsage(outbuff);
                }

            }
        }
    }

    /**
     * Obtain the media type from the media type string.  If mediastring is null, this returns
     * the default text media type for the protocol that the service is using.
     * 

* All of the parameters on the mediastring are ignored. * * @param mediastring media type string (e.g., "text/xml" or "application/soap+xml;action=xyz") * @param protocol the protocol that the service is using * @return media type with parameters removed * @throws ServletException if mediastring contains no supported media type */ private MediaType getMediaType(String mediastring, MediaTypeMapper mapper) throws ServletException { MediaType media; if (mediastring == null) { try { media = mapper.getMediaTypeFor(null); } catch (WsConfigurationException e) { throw new ServletException("Internal JiBX/WS error. Unable to find default media type due to '" + e.getMessage() + "'."); } } else { try { media = new MediaType(mediastring, true); } catch (ParseException e) { throw new ServletException("Error parsing media type in content-type from request: " + mediastring); } if (!CodecDirectory.hasCodecFor(media)) { throw new ServletException("No supported media type in content-type from request: " + mediastring); } } return media; } /** * See {@link CodecDirectory#getAcceptableMediaType(String)}. */ private MediaType getAcceptableMediaType(String acceptable, MediaType contentType) throws ServletException { try { return CodecDirectory.getAcceptableMediaType(acceptable, contentType); } catch (ParseException e) { throw new ServletException("Error parsing media type in accept-type from request: " + acceptable, e); } } /** * GET request handler. The only type of GET request supported is one to get the WSDL for a service. * * @param context the servlet context * @param req servlet request information * @param rsp servlet response information * @exception ServletException on message content or operational error * @exception IOException on error reading or writing */ protected void doGet(ServletContext context, HttpServletRequest req, HttpServletResponse rsp) throws ServletException, IOException { if (logger.isDebugEnabled()) { logger.debug("Entered WsServletDelegate.doGet() with query string: " + req.getQueryString()); } // look up service information and check request if ("wsdl".equalsIgnoreCase(req.getQueryString())) { Service service = null; try { logger.debug("Looking up service for WSDL"); service = m_serviceMapper.getServiceInstance(req); if (service == null) { logger.debug("Unable to find service for WSDL"); rsp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); } else { WsdlProvider wsdlProvider = service.getWsdlProvider(); if (wsdlProvider == null) { logger.debug("Unable to WSDL provider for service"); rsp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); } else { logger.debug("Returning WSDL"); wsdlProvider.writeWSDL(rsp.getOutputStream(), req); } } } catch (WsException e) { logger.error("Error creating WSDL", e); throw new ServletException(e.getMessage(), e); } finally { // release all resources acquired for processing request if (service != null) { service.releaseInstance(); } } } else { rsp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); } } /** * Inbound connection (data received from client). */ private static class InboundConnection implements InConnection { /** Request data. */ private final HttpServletRequest m_request; /** Codec to be used for input. */ private final XmlCodec m_codec; /** Buffer used for input data. */ private final InByteBuffer m_buffer; /** Reader for connection. */ private IXMLReader m_reader; /** An interceptor for intercepting input stream, or null if no interceptor. */ private InputStreamInterceptor m_interceptor; /** * Constructor. * * @param request * @param codec * @param buff */ public InboundConnection(HttpServletRequest request, XmlCodec codec, InByteBuffer buff) { m_request = request; m_codec = codec; m_buffer = buff; } /** {@inheritDoc} */ public String getCharacterEncoding() { return m_request.getCharacterEncoding(); } /** {@inheritDoc} */ public String getContentType() { return m_request.getContentType(); } /** {@inheritDoc} */ public String getDestination() { return null; } /** {@inheritDoc} */ public String getId() { return null; } /** {@inheritDoc} */ public String getOperationName() { return null; } /** {@inheritDoc} */ public String getOrigin() { return m_request.getRemoteHost(); } /** {@inheritDoc} */ public String getProperty(String name) { return m_request.getHeader(name); } /** {@inheritDoc} */ public IXMLReader getReader() throws IOException { if (m_reader == null) { InputStream inputStream = m_request.getInputStream(); if (m_interceptor != null) { inputStream = m_interceptor.intercept(inputStream); } m_buffer.setInput(inputStream); m_reader = m_codec.getReader(m_buffer, m_request.getCharacterEncoding(), m_request.getRemoteAddr(), true); } return m_reader; } /** {@inheritDoc} */ public void init() throws IOException { getReader(); m_reader.init(); } /** {@inheritDoc} */ public boolean hasError() throws IOException { return false; } /** {@inheritDoc} */ public String getErrorMessage() throws IOException { return null; } /** * Sets an interceptor for intercepting the input stream. * * @param interceptor the interceptor */ private void setInterceptor(InputStreamInterceptor interceptor) throws WsConfigurationException { m_interceptor = interceptor; } public void inputComplete() { if (m_interceptor != null) { m_interceptor.inputComplete(); } } /** {@inheritDoc} */ public void close() throws IOException { m_buffer.finish(); } } /** * Outbound connection (data sent to client). */ private static class OutboundConnection extends OutConnectionBase implements OutServerConnection { /** Response data. */ private final HttpServletResponse m_response; /** Codec to be used for output. */ private final XmlCodec m_codec; /** Character encoding to be used for output (null if unknown or not applicable). */ private final String m_characterCode; /** Buffer used by connection. */ private OutByteBuffer m_buffer; /** Writer for connection. */ private IXMLWriter m_writer; /** An interceptor for intercepting output stream, or null if no interceptor. */ private OutputStreamInterceptor m_interceptor; /** * Constructor. * * @param response * @param charcode * @param xmlOptions formatting options for outbound XML * @param codec * @param buff */ public OutboundConnection(HttpServletResponse response, String charcode, XmlOptions xmlOptions, XmlCodec codec, OutByteBuffer buff) { super(xmlOptions); m_response = response; m_buffer = buff; m_codec = codec; m_characterCode = charcode; } /** {@inheritDoc} */ public void sendNotFoundError() throws IOException { m_response.sendError(HttpServletResponse.SC_NOT_FOUND); } /** {@inheritDoc} */ public void setInternalServerError() { m_response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } /** {@inheritDoc} */ public boolean isCommitted() { return m_response.isCommitted(); } /** {@inheritDoc} */ public IXMLWriter getNormalWriter(String[] uris) throws IOException { if (m_writer == null) { // first set the output content type MediaType contentType = m_codec.getMediaType(); MediaType.Parameter charset = null; if (m_characterCode != null) { charset = new MediaType.Parameter(CHARSET_KEY, m_characterCode); } m_response.setContentType(contentType.toStringWithParams(new MediaType.Parameter[]{charset})); // set up the actual writer OutputStream outputStream = m_response.getOutputStream(); if (m_interceptor != null) { outputStream = m_interceptor.intercept(outputStream); } m_buffer.setOutput(outputStream); m_writer = m_codec.getWriter(m_buffer, null, uris); initializeWriter(m_writer); } return m_writer; } /** {@inheritDoc} */ public IXMLWriter getFaultWriter(String[] uris) throws IOException { return getNormalWriter(uris); } /** * Sets an interceptor for intercepting the output stream. * * @param interceptor the interceptor */ private void setInterceptor(OutputStreamInterceptor interceptor) { m_interceptor = interceptor; } /** {@inheritDoc} */ public void outputComplete() { if (m_interceptor != null) { m_interceptor.outputComplete(); } } /** {@inheritDoc} */ public void close() throws IOException { logger.debug("Closing output connection"); if (m_writer != null) { logger.debug("Closing writer"); m_writer.close(); m_writer.reset(); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy