diff options
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/util/ThreadLocalRequestScopePropagator.java')
-rw-r--r-- | gerrit-server/src/main/java/com/google/gerrit/server/util/ThreadLocalRequestScopePropagator.java | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/ThreadLocalRequestScopePropagator.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/ThreadLocalRequestScopePropagator.java new file mode 100644 index 0000000000..581ccc1483 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/ThreadLocalRequestScopePropagator.java @@ -0,0 +1,89 @@ +// 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 com.google.inject.OutOfScopeException; +import com.google.inject.Scope; + +import java.util.concurrent.Callable; + +/** + * {@link RequestScopePropagator} implementation for request scopes based on + * a {@link ThreadLocal} context. + * + * @param <C> "context" type stored in the {@link ThreadLocal}. + */ +public abstract class ThreadLocalRequestScopePropagator<C> + extends RequestScopePropagator { + + private final ThreadLocal<C> threadLocal; + + protected ThreadLocalRequestScopePropagator(Scope scope, + ThreadLocal<C> threadLocal) { + super(scope); + this.threadLocal = threadLocal; + } + + /** + * @see RequestScopePropagator#wrap(Callable) + */ + @Override + protected final <T> Callable<T> wrapImpl(final Callable<T> callable) { + final C ctx = continuingContext(requireContext()); + return new Callable<T>() { + @Override + public T call() throws Exception { + if (threadLocal.get() != null) { + // This is consistent with the Guice ServletScopes.continueRequest() + // behavior. + throw new IllegalStateException("Cannot continue request, " + + "thread already has request in progress. A new thread must " + + "be used to propagate the request scope context."); + } + + threadLocal.set(ctx); + try { + return callable.call(); + } finally { + threadLocal.remove(); + } + } + }; + } + + private C requireContext() { + C context = threadLocal.get(); + if (context == null) { + throw new OutOfScopeException("Cannot access scoped object"); + } + return context; + } + + /** + * Returns a new context object based on the passed in context that has no + * request scoped objects initialized. + * <p> + * Note that some code paths expect request-scoped objects like + * {@code CurrentUser} to be constructible starting from just the context + * object returned by this method. For example, in the SSH scope, the context + * includes the {@code SshSession}, which is used by + * {@code SshCurrentUserProvider} to construct a new {@code CurrentUser} in + * the new thread. + * + * @param ctx the context to continue. + * @return a new context. + */ + protected abstract C continuingContext(C ctx); +} |