RestClient Framework With Using HTTPClient

RESTClient Framework

Why Need It?

Recently I have worked on to consuming Rest APIs. We have different producers who expose REST APIs so instead of creating the different interface for all different REST APIs, I have decided to build a framework which can consume REST APIs and Return the object based on what I want.

So I have started exploring clients, I have explored

  • RESTEasy
  • Jersey
  • HTTPClient
  • Spring REST Template

After some research and finding some issue to conflict specification, I decided to use HTTPClient.

Why I choose HTTPClient?

RESTEasy and Jersey latest version have implemented JAX-RS 2.0 client specification which is part of J2EE/JAVAEE 7 and I really want use Specification but existing code based has been supported JAVAEE 6 so I could not use these two frameworks, however I can use their old/archive build but that too much old which I would like to avoid.

So finally I have decided to go with Apache HTTP Client which is not the specification but some of the frameworks are using as under the hood. And it is also seamless implementation and wide open community.

Here is REST Client Framework,

  • Here is pom.xml dependencies,

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jackson.version>2.8.5</jackson.version>
<httpclient.version>4.4</httpclient.version>
</properties>

<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>

<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.4</version>
</dependency>

</dependencies>

  • First thought came into my mind is that Developer should about that they are trying to use this Framework by passing request object and expecting response should be in-line with JSON format. So I have created below hierarchy for the JSON parsing,
package rest.model;
/**
* Develoepr need to aware about what they could pass to RestClient.
* @author Shailendra Soni
*
*/
public interface JSONPojo {

}

  • JSON request like if you want to call POST with passing values as JSON into Body.

package rest.model;
/**
* Implement this interface , if REST API is expecting JSON in the request body part.
* @author Shailendra Soni
*
*/
public interface JSONRequest extends JSONPojo{

}


package rest.model;
/**
* Implement this interface at POJO which member variables are corrosponding to JSON.
* @author Shailendra Soni
*
*/
public interface JSONResponse extends JSONPojo{

}

  • Below is convertor class which convert from JSON to POJO and POJO to JSON

package rest.restclient;

/**
* Convert from POJO to JSON and JSON to POJO
* @author Shailendra Soni
*/
import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

import rest.model.JSONPojo;

public class JSONConvertor<T extends JSONPojo> {

private static final String EMPTY_STRING="";

public T jsonToPojo(String jsonString, Class<T> classzz) throws JsonGenerationException, JsonMappingException, IOException {

ObjectMapper objMapper = new ObjectMapper();
JsonNode jsonNode = objMapper.readTree(jsonString);
objMapper.configure(MapperFeature.AUTO_DETECT_FIELDS, true);
objMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return objMapper.readValue(jsonNode.traverse(), classzz);
}

public String pojoToJson(T t) throws JsonProcessingException {
if (t == null) {
return EMPTY_STRING;
}
ObjectMapper objMapper = new ObjectMapper();
return objMapper.writeValueAsString(t);
}

}

  • Below is RestClient implementation, right now I am having POST and GET methods implementation but if someone wants to extend it then please implement new methods into this framework by forking the GIT repository.

package rest.restclient;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import rest.exception.APIException;
import rest.model.JSONPojo;
import rest.model.JSONRequest;
import rest.model.JSONResponse;

/**
* This class responsibility to call REST API and Response based on T.
*
* @author Shailendra Soni
*
* @param <T> :- extends JSONResponse
*/
public class RestClient<T extends JSONResponse> {

private String restURL;

private int timeOut = 0; // in second

public RestClient(String restURL) {
this.restURL = restURL;
}

public void setTimeOut(int timeOut) {
this.timeOut = timeOut;
}

/**
* Rest GET implementation
*
* @param clazz
* @param headers
* @return
* @throws InvalidKeyException
* @throws NoSuchAlgorithmException
* @throws URISyntaxException
* @throws ClientProtocolException
* @throws IOException
* @throws APIException
*/
public T getRest(Class<T> clazz, Map<String, String> headers)
throws InvalidKeyException, NoSuchAlgorithmException, URISyntaxException, ClientProtocolException, IOException, APIException {

CloseableHttpClient httpClient = null;
try {
httpClient = HttpClients.createDefault();

HttpGet httpGet = new HttpGet(this.restURL);
setTimeOut(httpGet);
setHeadder(httpGet, HttpGet.METHOD_NAME, headers);

CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK || httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) {
String result = getResponseInString(httpResponse);
System.out.println("Response :- " + result);
JSONConvertor<T> jsonConvertor1 = new JSONConvertor<T>();
T t = jsonConvertor1.jsonToPojo(result, clazz);
return t;
} else {
StringBuilder message = new StringBuilder("Status Code :- ");
message.append(httpResponse.getStatusLine().getStatusCode());
message.append(" Message :- ");
message.append(getResponseInString(httpResponse));
throw new APIException(message.toString());
}
} finally {
if (httpClient != null) {
httpClient.close();
}

}

}

