4435f5988be95a83c37544d0d4df171d41dda159
[teamcity/git-plugin.git] / git-server-tc / src / jetbrains / buildServer / buildTriggers / vcs / git / GitClonesUpdater.java
1 /*
2  * Copyright 2000-2018 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package jetbrains.buildServer.buildTriggers.vcs.git;
18
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;
27
28 import java.util.HashSet;
29 import java.util.Set;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.ExecutorService;
32
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;
38
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;
46
47     eventDispatcher.addListener(new RepositoryStateListenerAdapter() {
48       @Override
49       public void repositoryStateChanged(@NotNull final VcsRoot root,
50                                          @NotNull final RepositoryState oldState,
51                                          @NotNull final RepositoryState newState) {
52         if (serverResponsibility.canCheckForChanges()) return;
53
54         if (!TeamCityProperties.getBooleanOrTrue("teamcity.git.localClones.updateIfNoCheckingForChangesResponsibility")) return;
55
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));
62             }
63           }
64           myExecutor.submit(GitClonesUpdater.this::processVcsRootsScheduledForUpdate);
65         }
66       }
67     });
68
69     serverEventDispatcher.addListener(new BuildServerAdapter() {
70       @Override
71       public void serverShutdown() {
72         if (myExecutor != null) {
73           ThreadUtil.shutdownGracefully(myExecutor, "Git local clones updater");
74         }
75       }
76     });
77   }
78
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;
84
85       OperationContext context = myVcs.createContext(root, "updating local clone");
86       try {
87         ReadOnlyRestrictor.doReadOnlyCommandLine(() -> {
88           GitVcsRoot gitRoot = context.getGitRoot();
89           myRepositoryManager.runWithDisabledRemove(gitRoot.getRepositoryDir(), () -> {
90             Repository repo = context.getRepository();
91             try {
92               myVcs.getCollectChangesPolicy().ensureRepositoryStateLoadedFor(context, repo, true, state);
93             } catch (Exception e1) {
94               throw new VcsException(e1);
95             }
96           });
97         });
98       } catch (VcsException e1) {
99         Loggers.VCS.warnAndDebugDetails("Could not update local clone for: " + LogUtil.describe(root), e1);
100       } finally {
101         context.close();
102       }
103     }
104   }
105 }