xtremweb.communications.HTTPClient.java Source code

Java tutorial

Introduction

Here is the source code for xtremweb.communications.HTTPClient.java

Source

/*
 * Copyrights     : CNRS
 * Author         : Oleg Lodygensky
 * Acknowledgment : XtremWeb-HEP is based on XtremWeb 1.8.0 by inria : http://www.xtremweb.net/
 * Web            : http://www.xtremweb-hep.org
 *
 *      This file is part of XtremWeb-HEP.
 *
 *    XtremWeb-HEP is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    XtremWeb-HEP is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with XtremWeb-HEP.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package xtremweb.communications;

/**
 * This class implements HTTP client part to connect to the dispatcher<br>
 *
 * Created: Oct 5th, 2007
 * @author Oleg Lodygensky
 * @since XWHEP 1.0.0
 */

import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NoRouteToHostException;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.security.AccessControlException;
import java.security.InvalidKeyException;
import java.security.KeyStore;

import javax.net.ssl.SSLHandshakeException;

import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnection;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
import org.xml.sax.SAXException;

import xtremweb.common.AppInterface;
import xtremweb.common.DataInterface;
import xtremweb.common.GroupInterface;
import xtremweb.common.HostInterface;
import xtremweb.common.OSEnum;
import xtremweb.common.SessionInterface;
import xtremweb.common.StreamIO;
import xtremweb.common.Table;
import xtremweb.common.TaskInterface;
import xtremweb.common.TraceInterface;
import xtremweb.common.UserGroupInterface;
import xtremweb.common.UserInterface;
import xtremweb.common.Version;
import xtremweb.common.WorkInterface;
import xtremweb.common.XMLHashtable;
import xtremweb.common.XMLVector;
import xtremweb.common.XWConfigurator;
import xtremweb.common.XWPropertyDefs;
import xtremweb.common.XWTools;

public class HTTPClient extends CommClient {

    public class AuthSSLProtocolSocketFactory implements SecureProtocolSocketFactory {

        private final TCPClient tcpClient;
        private final String uriScheme;

        public AuthSSLProtocolSocketFactory(final String scheme, final KeyStore ks, final String passwd) {
            super();
            uriScheme = scheme;
            tcpClient = new TCPClient();
        }

        private URI newURI(final String host, final int port) throws URISyntaxException {
            return new URI(uriScheme + "://" + host + ":" + port);
        }

        /**
         * Attempts to get a new socket connection to the given host within the
         * given time limit.
         * <p>
         * To circumvent the limitations of older JREs that do not support
         * connect timeout a controller thread is executed. The controller
         * thread attempts to create a new socket within the given limit of
         * time. If socket constructor does not return until the timeout
         * expires, the controller terminates and throws an
         * {@link ConnectTimeoutException}
         * </p>
         *
         * @param host
         *            the host name/IP
         * @param port
         *            the port on the host
         * @param localAddress
         *            the local host name/IP to bind the socket to
         * @param localPort
         *            the port on the local machine
         * @param params
         *            {@link HttpConnectionParams Http connection parameters}
         *
         * @return Socket a new socket
         *
         * @throws IOException
         *             if an I/O error occurs while creating the socket
         * @throws UnknownHostException
         *             if the IP address of the host cannot be determined
         */
        @Override
        public Socket createSocket(final String host, final int port, final InetAddress localAddress,
                final int localPort, final HttpConnectionParams params)
                throws IOException, UnknownHostException, ConnectTimeoutException {
            try {
                tcpClient.open(newURI(host, port));
            } catch (final URISyntaxException e) {
                throw new IOException(e.getMessage());
            }
            return tcpClient.getSocket();
        }

        /**
         * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int)
         */
        @Override
        public Socket createSocket(final String host, final int port, final InetAddress clientHost,
                final int clientPort) throws IOException, UnknownHostException {
            try {
                tcpClient.open(newURI(host, port));
            } catch (final URISyntaxException e) {
                throw new IOException(e.getMessage());
            }
            return tcpClient.getSocket();
        }

