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

          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 static com.google.common.base.Preconditions.checkState;
      18             : 
      19             : import com.google.common.annotations.VisibleForTesting;
      20             : import com.google.gerrit.common.Nullable;
      21             : import com.google.gerrit.extensions.registration.DynamicItem;
      22             : import com.google.gerrit.extensions.registration.Extension;
      23             : import com.google.gerrit.server.plugincontext.PluginContext.CheckedExtensionImplFunction;
      24             : import com.google.gerrit.server.plugincontext.PluginContext.ExtensionImplConsumer;
      25             : import com.google.gerrit.server.plugincontext.PluginContext.ExtensionImplFunction;
      26             : import com.google.gerrit.server.plugincontext.PluginContext.PluginMetrics;
      27             : import com.google.inject.Inject;
      28             : 
      29             : /**
      30             :  * Context to invoke an extension from a {@link DynamicItem}.
      31             :  *
      32             :  * <p>When the plugin extension is invoked a logging tag with the plugin name is set. This way any
      33             :  * errors that are triggered by the plugin extension (even if they happen in Gerrit code which is
      34             :  * called by the plugin extension) can be easily attributed to the plugin.
      35             :  *
      36             :  * <p>The run* methods execute an extension but don't deliver a result back to the caller.
      37             :  * Exceptions can be caught and logged.
      38             :  *
      39             :  * <p>The call* methods execute an extension and deliver a result back to the caller.
      40             :  *
      41             :  * <p>Example if all exceptions should be caught and logged:
      42             :  *
      43             :  * <pre>{@code
      44             :  * fooPluginItemContext.run(foo -> foo.doFoo());
      45             :  * }</pre>
      46             :  *
      47             :  * <p>Example if all exceptions, but one, should be caught and logged:
      48             :  *
      49             :  * <pre>{@code
      50             :  * try {
      51             :  *   fooPluginItemContext.run(foo -> foo.doFoo(), MyException.class);
      52             :  * } catch (MyException e) {
      53             :  *   // handle the exception
      54             :  * }
      55             :  * }</pre>
      56             :  *
      57             :  * <p>Example if return values should be handled:
      58             :  *
      59             :  * <pre>{@code
      60             :  * Object result = fooPluginItemContext.call(foo -> foo.getFoo());
      61             :  * }</pre>
      62             :  *
      63             :  * <p>Example if return values and a single exception should be handled:
      64             :  *
      65             :  * <pre>{@code
      66             :  * Object result;
      67             :  * try {
      68             :  *   result = fooPluginItemContext.call(foo -> foo.getFoo(), MyException.class);
      69             :  * } catch (MyException e) {
      70             :  *   // handle the exception
      71             :  * }
      72             :  * }</pre>
      73             :  *
      74             :  * <p>Example if several exceptions should be handled:
      75             :  *
      76             :  * <pre>{@code
      77             :  * try (TraceContext traceContext = PluginContext.newTrace(fooDynamicItem.getEntry())) {
      78             :  *   fooDynamicItem.get().doFoo();
      79             :  * } catch (MyException1 | MyException2 | MyException3 e) {
      80             :  *   // handle the exception
      81             :  * }
      82             :  * }</pre>
      83             :  */
      84             : public class PluginItemContext<T> {
      85             :   @Nullable private final DynamicItem<T> dynamicItem;
      86             :   private final PluginMetrics pluginMetrics;
      87             : 
      88             :   @VisibleForTesting
      89             :   @Inject
      90         149 :   public PluginItemContext(DynamicItem<T> dynamicItem, PluginMetrics pluginMetrics) {
      91         149 :     this.dynamicItem = dynamicItem;
      92         149 :     this.pluginMetrics = pluginMetrics;
      93         149 :   }
      94             : 
      95             :   /**
      96             :    * Checks if an implementation for this extension point has been registered.
      97             :    *
      98             :    * @return {@code true} if an implementation for this extension point has been registered,
      99             :    *     otherwise {@code false}
     100             :    */
     101             :   public boolean hasImplementation() {
     102           3 :     return dynamicItem.getEntry() != null;
     103             :   }
     104             : 
     105             :   /**
     106             :    * Returns the name of the plugin that registered the extension.
     107             :    *
     108             :    * @return the plugin name, {@code null} if no implementation is registered for this extension
     109             :    *     point
     110             :    */
     111             :   @Nullable
     112             :   public String getPluginName() {
     113           0 :     return dynamicItem.getPluginName();
     114             :   }
     115             : 
     116             :   /**
     117             :    * Invokes the plugin extension of the item. All exceptions from the plugin extension are caught
     118             :    * and logged.
     119             :    *
     120             :    * <p>The consumer gets the extension implementation provided that should be invoked.
     121             :    *
     122             :    * <p>No-op if no implementation is registered for this extension point.
     123             :    *
     124             :    * @param extensionImplConsumer consumer that invokes the extension
     125             :    */
     126             :   public void run(ExtensionImplConsumer<T> extensionImplConsumer) {
     127         142 :     Extension<T> extension = dynamicItem.getEntry();
     128         142 :     if (extension == null) {
     129           4 :       return;
     130             :     }
     131         138 :     PluginContext.runLogExceptions(pluginMetrics, extension, extensionImplConsumer);
     132         138 :   }
     133             : 
     134             :   /**
     135             :    * Invokes the plugin extension of the item. All exceptions from the plugin extension are caught
     136             :    * and logged.
     137             :    *
     138             :    * <p>The consumer gets the extension implementation provided that should be invoked.
     139             :    *
     140             :    * <p>No-op if no implementation is registered for this extension point.
     141             :    *
     142             :    * @param extensionImplConsumer consumer that invokes the extension
     143             :    * @param exceptionClass type of the exceptions that should be thrown
     144             :    * @throws X expected exception from the plugin extension
     145             :    */
     146             :   public <X extends Exception> void run(
     147             :       ExtensionImplConsumer<T> extensionImplConsumer, Class<X> exceptionClass) throws X {
     148           0 :     Extension<T> extension = dynamicItem.getEntry();
     149           0 :     if (extension == null) {
     150           0 :       return;
     151             :     }
     152           0 :     PluginContext.runLogExceptions(pluginMetrics, extension, extensionImplConsumer, exceptionClass);
     153           0 :   }
     154             : 
     155             :   /**
     156             :    * Calls the plugin extension of the item and returns the result from the plugin extension call.
     157             :    *
     158             :    * <p>The function gets the extension implementation provided that should be invoked.
     159             :    *
     160             :    * <p>Fails with {@link IllegalStateException} if no implementation is registered for the item.
     161             :    *
     162             :    * @param extensionImplFunction function that invokes the extension
     163             :    * @return the result from the plugin extension
     164             :    * @throws IllegalStateException if no implementation is registered for the item
     165             :    */
     166             :   public <R> R call(ExtensionImplFunction<T, R> extensionImplFunction) {
     167         144 :     Extension<T> extension = dynamicItem.getEntry();
     168         144 :     checkState(extension != null);
     169         144 :     return PluginContext.call(pluginMetrics, extension, extensionImplFunction);
     170             :   }
     171             : 
     172             :   /**
     173             :    * Calls the plugin extension of the item and returns the result from the plugin extension call.
     174             :    * Exceptions of the specified type are thrown and must be handled by the caller.
     175             :    *
     176             :    * <p>The function gets the extension implementation provided that should be invoked.
     177             :    *
     178             :    * <p>Fails with {@link IllegalStateException} if no implementation is registered for the item.
     179             :    *
     180             :    * @param checkedExtensionImplFunction function that invokes the extension
     181             :    * @param exceptionClass type of the exceptions that should be thrown
     182             :    * @return the result from the plugin extension
     183             :    * @throws X expected exception from the plugin extension
     184             :    * @throws IllegalStateException if no implementation is registered for the item
     185             :    */
     186             :   public <R, X extends Exception> R call(
     187             :       CheckedExtensionImplFunction<T, R, X> checkedExtensionImplFunction, Class<X> exceptionClass)
     188             :       throws X {
     189           0 :     Extension<T> extension = dynamicItem.getEntry();
     190           0 :     checkState(extension != null);
     191           0 :     return PluginContext.call(
     192             :         pluginMetrics, extension, checkedExtensionImplFunction, exceptionClass);
     193             :   }
     194             : }

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