Package org.jibx.ws.http.client.javase

Source Code of org.jibx.ws.http.client.javase.HttpChannel$HttpOutConnection

/*
* 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.client.javase;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.text.ParseException;
import java.util.Iterator;

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.CodecCache;
import org.jibx.ws.codec.CodecDirectory;
import org.jibx.ws.codec.MediaType;
import org.jibx.ws.io.XmlOptions;
import org.jibx.ws.soap.SoapProtocol;
import org.jibx.ws.transport.Channel;
import org.jibx.ws.transport.DuplexConnection;
import org.jibx.ws.transport.InConnection;
import org.jibx.ws.transport.MessageProperties;
import org.jibx.ws.transport.OutConnection;
import org.jibx.ws.transport.OutConnectionBase;
import org.jibx.ws.transport.SimpleDuplexConnection;
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;

/**
* Channel for communicating with an HTTP endpoint. The methods exposed by this class are not threadsafe, so
* synchronization must be used if the channel is shared between threads.
*
* @author Dennis M. Sosnoski
*/
public final class HttpChannel implements Channel
{
    /** Key string for property defining the content type of the message being sent. */
    private static final String CONTENT_TYPE = "Content-Type";

    /** 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";

    /** The key of the action parameter, which can be used as part of the CONTENT_TYPE parameter. */ 
    private static final String ACTION_KEY = "action";

    /** Key string for property defining the content type(s) to be accepted for a response message. */
    private static final String ACCEPT_TYPE = "Accept";

   
    // TODO: make this a configuration parameter
    private static final int BUFFER_SIZE = 8192;
   
    /** URL for HTTP endpoint. */
    private final URL m_url;
   
    /** Cache for codec instances. */
    private final CodecCache m_codecCache;
   
    /** Input byte buffer pool. */
    private final StreamBufferInPool m_inBufferCache;
   
    /** Output byte buffer pool. */
    private final StreamBufferOutPool m_outBufferCache;
   
    private HttpTransportOptions m_transportOptions;
   
    /**
     * Constructor.
     *
     * @param url the target location
     * @param transportOptions options for customizing the transport. For HttpChannel, this must be an object of type
     * {@link HttpTransportOptions}.
     */
    public HttpChannel(URL url, HttpTransportOptions transportOptions) {
        m_url = url;
        m_transportOptions = transportOptions;
        m_codecCache = new CodecCache();
        m_inBufferCache = new StreamBufferInPool(BUFFER_SIZE);
        m_outBufferCache = new StreamBufferOutPool(BUFFER_SIZE);
    }
   
    /**
     * Setup output using an HTTP connection. This sets output to use the POST operation, and sets headers from the
     * properties map.
     * @param connection
     * @param props
     *
     * @throws ProtocolException
     */
    private static void setupOutput(HttpURLConnection connection, MessageProperties props) throws ProtocolException {
        connection.setDoOutput(true);
        connection.setRequestMethod("POST");
        connection.setRequestProperty(CONTENT_TYPE, getContentTypeProperty(props));
        connection.setRequestProperty(ACCEPT_TYPE, getAcceptTypeProperty(props));
        for (Iterator iter = props.getPropertyNames().iterator(); iter.hasNext();) {
            String propertyName = (String)iter.next();
            connection.setRequestProperty(propertyName, props.getProperty(propertyName));
        }
    }
   
    private static String getContentTypeProperty(MessageProperties props) {
        MediaType contentType = props.getContentType();
        MediaType.Parameter charset = null;
        MediaType.Parameter action = null;
        if (props.getCharset() != null) {
            charset = new MediaType.Parameter(CHARSET_KEY, props.getCharset().toLowerCase());
        }
        if (props.getOperation() != null) {
            action = new MediaType.Parameter(ACTION_KEY, props.getOperation());
        }
        return contentType.toStringWithParams(new MediaType.Parameter[] {charset, action});
    }

    private static String getAcceptTypeProperty(MessageProperties props) {
        MediaType[] acceptTypes = props.getAcceptTypes();
        if (acceptTypes == null || acceptTypes.length == 0) {
            throw new IllegalArgumentException("Internal JiBX/WS error. Expected Accept media type(s) to be set.");
        }
        StringBuffer buff = new StringBuffer(64);
        for (int i = 0; i < acceptTypes.length; i++) {
            if (i > 0) {
                buff.append(", ");
            }
            buff.append(acceptTypes[i].toString());
        }
        return buff.toString();
    }

    /** {@inheritDoc} */
    public InConnection getInbound() throws IOException, WsConfigurationException {
        HttpURLConnection connection = (HttpURLConnection)m_url.openConnection();
        connection.connect();
        return createInConnection(connection);
    }

