Simple Java Rest Client

So I had to do a project a while back that required me to write a REST client. Simple right? There are plenty of libraries that either wrap the Java standard HTTPConnection classes (ie. HttpClient) or just present a nice clean Rest Client for usage. The problems was that I could not use any of them, I had to use what was already in the project, which wasn’t much.

So I set about reinventing the wheel and wrote myself a rest client from scratch using only the standard java classes. It works, for exactly what it was designed to do. My thought was to open it up and turn it into a project to create an all in one class that a person can just drop into their project regardless of what libraries their project uses.

I am going to be making this better as I monkey with it. Right now it’s pretty barebones. I hope to make it better. If you have any recommendations, please indicate it in the comments. Please be kind, this is as much of a learning exercise as it is anything.

Version 2: Total rewrite, support for POST, but not HTTPS now, working on that, coming in V2.1. Put more work on the calling class to create the HashMaps of params and headers. I might put those convenience methods back just because I like them.

 
/**
 * 2013 - Jeffrey Robbins
 * SimpleRestClient v2 - Building a REST client from the standard java libs
 * This is by no means an example on the best way to do things, just a bit of fun in learning
 * Comments on making it better welcomed
 *
 * Licensed under the Apache License, V2.0
 */
 
 
import javax.net.ssl.HttpsURLConnection;
import java.net.HttpURLConnection;
import java.net.URLConnection;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Scanner;
 
public class SimpleRestClient {
 
    public Response doGetRequest(URL url, HashMap<String,String> headers, HashMap<String,String> params, boolean debug) {
        GET get = new GET(url, headers, params);
        return sendRequest(get, debug);
    }
 
    public Response doPostRequest(URL url, HashMap<String,String> headers, String body, boolean debug) {
        POST post = new POST(url, headers, body);
        return sendRequest(post, debug);
    }
 
    private Response sendRequest(Request request, boolean debug) {
        HttpURLConnection conn = null;
        Response response = null;
        long time = 0;
 
        try {
            conn = (HttpURLConnection) request.getUrl().openConnection();
 
            if (request.headers != null) {
                for (String header : request.headers.keySet()) {
                    conn.addRequestProperty(header, request.headers.get(header));
                }
            }
 
            time = System.currentTimeMillis();            
 
            if (request instanceof POST) {
                byte[] payload = ((POST)request).body.getBytes();
 
                conn.setDoOutput(true);
                conn.setFixedLengthStreamingMode(payload.length);
                conn.getOutputStream().write(payload);
            }
 
            int status = conn.getResponseCode();
 
            if(status != HttpURLConnection.HTTP_OK)
                response = new Response(status, conn.getResponseMessage());
            else
                response = new Response(status, readInputStream(conn.getInputStream()));
 
            response.time = System.currentTimeMillis() - time;
            if(debug) dumpRequest(request, response);
 
        } catch (IOException e) {
            e.printStackTrace(System.err);
 
        } finally {
            if (conn != null)
                conn.disconnect();
        }
 
        return response;
    }
 
    private static String readInputStream(InputStream is) throws IOException {
        Scanner s = new Scanner(is, "UTF-8").useDelimiter("\\A");
        return s.hasNext() ? s.next() : "";
    }
 
