LCOV - code coverage report
Current view: top level - server/plugincontext - PluginMapContext.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 7 14 50.0 %
Date: 2022-11-19 15:00:39 Functions: 4 8 50.0 %

          Line data    Source code
       1             : // Copyright (C) 2018 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.server.plugincontext;
      16             : 
      17             : import com.google.common.annotations.VisibleForTesting;
      18             : import com.google.common.collect.Iterators;
      19             : import com.google.gerrit.extensions.registration.DynamicMap;
      20             : import com.google.gerrit.extensions.registration.Extension;
      21             : import com.google.gerrit.server.plugincontext.PluginContext.ExtensionConsumer;
      22             : import com.google.gerrit.server.plugincontext.PluginContext.PluginMetrics;
      23             : import com.google.inject.Inject;
      24             : import java.util.Iterator;
      25             : import java.util.NavigableSet;
      26             : 
      27             : /**
      28             :  * Context to invoke extensions from a {@link DynamicMap}.
      29             :  *
      30             :  * <p>When a plugin extension is invoked a logging tag with the plugin name is set. This way any
      31             :  * errors that are triggered by the plugin extension (even if they happen in Gerrit code which is
      32             :  * called by the plugin extension) can be easily attributed to the plugin.
      33             :  *
      34             :  * <p>Example if all exceptions should be caught and logged:
      35             :  *
      36             :  * <pre>{@code
      37             :  * Map<String, Object> results = new HashMap<>();
      38             :  * fooPluginMapContext.runEach(
      39             :  *     extension -> results.put(extension.getExportName(), extension.get().getFoo());
      40             :  * }</pre>
      41             :  *
      42             :  * <p>Example if all exceptions, but one, should be caught and logged:
      43             :  *
      44             :  * <pre>{@code
      45             :  * Map<String, Object> results = new HashMap<>();
      46             :  * try {
      47             :  *   fooPluginMapContext.runEach(
      48             :  *       extension -> results.put(extension.getExportName(), extension.get().getFoo(),
      49             :  *       MyException.class);
      50             :  * } catch (MyException e) {
      51             :  *   // handle the exception
      52             :  * }
      53             :  * }</pre>
      54             :  *
      55             :  * <p>Example if return values should be handled:
      56             :  *
      57             :  * <pre>{@code
      58             :  * Map<String, Object> results = new HashMap<>();
      59             :  * for (PluginMapEntryContext<Foo> c : fooPluginMapContext) {
      60             :  *   if (c.call(extension -> extension.get().handles(x))) {
      61             :  *     c.run(extension -> results.put(extension.getExportName(), extension.get().getFoo());
      62             :  *   }
      63             :  * }
      64             :  * }</pre>
      65             :  *
      66             :  * <p>Example if return values and a single exception should be handled:
      67             :  *
      68             :  * <pre>{@code
      69             :  * Map<String, Object> results = new HashMap<>();
      70             :  * try {
      71             :  *   for (PluginMapEntryContext<Foo> c : fooPluginMapContext) {
      72             :  *     if (c.call(extension -> extension.handles(x), MyException.class)) {
      73             :  *       c.run(extension -> results.put(extension.getExportName(), extension.get().getFoo(),
      74             :  *           MyException.class);
      75             :  *     }
      76             :  *   }
      77             :  * } catch (MyException e) {
      78             :  *   // handle the exception
      79             :  * }
      80             :  * }</pre>
      81             :  *
      82             :  * <p>Example if several exceptions should be handled:
      83             :  *
      84             :  * <pre>{@code
      85             :  * for (Extension<Foo> fooExtension : fooDynamicMap) {
      86             :  *   try (TraceContext traceContext = PluginContext.newTrace(fooExtension)) {
      87             :  *     fooExtension.get().doFoo();
      88             :  *   } catch (MyException1 | MyException2 | MyException3 e) {
      89             :  *     // handle the exception
      90             :  *   }
      91             :  * }
      92             :  * }</pre>
      93             :  */
      94             : public class PluginMapContext<T> implements Iterable<PluginMapEntryContext<T>> {
      95             :   private final DynamicMap<T> dynamicMap;
      96             :   private final PluginMetrics pluginMetrics;
      97             : 
      98             :   @VisibleForTesting
      99             :   @Inject
     100         152 :   public PluginMapContext(DynamicMap<T> dynamicMap, PluginMetrics pluginMetrics) {
     101         152 :     this.dynamicMap = dynamicMap;
     102         152 :     this.pluginMetrics = pluginMetrics;
     103         152 :   }
     104             : 
     105             :   /**
     106             :    * Iterator that provides contexts for invoking the extensions in this map.
     107             :    *
     108             :    * <p>This is useful if:
     109             :    *
     110             :    * <ul>
     111             :    *   <li>invoking of each extension returns a result that should be handled
     112             :    *   <li>a sequence of invocations should be done on each extension
     113             :    * </ul>
     114             :    */
     115             :   @Override
     116             :   public Iterator<PluginMapEntryContext<T>> iterator() {
     117           0 :     return Iterators.transform(
     118           0 :         dynamicMap.iterator(), e -> new PluginMapEntryContext<>(e, pluginMetrics));
     119             :   }
     120             : 
     121             :   /**
     122             :    * Checks if no implementations for this extension point have been registered.
     123             :    *
     124             :    * @return {@code true} if no implementations for this extension point have been registered,
     125             :    *     otherwise {@code false}
     126             :    */
     127             :   public boolean isEmpty() {
     128           0 :     return !dynamicMap.iterator().hasNext();
     129             :   }
     130             : 
     131             :   /**
     132             :    * Returns a sorted list of the plugins that have registered implementations for this extension
     133             :    * point.
     134             :    *
     135             :    * @return sorted list of the plugins that have registered implementations for this extension
     136             :    *     point
     137             :    */
     138             :   public NavigableSet<String> plugins() {
     139           2 :     return dynamicMap.plugins();
     140             :   }
     141             : 
     142             :   /**
     143             :    * Invokes each extension in the map. All exceptions from the plugin extensions are caught and
     144             :    * logged.
     145             :    *
     146             :    * <p>The consumer get the {@link Extension} provided that should be invoked. The extension
     147             :    * provides access to the plugin name and the export name.
     148             :    *
     149             :    * <p>All extension in the map are invoked, even if invoking some of the extensions failed.
     150             :    *
     151             :    * @param extensionConsumer consumer that invokes the extension
     152             :    */
     153             :   public void runEach(ExtensionConsumer<Extension<T>> extensionConsumer) {
     154         146 :     dynamicMap.forEach(p -> PluginContext.runLogExceptions(pluginMetrics, p, extensionConsumer));
     155         146 :   }
     156             : 
     157             :   /**
     158             :    * Invokes each extension in the map. All exceptions from the plugin extensions except exceptions
     159             :    * of the specified type are caught and logged.
     160             :    *
     161             :    * <p>The consumer get the {@link Extension} provided that should be invoked. The extension
     162             :    * provides access to the plugin name and the export name.
     163             :    *
     164             :    * <p>All extension in the map are invoked, even if invoking some of the extensions failed.
     165             :    *
     166             :    * @param extensionConsumer consumer that invokes the extension
     167             :    * @param exceptionClass type of the exceptions that should be thrown
     168             :    * @throws X expected exception from the plugin extension
     169             :    */
     170             :   public <X extends Exception> void runEach(
     171             :       ExtensionConsumer<Extension<T>> extensionConsumer, Class<X> exceptionClass) throws X {
     172           0 :     for (Extension<T> extension : dynamicMap) {
     173           0 :       PluginContext.runLogExceptions(pluginMetrics, extension, extensionConsumer, exceptionClass);
     174           0 :     }
     175           0 :   }
     176             : }

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