Merge branch 'stable-2.15' into stable-2.16

* stable-2.15:
  Refactor json handling for simplicity and maintainability

Change-Id: Ia02a8956a9f004422a1c5292a5c7ea745d3166f6
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/CacheRestApiServlet.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/CacheRestApiServlet.java
index ffc05a5..60e782f 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/CacheRestApiServlet.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/CacheRestApiServlet.java
@@ -34,10 +34,13 @@
   private static final long serialVersionUID = -1L;
 
   private final ForwardedCacheEvictionHandler forwardedCacheEvictionHandler;
+  private final GsonParser gson;
 
   @Inject
-  CacheRestApiServlet(ForwardedCacheEvictionHandler forwardedCacheEvictionHandler) {
+  CacheRestApiServlet(
+      ForwardedCacheEvictionHandler forwardedCacheEvictionHandler, GsonParser gson) {
     this.forwardedCacheEvictionHandler = forwardedCacheEvictionHandler;
+    this.gson = gson;
   }
 
   @Override
@@ -48,7 +51,7 @@
       String cacheName = params.get(CACHENAME_INDEX);
       String json = req.getReader().readLine();
       forwardedCacheEvictionHandler.evict(
-          CacheEntry.from(cacheName, GsonParser.fromJson(cacheName, json)));
+          CacheEntry.from(cacheName, gson.fromJson(cacheName, json)));
       rsp.setStatus(SC_NO_CONTENT);
     } catch (CacheNotFoundException e) {
       log.atSevere().log("Failed to process eviction request: %s", e.getMessage());
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServlet.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServlet.java
index 9c5c5d9..61b714c 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServlet.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServlet.java
@@ -21,15 +21,10 @@
 import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
 
 import com.ericsson.gerrit.plugins.highavailability.forwarder.ForwardedEventHandler;
-import com.google.common.base.Supplier;
 import com.google.common.io.CharStreams;
 import com.google.common.net.MediaType;
 import com.google.gerrit.server.events.Event;
-import com.google.gerrit.server.events.EventDeserializer;
-import com.google.gerrit.server.events.SupplierDeserializer;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -42,10 +37,12 @@
   private static final long serialVersionUID = -1L;
 
   private final ForwardedEventHandler forwardedEventHandler;
+  private final GsonProvider gson;
 
   @Inject
-  EventRestApiServlet(ForwardedEventHandler forwardedEventHandler) {
+  EventRestApiServlet(ForwardedEventHandler forwardedEventHandler, GsonProvider gson) {
     this.forwardedEventHandler = forwardedEventHandler;
+    this.gson = gson;
   }
 
   @Override
@@ -67,13 +64,8 @@
     }
   }
 
-  private static Event getEventFromRequest(HttpServletRequest req) throws IOException {
+  private Event getEventFromRequest(HttpServletRequest req) throws IOException {
     String jsonEvent = CharStreams.toString(req.getReader());
-    Gson gson =
-        new GsonBuilder()
-            .registerTypeAdapter(Event.class, new EventDeserializer())
-            .registerTypeAdapter(Supplier.class, new SupplierDeserializer())
-            .create();
-    return gson.fromJson(jsonEvent, Event.class);
+    return gson.get().fromJson(jsonEvent, Event.class);
   }
 }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonParser.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonParser.java
index 974f20b..8db2488 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonParser.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonParser.java
@@ -19,14 +19,19 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
 
