Support iterating through all elements of a DynamicMap

Expose the plugin name, export name and the Provider on each element
entry. This makes it easier for callers to filter members.

Change-Id: I47f65bf38b8395e776e7fb9283909dc5a5e5e224
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicMap.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicMap.java
index 40bbb80..a4c5eb5 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicMap.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicMap.java
@@ -22,6 +22,7 @@
 import com.google.inject.util.Types;
 
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.SortedMap;
 import java.util.SortedSet;
@@ -39,7 +40,7 @@
  * internally, and resolve the provider to an instance on demand. This enables
  * registrations to decide between singleton and non-singleton members.
  */
-public abstract class DynamicMap<T> {
+public abstract class DynamicMap<T> implements Iterable<DynamicMap.Entry<T>> {
   /**
    * Declare a singleton {@code DynamicMap<T>} with a binder.
    * <p>
@@ -136,6 +137,50 @@
     return Collections.unmodifiableSortedMap(r);
   }
 
+  /** Iterate through all entries in an undefined order. */
+  public Iterator<Entry<T>> iterator() {
+    final Iterator<Map.Entry<NamePair, Provider<T>>> i =
+        items.entrySet().iterator();
+    return new Iterator<Entry<T>>() {
+      @Override
+      public boolean hasNext() {
+        return i.hasNext();
+      }
+
+      @Override
+      public Entry<T> next() {
+        final Map.Entry<NamePair, Provider<T>> e = i.next();
+        return new Entry<T>() {
+          @Override
+          public String getPluginName() {
+            return e.getKey().pluginName;
+          }
+
+          @Override
+          public String getExportName() {
+            return e.getKey().exportName;
+          }
+
+          @Override
+          public Provider<T> getProvider() {
+            return e.getValue();
+          }
+        };
+      }
+
+      @Override
+      public void remove() {
+        throw new UnsupportedOperationException();
+      }
+    };
+  }
+
+  public interface Entry<T> {
+    String getPluginName();
+    String getExportName();
+    Provider<T> getProvider();
+  }
+
   static class NamePair {
     private final String pluginName;
     private final String exportName;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java
index 500c84a..7f9e5db 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java
@@ -28,10 +28,8 @@
 
   protected SortedSet<String> cacheNames() {
     SortedSet<String> names = Sets.newTreeSet();
-    for (String plugin : cacheMap.plugins()) {
-      for (String name : cacheMap.byPlugin(plugin).keySet()) {
-        names.add(cacheNameOf(plugin, name));
-      }
+    for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) {
+      names.add(cacheNameOf(e.getPluginName(), e.getExportName()));
     }
     return names;
   }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/FlushCaches.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/FlushCaches.java
index 13abdf4..6c6ed53 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/FlushCaches.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/FlushCaches.java
@@ -17,6 +17,7 @@
 import com.google.common.cache.Cache;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.sshd.BaseCommand;
 import com.google.gerrit.sshd.CommandMetaData;
@@ -98,16 +99,13 @@
 
   private void doBulkFlush() {
     try {
-      for (String plugin : cacheMap.plugins()) {
-        for (Map.Entry<String, Provider<Cache<?, ?>>> entry :
-            cacheMap.byPlugin(plugin).entrySet()) {
-          String n = cacheNameOf(plugin, entry.getKey());
-          if (flush(n)) {
-            try {
-              entry.getValue().get().invalidateAll();
-            } catch (Throwable err) {
-              stderr.println("error: cannot flush cache \"" + n + "\": " + err);
-            }
+      for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) {
+        String n = cacheNameOf(e.getPluginName(), e.getExportName());
+        if (flush(n)) {
+          try {
+            e.getProvider().get().invalidateAll();
+          } catch (Throwable err) {
+            stderr.println("error: cannot flush cache \"" + n + "\": " + err);
           }
         }
       }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java
index 3366841..ae5dc56 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.server.cache.h2.H2CacheImpl;
 import com.google.gerrit.server.config.SitePath;
 import com.google.gerrit.server.git.WorkQueue;
@@ -208,13 +209,10 @@
 
   private Map<String, Cache<?, ?>> sortedPluginCaches() {
     SortedMap<String, Cache<?, ?>> m = Maps.newTreeMap();
-    for (String plugin : cacheMap.plugins()) {
-      if ("gerrit".equals(plugin)) {
-        continue;
-      }
-      for (Map.Entry<String, Provider<Cache<?, ?>>> entry :
-          cacheMap.byPlugin(plugin).entrySet()) {
-        m.put(cacheNameOf(plugin, entry.getKey()), entry.getValue().get());
+    for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) {
+      if (!"gerrit".equals(e.getPluginName())) {
+        m.put(cacheNameOf(e.getPluginName(), e.getExportName()),
+            e.getProvider().get());
       }
     }
     return m;