2 * Copyright 2000-2018 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package jetbrains.buildServer.buildTriggers.vcs.git;
19 import jetbrains.buildServer.log.Loggers;
20 import jetbrains.buildServer.serverSide.*;
21 import jetbrains.buildServer.util.EventDispatcher;
22 import jetbrains.buildServer.util.ThreadUtil;
23 import jetbrains.buildServer.util.executors.ExecutorsFactory;
24 import jetbrains.buildServer.vcs.*;
25 import org.eclipse.jgit.lib.Repository;
26 import org.jetbrains.annotations.NotNull;
28 import java.util.HashSet;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.ExecutorService;
33 public class GitClonesUpdater {
34 private final ConcurrentHashMap<VcsRoot, RepositoryStateData> myScheduledForUpdate = new ConcurrentHashMap<>();
35 private final GitVcsSupport myVcs;
36 private final RepositoryManager myRepositoryManager;
37 private volatile ExecutorService myExecutor;
39 public GitClonesUpdater(@NotNull EventDispatcher<RepositoryStateListener> eventDispatcher,
40 @NotNull EventDispatcher<BuildServerListener> serverEventDispatcher,
41 @NotNull ServerResponsibility serverResponsibility,
42 @NotNull GitVcsSupport gitVcsSupport,
43 @NotNull RepositoryManager repositoryManager) {
44 myVcs = gitVcsSupport;
45 myRepositoryManager = repositoryManager;
47 eventDispatcher.addListener(new RepositoryStateListenerAdapter() {
49 public void repositoryStateChanged(@NotNull final VcsRoot root,
50 @NotNull final RepositoryState oldState,
51 @NotNull final RepositoryState newState) {
52 if (serverResponsibility.canCheckForChanges()) return;
54 if (!TeamCityProperties.getBooleanOrTrue("teamcity.git.localClones.updateIfNoCheckingForChangesResponsibility")) return;
56 if (root.getVcsName().equals(Constants.VCS_NAME)) {
57 myScheduledForUpdate.put(root, RepositoryStateFactory.toData(newState));
58 synchronized (myScheduledForUpdate) {
59 if (myExecutor == null) {
60 myExecutor = ExecutorsFactory.newFixedDaemonExecutor("Git local clones updater",
61 TeamCityProperties.getInteger("teamcity.git.localClones.maxParallelUpdateThreads", 2));
64 myExecutor.submit(GitClonesUpdater.this::processVcsRootsScheduledForUpdate);
69 serverEventDispatcher.addListener(new BuildServerAdapter() {
71 public void serverShutdown() {
72 if (myExecutor != null) {
73 ThreadUtil.shutdownGracefully(myExecutor, "Git local clones updater");
79 private void processVcsRootsScheduledForUpdate() {
80 Set<VcsRoot> vcsRoots = new HashSet<>(myScheduledForUpdate.keySet());
81 for (VcsRoot root: vcsRoots) {
82 RepositoryStateData state = myScheduledForUpdate.remove(root);
83 if (state == null) continue;
85 OperationContext context = myVcs.createContext(root, "updating local clone");
87 ReadOnlyRestrictor.doReadOnlyCommandLine(() -> {
88 GitVcsRoot gitRoot = context.getGitRoot();
89 myRepositoryManager.runWithDisabledRemove(gitRoot.getRepositoryDir(), () -> {
90 Repository repo = context.getRepository();
92 myVcs.getCollectChangesPolicy().ensureRepositoryStateLoadedFor(context, repo, true, state);
93 } catch (Exception e1) {
94 throw new VcsException(e1);
98 } catch (VcsException e1) {
99 Loggers.VCS.warnAndDebugDetails("Could not update local clone for: " + LogUtil.describe(root), e1);