Extract JChannel creation into JChannelProvider

JChannel is currently used only from the JGroups based peer discovery
code but we also need to use JChannel from JGroups based message
forwarding code. Since JChannel's protocol stack is configurable we need
to extract that logic into one place: JChannelProvider.

Change-Id: I647ba7cd13bf8e1c4ac175152eaf95859eb7e5c5
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/JGroupsForwarderModule.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/JGroupsForwarderModule.java
index 13d94f5..ecab03e 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/JGroupsForwarderModule.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/JGroupsForwarderModule.java
@@ -15,6 +15,7 @@
 package com.ericsson.gerrit.plugins.highavailability.forwarder.jgroups;
 
 import com.ericsson.gerrit.plugins.highavailability.forwarder.Forwarder;
+import com.ericsson.gerrit.plugins.highavailability.peers.jgroups.JChannelProviderModule;
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.server.events.EventGson;
 import com.google.gson.Gson;
@@ -31,6 +32,7 @@
     bind(Forwarder.class).to(JGroupsForwarder.class);
     bind(MessageDispatcher.class).toProvider(MessageDispatcherProvider.class).in(Scopes.SINGLETON);
     bind(RequestHandler.class).to(MessageProcessor.class);
+    install(new JChannelProviderModule());
     listener().to(OnStartStop.class);
   }
 
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/MessageDispatcherProvider.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/MessageDispatcherProvider.java
index edf507e..4b2fff5 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/MessageDispatcherProvider.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/jgroups/MessageDispatcherProvider.java
@@ -36,19 +36,24 @@
   private final InetAddressFinder finder;
   private final JGroups jgroupsConfig;
   private final RequestHandler requestHandler;
