org.opentox.toxotis.client.https.PostHttpsClient.java Source code

Java tutorial

Introduction

Here is the source code for org.opentox.toxotis.client.https.PostHttpsClient.java

Source

/*
 *
 * ToxOtis
 *
 * ToxOtis is the Greek word for Sagittarius, that actually means archer. ToxOtis
 * is a Java interface to the predictive toxicology services of OpenTox. ToxOtis is
 * being developed to help both those who need a painless way to consume OpenTox
 * services and for ambitious service providers that dont want to spend half of
 * their time in RDF parsing and creation.
 *
 * Copyright (C) 2009-2010 Pantelis Sopasakis & Charalampos Chomenides
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contact:
 * Pantelis Sopasakis
 * chvng@mail.ntua.gr
 * Address: Iroon Politechniou St. 9, Zografou, Athens Greece
 * tel. +30 210 7723236
 *
 */
package org.opentox.toxotis.client.https;

import com.hp.hpl.jena.ontology.OntModel;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.apache.commons.lang.StringUtils;
import org.opentox.toxotis.client.IPostClient;
import org.opentox.toxotis.client.RequestHeaders;
import org.opentox.toxotis.client.VRI;
import org.opentox.toxotis.client.collection.Media;
import org.opentox.toxotis.core.IStAXWritable;
import org.opentox.toxotis.exceptions.impl.ConnectionException;
import org.opentox.toxotis.exceptions.impl.InternalServerError;
import org.opentox.toxotis.exceptions.impl.ServiceInvocationException;

/**
 *
 * @author Pantelis Sopasakis
 * @author Charalampos Chomenides
 */
public class PostHttpsClient extends AbstractHttpsClient implements IPostClient {

    /** Type of the posted content*/
    private String contentType = null;
    /** Parameters to be posted as application/x-www-form-urlencoded (if any) */
    private Map<String, String> postParameters = new HashMap<String, String>();
    private String postableString = null;
    private String postableBytes = null;
    private OntModel model;
    /** A StAX component that implements the interface {@link IStAXWritable }
    that will be posted to the remote server via the method {@link IStAXWritable#writeRdf(java.io.OutputStream)
    write(OutputStream)} that writes the component to an outputstream pointing to the
    remote stream
     */
    private IStAXWritable staxComponent;
    /** Arbitrary object to be posted to the remote server s*/
    private File fileContentToPost = null;
    private String fileUploadFieldName = "upload";
    private String fileUploadFilename = "uploadedFile";
    private WriteLock postLock = new ReentrantReadWriteLock().writeLock();

    public PostHttpsClient() {
        super();
    }

    public PostHttpsClient(VRI vri) {
        super(vri);
    }

    @Override
    public PostHttpsClient setPostable(String string, boolean binary) {
        if (binary) {
            this.postableBytes = string;
        } else {
            this.postableString = string;
        }
        return this;
    }

    /**
     * Set an ontological data model which is to be posted to the remote location
     * as application/rdf+xml. 
     * 
     * Invocations of this method set automatically the content-type
     * to application/rdf+xml though it can be overridden afterwards.
     * @param model
     *      Ontological Model to be posted
     * @return
     *      The PostHttpClient with the updated Ontological Model.
     */
    @Override
    public PostHttpsClient setPostable(OntModel model) {
        this.model = model;
        return this;
    }

    /** Initialize a connection to the target URI */
    @Override
    protected HttpURLConnection initializeConnection(URI uri) throws ServiceInvocationException {
        try {
            java.net.URL targetUrl = uri.toURL();
            setConnection((javax.net.ssl.HttpsURLConnection) targetUrl.openConnection());
            getConnection().setRequestMethod(METHOD);
            getConnection().setDoInput(true);
            getConnection().setDoOutput(true);
            getConnection().setUseCaches(false);
            if (contentType != null) {
                getConnection().setRequestProperty(RequestHeaders.CONTENT_TYPE, contentType);
            }
            if (getMediaType() != null) {
                getConnection().setRequestProperty(RequestHeaders.ACCEPT, getMediaType());
            }
            if (!getHeaderValues().isEmpty()) {
                for (Map.Entry<String, String> e : getHeaderValues().entrySet()) {
                    getConnection().setRequestProperty(e.getKey(), e.getValue());// These are already URI-encoded!
                }
            }
            return getConnection();
        } catch (final IOException ex) {
            throw new ConnectionException("Unable to connect to the remote service at '" + getUri() + "'", ex);
        } catch (final Exception unexpectedException) {
            throw new InternalServerError(
                    "Unexpected condition while attempting to " + "establish a connection to '" + uri + "'",
                    unexpectedException);
        }
    }

