Update the ThreadLocal based scopes to use RequestContext.

Bound the ThreadLocalRequestContext module, which makes the CurrentUser
available in the Global module. Removed any binding in the scopes that
provided the CurrentUser. Updated all of the scopes to propagate the
RequestContext. The PerThreadRequestScope.Propagator allows scoping
callables to enter a request context.

Change-Id: Idf682ed1d7485cf8c9cdd22cd89bfe1ad5296880
This commit is contained in:
Colby Ranger
2012-05-09 10:39:48 -07:00
parent b3a402124f
commit cfd994548e
20 changed files with 222 additions and 258 deletions

View File

@@ -1,41 +0,0 @@
// Copyright (C) 2009 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.httpd;
import com.google.gerrit.common.errors.NotSignedInException;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
class HttpIdentifiedUserProvider implements Provider<IdentifiedUser> {
private final Provider<CurrentUser> currUserProvider;
@Inject
HttpIdentifiedUserProvider(Provider<CurrentUser> currUserProvider) {
this.currUserProvider = currUserProvider;
}
@Override
public IdentifiedUser get() {
CurrentUser user = currUserProvider.get();
if (user instanceof IdentifiedUser) {
return (IdentifiedUser) user;
}
throw new ProvisionException(NotSignedInException.MESSAGE,
new NotSignedInException());
}
}

View File

@@ -15,19 +15,20 @@
package com.google.gerrit.httpd; package com.google.gerrit.httpd;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.util.RequestContext;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
class HttpCurrentUserProvider implements Provider<CurrentUser> { class HttpRequestContext implements RequestContext {
private final WebSession session; private final WebSession session;
@Inject @Inject
HttpCurrentUserProvider(final WebSession session) { HttpRequestContext(final WebSession session) {
this.session = session; this.session = session;
} }
@Override @Override
public CurrentUser get() { public CurrentUser getCurrentUser() {
return session.getCurrentUser(); return session.getCurrentUser();
} }
} }

View File

