Merge "Have common class for entries in DynamicMap/DynamicSet/DynamicItem"
diff --git a/java/com/google/gerrit/extensions/registration/DynamicItem.java b/java/com/google/gerrit/extensions/registration/DynamicItem.java
index 4f36ab4..67982d9 100644
--- a/java/com/google/gerrit/extensions/registration/DynamicItem.java
+++ b/java/com/google/gerrit/extensions/registration/DynamicItem.java
@@ -111,17 +111,22 @@
   }
 
   private final Key<DynamicItem<T>> key;
-  private final AtomicReference<NamedProvider<T>> ref;
+  private final AtomicReference<Extension<T>> ref;
 
   DynamicItem(Key<DynamicItem<T>> key, Provider<T> provider, String pluginName) {
-    NamedProvider<T> in = null;
+    Extension<T> in = null;
     if (provider != null) {
-      in = new NamedProvider<>(provider, pluginName);
+      in = new Extension<>(pluginName, provider);
     }
     this.key = key;
     this.ref = new AtomicReference<>(in);
   }
 
+  @Nullable
+  public Extension<T> getEntry() {
+    return ref.get();
+  }
+
   /**
    * Get the configured item, or null.
    *
@@ -130,8 +135,8 @@
    */
   @Nullable
   public T get() {
-    NamedProvider<T> item = ref.get();
-    return item != null ? item.impl.get() : null;
+    Extension<T> item = ref.get();
+    return item != null ? item.get() : null;
   }
 
   /**
@@ -143,8 +148,8 @@
    */
   @Nullable
   public String getPluginName() {
-    NamedProvider<T> item = ref.get();
-    return item != null ? item.pluginName : null;
+    Extension<T> item = ref.get();
+    return item != null ? item.getPluginName() : null;
   }
 
   /**
@@ -166,19 +171,19 @@
    * @return handle to remove the item at a later point in time.
    */
   public RegistrationHandle set(Provider<T> impl, String pluginName) {
-    final NamedProvider<T> item = new NamedProvider<>(impl, pluginName);
-    NamedProvider<T> old = null;
+    final Extension<T> item = new Extension<>(pluginName, impl);
+    Extension<T> old = null;
     while (!ref.compareAndSet(old, item)) {
       old = ref.get();
-      if (old != null && !PluginName.GERRIT.equals(old.pluginName)) {
+      if (old != null && !PluginName.GERRIT.equals(old.getPluginName())) {
         throw new ProvisionException(
             String.format(
                 "%s already provided by %s, ignoring plugin %s",
-                key.getTypeLiteral(), old.pluginName, pluginName));
+                key.getTypeLiteral(), old.getPluginName(), pluginName));
       }
     }
 