    @Override
    public String getContentType() {
        return contentType;
    }

    @Override
    public PostHttpsClient setContentType(String contentType) {
        this.contentType = contentType;
        return this;
    }

    private String getParametersAsQuery() {
        if (postParameters.isEmpty()) {
            return "";
        }
        StringBuilder string = new StringBuilder();
        final int nParams = postParameters.size();
        if (nParams > 0) {
            int counter = 0;
            for (Map.Entry<String, String> e : postParameters.entrySet()) {
                string.append(e.getKey());
                string.append("=");
                if (e.getValue() != null) {
                    string.append(e.getValue());
                }
                if (counter != nParams - 1) {
                    string.append("&");
                }
                counter++;
            }
        }
        return new String(string);
    }

    @Override
    public void post() throws ServiceInvocationException {
        String query = getParametersAsQuery();
        if (fileContentToPost != null && query != null && !query.isEmpty()) {
            postMultiPart();
        } else {

            connect(getUri().toURI());
            DataOutputStream wr;
            try {
                getPostLock().lock(); // LOCK
                wr = new DataOutputStream(getConnection().getOutputStream());

                if (query != null && !query.isEmpty()) {
                    wr.writeBytes(getParametersAsQuery());// POST the parameters
                } else if (model != null) {
                    model.write(wr);
                } else if (staxComponent != null) {
                    staxComponent.writeRdf(wr);
                } else if (postableString != null) {
                    wr.writeChars(postableString);
                } else if (postableBytes != null) {
                    wr.writeBytes(postableBytes);
                } else if (fileContentToPost != null) {
                    FileReader fr = null;
                    BufferedReader br = null;
                    try {
                        fr = new FileReader(fileContentToPost);
                        br = new BufferedReader(fr);
                        String line;
                        while ((line = br.readLine()) != null) {
                            wr.writeBytes(line);
                            wr.writeChars("\n");
                        }
                    } catch (IOException ex) {
                        throw new ConnectionException("Unable to post data to the remote service at '" + getUri()
                                + "' - The connection dropped " + "unexpectidly while POSTing.", ex);
                    } finally {
                        Throwable thr = null;
                        if (br != null) {
                            try {
                                br.close();
                            } catch (final IOException ex) {
                                thr = ex;
                            }
                        }
                        if (fr != null) {
                            try {
                                fr.close();
                            } catch (final IOException ex) {
                                thr = ex;
                            }
                        }
                        if (thr != null) {
                            ConnectionException connExc = new ConnectionException("Stream could not close", thr);
                            connExc.setActor(getUri() != null ? getUri().toString() : "N/A");
                        }
                    }
                }
                wr.flush();
                wr.close();
            } catch (final IOException ex) {
                ConnectionException postException = new ConnectionException(
                        "Exception caught while posting the parameters to the " + "remote web service located at '"
                                + getUri() + "'",
                        ex);
                postException.setActor(getUri() != null ? getUri().toString() : "N/A");
                throw postException;
            } finally {
                getPostLock().unlock(); // UNLOCK
            }
        }
    }

    private void postMultiPart() throws ServiceInvocationException {
        String charset = "UTF-8";
        String LINE_FEED = "\r\n";
        InputStream is;

        try {
            getPostLock().lock(); // LOCK

            is = new FileInputStream(fileContentToPost);

            // creates a unique boundary based on time stamp
            long boundaryTs = System.currentTimeMillis();
            String boundary = "===" + boundaryTs + "===";

            HttpURLConnection httpConn = connect(getUri().toURI());
            httpConn.setUseCaches(false);
            httpConn.setDoOutput(true); // indicates POST method
            httpConn.setDoInput(true);
            httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=\"" + boundary + "\"");
            httpConn.setRequestProperty("User-Agent", "CodeJava Agent");
            httpConn.setRequestProperty("Test", "Bonjour");

            OutputStream outputStream = httpConn.getOutputStream();
            PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true);

            final int nParams = postParameters.size();

            if (nParams > 0) {

                for (Map.Entry<String, String> e : postParameters.entrySet()) {
                    writer.append("--" + boundary).append(LINE_FEED);
                    writer.append("Content-Disposition: form-data; name=\"" + e.getKey() + "\"").append(LINE_FEED);
                    writer.append("Content-Type: text/plain; charset=" + charset).append(LINE_FEED);
                    writer.append(LINE_FEED);
                    writer.append(e.getValue()).append(LINE_FEED);
                    writer.flush();
                }
            }