    /** {@inheritDoc} */
    public OutConnection getOutbound(MessageProperties properties, XmlOptions xmlOptions) throws IOException,
            WsConfigurationException {
        HttpURLConnection connection = (HttpURLConnection)m_url.openConnection();
        connection.setDoInput(false);
        setupOutput(connection, properties);
        connection.connect();
        return createOutConnection(connection, properties, xmlOptions);
    }

    /** {@inheritDoc} */
    public DuplexConnection getDuplex(MessageProperties properties, XmlOptions xmlOptions) throws IOException,
            WsConfigurationException {
        HttpURLConnection connection = (HttpURLConnection)m_url.openConnection();
        connection.setDoInput(true);
        setupOutput(connection, properties);
        connection.connect();
        return new SimpleDuplexConnection(createInConnection(connection),
            createOutConnection(connection, properties, xmlOptions));
    }
   
    private HttpInConnection createInConnection(HttpURLConnection connection) throws WsConfigurationException {
        HttpInConnection inConn = new HttpInConnection(connection);
        if (m_transportOptions.getInputStreamInterceptor() != null) {
            inConn.setInterceptor(m_transportOptions.getInputStreamInterceptor());
        }
        return inConn;
    }
   
    private HttpOutConnection createOutConnection(HttpURLConnection connection, MessageProperties properties,
            XmlOptions xmlOptions) throws WsConfigurationException {
        HttpOutConnection outConn = new HttpOutConnection(connection, properties, xmlOptions);
        if (m_transportOptions.getOutputStreamInterceptor() != null) {
            outConn.setInterceptor(m_transportOptions.getOutputStreamInterceptor());
        }
        return outConn;
    }
   
    /** {@inheritDoc} */
    public void close() {
    }
   
    /**
     * An inbound connection wrapper for HttpURLConnection.
     */
    private class HttpInConnection implements InConnection
    {
        private static final int MIN_HTTP_ERROR_CODE = 400;

        private static final int ERROR_BUFFER_SIZE = 4000;

        /** Actual connection. */
        private final HttpURLConnection m_connection;
       
        /** Buffer used by connection. */
        private InByteBuffer m_buffer;
       
        /** Reader for connection. */
        private IXMLReader m_reader;

        /** An interceptor to intercept the input stream. */
        private InputStreamInterceptor m_interceptor;

        /**
         * @param connection
         */
        public HttpInConnection(HttpURLConnection connection) {
            m_connection = connection;
        }
       
        /** {@inheritDoc} */
        public void init() throws IOException, WsException {
            getReader();
            m_reader.init();
        }
       
        /** {@inheritDoc} */
        public String getCharacterEncoding() {
            return m_connection.getContentEncoding();
        }
       
        /** {@inheritDoc} */
        public String getContentType() {
            return m_connection.getContentType();
        }
       
        /** {@inheritDoc} */
        public String getDestination() {
            return null;
        }
       
        /** {@inheritDoc} */
        public String getId() {
            return null;
        }
       
        /**
         * {@inheritDoc}
         *
         * Gets the name of the operation. For HTTP, this will parse the content-type header for an "action"
         * parameter and return the value of this parameter if found.
         * <p>
         * This allows the operation name to be retrieved for SOAP 1.2 messages. SOAP 1.1 messages
         * will need to call {@link #getProperty(String)} with a parameter of {@link SoapProtocol#SOAPACTION_HEADER}
         * instead.    
         */
        public String getOperationName() {
            String opname = null;
            String contentTypeProp = getProperty(CONTENT_TYPE);
            if (contentTypeProp != null) {
                int i = contentTypeProp.indexOf("action=");
                if (i != -1) {
                    opname = contentTypeProp.substring(i);
                }
            }
            return opname;
        }
       
        /** {@inheritDoc} */
        public String getOrigin() {
            return null;
        }
       
        /** {@inheritDoc} */
        public String getProperty(String name) {
            return m_connection.getHeaderField(name);
        }
       
        /** {@inheritDoc} */
        public IXMLReader getReader() throws IOException, WsException {
            if (m_reader == null) {
                MediaType mediaType = getContentMediaType();
                m_buffer = (InByteBuffer)m_inBufferCache.getInstance();
               
                InputStream inputStream;
                if (hasError()) {
                    inputStream = m_connection.getErrorStream();
                } else {
                    inputStream = m_connection.getInputStream();
                }
               
                if (m_interceptor != null) {
                    inputStream = m_interceptor.intercept(inputStream);
                }
               
                m_buffer.setInput(inputStream);
                m_reader = m_codecCache.getCodec(mediaType).getReader(m_buffer,
                        getCharacterEncoding(), m_connection.getURL().toExternalForm(), true);
            }
            return m_reader;
        }

