Url.java

package emissary.util.web;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

public class Url {

    private static final Logger logger = LoggerFactory.getLogger(Url.class);

    public static final int UNINITIALIZED = 0;
    public static final int GET = 1;
    public static final int POST = 2;
    public static final int HEAD = 3;

    static final String[] theMethodString = {"GET", "POST", "HEAD"};

    /**
     * process a url depending on the method specified
     * 
     * @param toProcess the URL resource to process with GET HEAD or POST
     */
    public static UrlData doUrl(final UrlData toProcess) {
        if (toProcess == null) {
            throw new IllegalArgumentException("Url.doUrl: null UrlData arg");
        }

        final int method = toProcess.getTheMethod();

        if (method == Url.GET) {
            return getUrl(toProcess);
        }

        if (method == Url.POST) {
            return postUrl(toProcess);
        }

        throw new IllegalArgumentException("UrlData method " + method + " not implemented in UrlData.doUrl");
    }

    /**
     * get a url
     * 
     * @param toGet describe where to GET from
     */
    public static UrlData getUrl(final UrlData toGet) {
        return getUrl(toGet.getTheUrl(), toGet.getProps());
    }

    /**
     * get a Url without any extra properties
     * 
     * @param urlString the URL resource to GET
     */
    public static UrlData getUrl(final String urlString) {
        return getUrl(urlString, null);
    }

    /**
     * Get a url, specifying additional header properties
     * 
     * @param urlString the URL resource
     * @param props properties to use on the connection
     */
    public static UrlData getUrl(final String urlString, @Nullable final List<UrlRequestProperty> props) {
        return processUrl(urlString, props, null, Url.GET);
    }

    /**
     * post to a url
     * 
     * @param toPost descript where to POST
     */
    public static UrlData postUrl(final UrlData toPost) {
        return postUrl(toPost.getTheUrl(), toPost.getProps(), null);
    }

    /**
     * post to a url
     * 
     * @param toPost describe where to POST
     * @param parms the POST data
     */
    public static UrlData postUrl(final UrlData toPost, final HttpPostParameters parms) {
        return postUrl(toPost.getTheUrl(), toPost.getProps(), parms);
    }

    /**
     * post a Url without any extra properties
     * 
     * @param urlString the URL resource to POST on
     */
    public static UrlData postUrl(final String urlString) {
        return postUrl(urlString, null, null);
    }

    /**
     * Post on a url, specifying additional header properties and params
     * 
     * @param urlString the URL resource
     * @param props array of properties to use
     * @param parms the POST data
     */
    public static UrlData postUrl(final String urlString, @Nullable final List<UrlRequestProperty> props, @Nullable final HttpPostParameters parms) {

        // props.addProp(new UrlRequestProperty("Content-length",parms.length()));

        final UrlData u = new UrlData(urlString);
        u.setTheMethod(Url.POST);
        if (props != null) {
            u.setProps(props);
        }
        if (parms != null) {
            u.addProp(new UrlRequestProperty("Content-length", parms.length()));
        }

        return processUrl(u.getTheUrl(), u.getProps(), parms, u.getTheMethod());
    }

    /**
     * process (GET|POST|HEAD) a Url
     * 
     * @param urlString the URL resource
     * @param props array of properties to use as headers, must have Content-length if POSTing
     * @param parms parameters to use when POSTing
     * @param method GET, HEAD, POST
     */
    private static UrlData processUrl(final String urlString, @Nullable final List<UrlRequestProperty> props,
            @Nullable final HttpPostParameters parms,
            final int method) {
        final UrlData response = new UrlData(urlString);

        final StringBuilder theOutput = new StringBuilder();
        OutputStream os = null;
        BufferedReader dis = null;
        try {
            final URL theUrl = new URL(urlString);
            final HttpURLConnection conn = (HttpURLConnection) theUrl.openConnection();

            // Set up for POST or other
            conn.setDoOutput(method == Url.POST);
            conn.setDoInput(true);
            conn.setUseCaches(false);


            // Set user requested properties
            if (props != null) {
                for (UrlRequestProperty prop : props) {
                    conn.setRequestProperty(prop.getKey(), prop.getValue());
                }
            }


            // Write post data if POSTing
            if (method == Url.POST && parms != null) {
                os = conn.getOutputStream();
                os.write(parms.toPostString().getBytes());
                os.flush();
            }

            // Get response code
            response.setResponseCode(conn.getResponseCode());

            // Get the headers
            final Map<String, String> headers = new HashMap<>();
            String s;
            int hdr = 0;
            while ((s = conn.getHeaderField(hdr)) != null) {
                final String key = conn.getHeaderFieldKey(hdr);
                if (key != null) {
                    headers.put(key, s);
                } else {
                    headers.put("", s);
                }
                hdr++;
            }

            // Load headers into properties array
            final List<UrlRequestProperty> theProps = new ArrayList<>(headers.size());
            for (final Map.Entry<String, String> entry : headers.entrySet()) {
                theProps.add(new UrlRequestProperty(entry.getKey(), entry.getValue()));
            }
            response.setProps(theProps);

            // Get page unless HEADing
            if (method != Url.HEAD) {
                dis = new BufferedReader(new InputStreamReader(conn.getInputStream()));

                // Get the content
                String line;
                while ((line = dis.readLine()) != null) {
                    theOutput.append(line).append(System.getProperty("line.separator", "\n"));
                }

                response.setTheContent(theOutput.toString().getBytes());
            }
        } catch (MalformedURLException e) {
            logger.warn("Bad URL " + urlString, e);
        } catch (IOException e) {
            logger.warn("Read error on " + urlString, e);
        } catch (OutOfMemoryError e) {
            logger.warn("Not enough space for read on " + urlString, e);
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException ioe) {
                    logger.warn("Unable to close stream", ioe);
                }
            }
            if (dis != null) {
                try {
                    dis.close();
                } catch (IOException ioe) {
                    logger.warn("Unable to close stream", ioe);
                }
            }
        }

        return response;
    }

    /** This class is not meant to be instantiated. */
    private Url() {}
}