com.google.gerrit.server.util.RequestScopePropagator Maven / Gradle / Ivy
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.util;
import static java.util.Objects.requireNonNull;
import com.google.common.base.Throwables;
import com.google.gerrit.entities.Project;
import com.google.gerrit.server.RequestCleanup;
import com.google.gerrit.server.git.ProjectRunnable;
import com.google.inject.Key;
import com.google.inject.Scope;
import com.google.inject.servlet.ServletScopes;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
/**
 * Base class for propagating request-scoped data between threads.
 *
 * Request scopes are typically linked to a {@link ThreadLocal}, which is only available to the
 * current thread. In order to allow background work involving RequestScoped data, the ThreadLocal
 * data must be copied from the request thread to the new background thread.
 *
 * 
Every type of RequestScope must provide an implementation of RequestScopePropagator. See
 * {@link #wrap(Callable)} for details on the implementation, usage, and restrictions.
 *
 * @see ThreadLocalRequestScopePropagator
 */
public abstract class RequestScopePropagator {
  private final Scope scope;
  private final ThreadLocalRequestContext local;
  protected RequestScopePropagator(Scope scope, ThreadLocalRequestContext local) {
    this.scope = scope;
    this.local = local;
  }
  /**
   * Ensures that the current request state is available when the passed in Callable is invoked.
   *
   * 
If needed wraps the passed in Callable in a new {@link Callable} that propagates the current
   * request state when the returned Callable is invoked. The method must be called in a request
   * scope and the returned Callable may only be invoked in a thread that is not already in a
   * request scope or is in the same request scope. The returned Callable will inherit toString()
   * from the passed in Callable. A {@link ScheduledThreadPoolExecutor} does not accept a Callable,
   * so there is no ProjectCallable implementation. Implementations of this method must be
   * consistent with Guice's {@link ServletScopes#continueRequest(Callable, java.util.Map)}.
   *
   * 
There are some limitations:
   *
   * 
   *   - Derived objects (i.e. anything marked created in a request scope) will not be
   *       transported.
   *   
 - State changes to the request scoped context after this method is called will not be seen
   *       in the continued thread.
   * 
 
   *
   * @param callable the Callable to wrap.
   * @return a new Callable which will execute in the current request scope.
   */
  @SuppressWarnings("javadoc") // See GuiceRequestScopePropagator#wrapImpl
  public final  Callable wrap(Callable callable) {
    final RequestContext callerContext = requireNonNull(local.getContext());
    final Callable wrapped = wrapImpl(context(callerContext, cleanup(callable)));
    return new Callable<>() {
      @Override
      public T call() throws Exception {
        if (callerContext == local.getContext()) {
          return callable.call();
        }
        return wrapped.call();
      }
      @Override
      public String toString() {
        return callable.toString();
      }
    };
  }
  /**
   * Wraps runnable in a new {@link Runnable} that propagates the current request state when the
   * runnable is invoked. The method must be called in a request scope and the returned Runnable may
   * only be invoked in a thread that is not already in a request scope. The returned Runnable will
   * inherit toString() from the passed in Runnable. Furthermore, if the passed runnable is of type
   * {@link ProjectRunnable}, the returned runnable will be of the same type with the methods
   * delegated.
   *
   * See {@link #wrap(Callable)} for details on implementation and usage.
   *
   * @param runnable the Runnable to wrap.
   * @return a new Runnable which will execute in the current request scope.
   */
  public final Runnable wrap(Runnable runnable) {
    final Callable