Make ReviewDb available in the server on a per-thread basis

Allow types bound in the system injector to access the per-thread
ReviewDb if they are invoked from within a request context, without
caring about which entry they were invoked from. This allows email
sending and some other change update action code to be bound at the
global level and work in both SSH and HTTP invocations.

Change-Id: I411ded5196ae6920573233fb0cde48f35ce7ad01
This commit is contained in:
Shawn O. Pearce
2012-11-13 16:09:21 -08:00
parent 3d45206d1c
commit 401440f975
17 changed files with 187 additions and 72 deletions

View File

@@ -14,9 +14,12 @@
package com.google.gerrit.server.util;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.Singleton;
/**
@@ -37,4 +40,15 @@ public class FallbackRequestContext implements RequestContext {
public CurrentUser getCurrentUser() {
return user;
}
@Override
public Provider<ReviewDb> getReviewDbProvider() {
return new Provider<ReviewDb>() {
@Override
public ReviewDb get() {
throw new ProvisionException(
"Automatic ReviewDb only available in request scope");
}
};
}
}

View File

@@ -17,6 +17,7 @@ package com.google.gerrit.server.util;
import com.google.common.collect.Maps;
import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.Provider;
@@ -42,8 +43,9 @@ public class GuiceRequestScopePropagator extends RequestScopePropagator {
GuiceRequestScopePropagator(
@CanonicalWebUrl @Nullable Provider<String> urlProvider,
@RemotePeer Provider<SocketAddress> remotePeerProvider,
ThreadLocalRequestContext local) {
super(ServletScopes.REQUEST, local);
ThreadLocalRequestContext local,
Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
super(ServletScopes.REQUEST, local, dbProviderProvider);
this.url = urlProvider != null ? urlProvider.get() : null;
this.peer = remotePeerProvider.get();
}

View File

@@ -14,13 +14,15 @@
package com.google.gerrit.server.util;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.inject.Provider;
/**
* The RequestContext is an interface exposing the fields that are needed
* by the GerritGlobalModule scope.
*/
public interface RequestContext {
CurrentUser getCurrentUser();
Provider<ReviewDb> getReviewDbProvider();
}

View File

@@ -14,8 +14,12 @@
package com.google.gerrit.server.util;
import com.google.gerrit.reviewdb.client.Project.NameKey;
import com.google.common.base.Throwables;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.RequestCleanup;
import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
import com.google.gerrit.server.git.ProjectRunnable;
import com.google.inject.Key;
import com.google.inject.Provider;
@@ -43,11 +47,14 @@ public abstract class RequestScopePropagator {
private final Scope scope;
private final ThreadLocalRequestContext local;
private final Provider<RequestScopedReviewDbProvider> dbProviderProvider;
protected RequestScopePropagator(Scope scope,
ThreadLocalRequestContext local) {
ThreadLocalRequestContext local,
Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
this.scope = scope;
this.local = local;
this.dbProviderProvider = dbProviderProvider;
}
/**
@@ -111,15 +118,14 @@ public abstract class RequestScopePropagator {
public void run() {
try {
wrapped.call();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
Throwables.propagateIfPossible(e);
throw new RuntimeException(e); // Not possible.
}
}
@Override
public NameKey getProjectNameKey() {
public Project.NameKey getProjectNameKey() {
return ((ProjectRunnable) runnable).getProjectNameKey();
}
@@ -164,12 +170,23 @@ public abstract class RequestScopePropagator {
*/
protected abstract <T> Callable<T> wrapImpl(final Callable<T> callable);
protected <T> Callable<T> context(final RequestContext context,
protected <T> Callable<T> context(RequestContext context,
final Callable<T> callable) {
final CurrentUser user = context.getCurrentUser();
return new Callable<T>() {
@Override
public T call() throws Exception {
RequestContext old = local.setContext(context);
RequestContext old = local.setContext(new RequestContext() {
@Override
public CurrentUser getCurrentUser() {
return user;
}
@Override
public Provider<ReviewDb> getReviewDbProvider() {
return dbProviderProvider.get();
}
});
try {
return callable.call();
} finally {
@@ -191,7 +208,6 @@ public abstract class RequestScopePropagator {
return new RequestCleanup();
}
}).get();
try {
return callable.call();
} finally {

View File

@@ -16,6 +16,7 @@ package com.google.gerrit.server.util;
import com.google.common.base.Objects;
import com.google.gerrit.common.errors.NotSignedInException;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.inject.AbstractModule;
@@ -64,6 +65,11 @@ public class ThreadLocalRequestContext {
throw new ProvisionException(NotSignedInException.MESSAGE,
new NotSignedInException());
}
@Provides
ReviewDb provideReviewDb(RequestContext ctx) {
return ctx.getReviewDbProvider().get();
}
};
}

View File

@@ -14,7 +14,9 @@
package com.google.gerrit.server.util;
import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
import com.google.inject.OutOfScopeException;
import com.google.inject.Provider;
import com.google.inject.Scope;
import java.util.concurrent.Callable;
@@ -31,8 +33,10 @@ public abstract class ThreadLocalRequestScopePropagator<C>
private final ThreadLocal<C> threadLocal;
protected ThreadLocalRequestScopePropagator(Scope scope,
ThreadLocal<C> threadLocal, ThreadLocalRequestContext local) {
super(scope, local);
ThreadLocal<C> threadLocal,
ThreadLocalRequestContext local,
Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
super(scope, local, dbProviderProvider);
this.threadLocal = threadLocal;
}