Support core-plugins in gerrit.war as a repository

Change-Id: Iaaa736699de292a6b341f01bfb1c281e5d5d347a
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/Module.java b/src/main/java/com/googlesource/gerrit/plugins/manager/Module.java
index 0a96bfb..fa27964 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/manager/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/Module.java
@@ -20,6 +20,7 @@
 import com.google.inject.AbstractModule;
 import com.google.inject.internal.UniqueAnnotations;
 
+import com.googlesource.gerrit.plugins.manager.repository.CorePluginsRepository;
 import com.googlesource.gerrit.plugins.manager.repository.JenkinsCiPluginsRepository;
 import com.googlesource.gerrit.plugins.manager.repository.PluginsRepository;
 
@@ -31,6 +32,7 @@
 
     DynamicSet.setOf(binder(), PluginsRepository.class);
     DynamicSet.bind(binder(), PluginsRepository.class).to(JenkinsCiPluginsRepository.class);
+    DynamicSet.bind(binder(), PluginsRepository.class).to(CorePluginsRepository.class);
 
     install(PluginsCentralCache.module());
 
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
new file mode 100644
index 0000000..5655b19
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/CorePluginsRepository.java
@@ -0,0 +1,145 @@
+// Copyright (C) 2016 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.googlesource.gerrit.plugins.manager.repository;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.gerrit.common.Version;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.inject.Inject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+public class CorePluginsRepository implements PluginsRepository {
+  private static final Logger log = LoggerFactory
+      .getLogger(CorePluginsRepository.class);
+  private static final String GERRIT_VERSION = Version.getVersion();
+  private final SitePaths site;
+
+  @Inject
+  public CorePluginsRepository(SitePaths site) {
+    this.site = site;
+  }
+
+  static class SelectPluginsFromJar implements Predicate<JarEntry> {
+    @Override
+    public boolean apply(JarEntry entry) {
+      String entryName = entry.getName();
+      return (entryName.startsWith("WEB-INF/plugins") &&
+              entryName.endsWith(".jar"));
+    }
+  }
+
+  static class ExtractPluginInfoFromJarEntry implements
+      Function<JarEntry, PluginInfo> {
+    private String gerritWarFilename;
+
+    public ExtractPluginInfoFromJarEntry(String gerritWarFilename) {
+      this.gerritWarFilename = gerritWarFilename;
+    }
+
+    @Override
+    public PluginInfo apply(JarEntry entry) {
+      try {
+        Path entryName = Paths.get(entry.getName());
+        URI pluginUrl =
+            new URI("jar:file:" + gerritWarFilename + "!/" + entry.getName());
+        try (JarInputStream pluginJar =
+            new JarInputStream(pluginUrl.toURL().openStream())) {
+          Manifest manifestJarEntry = getManifestEntry(pluginJar);
+          if (manifestJarEntry != null) {
+            Attributes pluginAttributes = manifestJarEntry.getMainAttributes();
+            return new PluginInfo(
+                pluginAttributes.getValue("Gerrit-PluginName"),
+                pluginAttributes.getValue("Implementation-Version"), "",
+                pluginUrl.toString());
+          } else {
+            return new PluginInfo(entryName.getFileName().toString(), "", "",
+                pluginUrl.toString());
+          }
+        } catch (IOException e) {
+          log.error("Unable to open plugin " + pluginUrl, e);
+          return null;
+        }
+      } catch (URISyntaxException e) {
+        log.error("Invalid plugin filename", e);
+        return null;
+      }
+    }
+
+    private Manifest getManifestEntry(JarInputStream pluginJar)
+        throws IOException {
+      for (JarEntry entry = pluginJar.getNextJarEntry(); entry != null; entry =
+          pluginJar.getNextJarEntry()) {
+        if (entry.getName().equals("META-INF/MANIFEST.MF")) {
+          return new Manifest(pluginJar);
+        }
+      }
+      return null;
+    }
+  }
+
+  @Override
+  public Collection<PluginInfo> list(String gerritVersion) throws IOException {
+    if (!gerritVersion.equals(GERRIT_VERSION)) {
+      log.warn(
+          "No core plugins available for version {} which is different than " +
+          "the current running Gerrit", gerritVersion);
+      return Collections.emptyList();
+    }
+
+    final Path gerritWarPath = site.gerrit_war;
+    if (gerritWarPath == null) {
+      log.warn("Core plugins not available on non-war Gerrit distributions");
+      return Collections.emptyList();
+    }
+
+    try (JarFile gerritWar = new JarFile(gerritWarPath.toFile())) {
+
+      return FluentIterable
+          .from(Collections.list(gerritWar.entries()))
+          .filter(new SelectPluginsFromJar())
+          .transform(
+              new ExtractPluginInfoFromJarEntry(gerritWarPath.toString()))
+          .filter(new Predicate<PluginInfo>() {
+            @Override
+            public boolean apply(PluginInfo pluginInfo) {
+              return pluginInfo != null;
+            }
+          }).toSortedList(new Comparator<PluginInfo>() {
+            @Override
+            public int compare(PluginInfo a, PluginInfo b) {
+              return a.name.compareTo(b.name);
+            }
+          });
+    }
+  }
+}