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:
@@ -16,9 +16,11 @@ package com.google.gerrit.sshd;
|
||||
|
||||
import com.google.common.util.concurrent.Atomics;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.git.WorkQueue;
|
||||
import com.google.gerrit.sshd.SshScope.Context;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
@@ -56,15 +58,17 @@ class CommandFactoryProvider implements Provider<CommandFactory> {
|
||||
private final SshScope sshScope;
|
||||
private final ScheduledExecutorService startExecutor;
|
||||
private final Executor destroyExecutor;
|
||||
private final SchemaFactory<ReviewDb> schemaFactory;
|
||||
|
||||
@Inject
|
||||
CommandFactoryProvider(
|
||||
@CommandName(Commands.ROOT) final DispatchCommandProvider d,
|
||||
@GerritServerConfig final Config cfg, final WorkQueue workQueue,
|
||||
final SshLog l, final SshScope s) {
|
||||
final SshLog l, final SshScope s, SchemaFactory<ReviewDb> sf) {
|
||||
dispatcher = d;
|
||||
log = l;
|
||||
sshScope = s;
|
||||
schemaFactory = sf;
|
||||
|
||||
int threads = cfg.getInt("sshd","commandStartThreads", 2);
|
||||
startExecutor = workQueue.createQueue(threads, "SshCommandStart");
|
||||
@@ -122,7 +126,7 @@ class CommandFactoryProvider implements Provider<CommandFactory> {
|
||||
|
||||
public void setSession(final ServerSession session) {
|
||||
final SshSession s = session.getAttribute(SshSession.KEY);
|
||||
this.ctx = sshScope.newContext(s, commandLine);
|
||||
this.ctx = sshScope.newContext(schemaFactory, s, commandLine);
|
||||
}
|
||||
|
||||
public void start(final Environment env) throws IOException {
|
||||
|
@@ -170,7 +170,7 @@ class DatabasePubKeyAuth implements PublickeyAuthenticator {
|
||||
// session, record a login event in the log and add
|
||||
// a close listener to record a logout event.
|
||||
//
|
||||
Context ctx = sshScope.newContext(sd, null);
|
||||
Context ctx = sshScope.newContext(null, sd, null);
|
||||
Context old = sshScope.set(ctx);
|
||||
try {
|
||||
sshLog.onLogin();
|
||||
@@ -182,7 +182,7 @@ class DatabasePubKeyAuth implements PublickeyAuthenticator {
|
||||
new IoFutureListener<IoFuture>() {
|
||||
@Override
|
||||
public void operationComplete(IoFuture future) {
|
||||
final Context ctx = sshScope.newContext(sd, null);
|
||||
final Context ctx = sshScope.newContext(null, sd, null);
|
||||
final Context old = sshScope.set(ctx);
|
||||
try {
|
||||
sshLog.onLogout();
|
||||
|
@@ -15,10 +15,12 @@
|
||||
package com.google.gerrit.sshd;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||
import com.google.gerrit.server.ssh.SshInfo;
|
||||
import com.google.gerrit.sshd.SshScope.Context;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
@@ -58,6 +60,7 @@ class NoShell implements Factory<Command> {
|
||||
|
||||
static class SendMessage implements Command, SessionAware {
|
||||
private final Provider<MessageFactory> messageFactory;
|
||||
private final SchemaFactory<ReviewDb> schemaFactory;
|
||||
private final SshScope sshScope;
|
||||
|
||||
private InputStream in;
|
||||
@@ -67,8 +70,10 @@ class NoShell implements Factory<Command> {
|
||||
private Context context;
|
||||
|
||||
@Inject
|
||||
SendMessage(Provider<MessageFactory> messageFactory, SshScope sshScope) {
|
||||
SendMessage(Provider<MessageFactory> messageFactory,
|
||||
SchemaFactory<ReviewDb> sf, SshScope sshScope) {
|
||||
this.messageFactory = messageFactory;
|
||||
this.schemaFactory = sf;
|
||||
this.sshScope = sshScope;
|
||||
}
|
||||
|
||||
@@ -89,7 +94,8 @@ class NoShell implements Factory<Command> {
|
||||
}
|
||||
|
||||
public void setSession(final ServerSession session) {
|
||||
this.context = sshScope.newContext(session.getAttribute(SshSession.KEY), "");
|
||||
SshSession s = session.getAttribute(SshSession.KEY);
|
||||
this.context = sshScope.newContext(schemaFactory, s, "");
|
||||
}
|
||||
|
||||
public void start(final Environment env) throws IOException {
|
||||
|
@@ -14,19 +14,23 @@
|
||||
|
||||
package com.google.gerrit.sshd;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.RequestCleanup;
|
||||
import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
|
||||
import com.google.gerrit.server.util.RequestContext;
|
||||
import com.google.gerrit.server.util.ThreadLocalRequestContext;
|
||||
import com.google.gerrit.server.util.ThreadLocalRequestScopePropagator;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.OutOfScopeException;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Scope;
|
||||
import com.google.inject.util.Providers;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/** Guice scopes for state during an SSH connection. */
|
||||
@@ -34,29 +38,34 @@ class SshScope {
|
||||
private static final Key<RequestCleanup> RC_KEY =
|
||||
Key.get(RequestCleanup.class);
|
||||
|
||||
private static final Key<RequestScopedReviewDbProvider> DB_KEY =
|
||||
Key.get(RequestScopedReviewDbProvider.class);
|
||||
|
||||
class Context implements RequestContext {
|
||||
private final RequestCleanup cleanup;
|
||||
private final RequestCleanup cleanup = new RequestCleanup();
|
||||
private final Map<Key<?>, Object> map = Maps.newHashMap();
|
||||
private final SchemaFactory<ReviewDb> schemaFactory;
|
||||
private final SshSession session;
|
||||
private final String commandLine;
|
||||
private final Map<Key<?>, Object> map;
|
||||
|
||||
final long created;
|
||||
volatile long started;
|
||||
volatile long finished;
|
||||
|
||||
private Context(final SshSession s, final String c, final long at) {
|
||||
cleanup = new RequestCleanup();
|
||||
private Context(SchemaFactory<ReviewDb> sf, final SshSession s,
|
||||
final String c, final long at) {
|
||||
schemaFactory = sf;
|
||||
session = s;
|
||||
commandLine = c;
|
||||
|
||||
map = new HashMap<Key<?>, Object>();
|
||||
map.put(RC_KEY, cleanup);
|
||||
|
||||
created = started = finished = at;
|
||||
map.put(RC_KEY, cleanup);
|
||||
map.put(DB_KEY, new RequestScopedReviewDbProvider(
|
||||
schemaFactory,
|
||||
Providers.of(cleanup)));
|
||||
}
|
||||
|
||||
private Context(Context p, SshSession s, String c) {
|
||||
this(s, c, p.created);
|
||||
this(p.schemaFactory, s, c, p.created);
|
||||
started = p.started;
|
||||
finished = p.finished;
|
||||
}
|
||||
@@ -78,6 +87,11 @@ class SshScope {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider<ReviewDb> getReviewDbProvider() {
|
||||
return (RequestScopedReviewDbProvider) map.get(DB_KEY);
|
||||
}
|
||||
|
||||
synchronized <T> T get(Key<T> key, Provider<T> creator) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) map.get(key);
|
||||
@@ -113,8 +127,9 @@ class SshScope {
|
||||
private final SshScope sshScope;
|
||||
|
||||
@Inject
|
||||
Propagator(SshScope sshScope, ThreadLocalRequestContext local) {
|
||||
super(REQUEST, current, local);
|
||||
Propagator(SshScope sshScope, ThreadLocalRequestContext local,
|
||||
Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
|
||||
super(REQUEST, current, local, dbProviderProvider);
|
||||
this.sshScope = sshScope;
|
||||
}
|
||||
|
||||
@@ -147,8 +162,8 @@ class SshScope {
|
||||
this.userFactory = userFactory;
|
||||
}
|
||||
|
||||
Context newContext(SshSession session, String commandLine) {
|
||||
return new Context(session, commandLine, System.currentTimeMillis());
|
||||
Context newContext(SchemaFactory<ReviewDb> sf, SshSession s, String cmd) {
|
||||
return new Context(sf, s, cmd, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private Context newContinuingContext(Context ctx) {
|
||||
|
Reference in New Issue
Block a user