Don't perform REST calls when remote PeerInfo not available

Replace PeerInfo with Optional<PeerInfo>. When the PeerInfo value
is not available don't even try sending REST calls.

Assuming we are going to implement automatic remote peer discovery
the remote peer info may be unavailable when the remote peer is shut
down.

Change-Id: I8a4667328eedbab393ae672f54a8c2aef6c639ca
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSession.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSession.java
index 7a95f44..6e2c7a8 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSession.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSession.java
@@ -14,6 +14,7 @@
 
 package com.ericsson.gerrit.plugins.highavailability.forwarder.rest;
 
+import com.google.common.base.Optional;
 import com.google.common.base.Strings;
 import com.google.common.net.MediaType;
 import com.google.inject.Inject;
@@ -32,11 +33,11 @@
 
 class HttpSession {
   private final CloseableHttpClient httpClient;
-  private final Provider<PeerInfo> peerInfo;
+  private final Provider<Optional<PeerInfo>> peerInfo;
 
   @Inject
   HttpSession(CloseableHttpClient httpClient,
-      Provider<PeerInfo> peerInfo) {
+      Provider<Optional<PeerInfo>> peerInfo) {
     this.httpClient = httpClient;
     this.peerInfo = peerInfo;
   }
@@ -46,7 +47,7 @@
   }
 
   HttpResult post(String endpoint, String content) throws IOException {
-    HttpPost post = new HttpPost(peerInfo.get().getDirectUrl() + endpoint);
+    HttpPost post = new HttpPost(getPeerInfo().getDirectUrl() + endpoint);
     if (!Strings.isNullOrEmpty(content)) {
       post.addHeader("Content-Type", MediaType.JSON_UTF_8.toString());
       post.setEntity(new StringEntity(content, StandardCharsets.UTF_8));
@@ -56,7 +57,15 @@
 
   HttpResult delete(String endpoint) throws IOException {
     return httpClient.execute(
-        new HttpDelete(peerInfo.get().getDirectUrl() + endpoint),
+        new HttpDelete(getPeerInfo().getDirectUrl() + endpoint),
         new HttpResponseHandler());
   }
+
+  private PeerInfo getPeerInfo() throws PeerInfoNotAvailableException {
+    PeerInfo info = peerInfo.get().orNull();
+    if (info == null) {
+      throw new PeerInfoNotAvailableException();
+    }
+    return info;
+  }
 }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/PeerInfoNotAvailableException.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/PeerInfoNotAvailableException.java
new file mode 100644
index 0000000..df94f52
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/PeerInfoNotAvailableException.java
@@ -0,0 +1,21 @@
+// Copyright (C) 2017 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 java.io.IOException;
+
+public class PeerInfoNotAvailableException extends IOException {
+  private static final long serialVersionUID = 1L;
+}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/PeerInfoModule.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/PeerInfoModule.java
index 2384b3b..c4f38ce 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/PeerInfoModule.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/PeerInfoModule.java
@@ -14,11 +14,14 @@
 
 package com.ericsson.gerrit.plugins.highavailability.peers;
 
+import com.google.common.base.Optional;
 import com.google.inject.AbstractModule;
+import com.google.inject.TypeLiteral;
 
 public class PeerInfoModule extends AbstractModule {
   @Override
   protected void configure() {
-    bind(PeerInfo.class).toProvider(PluginConfigPeerInfoProvider.class);
+    bind(new TypeLiteral<Optional<PeerInfo>>() {})
+        .toProvider(PluginConfigPeerInfoProvider.class);
   }
 }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/PluginConfigPeerInfoProvider.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/PluginConfigPeerInfoProvider.java
index 9e937ac..076115b 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/PluginConfigPeerInfoProvider.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/PluginConfigPeerInfoProvider.java
@@ -14,22 +14,23 @@
 
 package com.ericsson.gerrit.plugins.highavailability.peers;
 
+import com.google.common.base.Optional;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
 import com.ericsson.gerrit.plugins.highavailability.Configuration;
 
-public class PluginConfigPeerInfoProvider implements Provider<PeerInfo> {
+public class PluginConfigPeerInfoProvider implements Provider<Optional<PeerInfo>> {
 
-  private final PeerInfo peerInfo;
+  private final Optional<PeerInfo> peerInfo;
 
   @Inject
   PluginConfigPeerInfoProvider(Configuration cfg) {
-    peerInfo = new PeerInfo(cfg.getUrl());
+    peerInfo = Optional.of(new PeerInfo(cfg.getUrl()));
   }
 
   @Override
-  public PeerInfo get() {
+  public Optional<PeerInfo> get() {
     return peerInfo;
   }
 }
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSessionTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSessionTest.java
index ca59f8d..bbf7800 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSessionTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSessionTest.java
@@ -15,26 +15,34 @@
 package com.ericsson.gerrit.plugins.highavailability.forwarder.rest;
 
 import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
 import static com.github.tomakehurst.wiremock.client.WireMock.delete;
 import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.exactly;
 import static com.github.tomakehurst.wiremock.client.WireMock.post;
 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.verify;
 import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import com.google.common.base.Optional;
+import com.google.inject.util.Providers;
+
 import com.ericsson.gerrit.plugins.highavailability.Configuration;
 import com.ericsson.gerrit.plugins.highavailability.forwarder.rest.HttpResponseHandler.HttpResult;
 import com.ericsson.gerrit.plugins.highavailability.peers.PeerInfo;
 import com.github.tomakehurst.wiremock.http.Fault;
 import com.github.tomakehurst.wiremock.junit.WireMockRule;
 import com.github.tomakehurst.wiremock.stubbing.Scenario;
-import com.google.inject.util.Providers;
 
 import org.junit.Before;
-import org.junit.ClassRule;
+import org.junit.Rule;
 import org.junit.Test;
 
+import java.io.IOException;
 import java.net.SocketTimeoutException;
 
 public class HttpSessionTest {
@@ -56,13 +64,15 @@
 
   private HttpSession httpSession;
 
-  @ClassRule
-  public static WireMockRule wireMockRule = new WireMockRule(0);
+  @Rule
+  public WireMockRule wireMockRule = new WireMockRule(0);
+
+  private Configuration cfg;
 
   @Before
   public void setUp() throws Exception {
     String url = "http://localhost:" + wireMockRule.port();
-    Configuration cfg = mock(Configuration.class);
+    cfg = mock(Configuration.class);
     when(cfg.getUser()).thenReturn("user");
     when(cfg.getPassword()).thenReturn("pass");
     when(cfg.getMaxTries()).thenReturn(MAX_TRIES);
@@ -74,8 +84,7 @@
     when(peerInfo.getDirectUrl()).thenReturn(url);
     httpSession = new HttpSession(
         new HttpClientProvider(cfg).get(),
-        Providers.of(peerInfo));
-    wireMockRule.resetRequests();
+        Providers.of(Optional.of(peerInfo)));
   }
 
   @Test
@@ -158,4 +167,18 @@
 
     assertThat(httpSession.post(ENDPOINT).isSuccessful()).isFalse();
   }
+
+  @Test
+  public void testNoRequestWhenPeerInfoUnknown() throws IOException {
+    httpSession = new HttpSession(
+        new HttpClientProvider(cfg).get(),
+        Providers.of(Optional.<PeerInfo> absent()));
+    try {
+      httpSession.post(ENDPOINT);
+      fail("Expected PeerInfoNotAvailableException");
+    } catch (PeerInfoNotAvailableException e) {
+      // good
+    }
+    verify(exactly(0), anyRequestedFor(anyUrl()));
+  }
 }