LCOV - code coverage report
Current view: top level - extensions/registration - DynamicMap.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 36 38 94.7 %
Date: 2022-11-19 15:00:39 Functions: 14 15 93.3 %

          Line data    Source code
       1             : // Copyright (C) 2012 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.extensions.registration;
      16             : 
      17             : import com.google.inject.Binder;
      18             : import com.google.inject.Key;
      19             : import com.google.inject.Provider;
      20             : import com.google.inject.ProvisionException;
      21             : import com.google.inject.Scopes;
      22             : import com.google.inject.TypeLiteral;
      23             : import com.google.inject.util.Types;
      24             : import java.util.Collections;
      25             : import java.util.Iterator;
      26             : import java.util.Map;
      27             : import java.util.NavigableMap;
      28             : import java.util.NavigableSet;
      29             : import java.util.TreeMap;
      30             : import java.util.TreeSet;
      31             : import java.util.concurrent.ConcurrentHashMap;
      32             : import java.util.concurrent.ConcurrentMap;
      33             : 
      34             : /**
      35             :  * A map of members that can be modified as plugins reload.
      36             :  *
      37             :  * <p>Maps index their members by plugin name and export name.
      38             :  *
      39             :  * <p>DynamicMaps are always mapped as singletons in Guice. Maps store Providers internally, and
      40             :  * resolve the provider to an instance on demand. This enables registrations to decide between
      41             :  * singleton and non-singleton members.
      42             :  */
      43             : public abstract class DynamicMap<T> implements Iterable<Extension<T>> {
      44             :   /**
      45             :    * Declare a singleton {@code DynamicMap<T>} with a binder.
      46             :    *
      47             :    * <p>Maps must be defined in a Guice module before they can be bound:
      48             :    *
      49             :    * <pre>
      50             :    * DynamicMap.mapOf(binder(), Interface.class);
      51             :    * bind(Interface.class)
      52             :    *   .annotatedWith(Exports.named(&quot;foo&quot;))
      53             :    *   .to(Impl.class);
      54             :    * </pre>
      55             :    *
      56             :    * @param binder a new binder created in the module.
      57             :    * @param member type of value in the map.
      58             :    */
      59             :   public static <T> void mapOf(Binder binder, Class<T> member) {
      60         152 :     mapOf(binder, TypeLiteral.get(member));
      61         152 :   }
      62             : 
      63             :   /**
      64             :    * Declare a singleton {@code DynamicMap<T>} with a binder.
      65             :    *
      66             :    * <p>Maps must be defined in a Guice module before they can be bound:
      67             :    *
      68             :    * <pre>
      69             :    * DynamicMap.mapOf(binder(), new TypeLiteral&lt;Thing&lt;Bar&gt;&gt;(){});
      70             :    * bind(new TypeLiteral&lt;Thing&lt;Bar&gt;&gt;() {})
      71             :    *   .annotatedWith(Exports.named(&quot;foo&quot;))
      72             :    *   .to(Impl.class);
      73             :    * </pre>
      74             :    *
      75             :    * @param binder a new binder created in the module.
      76             :    * @param member type of value in the map.
      77             :    */
      78             :   public static <T> void mapOf(Binder binder, TypeLiteral<T> member) {
      79             :     @SuppressWarnings("unchecked")
      80         152 :     Key<DynamicMap<T>> key =
      81             :         (Key<DynamicMap<T>>)
      82         152 :             Key.get(Types.newParameterizedType(DynamicMap.class, member.getType()));
      83         152 :     binder.bind(key).toProvider(new DynamicMapProvider<>(member)).in(Scopes.SINGLETON);
      84         152 :   }
      85             : 
      86             :   /** Returns an empty DynamicMap instance * */
      87             :   public static <T> DynamicMap<T> emptyMap() {
      88         151 :     return new PrivateInternals_DynamicMapImpl<>();
      89             :   }
      90             : 
      91             :   final ConcurrentMap<NamePair, Provider<T>> items;
      92             : 
      93         152 :   DynamicMap() {
      94         152 :     items =
      95             :         new ConcurrentHashMap<>(
      96             :             16 /* initial size */,
      97             :             0.75f /* load factor */,
      98             :             1 /* concurrency level of 1, load/unload is single threaded */);
      99         152 :   }
     100             : 
     101             :   /**
     102             :    * Lookup an implementation by name.
     103             :    *
     104             :    * @param pluginName local name of the plugin providing the item.
     105             :    * @param exportName name the plugin exports the item as.
     106             :    * @return the implementation. Null if the plugin is not running, or if the plugin does not export
     107             :    *     this name.
     108             :    * @throws ProvisionException if the registered provider is unable to obtain an instance of the
     109             :    *     requested implementation.
     110             :    */
     111             :   public T get(String pluginName, String exportName) throws ProvisionException {
     112          29 :     Provider<T> p = items.get(new NamePair(pluginName, exportName));
     113          29 :     return p != null ? p.get() : null;
     114             :   }
     115             : 
     116             :   /**
     117             :    * Get the names of all running plugins supplying this type.
     118             :    *
     119             :    * @return navigatable set of active plugins that supply at least one item.
     120             :    */
     121             :   public NavigableSet<String> plugins() {
     122         125 :     NavigableSet<String> r = new TreeSet<>();
     123         125 :     for (NamePair p : items.keySet()) {
     124           8 :       r.add(p.pluginName);
     125           8 :     }
     126         125 :     return Collections.unmodifiableNavigableSet(r);
     127             :   }
     128             : 
     129             :   /**
     130             :    * Get the items exported by a single plugin.
     131             :    *
     132             :    * @param pluginName name of the plugin.
     133             :    * @return items exported by a plugin, keyed by the export name.
     134             :    */
     135             :   public NavigableMap<String, Provider<T>> byPlugin(String pluginName) {
     136           8 :     NavigableMap<String, Provider<T>> r = new TreeMap<>();
     137           8 :     for (Map.Entry<NamePair, Provider<T>> e : items.entrySet()) {
     138           8 :       if (e.getKey().pluginName.equals(pluginName)) {
     139           7 :         r.put(e.getKey().exportName, e.getValue());
     140             :       }
     141           8 :     }
     142           8 :     return Collections.unmodifiableNavigableMap(r);
     143             :   }
     144             : 
     145             :   /** Iterate through all entries in an undefined order. */
     146             :   @Override
     147             :   public Iterator<Extension<T>> iterator() {
     148         151 :     final Iterator<Map.Entry<NamePair, Provider<T>>> i = items.entrySet().iterator();
     149         151 :     return new Iterator<>() {
     150             :       @Override
     151             :       public boolean hasNext() {
     152         151 :         return i.hasNext();
     153             :       }
     154             : 
     155             :       @Override
     156             :       public Extension<T> next() {
     157          82 :         Map.Entry<NamePair, Provider<T>> e = i.next();
     158          82 :         return new Extension<>(e.getKey().pluginName, e.getKey().exportName, e.getValue());
     159             :       }
     160             : 
     161             :       @Override
     162             :       public void remove() {
     163           0 :         throw new UnsupportedOperationException();
     164             :       }
     165             :     };
     166             :   }
     167             : 
     168             :   static class NamePair {
     169             :     private final String pluginName;
     170             :     private final String exportName;
     171             : 
     172         152 :     NamePair(String pn, String en) {
     173         152 :       pluginName = pn;
     174         152 :       exportName = en;
     175         152 :     }
     176             : 
     177             :     @Override
     178             :     public int hashCode() {
     179         152 :       return pluginName.hashCode() * 31 + exportName.hashCode();
     180             :     }
     181             : 
     182             :     @Override
     183             :     public boolean equals(Object other) {
     184          29 :       if (other instanceof NamePair) {
     185          29 :         NamePair np = (NamePair) other;
     186          29 :         return pluginName.equals(np.pluginName) && exportName.equals(np.exportName);
     187             :       }
     188           0 :       return false;
     189             :     }
     190             :   }
     191             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750