All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.continual.jsonHttpClient.JsonOverHttpClient Maven / Gradle / Ivy

There is a newer version: 0.3.16
Show newest version
package io.continual.jsonHttpClient;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONObject;

import io.continual.util.data.StreamTools;
import io.continual.util.data.json.CommentedJsonTokener;

/**
 * This is a simple abstraction of an HTTP client implementation that manipulates JSON resources. The
 * point is mainly to isolate our code from specific HTTP libraries, which helps us to embed into
 * systems with an existing preferred HTTP client like Apache or OkHttp.
 */
public interface JsonOverHttpClient
{
	/**
	 * An exception representing a failure to complete an operation.
	 */
	public class HttpServiceException extends Exception
	{
		public HttpServiceException ( String msg ) { super(msg); }
		public HttpServiceException ( Throwable t ) { super(t); }
		public HttpServiceException ( String msg, Throwable t ) { super(msg,t); }
		private static final long serialVersionUID = 1L;
	}

	/**
	 * An exception representing a body parsing problem.
	 */
	public class BodyFormatException extends Exception
	{
		public BodyFormatException ( Exception x ) { super(x); }
		public BodyFormatException ( String msg ) { super(msg); }
		public BodyFormatException ( String msg, Exception x ) { super(msg,x); }
		private static final long serialVersionUID = 1L;
	}

	/**
	 * An interface for creating a body instance given an HTTP payload.
	 * @param 
	 */
	interface BodyFactory
	{
		T getBody ( long contentLength, String mimeType, InputStream byteStream ) throws BodyFormatException;
	}

	/**
	 * A class to create JSON objects from HTTP payloads.
	 */
	public static class JsonObjectBodyFactory implements BodyFactory
	{
		@Override
		public JSONObject getBody ( long contentLength, String mimeType, InputStream byteStream )
		{
			return new JSONObject ( new CommentedJsonTokener ( byteStream ) );
		}
	}
	
	/**
	 * A class to create JSON arrays from HTTP payloads.
	 */
	public static class JsonArrayBodyFactory implements BodyFactory
	{
		@Override
		public JSONArray getBody ( long contentLength, String mimeType, InputStream byteStream )
		{
			return new JSONArray ( new CommentedJsonTokener ( byteStream ) );
		}
	}
	
	/**
	 * The HTTP call response 
	 */
	interface HttpResponse extends AutoCloseable
	{
		/**
		 * Get the HTTP status code.
		 * @return the HTTP status code
		 */
		int getCode ();

		/**
		 * Get the HTTP status message (e.g. "OK" in "200 OK")
		 * @return the HTTP status message
		 */
		String getMessage ();
		
		/**
		 * At this level, the close() call does not throw.
		 */
		@Override
	    void close ();

		/**
		 * Get the HTTP body as a JSON document
		 * @return a JSON document from the server
		 * @throws BodyFormatException 
		 */
		default JSONObject getBody () throws BodyFormatException
		{
			return getBody ( new JsonObjectBodyFactory () );
		}

		/**
		 * Get the HTTP body as a JSON array
		 * @return a JSON array from the server
		 * @throws BodyFormatException 
		 */
		default JSONArray getArrayBody () throws BodyFormatException
		{
			return getBody ( new JsonArrayBodyFactory () );
		}

		/**
		 * For troubleshooting, we can just read the response as a string
		 * @return a string
		 * @throws BodyFormatException 
		 */
		default String getStringBody () throws BodyFormatException
		{
			return getBody ( new BodyFactory ()
			{
				@Override
				public String getBody ( long contentLength, String mimeType, InputStream byteStream ) throws BodyFormatException
				{
					try
					{
						final byte[] bytes = StreamTools.readBytes ( byteStream );
						return new String ( bytes, StandardCharsets.UTF_8 );
					}
					catch ( IOException e )
					{
						throw new BodyFormatException ( e );
					}
				}
			} );
		}
		