            if (is.read() > 0) {
                is.reset();
                writer.append("--" + boundary).append(LINE_FEED);
                writer.append("Content-Disposition: form-data; name=\"" + fileUploadFieldName + "\"; filename=\""
                        + fileUploadFilename + "\"").append(LINE_FEED);
                writer.append(
                        "Content-Type: " + java.net.URLConnection.guessContentTypeFromName(fileUploadFilename))
                        .append(LINE_FEED);
                writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
                writer.append(LINE_FEED);
                writer.flush();

                byte[] buffer = new byte[4096];
                int bytesRead = -1;
                while ((bytesRead = is.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
                outputStream.flush();
                is.close();

                writer.append(LINE_FEED);
                writer.flush();
                writer.append(LINE_FEED).flush();
            }

            writer.append("--" + boundary + "--").append(LINE_FEED);
            writer.close();
        } catch (final IOException ex) {
            ConnectionException postException = new ConnectionException(
                    "Exception caught while posting the parameters to the " + "remote web service located at '"
                            + getUri() + "'",
                    ex);
            postException.setActor(getUri() != null ? getUri().toString() : "N/A");
            throw postException;
        } finally {
            getPostLock().unlock(); // UNLOCK
        }
    }

    @Override
    public PostHttpsClient setContentType(Media media) {
        this.contentType = media.getMime();
        return this;
    }

    /**
     * Set a StAX-writeable component to be posted to the remote location
     * @param staxWritable
     *      A StAX component that implements the interface {@link IStAXWritable }
     *      that will be posted to the remote server via the method {@link IStAXWritable#writeRdf(java.io.OutputStream)
     *      write(OutputStream)} that writes the component to an output stream pointing to the remote stream
     * @return
     *      The PostHttpClient with the updated writeable component.
     */
    @Override
    public PostHttpsClient setPostable(IStAXWritable staxWritable) {
        this.staxComponent = staxWritable;
        return this;
    }

    /**
     * Set a file whose contents are to be posted to the remote server specified
     * in the constructor of this class. 
     * 
     * If the file is not found under the specified
     * path, an IllegalArgumentException is thrown. Because the type of the file is
     * in general unknown and it is not considered to be a good practice to deduce the
     * file type from the file extension, it is up to the user to specify the content
     * type of the posted object using the method {@link #setContentType(java.lang.String) #setContentType}. 
     * Since it is not possible to POST entities of different content
     * types to an HTTP server, any invocation to this method will override any previous
     * invocation of {@link PostHttpsClient#setPostable(com.hp.hpl.jena.ontology.OntModel) #setPostable}
     * setPostable(OntModel) } and {@link #setPostable(java.lang.String, boolean) setPostable(String)}.
     *
     * @param objectToPost
     *      File whose contents are to be posted.
     * @return
     *      This post client
     * 
     * @throws IllegalArgumentException
     *      In case the provided file does not exist
     */
    @Override
    public PostHttpsClient setPostable(File objectToPost) {
        if (objectToPost != null && !objectToPost.exists()) {
            throw new IllegalArgumentException(
                    new FileNotFoundException("No file was found at the specified path!"));
        }
        this.fileContentToPost = objectToPost;
        return this;
    }

    /**
     * Add a parameter which will be posted to the target URI. Once the parameter is
     * submitted to the PostClient, it is stored as URL-encoded using the UTF-8 encoding.
     * @param paramName Parameter name
     * @param paramValue Parameter value
     * @return This object
     * @throws NullPointerException If paramName is <code>null</code>.
     */
    @Override
    public IPostClient addPostParameter(String paramName, String paramValue) throws NullPointerException {
        if (paramName == null) {
            throw new NullPointerException("paramName must be not null");
        }
        try {
            postParameters.put(URLEncoder.encode(paramName, URL_ENCODING),
                    paramValue != null ? URLEncoder.encode(paramValue, URL_ENCODING) : "");
        } catch (final UnsupportedEncodingException ex) {
            throw new IllegalArgumentException("Unsupported Encoding... Hmmm, strange!", ex);
        }
        return this;
    }

    @Override
    public WriteLock getPostLock() {
        return postLock;
    }

    @Override
    public IPostClient setPostable(InputStream model) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void setPostableFilename(String fieldName, String filename) {
        this.fileUploadFilename = (StringUtils.isEmpty(filename)) ? this.fileUploadFilename : filename;
        this.fileUploadFieldName = (StringUtils.isEmpty(fieldName)) ? this.fileUploadFieldName : fieldName;
    }
}