Fix server startup when replication.config exists
The addition of the per-request RefControl cache broke push replication as the PushOp "request" could not locate a Guice binding for the cache. Use the same trick we do with MergeOp to setup a per-operation "request" scope for Guice. Change-Id: I0cecbad4809493f43cb2fec3624b1a53e38fb48f
This commit is contained in:
@@ -22,16 +22,13 @@ import com.google.gerrit.reviewdb.Project;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.RemotePeer;
|
||||
import com.google.gerrit.server.RequestCleanup;
|
||||
import com.google.gerrit.server.config.GerritRequestModule;
|
||||
import com.google.gerrit.server.ssh.SshInfo;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.OutOfScopeException;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Scope;
|
||||
import com.google.inject.servlet.RequestScoped;
|
||||
|
||||
import com.jcraft.jsch.HostKey;
|
||||
@@ -65,7 +62,7 @@ public class ChangeMergeQueue implements MergeQueue {
|
||||
Injector child = parent.createChildInjector(new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bindScope(RequestScoped.class, MyScope.REQUEST);
|
||||
bindScope(RequestScoped.class, PerThreadRequestScope.REQUEST);
|
||||
install(new GerritRequestModule());
|
||||
|
||||
bind(CurrentUser.class).to(IdentifiedUser.class);
|
||||
@@ -186,8 +183,8 @@ public class ChangeMergeQueue implements MergeQueue {
|
||||
|
||||
private void mergeImpl(Branch.NameKey branch) {
|
||||
try {
|
||||
MyScope ctx = new MyScope();
|
||||
MyScope old = MyScope.set(ctx);
|
||||
PerThreadRequestScope ctx = new PerThreadRequestScope();
|
||||
PerThreadRequestScope old = PerThreadRequestScope.set(ctx);
|
||||
try {
|
||||
try {
|
||||
bgFactory.get().create(branch).merge();
|
||||
@@ -195,7 +192,7 @@ public class ChangeMergeQueue implements MergeQueue {
|
||||
ctx.cleanup.run();
|
||||
}
|
||||
} finally {
|
||||
MyScope.set(old);
|
||||
PerThreadRequestScope.set(old);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.error("Merge attempt for " + branch + " failed", e);
|
||||
@@ -261,65 +258,4 @@ public class ChangeMergeQueue implements MergeQueue {
|
||||
return "recheck " + project.get() + " " + dest.getShortName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyScope {
|
||||
private static final ThreadLocal<MyScope> current =
|
||||
new ThreadLocal<MyScope>();
|
||||
|
||||
private static MyScope getContext() {
|
||||
final MyScope ctx = current.get();
|
||||
if (ctx == null) {
|
||||
throw new OutOfScopeException("Not in command/request");
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static MyScope set(MyScope ctx) {
|
||||
MyScope old = current.get();
|
||||
current.set(ctx);
|
||||
return old;
|
||||
}
|
||||
|
||||
static final Scope REQUEST = new Scope() {
|
||||
public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
|
||||
return new Provider<T>() {
|
||||
public T get() {
|
||||
return getContext().get(key, creator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s[%s]", creator, REQUEST);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MergeQueue.REQUEST";
|
||||
}
|
||||
};
|
||||
|
||||
private static final Key<RequestCleanup> RC_KEY =
|
||||
Key.get(RequestCleanup.class);
|
||||
|
||||
private final RequestCleanup cleanup;
|
||||
private final Map<Key<?>, Object> map;
|
||||
|
||||
MyScope() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// 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.server.git;
|
||||
|
||||
import com.google.gerrit.server.RequestCleanup;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.OutOfScopeException;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Scope;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
class PerThreadRequestScope {
|
||||
private static final ThreadLocal<PerThreadRequestScope> current =
|
||||
new ThreadLocal<PerThreadRequestScope>();
|
||||
|
||||
private static PerThreadRequestScope getContext() {
|
||||
final PerThreadRequestScope ctx = current.get();
|
||||
if (ctx == null) {
|
||||
throw new OutOfScopeException("Not in command/request");
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static PerThreadRequestScope set(PerThreadRequestScope ctx) {
|
||||
PerThreadRequestScope old = current.get();
|
||||
current.set(ctx);
|
||||
return old;
|
||||
}
|
||||
|
||||
static final Scope REQUEST = new Scope() {
|
||||
public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
|
||||
return new Provider<T>() {
|
||||
public T get() {
|
||||
return getContext().get(key, creator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s[%s]", creator, REQUEST);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PerThreadRequestScope.REQUEST";
|
||||
}
|
||||
};
|
||||
|
||||
private static final Key<RequestCleanup> RC_KEY =
|
||||
Key.get(RequestCleanup.class);
|
||||
|
||||
final RequestCleanup cleanup;
|
||||
private final Map<Key<?>, Object> map;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -155,6 +155,16 @@ class PushOp implements ProjectRunnable {
|
||||
}
|
||||
|
||||
public void run() {
|
||||
PerThreadRequestScope ctx = new PerThreadRequestScope();
|
||||
PerThreadRequestScope old = PerThreadRequestScope.set(ctx);
|
||||
try {
|
||||
runPushOperation();
|
||||
} finally {
|
||||
PerThreadRequestScope.set(old);
|
||||
}
|
||||
}
|
||||
|
||||
private void runPushOperation() {
|
||||
// Lock the queue, and remove ourselves, so we can't be modified once
|
||||
// we start replication (instead a new instance, with the same URI, is
|
||||
// created and scheduled for a future point in time.)
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.google.gerrit.server.ReplicationUser;
|
||||
import com.google.gerrit.server.config.ConfigUtil;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||
import com.google.gerrit.server.project.PerRequestProjectControlCache;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.gwtorm.client.SchemaFactory;
|
||||
import com.google.inject.AbstractModule;
|
||||
@@ -29,6 +30,7 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.assistedinject.FactoryProvider;
|
||||
import com.google.inject.servlet.RequestScoped;
|
||||
|
||||
import com.jcraft.jsch.Session;
|
||||
|
||||
@@ -373,6 +375,8 @@ public class PushReplication implements ReplicationQueue {
|
||||
injector.createChildInjector(new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bindScope(RequestScoped.class, PerThreadRequestScope.REQUEST);
|
||||
bind(PerRequestProjectControlCache.class).in(RequestScoped.class);
|
||||
bind(CurrentUser.class).toInstance(remoteUser);
|
||||
}
|
||||
}).getInstance(ProjectControl.Factory.class);
|
||||
@@ -396,15 +400,22 @@ public class PushReplication implements ReplicationQueue {
|
||||
|
||||
void schedule(final Project.NameKey project, final String ref,
|
||||
final URIish uri) {
|
||||
PerThreadRequestScope ctx = new PerThreadRequestScope();
|
||||
PerThreadRequestScope old = PerThreadRequestScope.set(ctx);
|
||||
try {
|
||||
if (!controlFor(project).isVisible()) {
|
||||
try {
|
||||
if (!controlFor(project).isVisible()) {
|
||||
return;
|
||||
}
|
||||
} catch (NoSuchProjectException e1) {
|
||||
log.error("Internal error: project " + project
|
||||
+ " not found during replication");
|
||||
return;
|
||||
}
|
||||
} catch (NoSuchProjectException e1) {
|
||||
log.error("Internal error: project " + project
|
||||
+ " not found during replication");
|
||||
return;
|
||||
} finally {
|
||||
PerThreadRequestScope.set(old);
|
||||
}
|
||||
|
||||
synchronized (pending) {
|
||||
PushOp e = pending.get(uri);
|
||||
if (e == null) {
|
||||
@@ -443,16 +454,6 @@ public class PushReplication implements ReplicationQueue {
|
||||
* @param pushOp The PushOp instance to be scheduled.
|
||||
*/
|
||||
void reschedule(final PushOp pushOp) {
|
||||
try {
|
||||
if (!controlFor(pushOp.getProjectNameKey()).isVisible()) {
|
||||
return;
|
||||
}
|
||||
} catch (NoSuchProjectException e1) {
|
||||
log.error("Internal error: project " + pushOp.getProjectNameKey()
|
||||
+ " not found during replication");
|
||||
return;
|
||||
}
|
||||
|
||||
// It locks access to pending variable.
|
||||
synchronized (pending) {
|
||||
URIish uri = pushOp.getURI();
|
||||
|
||||
Reference in New Issue
Block a user