        /**
         * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int)
         */
        @Override
        public Socket createSocket(final String host, final int port) throws IOException, UnknownHostException {
            try {
                tcpClient.open(newURI(host, port));
            } catch (final URISyntaxException e) {
                throw new IOException(e.getMessage());
            }
            return tcpClient.getSocket();
        }

        /**
         * @see SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String,int,boolean)
         */
        @Override
        public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose)
                throws IOException, UnknownHostException {
            try {
                tcpClient.open(newURI(host, port));
            } catch (final URISyntaxException e) {
                throw new IOException(e.getMessage());
            }
            return tcpClient.getSocket();
        }
    }

    private boolean nio;

    private HttpClient httpClient;
    private PostMethod post;

    /**
     * This is the default constructor; this only calls super()
     */
    public HTTPClient() {
        super();
        httpClient = null;
    }

    /**
     * This retreives this client port number
     *
     * @since 5.9.0
     */
    @Override
    public int getPort() {
        if (getConfig() == null) {
            return -1;
        }
        return getConfig().getPort(Connection.HTTPPORT);
    }

    /**
     * This does nothing; everything is done in write()
     *
     * @throws IOException
     * @see #write(XMLRPCCommand)
     */
    @Override
    protected void open(URI uri) throws UnknownHostException, NoRouteToHostException, SSLHandshakeException,
            SocketTimeoutException, IOException {

        try {
            String serverName = XWTools.getHostName(uri.getHost());
            int serverPort = uri.getPort();

            httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());

            Protocol xwssl = null;
            final XWConfigurator config = getConfig();

            final KeyStore keyStore = config.getKeyStore();

            if (keyStore != null) {
                if (serverPort == -1) {
                    serverPort = config.getPort(Connection.HTTPSPORT);
                }
                xwssl = new Protocol(uri.getScheme(), new AuthSSLProtocolSocketFactory(uri.getScheme(), keyStore,
                        config.getProperty(XWPropertyDefs.SSLKEYPASSWORD)), serverPort);
                Protocol.registerProtocol(uri.getScheme(), xwssl);
            } else {
                getLogger().warn("unsecured communications : not using SSL");
                if (serverPort == -1) {
                    serverPort = config.getPort(Connection.HTTPPORT);
                }
            }

            final String proxyName = config.getProperty(XWPropertyDefs.PROXYSERVER);
            if ((proxyName != null) && (proxyName.trim().length() > 0)) {
                serverName = XWTools.getHostName(proxyName);
            }

            final String porttxt = config.getProperty(XWPropertyDefs.PROXYPORT);
            if ((porttxt != null) && (porttxt.trim().length() > 0)) {
                final int proxyPort = config.getPort(Connection.PROXYPORT);
                if (proxyPort > 0) {
                    serverPort = proxyPort;
                }
            }

            URI uri2 = null;
            try {
                final StringBuilder struri2 = new StringBuilder(
                        uri.getScheme() + Connection.getSchemeSeparator() + serverName);
                if (serverPort > 0) {
                    struri2.append(":" + serverPort);
                }
                if (uri.getPath() != null) {
                    struri2.append("/" + uri.getPath());
                }
                uri2 = new URI(struri2.toString());
            } catch (final Exception e) {
                uri2 = uri;
            }
            uri = uri2;
            uri2 = null;

            mileStone("<open uri=\"" + uri + "\">");
            httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(30000);
            final HostConfiguration hConfig = new HostConfiguration();
            if (xwssl == null) {
                hConfig.setHost(serverName, serverPort);
            } else {
                hConfig.setHost(serverName, serverPort, xwssl);
            }
            httpClient.setHostConfiguration(hConfig);

            nio = false;

            if (config.getBoolean(XWPropertyDefs.OPTIMIZENETWORK) && (OSEnum.getOs().isMacosx() == false)) {
                final HttpConnectionManagerParams params = httpClient.getHttpConnectionManager().getParams();
                params.setLinger(0); // don't wait on close
                params.setTcpNoDelay(true); // don't wait to send
            }

            final HttpConnection connection = httpClient.getHttpConnectionManager()
                    .getConnection(httpClient.getHostConfiguration());

            connection.open();
        } catch (final Exception e) {
            getLogger().exception(e);
            mileStone("<error method='open' msg='" + e.getMessage() + "' />");
            throw new IOException("HTTPClient : open failed " + e.toString());
        } finally {
            mileStone("</open>");
        }
    }

    /**
     * This closes communication channel
     */
    @Override
    public void close() {

        mileStone("<close>");
        post.releaseConnection();
        final HttpConnection connection = httpClient.getHttpConnectionManager()
                .getConnection(httpClient.getHostConfiguration());
        connection.close();

        mileStone("</close>");
    }

    /**
     * This sends a command to server
     *
     * @param cmd
     *            is the command to send
     */
    @Override
    protected void write(final XMLRPCCommand cmd) throws IOException {

        mileStone("<write cmd='" + cmd.getIdRpc() + "'>");
        cmd.setMandatingLogin(getConfig().getMandate());
        post = new PostMethod(httpClient.getHostConfiguration().getHostURL());
        post.addParameter(XWPostParams.XMLDESC.toString(), cmd.toXml());
        httpClient.executeMethod(post);

        mileStone("</write>");
    }

    /**
     * This creates an object from channel
     *
     * @throws AccessControlException
     * @throws InvalidKeyException
     */
    @Override
    protected Table newTableInterface()
            throws InvalidKeyException, AccessControlException, IOException, SAXException {

        return super.newTableInterface(post.getResponseBodyAsStream());
    }

    /**
     * This creates an object from channel
     *
     * @throws AccessControlException
     * @throws InvalidKeyException
     */
    @Override
    protected AppInterface newAppInterface()
            throws InvalidKeyException, AccessControlException, IOException, SAXException {
        return super.newAppInterface(post.getResponseBodyAsStream());
    }

    /**
     * This creates an object from channel
     */
    @Override
    protected DataInterface newDataInterface()
            throws InvalidKeyException, AccessControlException, IOException, SAXException {
        return super.newDataInterface(post.getResponseBodyAsStream());
    }

    /**
     * This creates an object from channel
     *
     * @throws AccessControlException
     * @throws InvalidKeyException
     */
    @Override
    protected GroupInterface newGroupInterface()
            throws IOException, SAXException, InvalidKeyException, AccessControlException {
        return super.newGroupInterface(post.getResponseBodyAsStream());
    }

    /**
     * This creates an object from channel
     *
     * @throws AccessControlException
     * @throws InvalidKeyException
     */
    @Override
    protected HostInterface newHostInterface()
            throws IOException, SAXException, InvalidKeyException, AccessControlException {
        return super.newHostInterface(post.getResponseBodyAsStream());
    }

    /**
     * This creates an object from channel
     *
     * @throws AccessControlException
     * @throws InvalidKeyException
     */
    @Override
    protected SessionInterface newSessionInterface()
            throws IOException, SAXException, InvalidKeyException, AccessControlException {
        return super.newSessionInterface(post.getResponseBodyAsStream());
    }

    /**
     * This creates an object from channel
     *
     * @throws AccessControlException
     * @throws InvalidKeyException
     */
    @Override
    protected TaskInterface newTaskInterface()
            throws IOException, SAXException, InvalidKeyException, AccessControlException {
        return super.newTaskInterface(post.getResponseBodyAsStream());
    }

    /**
     * This creates an object from channel
     *
     * @throws AccessControlException
     * @throws InvalidKeyException
     */
    @Override
    protected TraceInterface newTraceInterface()
            throws IOException, SAXException, InvalidKeyException, AccessControlException {
        return super.newTraceInterface(post.getResponseBodyAsStream());
    }

    /**
     * This creates an object from channel
     *
     * @throws AccessControlException
     * @throws InvalidKeyException
     */
    @Override
    protected UserInterface newUserInterface()
            throws IOException, SAXException, InvalidKeyException, AccessControlException {
        return super.newUserInterface(post.getResponseBodyAsStream());
    }

    /**
     * This creates an object from channel
     *
     * @throws AccessControlException
     * @throws InvalidKeyException
     */
    @Override
    protected UserGroupInterface newUserGroupInterface()
            throws IOException, SAXException, InvalidKeyException, AccessControlException {
        return super.newUserGroupInterface(post.getResponseBodyAsStream());
    }

    /**
     * This creates an object from channel
     *
     * @throws AccessControlException
     * @throws InvalidKeyException
     */
    @Override
    protected WorkInterface newWorkInterface()
            throws IOException, SAXException, InvalidKeyException, AccessControlException {
        return super.newWorkInterface(post.getResponseBodyAsStream());
    }

    /**
     * This creates an object from channel
     *
     * @throws AccessControlException
     * @throws InvalidKeyException
     */
    @Override
    protected XMLVector newXMLVector()
            throws IOException, SAXException, InvalidKeyException, AccessControlException {
        return super.newXMLVector(post.getResponseBodyAsStream());
    }

    /**
     * This creates an object from channel
     *
     * @throws AccessControlException
     * @throws InvalidKeyException
     */
    @Override
    protected Version newXMLVersion()
            throws IOException, SAXException, InvalidKeyException, AccessControlException {
        return super.newXMLVersion(post.getResponseBodyAsStream());
    }

    /**
     * This creates an object from channel
     *
     * @throws AccessControlException
     * @throws InvalidKeyException
     */
    @Override
    protected XMLHashtable newXMLHashtable()
            throws InvalidKeyException, AccessControlException, IOException, SAXException {
        return super.newXMLHashtable(post.getResponseBodyAsStream());
    }

    /**
     * This uploads a data content to server
     *
     * @param command
     *            is the command to send to server
     * @param content
     *            represents a File to get data to upload
     * @since XWHEP 1.0.0
     */
    @Override
    public void uploadData(final XMLRPCCommandUploadData command, final File content) throws IOException {

        try {
            mileStone("<uploadData>");

            open(command.getURI());

            command.setUser(getConfig().getUser());

            mileStone("Uploading " + command.toXml());

            post = new PostMethod(httpClient.getHostConfiguration().getHostURL());
            final Part[] parts = { new StringPart(XWPostParams.XMLDESC.toString(), command.toXml()),
                    new FilePart(XWPostParams.XWUPLOAD.toString(), content) };

            post.setRequestEntity(new MultipartRequestEntity(parts, post.getParams()));

            final int status = httpClient.executeMethod(post);
            if (status != HttpStatus.SC_OK) {
                throw new IOException("can't upload data status = " + status);
            }
        } catch (final Exception e) {
            mileStone("<error method='uploadData' msg='" + e.getMessage() + "' />");
            getLogger().error("Upload error " + command.getURI() + " " + e);
        } finally {
            post.releaseConnection();
            close();
            mileStone("</uploadData>");
        }
    }

    /**
     * This always throws an IOException. Please use uploadData instead
     *
     * @see #uploadData(XMLRPCCommandUploadData, File)
     * @deprecated
     */
    @Deprecated
    @Override
    public void writeFile(final File f) throws IOException {
        throw new IOException("HTTPClient#writeFile() is not implemented; please use HTTPClient#uploadData()");
    }

    /**
     * This reads a file from socket This is typically needed after a
     * workRequest to get stdin and/or dirin files
     *
     * @param f
     *            is the file to store received bytes
     */
    @Override
    public void readFile(final File f) throws IOException {

        try (final DataInputStream inputStream = new DataInputStream(post.getResponseBodyAsStream());
                final StreamIO io = new StreamIO(null, inputStream, nio);) {
            mileStone("<readFile>");
            io.readFile(f, XWPostParams.MAXUPLOADSIZE);
        } catch (final Exception e) {
            getLogger().exception(e);
            mileStone("<error method='readFile' msg='" + e.getMessage() + "' />");
            throw new IOException(e.toString());
        } finally {
            mileStone("</readFile>");
        }
    }
}