+  private final Provider<JChannel> channelProvider;
 
   @Inject
   MessageDispatcherProvider(
-      InetAddressFinder finder, Configuration pluginConfiguration, RequestHandler requestHandler) {
+      InetAddressFinder finder,
+      Configuration pluginConfiguration,
+      RequestHandler requestHandler,
+      Provider<JChannel> channelProvider) {
     this.finder = finder;
     this.jgroupsConfig = pluginConfiguration.jgroups();
     this.requestHandler = requestHandler;
+    this.channelProvider = channelProvider;
   }
 
   @Override
   public MessageDispatcher get() {
     try {
-      JChannel channel = new JChannel();
+      JChannel channel = channelProvider.get();
       Optional<InetAddress> address = finder.findAddress();
       if (address.isPresent()) {
         channel.getProtocolStack().getTransport().setBindAddress(address.get());
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 68707c2..0a7442c 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
@@ -15,6 +15,7 @@
 package com.ericsson.gerrit.plugins.highavailability.peers;
 
 import com.ericsson.gerrit.plugins.highavailability.Configuration;
+import com.ericsson.gerrit.plugins.highavailability.peers.jgroups.JChannelProviderModule;
 import com.ericsson.gerrit.plugins.highavailability.peers.jgroups.JGroupsPeerInfoProvider;
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.inject.TypeLiteral;
@@ -37,6 +38,7 @@
       case JGROUPS:
         bind(new TypeLiteral<Set<PeerInfo>>() {}).toProvider(JGroupsPeerInfoProvider.class);
         listener().to(JGroupsPeerInfoProvider.class);
+        install(new JChannelProviderModule());
         break;
       default:
         throw new IllegalArgumentException("Unsupported peer info strategy: " + strategy);
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JChannelProvider.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JChannelProvider.java
new file mode 100644
index 0000000..f39e641
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JChannelProvider.java
@@ -0,0 +1,63 @@
+// Copyright (C) 2018 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.peers.jgroups;
+
+import com.ericsson.gerrit.plugins.highavailability.Configuration;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.Singleton;
+import java.nio.file.Path;
+import java.util.Optional;
+import org.jgroups.JChannel;
+
+@Singleton
+public class JChannelProvider implements Provider<JChannel> {
+  private final Configuration.JGroups jgroupsConfig;
+  private final Configuration.JGroupsKubernetes jgroupsKubernetesConfig;
+
+  @Inject
+  JChannelProvider(Configuration pluginConfiguration) {
+    this.jgroupsConfig = pluginConfiguration.jgroups();
+    this.jgroupsKubernetesConfig = pluginConfiguration.jgroupsKubernetes();
+  }
+
+  @Override
+  public JChannel get() {
+    Optional<Path> protocolStack = jgroupsConfig.protocolStack();
+    try {
+      if (protocolStack.isPresent()) {
+        return new JChannel(protocolStack.get().toString());
+      }
+      if (jgroupsConfig.useKubernetes()) {
+        if (jgroupsKubernetesConfig.namespace() != null) {
+          System.setProperty("KUBERNETES_NAMESPACE", jgroupsKubernetesConfig.namespace());
+        }
+        if (!jgroupsKubernetesConfig.labels().isEmpty()) {
+          System.setProperty(
+              "KUBERNETES_LABELS", String.join(",", jgroupsKubernetesConfig.labels()));
+        }
+        return new JChannel(getClass().getResource("kubernetes.xml").toString());
+      }
+      return new JChannel();
+    } catch (Exception e) {
+      throw new ProvisionException(
+          String.format(
+              "Unable to create a channel with protocol stack: %s",
+              protocolStack.map(Path::toString).orElse("default")),
+          e);
+    }
+  }
+}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JChannelProviderModule.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JChannelProviderModule.java
new file mode 100644
index 0000000..c4764c8
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JChannelProviderModule.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2023 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.peers.jgroups;
+
+import com.google.inject.AbstractModule;
+import org.jgroups.JChannel;
+
+public class JChannelProviderModule extends AbstractModule {
+  @Override
+  protected void configure() {
+    bind(JChannel.class).toProvider(JChannelProvider.class);
+  }
+}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java
index 68efbec..fe421d0 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java
@@ -24,7 +24,6 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.net.InetAddress;
-import java.nio.file.Path;
 import java.util.Optional;
 import java.util.Set;
 import org.jgroups.Address;
@@ -57,7 +56,6 @@
   }
 
   private final Configuration.JGroups jgroupsConfig;
-  private final Configuration.JGroupsKubernetes jgroupsKubernetesConfig;
   private final InetAddressFinder finder;
   private final String myUrl;
 
@@ -67,11 +65,14 @@
 
   @Inject
   JGroupsPeerInfoProvider(
-      Configuration pluginConfiguration, InetAddressFinder finder, MyUrlProvider myUrlProvider) {
+      Configuration pluginConfiguration,
+      InetAddressFinder finder,
+      MyUrlProvider myUrlProvider,
+      JChannel channel) {
     this.jgroupsConfig = pluginConfiguration.jgroups();
-    this.jgroupsKubernetesConfig = pluginConfiguration.jgroupsKubernetes();
     this.finder = finder;
     this.myUrl = myUrlProvider.get();
+    this.channel = channel;
   }
 
   @Override
@@ -120,7 +121,6 @@
 
   public void connect() {
     try {
-      channel = getChannel();
       Optional<InetAddress> address = finder.findAddress();
       if (address.isPresent()) {
         log.atFine().log("Protocol stack: %s", channel.getProtocolStack());
@@ -146,31 +146,6 @@
     }
   }
 
-  private JChannel getChannel() throws Exception {
-    Optional<Path> protocolStack = jgroupsConfig.protocolStack();
-    try {
-      if (protocolStack.isPresent()) {
-        return new JChannel(protocolStack.get().toString());
-      }
-      if (jgroupsConfig.useKubernetes()) {
-        if (jgroupsKubernetesConfig.namespace() != null) {
-          System.setProperty("KUBERNETES_NAMESPACE", jgroupsKubernetesConfig.namespace());
-        }
-        if (!jgroupsKubernetesConfig.labels().isEmpty()) {
-          System.setProperty(
-              "KUBERNETES_LABELS", String.join(",", jgroupsKubernetesConfig.labels()));
-        }
-        return new JChannel(getClass().getResource("kubernetes.xml").toString());
-      }
-      return new JChannel();
-    } catch (Exception e) {
-      log.atSevere().withCause(e).log(
-          "Unable to create a channel with protocol stack: %s",
-          protocolStack.isPresent() ? protocolStack : "default");
-      throw e;
-    }
-  }
-
   @VisibleForTesting
   void setChannel(JChannel channel) {
     this.channel = channel;
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsKubernetesPeerInfoProviderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsKubernetesPeerInfoProviderTest.java
index 0dd27e1..05ae4c5 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsKubernetesPeerInfoProviderTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsKubernetesPeerInfoProviderTest.java
@@ -37,6 +37,7 @@
 import java.util.List;
 import java.util.Optional;
 import org.apache.http.HttpStatus;
+import org.jgroups.JChannel;
 import org.jgroups.Message;
 import org.junit.After;
 import org.junit.Before;
@@ -93,12 +94,16 @@
     } else {
       when(finder.findAddress()).thenReturn(Optional.of(Inet4Address.getByName("127.0.0.1")));
     }
+    JChannel channel1 = new JChannelProvider(pluginConfigurationMock).get();
+    JChannel channel2 = new JChannelProvider(pluginConfigurationMock).get();
     jGroupsPeerInfoProvider =
         Mockito.spy(
-            new JGroupsPeerInfoProvider(pluginConfigurationMock, finder, myUrlProviderTest));
+            new JGroupsPeerInfoProvider(
+                pluginConfigurationMock, finder, myUrlProviderTest, channel1));
     jGroupsPeerInfoProvider2 =
         Mockito.spy(
-            new JGroupsPeerInfoProvider(pluginConfigurationMock, finder, myUrlProviderTest));
+            new JGroupsPeerInfoProvider(
+                pluginConfigurationMock, finder, myUrlProviderTest, channel2));
 
     StringBuilder kubeApiUrlBuilder = new StringBuilder();
     kubeApiUrlBuilder.append("/api/v1/namespaces/");
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProviderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProviderTest.java
index 1110fc3..fd203e0 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProviderTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProviderTest.java
@@ -54,8 +54,9 @@
   @Before
   public void setUp() throws Exception {
     finder = new InetAddressFinder(pluginConfigurationMock);
+    JChannel channel = new JChannelProvider(pluginConfigurationMock).get();
     jGroupsPeerInfoProvider =
-        new JGroupsPeerInfoProvider(pluginConfigurationMock, finder, myUrlProviderTest);
+        new JGroupsPeerInfoProvider(pluginConfigurationMock, finder, myUrlProviderTest, channel);
     peerInfo = Optional.of(new PeerInfo("test message"));
     channel.setName("testChannel");
   }
@@ -95,14 +96,6 @@
     jGroupsPeerInfoProvider.viewAccepted(view);
   }
 
-  @Test(expected = NullPointerException.class)
-  public void testViewAcceptedWithExceptionThrown() throws Exception {
-    when(view.getMembers()).thenReturn(members);
-    when(view.size()).thenReturn(2);
-    when(members.size()).thenReturn(2);
-    jGroupsPeerInfoProvider.viewAccepted(view);
-  }
-
   @Test
   public void testViewAcceptedWhenPeerAddressIsNotNullAndIsNotMemberOfView() {
     when(view.getMembers()).thenReturn(members);