@@ -15,9 +15,13 @@
package com.google.gerrit.httpd; package com.google.gerrit.httpd;
import com.google.gerrit.server.RequestCleanup; import com.google.gerrit.server.RequestCleanup;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.google.inject.servlet.ServletModule;
import java.io.IOException; import java.io.IOException;
@@ -30,12 +34,27 @@ import javax.servlet.ServletResponse;
/** Executes any pending {@link RequestCleanup} at the end of a request. */ /** Executes any pending {@link RequestCleanup} at the end of a request. */
@Singleton @Singleton
class RequestCleanupFilter implements Filter { public class RequestContextFilter implements Filter {
public static Module module() {
return new ServletModule() {
@Override
protected void configureServlets() {
filter("/*").through(RequestContextFilter.class);
}
};
}
private final Provider<RequestCleanup> cleanup; private final Provider<RequestCleanup> cleanup;
private final Provider<HttpRequestContext> requestContext;
private final ThreadLocalRequestContext local;
@Inject @Inject
RequestCleanupFilter(final Provider<RequestCleanup> r) { RequestContextFilter(final Provider<RequestCleanup> r,
final Provider<HttpRequestContext> c,
final ThreadLocalRequestContext l) {
cleanup = r; cleanup = r;
requestContext = c;
local = l;
} }
@Override @Override
@@ -50,10 +69,15 @@ class RequestCleanupFilter implements Filter {
public void doFilter(final ServletRequest request, public void doFilter(final ServletRequest request,
final ServletResponse response, final FilterChain chain) final ServletResponse response, final FilterChain chain)
throws IOException, ServletException { throws IOException, ServletException {
RequestContext old = local.setContext(requestContext.get());
try {
try { try {
chain.doFilter(request, response); chain.doFilter(request, response);
} finally { } finally {
cleanup.get().run(); cleanup.get().run();
} }
} finally {
local.setContext(old);
}
} }
} }

View File

@@ -25,8 +25,6 @@ import com.google.gerrit.httpd.auth.ldap.LdapAuthModule;
import com.google.gerrit.httpd.gitweb.GitWebModule; import com.google.gerrit.httpd.gitweb.GitWebModule;
import com.google.gerrit.httpd.rpc.UiRpcModule; import com.google.gerrit.httpd.rpc.UiRpcModule;
import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.RemotePeer; import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.account.AccountManager; import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.ChangeUserName; import com.google.gerrit.server.account.ChangeUserName;
@@ -78,13 +76,8 @@ public class WebModule extends FactoryModule {
@Override @Override
protected void configure() { protected void configure() {
install(new ServletModule() {
@Override
protected void configureServlets() {
filter("/*").through(RequestCleanupFilter.class);
}
});
bind(RequestScopePropagator.class).to(GuiceRequestScopePropagator.class); bind(RequestScopePropagator.class).to(GuiceRequestScopePropagator.class);
bind(HttpRequestContext.class);
if (wantSSL) { if (wantSSL) {
install(new RequireSslFilter.Module()); install(new RequireSslFilter.Module());
@@ -147,9 +140,6 @@ public class WebModule extends FactoryModule {
bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider( bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
HttpRemotePeerProvider.class).in(RequestScoped.class); HttpRemotePeerProvider.class).in(RequestScoped.class);
bind(CurrentUser.class).toProvider(HttpCurrentUserProvider.class);
bind(IdentifiedUser.class).toProvider(HttpIdentifiedUserProvider.class);
install(new LifecycleModule() { install(new LifecycleModule() {
@Override @Override
protected void configure() { protected void configure() {

View File

@@ -21,6 +21,7 @@ import com.google.gerrit.ehcache.EhcachePoolImpl;
import com.google.gerrit.httpd.CacheBasedWebSession; import com.google.gerrit.httpd.CacheBasedWebSession;
import com.google.gerrit.httpd.GitOverHttpModule; import com.google.gerrit.httpd.GitOverHttpModule;
import com.google.gerrit.httpd.HttpCanonicalWebUrlProvider; import com.google.gerrit.httpd.HttpCanonicalWebUrlProvider;
import com.google.gerrit.httpd.RequestContextFilter;
import com.google.gerrit.httpd.WebModule; import com.google.gerrit.httpd.WebModule;
import com.google.gerrit.httpd.WebSshGlueModule; import com.google.gerrit.httpd.WebSshGlueModule;
import com.google.gerrit.httpd.auth.openid.OpenIdModule; import com.google.gerrit.httpd.auth.openid.OpenIdModule;
@@ -271,6 +272,7 @@ public class Daemon extends SiteProgram {
private Injector createWebInjector() { private Injector createWebInjector() {
final List<Module> modules = new ArrayList<Module>(); final List<Module> modules = new ArrayList<Module>();
modules.add(RequestContextFilter.module());
modules.add(CacheBasedWebSession.module()); modules.add(CacheBasedWebSession.module());
modules.add(HttpContactStoreConnection.module()); modules.add(HttpContactStoreConnection.module());
modules.add(sysInjector.getInstance(GitOverHttpModule.class)); modules.add(sysInjector.getInstance(GitOverHttpModule.class));

View File

@@ -23,6 +23,7 @@ import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.AuthType; import com.google.gerrit.reviewdb.client.AuthType;
import com.google.gerrit.rules.PrologModule; import com.google.gerrit.rules.PrologModule;
import com.google.gerrit.rules.RulesCache; import com.google.gerrit.rules.RulesCache;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.FileTypeRegistry; import com.google.gerrit.server.FileTypeRegistry;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.InternalUser; import com.google.gerrit.server.InternalUser;
@@ -64,8 +65,10 @@ import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SectionSortCache; import com.google.gerrit.server.project.SectionSortCache;
import com.google.gerrit.server.tools.ToolsCatalog; import com.google.gerrit.server.tools.ToolsCatalog;
import com.google.gerrit.server.util.IdGenerator; import com.google.gerrit.server.util.IdGenerator;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.server.workflow.FunctionState; import com.google.gerrit.server.workflow.FunctionState;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.servlet.RequestScoped;
import org.apache.velocity.runtime.RuntimeInstance; import org.apache.velocity.runtime.RuntimeInstance;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
@@ -117,6 +120,7 @@ public class GerritGlobalModule extends FactoryModule {
install(new AccessControlModule()); install(new AccessControlModule());
install(new GitModule()); install(new GitModule());
install(new PrologModule()); install(new PrologModule());
install(ThreadLocalRequestContext.module());
factory(AccountInfoCacheFactory.Factory.class); factory(AccountInfoCacheFactory.Factory.class);
factory(CapabilityControl.Factory.class); factory(CapabilityControl.Factory.class);
@@ -154,5 +158,7 @@ public class GerritGlobalModule extends FactoryModule {
bind(GitReferenceUpdated.class); bind(GitReferenceUpdated.class);
DynamicSet.setOf(binder(), GitReferenceUpdatedListener.class); DynamicSet.setOf(binder(), GitReferenceUpdatedListener.class);
DynamicSet.setOf(binder(), NewProjectCreatedListener.class); DynamicSet.setOf(binder(), NewProjectCreatedListener.class);
bind(AnonymousUser.class);
} }
} }

View File

@@ -77,7 +77,6 @@ public class GerritRequestModule extends FactoryModule {
bind(ListProjects.class); bind(ListProjects.class);
bind(ApprovalsUtil.class); bind(ApprovalsUtil.class);
bind(AnonymousUser.class).in(RequestScoped.class);
bind(PerRequestProjectControlCache.class).in(RequestScoped.class); bind(PerRequestProjectControlCache.class).in(RequestScoped.class);
bind(ChangeControl.Factory.class).in(SINGLETON); bind(ChangeControl.Factory.class).in(SINGLETON);
bind(GroupControl.Factory.class).in(SINGLETON); bind(GroupControl.Factory.class).in(SINGLETON);

View File

@@ -20,16 +20,17 @@ import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.RemotePeer; import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.config.GerritRequestModule; import com.google.gerrit.server.config.GerritRequestModule;
import com.google.gerrit.server.ssh.SshInfo; import com.google.gerrit.server.ssh.SshInfo;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.RequestScopePropagator; import com.google.gerrit.server.util.RequestScopePropagator;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.OutOfScopeException; import com.google.inject.OutOfScopeException;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.Provides;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.google.inject.servlet.RequestScoped; import com.google.inject.servlet.RequestScoped;
@@ -43,6 +44,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Singleton @Singleton
@@ -57,6 +59,7 @@ public class ChangeMergeQueue implements MergeQueue {
private final WorkQueue workQueue; private final WorkQueue workQueue;
private final Provider<MergeOp.Factory> bgFactory; private final Provider<MergeOp.Factory> bgFactory;
private final PerThreadRequestScope.Scoper threadScoper;
@Inject @Inject
ChangeMergeQueue(final WorkQueue wq, Injector parent) { ChangeMergeQueue(final WorkQueue wq, Injector parent) {
@@ -68,15 +71,9 @@ public class ChangeMergeQueue implements MergeQueue {
bindScope(RequestScoped.class, PerThreadRequestScope.REQUEST); bindScope(RequestScoped.class, PerThreadRequestScope.REQUEST);
bind(RequestScopePropagator.class) bind(RequestScopePropagator.class)
.to(PerThreadRequestScope.Propagator.class); .to(PerThreadRequestScope.Propagator.class);
bind(PerThreadRequestScope.Propagator.class);
install(new GerritRequestModule()); install(new GerritRequestModule());
bind(CurrentUser.class).to(IdentifiedUser.class);
bind(IdentifiedUser.class).toProvider(new Provider<IdentifiedUser>() {
@Override
public IdentifiedUser get() {
throw new OutOfScopeException("No user on merge thread");
}
});
bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider( bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
new Provider<SocketAddress>() { new Provider<SocketAddress>() {
@Override @Override
@@ -91,8 +88,26 @@ public class ChangeMergeQueue implements MergeQueue {
} }
}); });
} }
@Provides
public PerThreadRequestScope.Scoper provideScoper(
final PerThreadRequestScope.Propagator propagator) {
final RequestContext requestContext = new RequestContext() {
@Override
public CurrentUser getCurrentUser() {
throw new OutOfScopeException("No user on merge thread");
}
};
return new PerThreadRequestScope.Scoper() {
@Override
public <T> Callable<T> scope(Callable<T> callable) {
return propagator.scope(requestContext, callable);
}
};
}
}); });
bgFactory = child.getProvider(MergeOp.Factory.class); bgFactory = child.getProvider(MergeOp.Factory.class);
threadScoper = child.getInstance(PerThreadRequestScope.Scoper.class);
} }
@Override @Override
@@ -186,19 +201,15 @@ public class ChangeMergeQueue implements MergeQueue {
} }
} }
private void mergeImpl(Branch.NameKey branch) { private void mergeImpl(final Branch.NameKey branch) {
try {
PerThreadRequestScope ctx = new PerThreadRequestScope();
PerThreadRequestScope old = PerThreadRequestScope.set(ctx);
try {
try { try {
threadScoper.scope(new Callable<Void>(){
@Override
public Void call() throws Exception {
bgFactory.get().create(branch).merge(); bgFactory.get().create(branch).merge();
} finally { return null;
ctx.cleanup.run();
}
} finally {
PerThreadRequestScope.set(old);
} }
}).call();
} catch (Throwable e) { } catch (Throwable e) {
log.error("Merge attempt for " + branch + " failed", e); log.error("Merge attempt for " + branch + " failed", e);
} finally { } finally {

View File

@@ -14,50 +14,86 @@
package com.google.gerrit.server.git; package com.google.gerrit.server.git;
import com.google.gerrit.server.RequestCleanup; import com.google.common.collect.Maps;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestScopePropagator; import com.google.gerrit.server.util.ThreadLocalRequestScopePropagator;
import com.google.inject.Inject;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.OutOfScopeException; import com.google.inject.OutOfScopeException;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.Scope; import com.google.inject.Scope;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable;
public class PerThreadRequestScope { public class PerThreadRequestScope {
static class Propagator public interface Scoper {
extends ThreadLocalRequestScopePropagator<PerThreadRequestScope> { <T> Callable<T> scope(Callable<T> callable);
Propagator() { }
super(REQUEST, current);
private static class Context {
private final Map<Key<?>, Object> map;
private Context() {
map = Maps.newHashMap();
}
private <T> T get(Key<T> key, Provider<T> creator) {
@SuppressWarnings("unchecked")
T t = (T) map.get(key);
if (t == null) {
t = creator.get();
map.put(key, t);
}
return t;
}
}
public static class Propagator extends ThreadLocalRequestScopePropagator<Context> {
@Inject
Propagator(ThreadLocalRequestContext local) {
super(REQUEST, current, local);
} }
@Override @Override
protected PerThreadRequestScope continuingContext( protected Context continuingContext(Context ctx) {
PerThreadRequestScope ctx) { return new Context();
return new PerThreadRequestScope(); }
public <T> Callable<T> scope(RequestContext requestContext, Callable<T> callable) {
final Context ctx = new Context();
final Callable<T> wrapped = context(requestContext, cleanup(callable));
return new Callable<T>() {
@Override
public T call() throws Exception {
Context old = current.get();
current.set(ctx);
try {
return wrapped.call();
} finally {
current.set(old);
}
}
};
} }
} }
private static final ThreadLocal<PerThreadRequestScope> current = private static final ThreadLocal<Context> current = new ThreadLocal<Context>();
new ThreadLocal<PerThreadRequestScope>();
private static PerThreadRequestScope requireContext() { private static Context requireContext() {
final PerThreadRequestScope ctx = current.get(); final Context ctx = current.get();
if (ctx == null) { if (ctx == null) {
throw new OutOfScopeException("Not in command/request"); throw new OutOfScopeException("Not in command/request");
} }
return ctx; return ctx;
} }
public static PerThreadRequestScope set(PerThreadRequestScope ctx) {
PerThreadRequestScope old = current.get();
current.set(ctx);
return old;
}
public static final Scope REQUEST = new Scope() { public static final Scope REQUEST = new Scope() {
@Override
public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) { public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
return new Provider<T>() { return new Provider<T>() {
@Override
public T get() { public T get() {
return requireContext().get(key, creator); return requireContext().get(key, creator);
} }
@@ -74,26 +110,4 @@ public class PerThreadRequestScope {
return "PerThreadRequestScope.REQUEST"; return "PerThreadRequestScope.REQUEST";
} }
}; };
private static final Key<RequestCleanup> RC_KEY =
Key.get(RequestCleanup.class);
final RequestCleanup cleanup;
private final Map<Key<?>, Object> map;
public PerThreadRequestScope() {
cleanup = new RequestCleanup();
map = new HashMap<Key<?>, Object>();
map.put(RC_KEY, cleanup);
}
synchronized <T> T get(Key<T> key, Provider<T> creator) {
@SuppressWarnings("unchecked")
T t = (T) map.get(key);
if (t == null) {
t = creator.get();
map.put(key, t);
}
return t;
}
} }

View File

@@ -38,17 +38,15 @@ public class GuiceRequestScopePropagator extends RequestScopePropagator {
private final String url; private final String url;
private final SocketAddress peer; private final SocketAddress peer;
private final CurrentUser user;
@Inject @Inject
GuiceRequestScopePropagator( GuiceRequestScopePropagator(
@CanonicalWebUrl @Nullable Provider<String> urlProvider, @CanonicalWebUrl @Nullable Provider<String> urlProvider,
@RemotePeer Provider<SocketAddress> remotePeerProvider, @RemotePeer Provider<SocketAddress> remotePeerProvider,
Provider<CurrentUser> currentUserProvider) { ThreadLocalRequestContext local) {
super(ServletScopes.REQUEST); super(ServletScopes.REQUEST, local);
this.url = urlProvider != null ? urlProvider.get() : null; this.url = urlProvider != null ? urlProvider.get() : null;
this.peer = remotePeerProvider.get(); this.peer = remotePeerProvider.get();
this.user = currentUserProvider.get();
} }
/** /**
@@ -69,9 +67,6 @@ public class GuiceRequestScopePropagator extends RequestScopePropagator {
Providers.of(peer)); Providers.of(peer));
seedMap.put(Key.get(SocketAddress.class, RemotePeer.class), peer); seedMap.put(Key.get(SocketAddress.class, RemotePeer.class), peer);
seedMap.put(Key.get(typeOfProvider(CurrentUser.class)), Providers.of(user));
seedMap.put(Key.get(CurrentUser.class), user);
return ServletScopes.continueRequest(callable, seedMap); return ServletScopes.continueRequest(callable, seedMap);
} }

View File

@@ -42,9 +42,12 @@ import java.util.concurrent.Executors;
public abstract class RequestScopePropagator { public abstract class RequestScopePropagator {
private final Scope scope; private final Scope scope;
private final ThreadLocalRequestContext local;
protected RequestScopePropagator(Scope scope) { protected RequestScopePropagator(Scope scope,
ThreadLocalRequestContext local) {
this.scope = scope; this.scope = scope;
this.local = local;
} }
/** /**
@@ -70,26 +73,8 @@ public abstract class RequestScopePropagator {
* @return a new Callable which will execute in the current request scope. * @return a new Callable which will execute in the current request scope.
*/ */
public final <T> Callable<T> wrap(final Callable<T> callable) { public final <T> Callable<T> wrap(final Callable<T> callable) {
final Callable<T> wrapped = wrapImpl(new Callable<T>() { final Callable<T> wrapped =
@Override wrapImpl(context(local.getContext(), cleanup(callable)));
public T call() throws Exception {
RequestCleanup cleanup = scope.scope(
Key.get(RequestCleanup.class),
new Provider<RequestCleanup>() {
@Override
public RequestCleanup get() {
return new RequestCleanup();
}
}).get();
try {
return callable.call();
} finally {
cleanup.run();
}
}
});
return new Callable<T>() { return new Callable<T>() {
@Override @Override
public T call() throws Exception { public T call() throws Exception {
@@ -178,4 +163,41 @@ public abstract class RequestScopePropagator {
* @see #wrap(Callable) * @see #wrap(Callable)
*/ */
protected abstract <T> Callable<T> wrapImpl(final Callable<T> callable); protected abstract <T> Callable<T> wrapImpl(final Callable<T> callable);
protected <T> Callable<T> context(final RequestContext context,
final Callable<T> callable) {
return new Callable<T>() {
@Override
public T call() throws Exception {
RequestContext old = local.setContext(context);
try {
return callable.call();
} finally {
local.setContext(old);
}
}
};
}
protected <T> Callable<T> cleanup(final Callable<T> callable) {
return new Callable<T>() {
@Override
public T call() throws Exception {
RequestCleanup cleanup = scope.scope(
Key.get(RequestCleanup.class),
new Provider<RequestCleanup>() {
@Override
public RequestCleanup get() {
return new RequestCleanup();
}
}).get();
try {
return callable.call();
} finally {
cleanup.run();
}
}
};
}
} }

View File

@@ -31,8 +31,8 @@ public abstract class ThreadLocalRequestScopePropagator<C>
private final ThreadLocal<C> threadLocal; private final ThreadLocal<C> threadLocal;
protected ThreadLocalRequestScopePropagator(Scope scope, protected ThreadLocalRequestScopePropagator(Scope scope,
ThreadLocal<C> threadLocal) { ThreadLocal<C> threadLocal, ThreadLocalRequestContext local) {
super(scope); super(scope, local);
this.threadLocal = threadLocal; this.threadLocal = threadLocal;
} }

View File

@@ -122,7 +122,7 @@ class CommandFactoryProvider implements Provider<CommandFactory> {
public void setSession(final ServerSession session) { public void setSession(final ServerSession session) {
final SshSession s = session.getAttribute(SshSession.KEY); final SshSession s = session.getAttribute(SshSession.KEY);
this.ctx = new Context(s, commandLine); this.ctx = sshScope.newContext(s, commandLine);
} }
public void start(final Environment env) throws IOException { public void start(final Environment env) throws IOException {

View File

@@ -173,7 +173,7 @@ class DatabasePubKeyAuth implements PublickeyAuthenticator {
// session, record a login event in the log and add // session, record a login event in the log and add
// a close listener to record a logout event. // a close listener to record a logout event.
// //
Context ctx = new Context(sd, null); Context ctx = sshScope.newContext(sd, null);
Context old = sshScope.set(ctx); Context old = sshScope.set(ctx);
try { try {
sshLog.onLogin(); sshLog.onLogin();
@@ -185,7 +185,7 @@ class DatabasePubKeyAuth implements PublickeyAuthenticator {
new IoFutureListener<IoFuture>() { new IoFutureListener<IoFuture>() {
@Override @Override
public void operationComplete(IoFuture future) { public void operationComplete(IoFuture future) {
final Context ctx = new Context(sd, null); final Context ctx = sshScope.newContext(sd, null);
final Context old = sshScope.set(ctx); final Context old = sshScope.set(ctx);
try { try {
sshLog.onLogout(); sshLog.onLogout();

View File

@@ -89,7 +89,7 @@ class NoShell implements Factory<Command> {
} }
public void setSession(final ServerSession session) { public void setSession(final ServerSession session) {
this.context = new Context(session.getAttribute(SshSession.KEY), ""); this.context = sshScope.newContext(session.getAttribute(SshSession.KEY), "");
} }
public void start(final Environment env) throws IOException { public void start(final Environment env) throws IOException {

View File

@@ -1,42 +0,0 @@
// Copyright (C) 2010 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.sshd;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
class SshCurrentUserProvider implements Provider<CurrentUser> {
private final Provider<SshSession> session;
private final Provider<IdentifiedUser> identifiedProvider;
@Inject
SshCurrentUserProvider(Provider<SshSession> s, Provider<IdentifiedUser> p) {
session = s;
identifiedProvider = p;
}
@Override
public CurrentUser get() {
final CurrentUser user = session.get().getCurrentUser();
if (user instanceof IdentifiedUser) {
return identifiedProvider.get();
}
return session.get().getCurrentUser();
}
}

View File

@@ -1,47 +0,0 @@
// Copyright (C) 2010 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.sshd;
import com.google.gerrit.common.errors.NotSignedInException;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.Singleton;
@Singleton
class SshIdentifiedUserProvider implements Provider<IdentifiedUser> {
private final Provider<SshSession> session;
private final IdentifiedUser.RequestFactory factory;
@Inject
SshIdentifiedUserProvider(Provider<SshSession> s,
IdentifiedUser.RequestFactory f) {
session = s;
factory = f;
}
@Override
public IdentifiedUser get() {
final CurrentUser user = session.get().getCurrentUser();
if (user instanceof IdentifiedUser) {
return factory.create(user.getAccessPath(), //
((IdentifiedUser) user).getAccountId());
}
throw new ProvisionException(NotSignedInException.MESSAGE,
new NotSignedInException());
}
}

View File

@@ -152,11 +152,6 @@ public class SshModule extends FactoryModule {
bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider( bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
SshRemotePeerProvider.class).in(SshScope.REQUEST); SshRemotePeerProvider.class).in(SshScope.REQUEST);
bind(CurrentUser.class).toProvider(SshCurrentUserProvider.class).in(
SshScope.REQUEST);
bind(IdentifiedUser.class).toProvider(SshIdentifiedUserProvider.class).in(
SshScope.REQUEST);
bind(WorkQueue.Executor.class).annotatedWith(CommandExecutor.class) bind(WorkQueue.Executor.class).annotatedWith(CommandExecutor.class)
.toProvider(CommandExecutorProvider.class).in(SshScope.REQUEST); .toProvider(CommandExecutorProvider.class).in(SshScope.REQUEST);

View File

@@ -14,8 +14,13 @@
package com.google.gerrit.sshd; package com.google.gerrit.sshd;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.RequestCleanup; import com.google.gerrit.server.RequestCleanup;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestScopePropagator; import com.google.gerrit.server.util.ThreadLocalRequestScopePropagator;
import com.google.inject.Inject;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.OutOfScopeException; import com.google.inject.OutOfScopeException;
import com.google.inject.Provider; import com.google.inject.Provider;
@@ -26,10 +31,10 @@ import java.util.Map;
/** Guice scopes for state during an SSH connection. */ /** Guice scopes for state during an SSH connection. */
class SshScope { class SshScope {
static class Context {
private static final Key<RequestCleanup> RC_KEY = private static final Key<RequestCleanup> RC_KEY =
Key.get(RequestCleanup.class); Key.get(RequestCleanup.class);
class Context implements RequestContext {
private final RequestCleanup cleanup; private final RequestCleanup cleanup;
private final SshSession session; private final SshSession session;
private final String commandLine; private final String commandLine;
@@ -56,10 +61,6 @@ class SshScope {
finished = p.finished; finished = p.finished;
} }
Context(final SshSession s, final String c) {
this(s, c, System.currentTimeMillis());
}
String getCommandLine() { String getCommandLine() {
return commandLine; return commandLine;
} }
@@ -68,6 +69,16 @@ class SshScope {
return session; return session;
} }
@Override
public CurrentUser getCurrentUser() {
final CurrentUser user = session.getCurrentUser();
if (user instanceof IdentifiedUser) {
return userFactory.create(user.getAccessPath(), //
((IdentifiedUser) user).getAccountId());
}
return user;
}
synchronized <T> T get(Key<T> key, Provider<T> creator) { synchronized <T> T get(Key<T> key, Provider<T> creator) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
T t = (T) map.get(key); T t = (T) map.get(key);
@@ -100,15 +111,19 @@ class SshScope {
} }
static class Propagator extends ThreadLocalRequestScopePropagator<Context> { static class Propagator extends ThreadLocalRequestScopePropagator<Context> {
Propagator() { private final SshScope sshScope;
super(REQUEST, current);
@Inject
Propagator(SshScope sshScope, ThreadLocalRequestContext local) {
super(REQUEST, current, local);
this.sshScope = sshScope;
} }
@Override @Override
protected Context continuingContext(Context ctx) { protected Context continuingContext(Context ctx) {
// The cleanup is not chained, since the RequestScopePropagator executors // The cleanup is not chained, since the RequestScopePropagator executors
// the Context's cleanup when finished executing. // the Context's cleanup when finished executing.
return new Context(ctx, ctx.getSession(), ctx.getCommandLine()); return sshScope.newContinuingContext(ctx);
} }
} }
@@ -123,9 +138,28 @@ class SshScope {
return ctx; return ctx;
} }
private final ThreadLocalRequestContext local;
private final IdentifiedUser.RequestFactory userFactory;
@Inject
SshScope(ThreadLocalRequestContext local,
IdentifiedUser.RequestFactory userFactory) {
this.local = local;
this.userFactory = userFactory;
}
Context newContext(SshSession session, String commandLine) {
return new Context(session, commandLine, System.currentTimeMillis());
}
private Context newContinuingContext(Context ctx) {
return new Context(ctx, ctx.getSession(), ctx.getCommandLine());
}
Context set(Context ctx) { Context set(Context ctx) {
Context old = current.get(); Context old = current.get();
current.set(ctx); current.set(ctx);
local.setContext(ctx);
return old; return old;
} }

View File

@@ -223,6 +223,7 @@ public class WebAppInitializer extends GuiceServletContextListener {
private Injector createWebInjector() { private Injector createWebInjector() {
final List<Module> modules = new ArrayList<Module>(); final List<Module> modules = new ArrayList<Module>();
modules.add(RequestContextFilter.module());
modules.add(sysInjector.getInstance(GitOverHttpModule.class)); modules.add(sysInjector.getInstance(GitOverHttpModule.class));
modules.add(sshInjector.getInstance(WebModule.class)); modules.add(sshInjector.getInstance(WebModule.class));
modules.add(sshInjector.getInstance(WebSshGlueModule.class)); modules.add(sshInjector.getInstance(WebSshGlueModule.class));