Convert ElasticRestClientBuilder to a provider

Convert to a provider so that we only have one instance of the
client which is initialized once.

Change-Id: I4747114e28ea9a509fb86350713a7736a3becd44
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
index 48cb35d..190c498 100644
--- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
+++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
@@ -47,7 +47,6 @@
 import org.apache.http.entity.ContentType;
 import org.apache.http.nio.entity.NStringEntity;
 import org.elasticsearch.client.Response;
-import org.elasticsearch.client.RestClient;
 
 abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
   protected static final String BULK = "_bulk";
@@ -81,7 +80,7 @@
   private final Schema<V> schema;
   private final SitePaths sitePaths;
   private final String indexNameRaw;
-  private final RestClient client;
+  private final ElasticRestClientProvider client;
 
   protected final String indexName;
   protected final Gson gson;
@@ -91,7 +90,7 @@
       ElasticConfiguration cfg,
       SitePaths sitePaths,
       Schema<V> schema,
-      ElasticRestClientBuilder clientBuilder,
+      ElasticRestClientProvider client,
       String indexName) {
     this.sitePaths = sitePaths;
     this.schema = schema;
@@ -99,7 +98,7 @@
     this.queryBuilder = new ElasticQueryBuilder();
     this.indexName = cfg.getIndexName(indexName, schema.getVersion());
     this.indexNameRaw = indexName;
-    this.client = clientBuilder.build();
+    this.client = client;
   }
 
   @Override
@@ -109,11 +108,7 @@
 
   @Override
   public void close() {
-    try {
-      client.close();
-    } catch (IOException e) {
-      // Ignored.
-    }
+    // Do nothing. Client is closed by the provider.
   }
 
   @Override