        private MediaType getContentMediaType() throws IOException {
            MediaType mediaType = null;
            String ctype = getContentType();
            MediaType contentType;
            if (ctype != null) {
                try {
                    contentType = new MediaType(ctype);
                } catch (ParseException e) {
                    throw new IOException("Unable to parse content-type '" + ctype + "'");
                }
                if (CodecDirectory.hasCodecFor(contentType)) {
                    mediaType = contentType;
                }
            }
            if (mediaType == null) {
                mediaType = CodecDirectory.TEXT_XML_MEDIA_TYPE;
            }
            return mediaType;
        }

        /** {@inheritDoc} */
        public boolean hasError() throws IOException {
            return m_connection.getResponseCode() >= MIN_HTTP_ERROR_CODE;
        }

        /** {@inheritDoc} */
        public String getErrorMessage() throws IOException {
            if (!hasError()) {
                return null;
            }
           
            StringBuffer error = new StringBuffer(ERROR_BUFFER_SIZE);
            String newLine = System.getProperty("line.separator");
            error.append(m_connection.getResponseCode()).append(" ").append(m_connection.getResponseMessage())
                    .append(newLine);
            InputStream errorStream = m_connection.getErrorStream();
            if (m_interceptor != null) {
                errorStream = m_interceptor.intercept(errorStream);
            }
            BufferedReader in = new BufferedReader(new InputStreamReader(errorStream));
            String line;
            while ((line = in.readLine()) != null) {
                error.append(line).append(newLine);
            }
            return error.toString();
        }

        /**
         * Sets the interceptor for intercepting the input stream.
         * @param interceptor the interceptor
         * @throws WsConfigurationException on error configuring interceptor, for example more than one input stream
         * interceptor is configured
         */
        public void setInterceptor(InputStreamInterceptor interceptor) throws WsConfigurationException  {
            if (m_interceptor != null) {
                throw new WsConfigurationException("Only a single input stream interceptor is supported.");
            }
            m_interceptor = interceptor;
        }

        public void inputComplete() {
            if (m_interceptor != null) {
                m_interceptor.inputComplete();
            }
        }
       
        /** {@inheritDoc} */
        public void close() throws IOException {
            if (m_buffer != null) {
                m_inBufferCache.endUsage(m_buffer);
                m_buffer = null;
            }
            m_reader = null;
        }
    }

    /**
     * An outbound connection wrapper for HttpURLConnection.
     */
    private class HttpOutConnection extends OutConnectionBase
    {
        /** Actual connection. */
        private final HttpURLConnection m_connection;
       
        /** Buffer used by connection. */
        private OutByteBuffer m_buffer;
       
        /** Writer for connection. */
        private IXMLWriter m_writer;

        /** An interceptor to intercept the output stream. */
        private OutputStreamInterceptor m_interceptor;

        private MessageProperties m_msgProps;
       
        /**
         * @param connection
         * @param msgProps message specific properties
         * @param xmlOptions XML formatting options
         */
        public HttpOutConnection(HttpURLConnection connection, MessageProperties msgProps, XmlOptions xmlOptions) {
            super(xmlOptions);
            m_connection = connection;
            m_msgProps = msgProps;
        }
       
        /** {@inheritDoc} */
        public IXMLWriter getNormalWriter(String[] uris) throws IOException, WsException {
            if (m_writer == null) {
                m_buffer = (OutByteBuffer)m_outBufferCache.getInstance();

                OutputStream outputStream = m_connection.getOutputStream();
                if (m_interceptor != null) {
                    outputStream = m_interceptor.intercept(outputStream);
                }
                m_buffer.setOutput(outputStream);
                m_writer = m_codecCache.getCodec(m_msgProps.getContentType()).getWriter(m_buffer, null, uris);
                initializeWriter(m_writer);
            }
            return m_writer;
        }
       
        /** {@inheritDoc} */
        public IXMLWriter getFaultWriter(String[] uris) throws IOException, WsException {
            return getNormalWriter(uris);
        }
       
        /**
         * Sets the interceptor for intercepting the output stream.
         * @param interceptor the interceptor
         * @throws WsConfigurationException on error configuring interceptor, for example more than one output stream
         * interceptor is configured
         */
        public void setInterceptor(OutputStreamInterceptor interceptor) throws WsConfigurationException {
            if (m_interceptor != null) {
                throw new WsConfigurationException("Only a single output stream interceptor is supported.");
            }
            this.m_interceptor = interceptor;
        }

        /** {@inheritDoc} */
        public void outputComplete() {
            if (m_interceptor != null) {
                m_interceptor.outputComplete();
            }
        }

        /** {@inheritDoc} */
        public void close() throws IOException {
            if (m_writer != null) {
                m_writer.close();
                m_writer.reset();
            }
            if (m_buffer != null) {
                m_outBufferCache.endUsage(m_buffer);
                m_buffer = null;
            }
        }
    }
}
TOP

Related Classes of org.jibx.ws.http.client.javase.HttpChannel$HttpOutConnection

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.