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,6 +400,9 @@ 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 {
 | 
			
		||||
        try {
 | 
			
		||||
          if (!controlFor(project).isVisible()) {
 | 
			
		||||
            return;
 | 
			
		||||
@@ -405,6 +412,10 @@ public class PushReplication implements ReplicationQueue {
 | 
			
		||||
              + " 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