    /**
     * Convenience method to output everything about the request
     */
    public void dumpRequest(Request req, Response resp)
            throws MalformedURLException {
        StringBuilder sb = new StringBuilder();
        sb.append("=> Dumping request information:");
        sb.append("\n").append("======================= REQUEST ==========================");
        sb.append("\n==> ").append("URL: ").append(req.getUrl());
        sb.append("\n==> ").append("Method: ").append((req instanceof POST ? "POST" : "GET"));
 
        if(req.headers != null) {
            for(String header : req.headers.keySet())
                sb.append("\n===> ").append("Header: ").append(header).append(": ").append(req.headers.get(header));
        }
 
        if(req instanceof GET && ((GET)req).params != null){
            for(String param : ((GET)req).params.keySet())
                sb.append("\n===> ").append("Param: ").append(param).append("=").append(((GET)req).params.get(param));
        }
 
        if(req instanceof POST)
            sb.append("\n==> ").append("Request body: ").append(((POST)req).body);
 
        sb.append("\n").append("======================= RESPONSE =========================");
 
        sb.append("\n==> ").append("Round trip time: ").append(resp.time).append(" ms");
        sb.append("\n==> ").append("Response status: ").append(resp.status);        
        sb.append("\n==> ").append("Response body:\n").append(resp.body);        
 
        sb.append("\n==========================================================");
 
        System.out.println(sb.toString());
    }
 
 
    private class Request {
        public URL baseUrl;
        public HashMap<String,String> headers;
 
        public Request(URL baseUrl, HashMap<String,String> headers) {
            this.baseUrl = baseUrl;
            this.headers = headers;
        }
 
        public URL getUrl() throws MalformedURLException{
            return baseUrl;
        }
    }
 
    private class POST extends Request {
        public String body;
 
        public POST(URL baseUrl, HashMap<String,String> headers, String body){
            super(baseUrl, headers);
            this.body = body;
        }
    }
 
    private class GET extends Request {
        public HashMap<String,String> params;
 
        public GET(URL baseUrl, HashMap<String,String> headers, HashMap<String,String> params){
            super(baseUrl, headers);
            this.params = params;
        }
 
        @Override
        public URL getUrl() throws MalformedURLException {
            StringBuilder sb = new StringBuilder(baseUrl.toString());
            if(params != null && params.size() > 0)
                sb.append(createParamString());
 
            return new URL(sb.toString());
        }
 
        private String createParamString() {
            StringBuilder sb = new StringBuilder();
            if(params != null && params.size() > 0) {
                sb.append("?");
                //TODO: Need to encode the paramater values
                for (String param : params.keySet()) {
                    sb.append(param).append("=").append(params.get(param)).append("&");
                }
                sb.deleteCharAt(sb.length()-1);            
            }
            return sb.toString();
        }
    }
 
    public class Response {
        public int status;
        public String body;
        public long time;
 
        protected Response(int status, String body) {
            this.status = status;
            this.body = body;
        }
    }
}
  1. #1 by Ted M. Young (@jitterted) on June 10, 2013 - 8:49 pm

    Hi,

    You have a bug which will cause this not to work for GET requests: the urlConn.setDoOutput(true); should be false for GETs, since you’re not doing output (setting it to true will force the method to be a POST). You should also not set the request body for the GET.

    You should use HashMap instead of Hashtable and you don’t need the check for .size() == 0 in the addHeader() and addParam() methods — no need to create a new HashMap even if you have an empty one.

  2. #2 by jeffro on June 11, 2013 - 10:48 am

    Thanks Ted, since I posted this page I haven’t given it much attention. I will implement your recommendations, all good stuff. I’ve made a couple updates myself, but I’ve been too lazy to post them.

  3. #3 by Jovino Goncalves on January 27, 2014 - 8:57 am

    how I do to make PUT and DELETE requests?

  4. #4 by jeffro on February 3, 2014 - 11:32 am

    @jovinosumicity This has not been implemented yet, it’s planned though

  5. #5 by T.A. Nguyen on February 19, 2014 - 1:48 pm

    Nice and clean. If you use the URLConnection instead of HTTP* then it would support https as requested in the url. Just need to do a “flush” whenever you finish writing/sending data just to signal the other end ok to read.

    I have a much simpler version here:

    http://ta.cnci.org/more-about-java/29-java-servers-and-services/232-remoteservice-a-simple-client-to-test-your-restful-and-soap-services

  6. #6 by jeffro on March 7, 2014 - 9:53 am

    Thanks for the comment! I will look into using you advice, I am always curious about new ways to do things and making my code better.

(will not be published)