		/**
		 * Get the HTTP body as an arbitrary type
		 * @param 
		 * @param bf
		 * @return a T
		 * @throws BodyFormatException 
		 */
		 T getBody ( BodyFactory bf ) throws BodyFormatException;
		
		/**
		 * Return true if the call was a success
		 * @return true if the HTTP call replied with a success code
		 */
		default boolean isSuccess ()
		{
			final int code = getCode();
			return code >= 200 && code < 300;
		}

		/**
		 * Return true if the server replied with 401 status. Note that isClientError() will 
		 * also return true in this case.
		 * @return true if the server replied with 401 status.
		 */
		default boolean isAuthError ()
		{
			return getCode() == 401;
		}

		/**
		 * Return true if the server replied "not found"
		 * @return true if the server replied "not found"
		 */
		default boolean isNotFound ()
		{
			return getCode() == 404;
		}

		/**
		 * Return true if the call resulted in a client-side error
		 * @return true if there was a client request error
		 */
		default boolean isClientError ()
		{
			final int code = getCode();
			return code >= 400 && code < 500;
		}

		/**
		 * Return true if the call resulted in a server-side error
		 * @return true if there was a server error
		 */
		default boolean isServerError ()
		{
			return getCode () >= 500;
		}
	}

	/**
	 * An HTTP request
	 */
	public interface HttpRequest
	{
		/**
		 * Specify the URL for the request
		 * @param url
		 * @return this request
		 */
		HttpRequest onPath ( String url );

		/**
		 * Specify the user credentials
		 * @param creds
		 * @return this request
		 */
		HttpRequest asUser ( HttpUsernamePasswordCredentials creds );

		/**
		 * Add a header to the request
		 * @param key
		 * @param value
		 * @return this request
		 */
		HttpRequest withHeader ( String key, String value );

		/**
		 * Add a set of headers to the request
		 * @param headers
		 * @return this request
		 */
		HttpRequest withHeaders ( Map headers );

		/**
		 * Set a query string on the request, overwriting any other query settings.
		 * If this value is provided, prior calls to addQueryParam are ignored.
		 * @param qs
		 * @return this request
		 */
		HttpRequest withExplicitQueryString ( String qs );

		/**
		 * Add a query key/value to the request, preserving any existing settings. If
		 * withExplicitQueryString() has been called prior to this call, its setting is ignored.
		 * @param key
		 * @param val
		 * @return this request
		 */
		HttpRequest addQueryParam ( String key, String val );

		/**
		 * Add a map of query key/values to the request. This is equivalent to calling
		 * addQueryParam for each entry.
		 * @param qsMap
		 * @return this request
		 */
		HttpRequest withQueryString ( Map qsMap );

		/**
		 * Execute a GET and return the response
		 * @return a response which must be closed
		 */
		HttpResponse get () throws HttpServiceException;

		/**
		 * Execute a DELETE and return the response
		 * @return a response which must be closed
		 */
		HttpResponse delete () throws HttpServiceException;

		/**
		 * Execute a PUT and return the response
		 * @param body the JSON to post
		 * @return a response which must be closed
		 */
		HttpResponse put ( JSONObject body ) throws HttpServiceException;

		/**
		 * Execute a PATCH and return the response
		 * @param body the JSON to post
		 * @return a response which must be closed
		 */
		HttpResponse patch ( JSONObject body ) throws HttpServiceException;

		/**
		 * Execute a POST and return the response
		 * @param body the JSON to post
		 * @return a response which must be closed
		 */
		HttpResponse post ( JSONObject body ) throws HttpServiceException;

		/**
		 * Execute a POST and return the response
		 * @param body the JSON to post
		 * @return a response which must be closed
		 */
		HttpResponse post ( JSONArray body ) throws HttpServiceException;
	}

	/**
	 * Start a request on this client
	 * @return a request
	 */
	HttpRequest newRequest ();

	/**
	 * Close this client. Calling newRequest() after close() is an illegal state.
	 */
	default void close () {}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy