Fix loading of core plugins on Windows

[2020-06-07T13:26:26.841+0300] [plugin-manager-preloader] ERROR com.googlesource.gerrit.plugins.manager.repository.CorePluginsRepository : Invalid plugin filename
java.net.URISyntaxException: Illegal character in opaque part at index 11: Jar:file:c:\Projects\gerrit\bin\gerrit.war!/WEB-INF/plugins/gitiles.jar
	at java.base/java.net.URI$Parser.fail(URI.java:2915)
	at java.base/java.net.URI$Parser.checkChars(URI.java:3086)
	at java.base/java.net.URI$Parser.parse(URI.java:3122)
	at java.base/java.net.URI.<init>(URI.java:600)
	at com.googlesource.gerrit.plugins.manager.repository.CorePluginsRepository.extractPluginInfoFromJarEntry(CorePluginsRepository.java:59)

Bug: Issue 12885
Change-Id: Icade084c2c156ca3caa1bffd9ae6dcde61a43c3e
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/repository/CorePluginsRepository.java b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/CorePluginsRepository.java
index 55b4639..9ff15fb 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/manager/repository/CorePluginsRepository.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/CorePluginsRepository.java
@@ -16,8 +16,8 @@
 
 import static com.google.common.collect.ImmutableList.toImmutableList;
 import static java.util.Comparator.comparing;
-import static java.util.Objects.requireNonNull;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
@@ -41,21 +41,30 @@
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
   private static final String GERRIT_VERSION = Version.getVersion();
 
-  private final SitePaths site;
   private final CorePluginsDescriptions pluginsDescriptions;
+  private final String gerritWarUri;
+  private final Path siteGerritWar;
+  private static final char WINDOWS_FILE_SEPARATOR = '\\';
+  private static final char UNIX_FILE_SEPARATOR = '/';
 
   @Inject
   public CorePluginsRepository(SitePaths site, CorePluginsDescriptions pd) {
-    this.site = site;
+    this(site.gerrit_war, site.gerrit_war.toString(), pd);
+  }
+
+  @VisibleForTesting
+  public CorePluginsRepository(Path siteGerritWar, String gerritWar, CorePluginsDescriptions pd) {
     this.pluginsDescriptions = pd;
+    final String normalizedWar = gerritWar.replace(WINDOWS_FILE_SEPARATOR, UNIX_FILE_SEPARATOR);
+    this.gerritWarUri = Paths.get(normalizedWar).toUri().toString();
+    this.siteGerritWar = siteGerritWar;
   }
 
   @Nullable
   private PluginInfo extractPluginInfoFromJarEntry(JarEntry entry) {
     try {
       Path entryName = Paths.get(entry.getName());
-      URI pluginUrl =
-          new URI("jar:file:" + requireNonNull(site.gerrit_war) + "!/" + entry.getName());
+      URI pluginUrl = new URI("jar:" + gerritWarUri + "!/" + entry.getName());
       try (JarInputStream pluginJar = new JarInputStream(pluginUrl.toURL().openStream())) {
         return getManifestEntry(pluginJar)
             .map(
@@ -114,12 +123,12 @@
       return ImmutableList.of();
     }
 
-    if (site.gerrit_war == null) {
+    if (siteGerritWar == null) {
       logger.atWarning().log("Core plugins not available in non-war Gerrit distributions");
       return ImmutableList.of();
     }
 
-    try (JarFile gerritWar = new JarFile(site.gerrit_war.toFile())) {
+    try (JarFile gerritWar = new JarFile(siteGerritWar.toFile())) {
       return gerritWar.stream()
           .filter(e -> e.getName().startsWith("WEB-INF/plugins") && e.getName().endsWith(".jar"))
           .map(this::extractPluginInfoFromJarEntry)
diff --git a/src/test/java/com/googlesource/gerrit/plugins/manager/repository/PluginsRepositoryTest.java b/src/test/java/com/googlesource/gerrit/plugins/manager/repository/PluginsRepositoryTest.java
index d01d287..bd74235 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/manager/repository/PluginsRepositoryTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/manager/repository/PluginsRepositoryTest.java
@@ -46,14 +46,9 @@
 
   @Test
   public void corePluginsRepositoryShouldReturnCorePluginsFromReleaseWar() throws IOException {
-    SitePaths site = new SitePaths(random());
-    PluginsRepository pluginRepo = new CorePluginsRepository(site, new CorePluginsDescriptions());
+    SitePaths site = prepareSiteDirWithReleaseWar();
 
-    Path pathToReleaseWar =
-        Paths.get(getenv("TEST_SRCDIR"), getenv("TEST_WORKSPACE"), "release.war");
-    assume().that(pathToReleaseWar.toFile().exists()).isTrue();
-    Files.createDirectories(site.bin_dir);
-    Files.createSymbolicLink(site.gerrit_war, pathToReleaseWar);
+    PluginsRepository pluginRepo = new CorePluginsRepository(site, new CorePluginsDescriptions());
 
     Collection<PluginInfo> plugins = pluginRepo.list(Version.getVersion());
     assertThat(plugins.stream().map(p -> p.name).sorted().collect(toList()))
@@ -61,6 +56,30 @@
         .inOrder();
   }
 
+  @Test
+  public void corePluginsListFromReleaseWarShouldNotBeEmptyOnWindows() throws IOException {
+    char windowsSeparator = '\\';
+    SitePaths site = prepareSiteDirWithReleaseWar();
+
+    PluginsRepository pluginRepo =
+        new CorePluginsRepository(
+            site.gerrit_war,
+            site.gerrit_war.toString().replace('/', windowsSeparator),
+            new CorePluginsDescriptions());
+
+    assertThat(pluginRepo.list(Version.getVersion())).isNotEmpty();
+  }
+
+  private SitePaths prepareSiteDirWithReleaseWar() throws IOException {
+    SitePaths site = new SitePaths(random());
+    Path pathToReleaseWar =
+        Paths.get(getenv("TEST_SRCDIR"), getenv("TEST_WORKSPACE"), "release.war");
+    assume().that(pathToReleaseWar.toFile().exists()).isTrue();
+    Files.createDirectories(site.bin_dir);
+    Files.createSymbolicLink(site.gerrit_war, pathToReleaseWar);
+    return site;
+  }
+
   private static String getenv(String name) {
     String value = System.getenv(name);
     assume().that(value).isNotNull();