LCOV - code coverage report
Current view: top level - server/util - RequestScopePropagator.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 30 42 71.4 %
Date: 2022-11-19 15:00:39 Functions: 14 19 73.7 %

          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.server.util;
      16             : 
      17             : import static java.util.Objects.requireNonNull;
      18             : 
      19             : import com.google.common.base.Throwables;
      20             : import com.google.gerrit.entities.Project;
      21             : import com.google.gerrit.server.RequestCleanup;
      22             : import com.google.gerrit.server.git.ProjectRunnable;
      23             : import com.google.inject.Key;
      24             : import com.google.inject.Scope;
      25             : import com.google.inject.servlet.ServletScopes;
      26             : import java.util.concurrent.Callable;
      27             : import java.util.concurrent.Executors;
      28             : import java.util.concurrent.ScheduledThreadPoolExecutor;
      29             : 
      30             : /**
      31             :  * Base class for propagating request-scoped data between threads.
      32             :  *
      33             :  * <p>Request scopes are typically linked to a {@link ThreadLocal}, which is only available to the
      34             :  * current thread. In order to allow background work involving RequestScoped data, the ThreadLocal
      35             :  * data must be copied from the request thread to the new background thread.
      36             :  *
      37             :  * <p>Every type of RequestScope must provide an implementation of RequestScopePropagator. See
      38             :  * {@link #wrap(Callable)} for details on the implementation, usage, and restrictions.
      39             :  *
      40             :  * @see ThreadLocalRequestScopePropagator
      41             :  */
      42             : public abstract class RequestScopePropagator {
      43             : 
      44             :   private final Scope scope;
      45             :   private final ThreadLocalRequestContext local;
      46             : 
      47          97 :   protected RequestScopePropagator(Scope scope, ThreadLocalRequestContext local) {
      48          97 :     this.scope = scope;
      49          97 :     this.local = local;
      50          97 :   }
      51             : 
      52             :   /**
      53             :    * Ensures that the current request state is available when the passed in Callable is invoked.
      54             :    *
      55             :    * <p>If needed wraps the passed in Callable in a new {@link Callable} that propagates the current
      56             :    * request state when the returned Callable is invoked. The method must be called in a request
      57             :    * scope and the returned Callable may only be invoked in a thread that is not already in a
      58             :    * request scope or is in the same request scope. The returned Callable will inherit toString()
      59             :    * from the passed in Callable. A {@link ScheduledThreadPoolExecutor} does not accept a Callable,
      60             :    * so there is no ProjectCallable implementation. Implementations of this method must be
      61             :    * consistent with Guice's {@link ServletScopes#continueRequest(Callable, java.util.Map)}.
      62             :    *
      63             :    * <p>There are some limitations:
      64             :    *
      65             :    * <ul>
      66             :    *   <li>Derived objects (i.e. anything marked created in a request scope) will not be
      67             :    *       transported.
      68             :    *   <li>State changes to the request scoped context after this method is called will not be seen
      69             :    *       in the continued thread.
      70             :    * </ul>
      71             :    *
      72             :    * @param callable the Callable to wrap.
      73             :    * @return a new Callable which will execute in the current request scope.
      74             :    */
      75             :   @SuppressWarnings("javadoc") // See GuiceRequestScopePropagator#wrapImpl
      76             :   public final <T> Callable<T> wrap(Callable<T> callable) {
      77          96 :     final RequestContext callerContext = requireNonNull(local.getContext());
      78          96 :     final Callable<T> wrapped = wrapImpl(context(callerContext, cleanup(callable)));
      79          96 :     return new Callable<>() {
      80             :       @Override
      81             :       public T call() throws Exception {
      82          96 :         if (callerContext == local.getContext()) {
      83          88 :           return callable.call();
      84             :         }
      85          96 :         return wrapped.call();
      86             :       }
      87             : 
      88             :       @Override
      89             :       public String toString() {
      90           0 :         return callable.toString();
      91             :       }
      92             :     };
      93             :   }
      94             : 
      95             :   /**
      96             :    * Wraps runnable in a new {@link Runnable} that propagates the current request state when the
      97             :    * runnable is invoked. The method must be called in a request scope and the returned Runnable may
      98             :    * only be invoked in a thread that is not already in a request scope. The returned Runnable will
      99             :    * inherit toString() from the passed in Runnable. Furthermore, if the passed runnable is of type
     100             :    * {@link ProjectRunnable}, the returned runnable will be of the same type with the methods
     101             :    * delegated.
     102             :    *
     103             :    * <p>See {@link #wrap(Callable)} for details on implementation and usage.
     104             :    *
     105             :    * @param runnable the Runnable to wrap.
     106             :    * @return a new Runnable which will execute in the current request scope.
     107             :    */
     108             :   public final Runnable wrap(Runnable runnable) {
     109          96 :     final Callable<Object> wrapped = wrap(Executors.callable(runnable));
     110             : 
     111          96 :     if (runnable instanceof ProjectRunnable) {
     112          96 :       return new ProjectRunnable() {
     113             :         @Override
     114             :         public void run() {
     115             :           try {
     116          96 :             wrapped.call();
     117           0 :           } catch (Exception e) {
     118           0 :             Throwables.throwIfUnchecked(e);
     119           0 :             throw new RuntimeException(e); // Not possible.
     120          96 :           }
     121          96 :         }
     122             : 
     123             :         @Override
     124             :         public Project.NameKey getProjectNameKey() {
     125           0 :           return ((ProjectRunnable) runnable).getProjectNameKey();
     126             :         }
     127             : 
     128             :         @Override
     129             :         public String getRemoteName() {
     130           0 :           return ((ProjectRunnable) runnable).getRemoteName();
     131             :         }
     132             : 
     133             :         @Override
     134             :         public boolean hasCustomizedPrint() {
     135           0 :           return ((ProjectRunnable) runnable).hasCustomizedPrint();
     136             :         }
     137             : 
     138             :         @Override
     139             :         public String toString() {
     140          10 :           return runnable.toString();
     141             :         }
     142             :       };
     143             :     }
     144          88 :     return new Runnable() {
     145             :       @Override
     146             :       public void run() {
     147             :         try {
     148          88 :           wrapped.call();
     149           0 :         } catch (RuntimeException e) {
     150           0 :           throw e;
     151           0 :         } catch (Exception e) {
     152           0 :           throw new RuntimeException(e); // Not possible.
     153          88 :         }
     154          88 :       }
     155             : 
     156             :       @Override
     157             :       public String toString() {
     158           0 :         return runnable.toString();
     159             :       }
     160             :     };
     161             :   }
     162             : 
     163             :   /**
     164             :    * Ensures that the current request state is available when the passed in Callable is invoked
     165             :    *
     166             :    * <p>See {@link #wrap(Callable)}
     167             :    */
     168             :   protected abstract <T> Callable<T> wrapImpl(Callable<T> callable);
     169             : 
     170             :   protected <T> Callable<T> context(RequestContext context, Callable<T> callable) {
     171          96 :     return () -> {
     172          96 :       RequestContext old = local.setContext(context);
     173             :       try {
     174          96 :         return callable.call();
     175             :       } finally {
     176          96 :         local.setContext(old);
     177             :       }
     178             :     };
     179             :   }
     180             : 
     181             :   protected <T> Callable<T> cleanup(Callable<T> callable) {
     182          96 :     return () -> {
     183          96 :       RequestCleanup cleanup =
     184          96 :           scope.scope(Key.get(RequestCleanup.class), RequestCleanup::new).get();
     185             :       try {
     186          96 :         return callable.call();
     187             :       } finally {
     188          96 :         cleanup.run();
     189             :       }
     190             :     };
     191             :   }
     192             : }

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