content: 
  package com.github.alexcojocaru.mojo.elasticsearch.v2.client;
  
  import java.io.BufferedReader;
  import java.io.Closeable;
  import java.io.IOException;
  import java.io.InputStreamReader;
  
  import org.apache.commons.lang3.Validate;
  import org.apache.http.HttpEntity;
  import org.apache.http.HttpHeaders;
  import org.apache.http.HttpResponse;
  import org.apache.http.client.HttpClient;
  import org.apache.http.client.config.RequestConfig;
  import org.apache.http.client.methods.HttpDelete;
  import org.apache.http.client.methods.HttpGet;
  import org.apache.http.client.methods.HttpPost;
  import org.apache.http.client.methods.HttpPut;
  import org.apache.http.client.methods.HttpRequestBase;
  import org.apache.http.config.SocketConfig;
  import org.apache.http.conn.HttpClientConnectionManager;
  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.impl.conn.PoolingHttpClientConnectionManager;
  import org.apache.maven.plugin.logging.Log;
  
  import com.fasterxml.jackson.databind.DeserializationFeature;
  import com.fasterxml.jackson.databind.ObjectMapper;
  import com.github.alexcojocaru.mojo.elasticsearch.v2.InstanceConfiguration;
  
  /**
   * A REST client for Elasticsearch.
   *
   * @author Alex Cojocaru
   */
  public class ElasticsearchClient implements Closeable
  {
      private final ObjectMapper mapper;
      private final HttpClient httpClient;
      private final PoolingHttpClientConnectionManager connectionManager;
  
      private final Log log;
      private final String hostname;
      private final int port;
  
  
      private ElasticsearchClient(
              Log log,
              ObjectMapper mapper,
              HttpClient httpClient,
              PoolingHttpClientConnectionManager connectionManager,
              String hostname,
              int port)
      {
          this.log = log;
          this.mapper = mapper;
          this.httpClient = httpClient;
          this.connectionManager = connectionManager;
          
          this.hostname = hostname;
          this.port = port;
      }
  
  
      @Override
      public void close()
      {
          connectionManager.close();
      }
  
  
      public <T> T get(String path, Class<T> clazz) throws ElasticsearchClientException
      {
          String uri = String.format("http://%s:%d%s", hostname, port, path);
          log.debug(String.format("Sending GET request to %s", uri));
  
          HttpGet request = new HttpGet(uri);
          request.setHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.getMimeType());
  
          String content = executeRequest(request);
          T result = deserialize(content, clazz);
  
          return result;
      }
  
      public <T> T get(String path, String entity, Class<T> clazz) throws ElasticsearchClientException
      {
          String uri = String.format("http://%s:%d%s", hostname, port, path);
          log.debug(String.format("Sending GET request to %s with entity '%s'", uri, entity));
  
          HttpGetWithEntity request = new HttpGetWithEntity(uri);
          request.setHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.getMimeType());
          request.setEntity(new StringEntity(entity, ContentType.APPLICATION_JSON));
  
          String content = executeRequest(request);
          T result = deserialize(content, clazz);
  
          return result;
      }
  
      public void put(String path, String entity) throws ElasticsearchClientException
      {
          String uri = String.format("http://%s:%d%s", hostname, port, path);
          log.info(String.format("Sending PUT request to %s with entity '%s'", uri, entity));
  
          HttpPut request = new HttpPut(uri);
          request.setEntity(new StringEntity(entity, ContentType.APPLICATION_JSON));
          executeRequest(request);
      }
  
      public <T> T post(String path, String entity, Class<T> clazz)
              throws ElasticsearchClientException
      {
          String uri = String.format("http://%s:%d%s", hostname, port, path);
          log.debug(String.format("Sending POST request to %s with entity '%s'", uri, entity));
  
          HttpPost request = new HttpPost(uri);
          request.setHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.getMimeType());
          request.setEntity(new StringEntity(entity, ContentType.APPLICATION_JSON));
  
          String content = executeRequest(request);
          T result = deserialize(content, clazz);
  
          return result;
      }
  
      public void delete(String path) throws ElasticsearchClientException
      {
          String uri = String.format("http://%s:%d%s", hostname, port, path);
          log.debug(String.format("Sending DELETE request to %s", uri));
  
          HttpDelete request = new HttpDelete(uri);
          executeRequest(request);
      }
  
      protected String readContent(HttpEntity entity)
              throws UnsupportedOperationException, IOException
      {
          try (BufferedReader br = new BufferedReader(new InputStreamReader(entity.getContent())))
          {
              StringBuilder content = new StringBuilder();
  
              String line;
              while ((line = br.readLine()) != null)
              {
                  content.append(line);
              }
  
              return content.toString();
          }
      }
  
      @SuppressWarnings("unchecked")
      public <T> T deserialize(String content, Class<T> clazz) throws ElasticsearchClientException
      {
          if (clazz == String.class)
          {
              return (T)content;
          }
  
          Validate.notNull(mapper, "Has the ElasticsearchClient been initialized?");
          
          try
          {
              return mapper.readValue(content, clazz);
          }
          catch (IOException ex)
          {
              throw new ElasticsearchClientException(String.format(
                      "Cannot deserialize the content '%s' to class %s", content, clazz));
          }
      }
  
      protected String executeRequest(HttpRequestBase request) throws ElasticsearchClientException
      {
          Validate.notNull(httpClient, "Has the ElasticsearchClient been initialized?");
          
          try
          {
              HttpResponse response = httpClient.execute(request);
  
              int statusCode = response.getStatusLine().getStatusCode();
              String content = readContent(response.getEntity());
              log.debug(String.format(
                      "Response with status code %d and content: %s", statusCode, content));
  
              // some PUT requests return 200, some 201 :-O
              if (statusCode != 200 && statusCode != 201)
              {
                  throw new ElasticsearchClientException(request.getMethod(), statusCode, content);
              }
  
              return content;
          }
          catch (IOException e)
          {
              throw new ElasticsearchClientException(e);
          }
          finally
          {
              request.releaseConnection();
          }
      }
      
      
      public static class Builder
      {
          private Log log;
          private String hostname;
          private int port;
          private int socketTimeout;
          
          public Builder withLog(Log log)
          {
              this.log = log;
              return this;
          }
          
          public Builder withHostname(String hostname)
          {
              this.hostname = hostname;
              return this;
          }
          
          public Builder withPort(int port)
          {
              this.port = port;
              return this;
          }
          
          public Builder withSocketTimeout(int socketTimeout)
          {
              this.socketTimeout = socketTimeout;
              return this;
          }
          
          public Builder withInstanceConfiguration(InstanceConfiguration config)
          {
              this.log = config.getClusterConfiguration().getLog();
              this.socketTimeout = config.getClusterConfiguration().getClientSocketTimeout();
              this.port = config.getHttpPort();
              return this;
          }
          
          public ElasticsearchClient build()
          {
              PoolingHttpClientConnectionManager connectionManager = buildHttpClientManager(
                      socketTimeout);
  
              return new ElasticsearchClient(
                      log,
                      buildObjectMapper(),
                      buildHttpClient(connectionManager),
                      connectionManager,
                      hostname,
                      port);
          }
          
          private ObjectMapper buildObjectMapper()
          {
              return new ObjectMapper()
                      .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
          }
      
          private static PoolingHttpClientConnectionManager buildHttpClientManager(int socketTimeout)
          {
              PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
              cm.setMaxTotal(3);
              cm.setDefaultMaxPerRoute(2);
  
              cm.setValidateAfterInactivity(1);
  
              cm.setDefaultSocketConfig(SocketConfig.custom()
                      .setSoTimeout(socketTimeout)
                      .setSoLinger(0)
                      .setTcpNoDelay(true)
                      .build());
  
              Runtime.getRuntime().addShutdownHook(new Thread()
              {
                  @Override
                  public void run()
                  {
                      cm.close();
                  }
              });
  
              return cm;
          }
  
          private static HttpClient buildHttpClient(HttpClientConnectionManager connectionManager)
          {
              RequestConfig requestConfig = RequestConfig.custom()
                      .setConnectTimeout(1500)
                      .setConnectionRequestTimeout(1500)
                      .build();
  
              CloseableHttpClient httpClient = HttpClients.custom()
                      .setConnectionManager(connectionManager)
                      .setDefaultRequestConfig(requestConfig)
                      // use the default retry handler:
                      // https://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html#d5e305
                      .build();
  
              return httpClient;
          }
      }
  }
  