-final class GsonParser {
+@Singleton
+class GsonParser {
+  private final Gson gson;
 
-  private GsonParser() {}
+  @Inject
+  GsonParser(GsonProvider gson) {
+    this.gson = gson.get();
+  }
 
-  static Object fromJson(String cacheName, String json) {
-    Gson gson = new GsonBuilder().create();
+  public Object fromJson(String cacheName, String json) {
     Object key;
     // Need to add a case for 'adv_bases'
     switch (cacheName) {
@@ -52,26 +57,4 @@
     }
     return key;
   }
-
-  static String toJson(String cacheName, Object key) {
-    Gson gson = new GsonBuilder().create();
-    String json;
-    // Need to add a case for 'adv_bases'
-    switch (cacheName) {
-      case Constants.ACCOUNTS:
-        json = gson.toJson(key, Account.Id.class);
-        break;
-      case Constants.GROUPS:
-        json = gson.toJson(key, AccountGroup.Id.class);
-        break;
-      case Constants.GROUPS_BYINCLUDE:
-      case Constants.GROUPS_MEMBERS:
-        json = gson.toJson(key, AccountGroup.UUID.class);
-        break;
-      case Constants.PROJECT_LIST:
-      default:
-        json = gson.toJson(key);
-    }
-    return json;
-  }
 }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonProvider.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonProvider.java
new file mode 100644
index 0000000..e62cec2
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonProvider.java
@@ -0,0 +1,34 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.ericsson.gerrit.plugins.highavailability.forwarder.rest;
+
+import com.google.common.base.Supplier;
+import com.google.gerrit.server.events.Event;
+import com.google.gerrit.server.events.EventDeserializer;
+import com.google.gerrit.server.events.SupplierDeserializer;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.inject.Provider;
+
+public class GsonProvider implements Provider<Gson> {
+
+  @Override
+  public Gson get() {
+    return new GsonBuilder()
+        .registerTypeAdapter(Event.class, new EventDeserializer())
+        .registerTypeAdapter(Supplier.class, new SupplierDeserializer())
+        .create();
+  }
+}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java
index 044fce0..fb3aba9 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java
@@ -48,17 +48,20 @@
   private final String pluginRelativePath;
   private final Configuration cfg;
   private final Provider<Set<PeerInfo>> peerInfoProvider;
+  private final GsonProvider gson;
 
   @Inject
   RestForwarder(
       HttpSession httpClient,
       @PluginName String pluginName,
       Configuration cfg,
-      Provider<Set<PeerInfo>> peerInfoProvider) {
+      Provider<Set<PeerInfo>> peerInfoProvider,
+      GsonProvider gson) {
     this.httpSession = httpClient;
     this.pluginRelativePath = Joiner.on("/").join("plugins", pluginName);
     this.cfg = cfg;
     this.peerInfoProvider = peerInfoProvider;
+    this.gson = gson;
   }
 
   @Override
@@ -109,7 +112,7 @@
 
   @Override
   public boolean evict(final String cacheName, final Object key) {
-    String json = GsonParser.toJson(cacheName, key);
+    String json = gson.get().toJson(key);
     return execute(RequestMethod.POST, "invalidate cache " + cacheName, "cache", cacheName, json);
   }
 
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/CacheRestApiServletTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/CacheRestApiServletTest.java
index 29f63fd..8b08d24 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/CacheRestApiServletTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/CacheRestApiServletTest.java
@@ -44,7 +44,9 @@
 
   @Before
   public void setUp() {
-    servlet = new CacheRestApiServlet(forwardedCacheEvictionHandlerMock);
+    servlet =
+        new CacheRestApiServlet(
+            forwardedCacheEvictionHandlerMock, new GsonParser(new GsonProvider()));
   }
 
   @Test
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServletTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServletTest.java
index e8747b7..835977f 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServletTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/EventRestApiServletTest.java
@@ -58,7 +58,7 @@
 
   @Before
   public void createEventsRestApiServlet() throws Exception {
-    eventRestApiServlet = new EventRestApiServlet(forwardedEventHandlerMock);
+    eventRestApiServlet = new EventRestApiServlet(forwardedEventHandlerMock, new GsonProvider());
     when(requestMock.getContentType()).thenReturn(MediaType.JSON_UTF_8.toString());
   }
 
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonParserTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonParserTest.java
index b4f4dac..2f6ce7e 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonParserTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/GsonParserTest.java
@@ -19,43 +19,47 @@
 import com.ericsson.gerrit.plugins.highavailability.cache.Constants;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gson.Gson;
 import org.junit.Test;
 
 public class GsonParserTest {
   private static final Object EMPTY_JSON = "{}";
+  private final Gson gson = new Gson();
+  private final GsonParser objectUnderTest = new GsonParser(new GsonProvider());
 
   @Test
   public void accountIDParse() {
     Account.Id accountId = new Account.Id(1);
-    String json = GsonParser.toJson(Constants.ACCOUNTS, accountId);
-    assertThat(accountId).isEqualTo(GsonParser.fromJson(Constants.ACCOUNTS, json));
+    String json = gson.toJson(accountId);
+    assertThat(accountId).isEqualTo(objectUnderTest.fromJson(Constants.ACCOUNTS, json));
   }
 
   @Test
   public void accountGroupIDParse() {
     AccountGroup.Id accountGroupId = new AccountGroup.Id(1);
-    String json = GsonParser.toJson(Constants.GROUPS, accountGroupId);
-    assertThat(accountGroupId).isEqualTo(GsonParser.fromJson(Constants.GROUPS, json));
+    String json = gson.toJson(accountGroupId);
+    assertThat(accountGroupId).isEqualTo(objectUnderTest.fromJson(Constants.GROUPS, json));
   }
 
   @Test
   public void accountGroupUUIDParse() {
     AccountGroup.UUID accountGroupUuid = new AccountGroup.UUID("abc123");
-    String json = GsonParser.toJson(Constants.GROUPS_BYINCLUDE, accountGroupUuid);
-    assertThat(accountGroupUuid).isEqualTo(GsonParser.fromJson(Constants.GROUPS_BYINCLUDE, json));
+    String json = gson.toJson(accountGroupUuid);
+    assertThat(accountGroupUuid)
+        .isEqualTo(objectUnderTest.fromJson(Constants.GROUPS_BYINCLUDE, json));
   }
 
   @Test
   public void stringParse() {
     String key = "key";
-    String json = GsonParser.toJson(Constants.PROJECTS, key);
-    assertThat(key).isEqualTo(GsonParser.fromJson(Constants.PROJECTS, json));
+    String json = gson.toJson(key);
+    assertThat(key).isEqualTo(objectUnderTest.fromJson(Constants.PROJECTS, json));
   }
 
   @Test
   public void noKeyParse() {
     Object object = new Object();
-    String json = GsonParser.toJson(Constants.PROJECT_LIST, object);
+    String json = gson.toJson(object);
     assertThat(json).isEqualTo(EMPTY_JSON);
   }
 }
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java
index d516d09..55a3c2a 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java
@@ -33,7 +33,7 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.events.Event;
-import com.google.gson.GsonBuilder;
+import com.google.gson.Gson;
 import com.google.inject.Provider;
 import java.io.IOException;
 import java.util.Set;
@@ -75,6 +75,9 @@
   private static final String INDEX_GROUP_ENDPOINT =
       Joiner.on("/").join(URL, PLUGINS, PLUGIN_NAME, "index/group", UUID);
 
+  private GsonProvider gsonProvider = new GsonProvider();
+  private Gson gson = gsonProvider.get();
+
   // Event
   private static Event event = new TestEvent();
   private static final String EVENT_ENDPOINT =
@@ -94,7 +97,11 @@
     when(peersMock.get()).thenReturn(ImmutableSet.of(new PeerInfo(URL)));
     forwarder =
         new RestForwarder(
-            httpSessionMock, PLUGIN_NAME, configMock, peersMock); // TODO: Create provider
+            httpSessionMock,
+            PLUGIN_NAME,
+            configMock,
+            peersMock,
+            gsonProvider); // TODO: Create provider
   }
 
   @Test