-    final NamedProvider<T> defaultItem = old;
+    final Extension<T> defaultItem = old;
     return new RegistrationHandle() {
       @Override
       public void remove() {
@@ -198,13 +203,13 @@
    * @return a handle that can remove this item later, or hot-swap the item.
    */
   public ReloadableRegistrationHandle<T> set(Key<T> key, Provider<T> impl, String pluginName) {
-    final NamedProvider<T> item = new NamedProvider<>(impl, pluginName);
-    NamedProvider<T> old = null;
+    final Extension<T> item = new Extension<>(pluginName, impl);
+    Extension<T> old = null;
     while (!ref.compareAndSet(old, item)) {
       old = ref.get();
       if (old != null
-          && !PluginName.GERRIT.equals(old.pluginName)
-          && !pluginName.equals(old.pluginName)) {
+          && !PluginName.GERRIT.equals(old.getPluginName())
+          && !pluginName.equals(old.getPluginName())) {
         // We allow to replace:
         // 1. Gerrit core items, e.g. websession cache
         //    can be replaced by plugin implementation
@@ -212,7 +217,7 @@
         throw new ProvisionException(
             String.format(
                 "%s already provided by %s, ignoring plugin %s",
-                this.key.getTypeLiteral(), old.pluginName, pluginName));
+                this.key.getTypeLiteral(), old.getPluginName(), pluginName));
       }
     }
     return new ReloadableHandle(key, item, old);
@@ -220,10 +225,10 @@
 
   private class ReloadableHandle implements ReloadableRegistrationHandle<T> {
     private final Key<T> handleKey;
-    private final NamedProvider<T> item;
-    private final NamedProvider<T> defaultItem;
+    private final Extension<T> item;
+    private final Extension<T> defaultItem;
 
-    ReloadableHandle(Key<T> handleKey, NamedProvider<T> item, NamedProvider<T> defaultItem) {
+    ReloadableHandle(Key<T> handleKey, Extension<T> item, Extension<T> defaultItem) {
       this.handleKey = handleKey;
       this.item = item;
       this.defaultItem = defaultItem;
@@ -242,7 +247,7 @@
     @Override
     @Nullable
     public ReloadableHandle replace(Key<T> newKey, Provider<T> newItem) {
-      NamedProvider<T> n = new NamedProvider<>(newItem, item.pluginName);
+      Extension<T> n = new Extension<>(item.getPluginName(), newItem);
       if (ref.compareAndSet(item, n)) {
         return new ReloadableHandle(newKey, n, defaultItem);
       }
diff --git a/java/com/google/gerrit/extensions/registration/DynamicMap.java b/java/com/google/gerrit/extensions/registration/DynamicMap.java
index 96d19b2..48b1279 100644
--- a/java/com/google/gerrit/extensions/registration/DynamicMap.java
+++ b/java/com/google/gerrit/extensions/registration/DynamicMap.java
@@ -40,29 +40,7 @@
  * resolve the provider to an instance on demand. This enables registrations to decide between
  * singleton and non-singleton members.
  */
-public abstract class DynamicMap<T> implements Iterable<DynamicMap.Entry<T>> {
-  public static class Entry<T> {
-    private final NamePair namePair;
-    private final Provider<T> provider;
-
-    private Entry(NamePair namePair, Provider<T> provider) {
-      this.namePair = namePair;
-      this.provider = provider;
-    }
-
-    public String getPluginName() {
-      return namePair.pluginName;
-    }
-
-    public String getExportName() {
-      return namePair.exportName;
-    }
-
-    public Provider<T> getProvider() {
-      return provider;
-    }
-  }
-
+public abstract class DynamicMap<T> implements Iterable<Extension<T>> {
   /**
    * Declare a singleton {@code DynamicMap<T>} with a binder.
    *
@@ -166,18 +144,18 @@
 
   /** Iterate through all entries in an undefined order. */
   @Override
-  public Iterator<Entry<T>> iterator() {
+  public Iterator<Extension<T>> iterator() {
     final Iterator<Map.Entry<NamePair, Provider<T>>> i = items.entrySet().iterator();
-    return new Iterator<Entry<T>>() {
+    return new Iterator<Extension<T>>() {
       @Override
       public boolean hasNext() {
         return i.hasNext();
       }
 
       @Override
-      public Entry<T> next() {
+      public Extension<T> next() {
         Map.Entry<NamePair, Provider<T>> e = i.next();
-        return new Entry<>(e.getKey(), e.getValue());
+        return new Extension<>(e.getKey().pluginName, e.getKey().exportName, e.getValue());
       }
 
       @Override
diff --git a/java/com/google/gerrit/extensions/registration/DynamicSet.java b/java/com/google/gerrit/extensions/registration/DynamicSet.java
index 6b3a49b..dcd0d8f 100644
--- a/java/com/google/gerrit/extensions/registration/DynamicSet.java
+++ b/java/com/google/gerrit/extensions/registration/DynamicSet.java
@@ -45,24 +45,6 @@
  * singleton and non-singleton members.
  */
 public class DynamicSet<T> implements Iterable<T> {
-  public static class Entry<T> {
-    private final String pluginName;
-    private final Provider<T> provider;
-
-    private Entry(String pluginName, Provider<T> provider) {
-      this.pluginName = pluginName;
-      this.provider = provider;
-    }
-
-    public String getPluginName() {
-      return pluginName;
-    }
-
-    public Provider<T> getProvider() {
-      return provider;
-    }
-  }
-
   /**
    * Declare a singleton {@code DynamicSet<T>} with a binder.
    *
@@ -153,12 +135,12 @@
   }
 
   public static <T> DynamicSet<T> emptySet() {
-    return new DynamicSet<>(Collections.<AtomicReference<NamedProvider<T>>>emptySet());
+    return new DynamicSet<>(Collections.<AtomicReference<Extension<T>>>emptySet());
   }
 
-  private final CopyOnWriteArrayList<AtomicReference<NamedProvider<T>>> items;
+  private final CopyOnWriteArrayList<AtomicReference<Extension<T>>> items;
 
-  DynamicSet(Collection<AtomicReference<NamedProvider<T>>> base) {
+  DynamicSet(Collection<AtomicReference<Extension<T>>> base) {
     items = new CopyOnWriteArrayList<>(base);
   }
 
@@ -168,7 +150,7 @@
 
   @Override
   public Iterator<T> iterator() {
-    Iterator<Entry<T>> entryIterator = entries().iterator();
+    Iterator<Extension<T>> entryIterator = entries().iterator();
     return new Iterator<T>() {
       @Override
       public boolean hasNext() {
@@ -177,39 +159,35 @@
 
       @Override
       public T next() {
-        Entry<T> next = entryIterator.next();
+        Extension<T> next = entryIterator.next();
         return next != null ? next.getProvider().get() : null;
       }
     };
   }
 
-  public Iterable<Entry<T>> entries() {
-    final Iterator<AtomicReference<NamedProvider<T>>> itr = items.iterator();
-    return new Iterable<Entry<T>>() {
+  public Iterable<Extension<T>> entries() {
+    final Iterator<AtomicReference<Extension<T>>> itr = items.iterator();
+    return new Iterable<Extension<T>>() {
       @Override
-      public Iterator<Entry<T>> iterator() {
-        return new Iterator<Entry<T>>() {
-          private Entry<T> next;
+      public Iterator<Extension<T>> iterator() {
+        return new Iterator<Extension<T>>() {
+          private Extension<T> next;
 
           @Override
           public boolean hasNext() {
             while (next == null && itr.hasNext()) {
-              NamedProvider<T> p = itr.next().get();
+              Extension<T> p = itr.next().get();
               if (p != null) {
-                try {
-                  next = new Entry<>(p.pluginName, p.impl);
-                } catch (RuntimeException e) {
-                  // TODO Log failed member of DynamicSet.
-                }
+                next = p;
               }
             }
             return next != null;
           }
 
           @Override
-          public Entry<T> next() {
+          public Extension<T> next() {
             if (hasNext()) {
-              Entry<T> result = next;
+              Extension<T> result = next;
               next = null;
               return result;
             }
@@ -250,7 +228,7 @@
   public ImmutableSortedSet<String> plugins() {
     return items
         .stream()
-        .map(i -> i.get().pluginName)
+        .map(i -> i.get().getPluginName())
         .collect(toImmutableSortedSet(naturalOrder()));
   }
 
@@ -263,8 +241,8 @@
   public ImmutableSet<Provider<T>> byPlugin(String pluginName) {
     return items
         .stream()
-        .filter(i -> i.get().pluginName.equals(pluginName))
-        .map(i -> i.get().impl)
+        .filter(i -> i.get().getPluginName().equals(pluginName))
+        .map(i -> i.get().getProvider())
         .collect(toImmutableSet());
   }
 
@@ -285,8 +263,8 @@
    * @return handle to remove the item at a later point in time.
    */
   public RegistrationHandle add(String pluginName, Provider<T> item) {
-    final AtomicReference<NamedProvider<T>> ref =
-        new AtomicReference<>(new NamedProvider<>(item, pluginName));
+    final AtomicReference<Extension<T>> ref =
+        new AtomicReference<>(new Extension<>(pluginName, item));
     items.add(ref);
     return new RegistrationHandle() {
       @Override
@@ -310,18 +288,17 @@
    *     the collection.
    */
   public ReloadableRegistrationHandle<T> add(String pluginName, Key<T> key, Provider<T> item) {
-    AtomicReference<NamedProvider<T>> ref =
-        new AtomicReference<>(new NamedProvider<>(item, pluginName));
+    AtomicReference<Extension<T>> ref = new AtomicReference<>(new Extension<>(pluginName, item));
     items.add(ref);
     return new ReloadableHandle(ref, key, ref.get());
   }
 
   private class ReloadableHandle implements ReloadableRegistrationHandle<T> {
-    private final AtomicReference<NamedProvider<T>> ref;
+    private final AtomicReference<Extension<T>> ref;
     private final Key<T> key;
-    private final NamedProvider<T> item;
+    private final Extension<T> item;
 
-    ReloadableHandle(AtomicReference<NamedProvider<T>> ref, Key<T> key, NamedProvider<T> item) {
+    ReloadableHandle(AtomicReference<Extension<T>> ref, Key<T> key, Extension<T> item) {
       this.ref = ref;
       this.key = key;
       this.item = item;
@@ -341,7 +318,7 @@
 
     @Override
     public ReloadableHandle replace(Key<T> newKey, Provider<T> newItem) {
-      NamedProvider<T> n = new NamedProvider<>(newItem, item.pluginName);
+      Extension<T> n = new Extension<>(item.getPluginName(), newItem);
       if (ref.compareAndSet(item, n)) {
         return new ReloadableHandle(ref, newKey, n);
       }
diff --git a/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java b/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java
index 6d36f54..832933b 100644
--- a/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java
+++ b/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java
@@ -38,17 +38,16 @@
     return new DynamicSet<>(find(injector, type));
   }
 
-  private static <T> List<AtomicReference<NamedProvider<T>>> find(
-      Injector src, TypeLiteral<T> type) {
+  private static <T> List<AtomicReference<Extension<T>>> find(Injector src, TypeLiteral<T> type) {
     List<Binding<T>> bindings = src.findBindingsByType(type);
     int cnt = bindings != null ? bindings.size() : 0;
     if (cnt == 0) {
       return Collections.emptyList();
     }
-    List<AtomicReference<NamedProvider<T>>> r = new ArrayList<>(cnt);
+    List<AtomicReference<Extension<T>>> r = new ArrayList<>(cnt);
     for (Binding<T> b : bindings) {
       if (b.getKey().getAnnotation() != null) {
-        r.add(new AtomicReference<>(new NamedProvider<>(b.getProvider(), PluginName.GERRIT)));
+        r.add(new AtomicReference<>(new Extension<>(PluginName.GERRIT, b.getProvider())));
       }
     }
     return r;
diff --git a/java/com/google/gerrit/extensions/registration/Extension.java b/java/com/google/gerrit/extensions/registration/Extension.java
new file mode 100644
index 0000000..aaec201
--- /dev/null
+++ b/java/com/google/gerrit/extensions/registration/Extension.java
@@ -0,0 +1,61 @@
+// 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.google.gerrit.extensions.registration;
+
+import com.google.gerrit.common.Nullable;
+import com.google.inject.Provider;
+
+/**
+ * An extension that is provided by a plugin.
+ *
+ * <p>Contains the name of the plugin that provides the extension, the extension point
+ * implementation and optionally the export name under which the extension was exported.
+ *
+ * <p>An export name is only available if this extension is an entry in a {@link DynamicMap}.
+ *
+ * @param <T> Type of extension point that this extension implements
+ */
+public class Extension<T> {
+  private final String pluginName;
+  private final @Nullable String exportName;
+  private final Provider<T> provider;
+
+  protected Extension(String pluginName, Provider<T> provider) {
+    this(pluginName, null, provider);
+  }
+
+  protected Extension(String pluginName, @Nullable String exportName, Provider<T> provider) {
+    this.pluginName = pluginName;
+    this.exportName = exportName;
+    this.provider = provider;
+  }
+
+  public String getPluginName() {
+    return pluginName;
+  }
+
+  @Nullable
+  public String getExportName() {
+    return exportName;
+  }
+
+  public Provider<T> getProvider() {
+    return provider;
+  }
+
+  public T get() {
+    return provider.get();
+  }
+}
diff --git a/java/com/google/gerrit/extensions/registration/NamedProvider.java b/java/com/google/gerrit/extensions/registration/NamedProvider.java
deleted file mode 100644
index aca651b..0000000
--- a/java/com/google/gerrit/extensions/registration/NamedProvider.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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.google.gerrit.extensions.registration;
-
-import com.google.inject.Provider;
-
-/** Pair of provider implementation and plugin providing it. */
-class NamedProvider<T> {
-  final Provider<T> impl;
-  final String pluginName;
-
-  NamedProvider(Provider<T> provider, String pluginName) {
-    this.impl = provider;
-    this.pluginName = pluginName;
-  }
-}
diff --git a/java/com/google/gerrit/httpd/ProjectOAuthFilter.java b/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
index 5e234d2e..30ebe6e 100644
--- a/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
+++ b/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
@@ -25,7 +25,7 @@
 import com.google.gerrit.extensions.auth.oauth.OAuthLoginProvider;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.registration.DynamicMap.Entry;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.AccessPath;
 import com.google.gerrit.server.account.AccountCache;
@@ -191,7 +191,7 @@
    */
   private void pickOnlyProvider() throws ServletException {
     try {
-      Entry<OAuthLoginProvider> loginProvider = Iterables.getOnlyElement(loginProviders);
+      Extension<OAuthLoginProvider> loginProvider = Iterables.getOnlyElement(loginProviders);
       defaultAuthPlugin = loginProvider.getPluginName();
       defaultAuthProvider = loginProvider.getExportName();
     } catch (NoSuchElementException e) {
diff --git a/java/com/google/gerrit/server/cache/CacheMetrics.java b/java/com/google/gerrit/server/cache/CacheMetrics.java
index 3435652..c652d50 100644
--- a/java/com/google/gerrit/server/cache/CacheMetrics.java
+++ b/java/com/google/gerrit/server/cache/CacheMetrics.java
@@ -18,6 +18,7 @@
 import com.google.common.cache.CacheStats;
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.extensions.registration.PluginName;
 import com.google.gerrit.metrics.CallbackMetric;
 import com.google.gerrit.metrics.CallbackMetric1;
@@ -71,7 +72,7 @@
     metrics.newTrigger(
         cacheMetrics,
         () -> {
-          for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) {
+          for (Extension<Cache<?, ?>> e : cacheMap) {
             Cache<?, ?> c = e.getProvider().get();
             String name = metricNameOf(e);
             CacheStats cstats = c.stats();
@@ -95,7 +96,7 @@
     return ((double) d.hitCount() / d.requestCount() * 100);
   }
 
-  private static String metricNameOf(DynamicMap.Entry<Cache<?, ?>> e) {
+  private static String metricNameOf(Extension<Cache<?, ?>> e) {
     if (PluginName.GERRIT.equals(e.getPluginName())) {
       return e.getExportName();
     }
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index 43f7b2f..e1a0aa79 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -88,6 +88,7 @@
 import com.google.gerrit.extensions.config.DownloadCommand;
 import com.google.gerrit.extensions.config.DownloadScheme;
 import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.Url;
 import com.google.gerrit.index.query.QueryResult;
@@ -1471,7 +1472,7 @@
   private Map<String, FetchInfo> makeFetchMap(ChangeData cd, PatchSet in)
       throws PermissionBackendException, OrmException, IOException {
     Map<String, FetchInfo> r = new LinkedHashMap<>();
-    for (DynamicMap.Entry<DownloadScheme> e : downloadSchemes) {
+    for (Extension<DownloadScheme> e : downloadSchemes) {
       String schemeName = e.getExportName();
       DownloadScheme scheme = e.getProvider().get();
       if (!scheme.isEnabled()
@@ -1502,7 +1503,7 @@
       String projectName,
       String refName,
       FetchInfo fetchInfo) {
-    for (DynamicMap.Entry<DownloadCommand> e2 : commands) {
+    for (Extension<DownloadCommand> e2 : commands) {
       String commandName = e2.getExportName();
       DownloadCommand command = e2.getProvider().get();
       String c = command.getCommand(scheme, projectName, refName);
diff --git a/java/com/google/gerrit/server/config/ProjectConfigEntry.java b/java/com/google/gerrit/server/config/ProjectConfigEntry.java
index d30e080..5515f0e 100644
--- a/java/com/google/gerrit/server/config/ProjectConfigEntry.java
+++ b/java/com/google/gerrit/server/config/ProjectConfigEntry.java
@@ -22,7 +22,7 @@
 import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
 import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
 import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.registration.DynamicMap.Entry;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -321,7 +321,7 @@
         ProjectConfig oldCfg = parseConfig(p, event.getOldObjectId());
         ProjectConfig newCfg = parseConfig(p, event.getNewObjectId());
         if (oldCfg != null && newCfg != null) {
-          for (Entry<ProjectConfigEntry> e : pluginConfigEntries) {
+          for (Extension<ProjectConfigEntry> e : pluginConfigEntries) {
             ProjectConfigEntry configEntry = e.getProvider().get();
             String newValue = getValue(newCfg, e);
             String oldValue = getValue(oldCfg, e);
@@ -367,7 +367,7 @@
       }
     }
 
-    private static String getValue(ProjectConfig cfg, Entry<ProjectConfigEntry> e) {
+    private static String getValue(ProjectConfig cfg, Extension<ProjectConfigEntry> e) {
       String value = cfg.getPluginConfig(e.getPluginName()).getString(e.getExportName());
       if (value == null) {
         value = e.getProvider().get().getDefaultValue();
diff --git a/java/com/google/gerrit/server/edit/ChangeEditJson.java b/java/com/google/gerrit/server/edit/ChangeEditJson.java
index 78baef7..bd9c3a6 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditJson.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditJson.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.config.DownloadCommand;
 import com.google.gerrit.extensions.config.DownloadScheme;
 import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.server.CommonConverters;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.change.ChangeJson;
@@ -78,7 +79,7 @@
 
   private Map<String, FetchInfo> fillFetchMap(ChangeEdit edit) {
     Map<String, FetchInfo> r = new LinkedHashMap<>();
-    for (DynamicMap.Entry<DownloadScheme> e : downloadSchemes) {
+    for (Extension<DownloadScheme> e : downloadSchemes) {
       String schemeName = e.getExportName();
       DownloadScheme scheme = e.getProvider().get();
       if (!scheme.isEnabled()
diff --git a/java/com/google/gerrit/server/extensions/webui/UiActions.java b/java/com/google/gerrit/server/extensions/webui/UiActions.java
index af28bed3..3ca2bdb 100644
--- a/java/com/google/gerrit/server/extensions/webui/UiActions.java
+++ b/java/com/google/gerrit/server/extensions/webui/UiActions.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
 import com.google.gerrit.extensions.conditions.BooleanCondition;
 import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.extensions.registration.PluginName;
 import com.google.gerrit.extensions.restapi.RestCollection;
 import com.google.gerrit.extensions.restapi.RestResource;
@@ -121,7 +122,7 @@
 
   @Nullable
   private <R extends RestResource> UiAction.Description describe(
-      DynamicMap.Entry<RestView<R>> e, R resource) {
+      Extension<RestView<R>> e, R resource) {
     int d = e.getExportName().indexOf('.');
     if (d < 0) {
       return null;
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 7566b55..ef699b3 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -70,8 +70,8 @@
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.registration.DynamicMap.Entry;
 import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -1131,7 +1131,7 @@
    * fails.
    */
   private void validatePluginConfig(ReceiveCommand cmd, ProjectConfig cfg) {
-    for (Entry<ProjectConfigEntry> e : pluginConfigEntries) {
+    for (Extension<ProjectConfigEntry> e : pluginConfigEntries) {
       PluginConfig pluginCfg = cfg.getPluginConfig(e.getPluginName());
       ProjectConfigEntry configEntry = e.getProvider().get();
       String value = pluginCfg.getString(e.getExportName());
diff --git a/java/com/google/gerrit/server/git/validators/MergeValidators.java b/java/com/google/gerrit/server/git/validators/MergeValidators.java
index c4df4dd..f755aab 100644
--- a/java/com/google/gerrit/server/git/validators/MergeValidators.java
+++ b/java/com/google/gerrit/server/git/validators/MergeValidators.java
@@ -19,8 +19,8 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
 import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.registration.DynamicMap.Entry;
 import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Branch;
@@ -207,7 +207,7 @@
             }
           }
 
-          for (Entry<ProjectConfigEntry> e : pluginConfigEntries) {
+          for (Extension<ProjectConfigEntry> e : pluginConfigEntries) {
             PluginConfig pluginCfg = cfg.getPluginConfig(e.getPluginName());
             ProjectConfigEntry configEntry = e.getProvider().get();
 
diff --git a/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
index af549f7..262e82b 100644
--- a/java/com/google/gerrit/server/mail/receive/MailProcessor.java
+++ b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.client.Side;
 import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.mail.HtmlParser;
@@ -148,7 +149,7 @@
 
   private void processImpl(BatchUpdate.Factory buf, MailMessage message)
       throws OrmException, UpdateException, RestApiException, IOException {
-    for (DynamicMap.Entry<MailFilter> filter : mailFilters) {
+    for (Extension<MailFilter> filter : mailFilters) {
       if (!filter.getProvider().get().shouldProcessMessage(message)) {
         logger.atWarning().log(
             "Message %s filtered by plugin %s %s. Will delete message.",
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 3db72ef..2ec2e7c 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -31,6 +31,7 @@
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.common.errors.NotSignedInException;
 import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.index.IndexConfig;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.SchemaUtil;
@@ -427,7 +428,7 @@
   }
 
   private void setupDynamicOperators() {
-    for (DynamicMap.Entry<ChangeOperatorFactory> e : args.opFactories) {
+    for (Extension<ChangeOperatorFactory> e : args.opFactories) {
       String name = e.getExportName() + "_" + e.getPluginName();
       opFactories.put(name, e.getProvider().get());
     }
diff --git a/java/com/google/gerrit/server/restapi/account/SetPreferences.java b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
index fccdabe..2471689 100644
--- a/java/com/google/gerrit/server/restapi/account/SetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
 import com.google.gerrit.extensions.config.DownloadScheme;
 import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -83,7 +84,7 @@
       return;
     }
 
-    for (DynamicMap.Entry<DownloadScheme> e : downloadSchemes) {
+    for (Extension<DownloadScheme> e : downloadSchemes) {
       if (e.getExportName().equals(downloadScheme) && e.getProvider().get().isEnabled()) {
         return;
       }
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
index 6b7a708..ae6e5d1 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.reviewdb.client.Account;
@@ -130,7 +131,7 @@
         new ArrayList<>(reviewerSuggestionPluginMap.plugins().size());
     List<Double> weights = new ArrayList<>(reviewerSuggestionPluginMap.plugins().size());
 
-    for (DynamicMap.Entry<ReviewerSuggestion> plugin : reviewerSuggestionPluginMap) {
+    for (Extension<ReviewerSuggestion> plugin : reviewerSuggestionPluginMap) {
       tasks.add(
           () ->
               plugin
diff --git a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
index d6071d5..e1144d5 100644
--- a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
+++ b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
@@ -39,6 +39,7 @@
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.webui.WebUiPlugin;
 import com.google.gerrit.server.EnableSignedPush;
@@ -252,7 +253,7 @@
   private DownloadInfo getDownloadInfo() {
     DownloadInfo info = new DownloadInfo();
     info.schemes = new HashMap<>();
-    for (DynamicMap.Entry<DownloadScheme> e : downloadSchemes) {
+    for (Extension<DownloadScheme> e : downloadSchemes) {
       DownloadScheme scheme = e.getProvider().get();
       if (scheme.isEnabled() && scheme.getUrl("${project}") != null) {
         info.schemes.put(e.getExportName(), getDownloadSchemeInfo(scheme));
@@ -270,7 +271,7 @@
     info.isAuthSupported = toBoolean(scheme.isAuthSupported());
 
     info.commands = new HashMap<>();
-    for (DynamicMap.Entry<DownloadCommand> e : downloadCommands) {
+    for (Extension<DownloadCommand> e : downloadCommands) {
       String commandName = e.getExportName();
       DownloadCommand command = e.getProvider().get();
       String c = command.getCommand(scheme, "${project}", "${ref}");
@@ -280,7 +281,7 @@
     }
 
     info.cloneCommands = new HashMap<>();
-    for (DynamicMap.Entry<CloneCommand> e : cloneCommands) {
+    for (Extension<CloneCommand> e : cloneCommands) {
       String commandName = e.getExportName();
       CloneCommand command = e.getProvider().get();
       String c = command.getCommand(scheme, "${project-path}/${project-base-name}");
diff --git a/java/com/google/gerrit/server/restapi/config/ListCaches.java b/java/com/google/gerrit/server/restapi/config/ListCaches.java
index 38664fb..f310ed7 100644
--- a/java/com/google/gerrit/server/restapi/config/ListCaches.java
+++ b/java/com/google/gerrit/server/restapi/config/ListCaches.java
@@ -26,6 +26,7 @@
 import com.google.common.collect.Streams;
 import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
 import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.cache.PersistentCache;
@@ -60,7 +61,7 @@
 
   public Map<String, CacheInfo> getCacheInfos() {
     Map<String, CacheInfo> cacheInfos = new TreeMap<>();
-    for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) {
+    for (Extension<Cache<?, ?>> e : cacheMap) {
       cacheInfos.put(
           cacheNameOf(e.getPluginName(), e.getExportName()), new CacheInfo(e.getProvider().get()));
     }
diff --git a/java/com/google/gerrit/server/restapi/config/PostCaches.java b/java/com/google/gerrit/server/restapi/config/PostCaches.java
index 57ba097..c633af0 100644
--- a/java/com/google/gerrit/server/restapi/config/PostCaches.java
+++ b/java/com/google/gerrit/server/restapi/config/PostCaches.java
@@ -20,6 +20,7 @@
 import com.google.common.cache.Cache;
 import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
 import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.extensions.registration.PluginName;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -96,7 +97,7 @@
   }
 
   private void flushAll() throws AuthException, PermissionBackendException {
-    for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) {
+    for (Extension<Cache<?, ?>> e : cacheMap) {
       CacheResource cacheResource =
           new CacheResource(e.getPluginName(), e.getExportName(), e.getProvider());
       if (FlushCache.WEB_SESSIONS.equals(cacheResource.getName())) {
diff --git a/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java b/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
index 60b5dee..e179896 100644
--- a/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
+++ b/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
@@ -22,7 +22,7 @@
 import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
 import com.google.gerrit.extensions.common.ActionInfo;
 import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.registration.DynamicMap.Entry;
+import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
@@ -122,7 +122,7 @@
       PluginConfigFactory cfgFactory,
       AllProjectsName allProjects) {
     TreeMap<String, Map<String, ConfigParameterInfo>> pluginConfig = new TreeMap<>();
-    for (Entry<ProjectConfigEntry> e : pluginConfigEntries) {
+    for (Extension<ProjectConfigEntry> e : pluginConfigEntries) {
       ProjectConfigEntry configEntry = e.getProvider().get();
       PluginConfig cfg = cfgFactory.getFromProjectConfig(project, e.getPluginName());
       String configuredValue = cfg.getString(e.getExportName());
@@ -165,7 +165,7 @@
   }
 
   private String getInheritedValue(
-      ProjectState project, PluginConfigFactory cfgFactory, Entry<ProjectConfigEntry> e) {
+      ProjectState project, PluginConfigFactory cfgFactory, Extension<ProjectConfigEntry> e) {
     ProjectConfigEntry configEntry = e.getProvider().get();
     ProjectState parent = Iterables.getFirst(project.parents(), null);
     String inheritedValue = configEntry.getDefaultValue();
diff --git a/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java b/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java
index c86160f..0542c35 100644
--- a/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java
+++ b/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java
@@ -123,8 +123,8 @@
     ds.add("bar", 2);
     ds.add("bar", 3);
 
-    Iterator<DynamicSet.Entry<Integer>> entryIterator = ds.entries().iterator();
-    DynamicSet.Entry<Integer> next = entryIterator.next();
+    Iterator<Extension<Integer>> entryIterator = ds.entries().iterator();
+    Extension<Integer> next = entryIterator.next();
     assertThat(next.getPluginName()).isEqualTo("foo");
     assertThat(next.getProvider().get()).isEqualTo(1);