/**
* This method server POST method of REST API.
*
* @param requestObject
* - Convert POJO to JSON :- JSONRequest
* @param class1
* @param headers
* :- Map<String,String>
* @return T :- Response JSON to POJO
* @throws IOException
* @throws APIException
* @throws URISyntaxException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
*/
public T postRest(JSONRequest requestObject, Class<T> clazz, Map<String, String> headers)
throws IOException, APIException, InvalidKeyException, NoSuchAlgorithmException, URISyntaxException {
CloseableHttpClient httpClient = null;
try {
httpClient = HttpClients.createDefault();

JSONConvertor<JSONPojo> jsonConvertor = new JSONConvertor<JSONPojo>();
StringEntity jsonRequest = new StringEntity(jsonConvertor.pojoToJson(requestObject));

HttpPost httpPost = new HttpPost(this.restURL);
setTimeOut(httpPost);
setHeadder(httpPost, HttpPost.METHOD_NAME, headers);
httpPost.setEntity(jsonRequest);

CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK || httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) {
String result = getResponseInString(httpResponse);
JSONConvertor<T> jsonConvertor1 = new JSONConvertor<T>();
T t = jsonConvertor1.jsonToPojo(result, clazz);
return t;
} else {
StringBuilder message = new StringBuilder("Status Code :- ");
message.append(httpResponse.getStatusLine().getStatusCode());
message.append(" Message :- ");
message.append(getResponseInString(httpResponse));
throw new APIException(message.toString());
}
} finally {
if (httpClient != null) {
httpClient.close();
}

}
}

private void setHeadder(HttpRequestBase httpRequest, String httpVerb, Map<String, String> headers)
throws InvalidKeyException, NoSuchAlgorithmException, MalformedURLException, URISyntaxException {

httpRequest.addHeader("content-type", ContentType.APPLICATION_JSON.getMimeType());
if (headers != null && !headers.isEmpty()) {
for (String keyHeader : headers.keySet()) {
httpRequest.addHeader(keyHeader, headers.get(keyHeader));
}
}
}

private void setTimeOut(HttpRequestBase httpRequest) {
if (this.timeOut > 0) {
httpRequest.setConfig(getConfigurationForTimeOut());
}
}

private String getResponseInString(CloseableHttpResponse httpResponse) throws IOException {
HttpEntity entity = httpResponse.getEntity();
String result = EntityUtils.toString(entity);
return result;
}

private RequestConfig getConfigurationForTimeOut() {
RequestConfig config = RequestConfig.custom().setConnectTimeout(this.timeOut * 1000).setConnectionRequestTimeout(this.timeOut * 1000).setSocketTimeout(this.timeOut * 1000)
.build();
return config;
}
}

  • By using this framework, developer do not need to explore REST client and also developer do not worry about following things
    • Convert request object to JSON
    • Core implementation to calling REST APIs
    • Convert JSON to expected class object
    • Connection Timeout implementation
  • Below is my implementation of RESTClient Framework
import java.util.HashMap;
import java.util.Map;

import com.americanexpress.sis.rest.model.JSONRequest;
import com.americanexpress.sis.rest.model.JSONResponse;
import com.americanexpress.sis.rest.restclient.RestClient;

public class TestRestClient {

public static void main(String[] args) throws Exception {
String restURL = "http://localhost:8080/RESTfulExample/json/product/postproduct";

Product p = new Product();

RestClient<Product> rc = new RestClient<Product>(restURL);
Product productResponse = rc.postRest( p, Product.class, getHeaders());
System.out.println("Product Information :- " + ps.toString());
}

public static Map<String,String> getHeaders(){
Map<String,String> headers = new HashMap<String, String>();
headers.put("accountid", "testrest");
return headers;
}

}
class Product implements JSONResponse, JSONRequest{

private String name = "testing";
private int qty = 111;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getQty() {
return qty;
}

public void setQty(int qty) {
this.qty = qty;
}

@Override
public String toString() {
return "Product [name=" + name + ", qty=" + qty + "]";
}
}

    It’s Easy…
  • This is small Framework, it needs to implement more things like
    • Security:- Most of REST APIs are secure, so need to pass certificate while calling them.
    • PUT and Delete REST methods implementations
    • Cookies Implementation

    If anybody interested to implementing above things then please contact me at soni.shailendra@outlook.com for helping me to enhance this framework or fork GIT repo.

  • GIT repo :- https://github.com/sonishailendra/restclient
Advertisements

2 thoughts on “RestClient Framework With Using HTTPClient

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s