@@ -199,7 +206,7 @@
   @Test
   public void testEvictProjectOK() throws Exception {
     String key = PROJECT_NAME;
-    String keyJson = new GsonBuilder().create().toJson(key);
+    String keyJson = gson.toJson(key);
     when(httpSessionMock.post(buildCacheEndpoint(Constants.PROJECTS), keyJson))
         .thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
     assertThat(forwarder.evict(Constants.PROJECTS, key)).isTrue();
@@ -208,7 +215,7 @@
   @Test
   public void testEvictAccountsOK() throws Exception {
     Account.Id key = new Account.Id(123);
-    String keyJson = new GsonBuilder().create().toJson(key);
+    String keyJson = gson.toJson(key);
     when(httpSessionMock.post(buildCacheEndpoint(Constants.ACCOUNTS), keyJson))
         .thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
     assertThat(forwarder.evict(Constants.ACCOUNTS, key)).isTrue();
@@ -217,7 +224,7 @@
   @Test
   public void testEvictGroupsOK() throws Exception {
     AccountGroup.Id key = new AccountGroup.Id(123);
-    String keyJson = new GsonBuilder().create().toJson(key);
+    String keyJson = gson.toJson(key);
     String endpoint = buildCacheEndpoint(Constants.GROUPS);
     when(httpSessionMock.post(endpoint, keyJson)).thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
     assertThat(forwarder.evict(Constants.GROUPS, key)).isTrue();
@@ -226,7 +233,7 @@
   @Test
   public void testEvictGroupsByIncludeOK() throws Exception {
     AccountGroup.UUID key = new AccountGroup.UUID("90b3042d9094a37985f3f9281391dbbe9a5addad");
-    String keyJson = new GsonBuilder().create().toJson(key);
+    String keyJson = gson.toJson(key);
     when(httpSessionMock.post(buildCacheEndpoint(Constants.GROUPS_BYINCLUDE), keyJson))
         .thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
     assertThat(forwarder.evict(Constants.GROUPS_BYINCLUDE, key)).isTrue();
@@ -235,7 +242,7 @@
   @Test
   public void testEvictGroupsMembersOK() throws Exception {
     AccountGroup.UUID key = new AccountGroup.UUID("90b3042d9094a37985f3f9281391dbbe9a5addad");
-    String keyJson = new GsonBuilder().create().toJson(key);
+    String keyJson = gson.toJson(key);
     when(httpSessionMock.post(buildCacheEndpoint(Constants.GROUPS_MEMBERS), keyJson))
         .thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
     assertThat(forwarder.evict(Constants.GROUPS_MEMBERS, key)).isTrue();
@@ -244,7 +251,7 @@
   @Test
   public void testEvictCacheFailed() throws Exception {
     String key = PROJECT_NAME;
-    String keyJson = new GsonBuilder().create().toJson(key);
+    String keyJson = gson.toJson(key);
     when(httpSessionMock.post(buildCacheEndpoint(Constants.PROJECTS), keyJson))
         .thenReturn(new HttpResult(FAILED, EMPTY_MSG));
     assertThat(forwarder.evict(Constants.PROJECTS, key)).isFalse();
@@ -253,7 +260,7 @@
   @Test
   public void testEvictCacheThrowsException() throws Exception {
     String key = PROJECT_NAME;
-    String keyJson = new GsonBuilder().create().toJson(key);
+    String keyJson = gson.toJson(key);
     doThrow(new IOException())
         .when(httpSessionMock)
         .post(buildCacheEndpoint(Constants.PROJECTS), keyJson);