@@ -135,10 +130,10 @@
   @Override
   public void deleteAll() throws IOException {
     // Delete the index, if it exists.
-    Response response = client.performRequest("HEAD", indexName);
+    Response response = client.get().performRequest("HEAD", indexName);
     int statusCode = response.getStatusLine().getStatusCode();
     if (statusCode == HttpStatus.SC_OK) {
-      response = client.performRequest("DELETE", indexName);
+      response = client.get().performRequest("DELETE", indexName);
       statusCode = response.getStatusLine().getStatusCode();
       if (statusCode != HttpStatus.SC_OK) {
         throw new IOException(
@@ -209,6 +204,6 @@
       String method, Object payload, String uri, Map<String, String> params) throws IOException {
     String payloadStr = payload instanceof String ? (String) payload : payload.toString();
     HttpEntity entity = new NStringEntity(payloadStr, ContentType.APPLICATION_JSON);
-    return client.performRequest(method, uri, params, entity);
+    return client.get().performRequest(method, uri, params, entity);
   }
 }
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
index 538f49c..ba0ee58 100644
--- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
+++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
@@ -79,9 +79,9 @@
       ElasticConfiguration cfg,
       SitePaths sitePaths,
       Provider<AccountCache> accountCache,
-      ElasticRestClientBuilder clientBuilder,
+      ElasticRestClientProvider client,
       @Assisted Schema<AccountState> schema) {
-    super(cfg, sitePaths, schema, clientBuilder, ACCOUNTS);
+    super(cfg, sitePaths, schema, client, ACCOUNTS);
     this.accountCache = accountCache;
     this.mapping = new AccountMapping(schema);
     this.schema = schema;
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
index bca52f0..e71a757 100644
--- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
+++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
@@ -110,9 +110,9 @@
       ChangeData.Factory changeDataFactory,
       FillArgs fillArgs,
       SitePaths sitePaths,
-      ElasticRestClientBuilder clientBuilder,
+      ElasticRestClientProvider client,
       @Assisted Schema<ChangeData> schema) {
-    super(cfg, sitePaths, schema, clientBuilder, CHANGES);
+    super(cfg, sitePaths, schema, client, CHANGES);
     this.db = db;
     this.changeDataFactory = changeDataFactory;
     this.fillArgs = fillArgs;
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
index 04695c7..58fe997 100644
--- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
+++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
@@ -76,9 +76,9 @@
       ElasticConfiguration cfg,
       SitePaths sitePaths,
       Provider<GroupCache> groupCache,
-      ElasticRestClientBuilder clientBuilder,
+      ElasticRestClientProvider client,
       @Assisted Schema<AccountGroup> schema) {
-    super(cfg, sitePaths, schema, clientBuilder, GROUPS);
+    super(cfg, sitePaths, schema, client, GROUPS);
     this.groupCache = groupCache;
     this.mapping = new GroupMapping(schema);
     this.schema = schema;
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java
index b98a414..5c3c051 100644
--- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java
+++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java
@@ -37,6 +37,12 @@
   }
 
   @Override
+  public void configure() {
+    super.configure();
+    install(ElasticRestClientProvider.module());
+  }
+
+  @Override
   protected Class<? extends AccountIndex> getAccountIndex() {
     return ElasticAccountIndex.class;
   }
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexVersionDiscovery.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexVersionDiscovery.java
index 85fa2c1..3314a38 100644
--- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexVersionDiscovery.java
+++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexVersionDiscovery.java
@@ -25,20 +25,19 @@
 import org.apache.http.HttpStatus;
 import org.apache.http.client.methods.HttpGet;
 import org.elasticsearch.client.Response;
-import org.elasticsearch.client.RestClient;
 
 @Singleton
 class ElasticIndexVersionDiscovery {
-  private final RestClient client;
+  private final ElasticRestClientProvider client;
 
   @Inject
-  ElasticIndexVersionDiscovery(ElasticRestClientBuilder clientBuilder) {
-    this.client = clientBuilder.build();
+  ElasticIndexVersionDiscovery(ElasticRestClientProvider client) {
+    this.client = client;
   }
 
   List<String> discover(String prefix, String indexName) throws IOException {
     String name = prefix + indexName + "_";
-    Response response = client.performRequest(HttpGet.METHOD_NAME, name + "*/_aliases");
+    Response response = client.get().performRequest(HttpGet.METHOD_NAME, name + "*/_aliases");
 
     if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
       return new JsonParser()
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticRestClientBuilder.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java
similarity index 66%
rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticRestClientBuilder.java
rename to gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java
index faf1c71..5217cfb 100644
--- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticRestClientBuilder.java
+++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java
@@ -14,8 +14,12 @@
 
 package com.google.gerrit.elasticsearch;
 
+import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import com.google.inject.Singleton;
+import java.io.IOException;
 import org.apache.http.HttpHost;
 import org.apache.http.auth.AuthScope;
 import org.apache.http.auth.UsernamePasswordCredentials;
@@ -26,20 +30,57 @@
 import org.elasticsearch.client.RestClientBuilder;
 
 @Singleton
-class ElasticRestClientBuilder {
+class ElasticRestClientProvider implements Provider<RestClient>, LifecycleListener {
 
   private final HttpHost[] hosts;
   private final String username;
   private final String password;
 
+  private RestClient client;
+
   @Inject
-  ElasticRestClientBuilder(ElasticConfiguration cfg) {
+  ElasticRestClientProvider(ElasticConfiguration cfg) {
     hosts = cfg.urls.toArray(new HttpHost[cfg.urls.size()]);
     username = cfg.username;
     password = cfg.password;
   }
 
-  RestClient build() {
+  public static LifecycleModule module() {
+    return new LifecycleModule() {
+      @Override
+      protected void configure() {
+        listener().to(ElasticRestClientProvider.class);
+      }
+    };
+  }
+
+  @Override
+  public RestClient get() {
+    if (client == null) {
+      synchronized (this) {
+        if (client == null) {
+          client = build();
+        }
+      }
+    }
+    return client;
+  }
+
+  @Override
+  public void start() {}
+
+  @Override
+  public void stop() {
+    if (client != null) {
+      try {
+        client.close();
+      } catch (IOException e) {
+        // Ignore. We can't do anything about it.
+      }
+    }
+  }
+
+  private RestClient build() {
     RestClientBuilder builder = RestClient.builder(hosts);
     setConfiguredCredentialsIfAny(builder);
     return builder.build();