LCOV - code coverage report
Current view: top level - server/plugincontext - PluginSetContext.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 16 17 94.1 %
Date: 2022-11-19 15:00:39 Functions: 8 9 88.9 %

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

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