switch to ReentrantLock: this will provide ability to use tryLock
[teamcity/git-plugin.git] / git-server / src / jetbrains / buildServer / buildTriggers / vcs / git / CommitLoaderImpl.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 com.intellij.openapi.diagnostic.Logger;
20 import jetbrains.buildServer.vcs.VcsException;
21 import org.eclipse.jgit.lib.ObjectId;
22 import org.eclipse.jgit.lib.Ref;
23 import org.eclipse.jgit.lib.Repository;
24 import org.eclipse.jgit.revwalk.RevCommit;
25 import org.eclipse.jgit.revwalk.RevWalk;
26 import org.eclipse.jgit.transport.RefSpec;
27 import org.eclipse.jgit.transport.URIish;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
30
31 import java.io.File;
32 import java.io.IOException;
33 import java.util.Collection;
34 import java.util.HashMap;
35 import java.util.Map;
36 import java.util.concurrent.locks.ReentrantLock;
37
38 import static java.util.Arrays.asList;
39
40 public class CommitLoaderImpl implements CommitLoader {
41
42   private static final Logger LOG = Logger.getInstance(CommitLoaderImpl.class.getName());
43   public static final Logger PERFORMANCE_LOG = Logger.getInstance(CommitLoaderImpl.class.getName() + ".Performance");
44
45   private final RepositoryManager myRepositoryManager;
46   private final FetchCommand myFetchCommand;
47   private final GitMapFullPath myMapFullPath;
48
49   public CommitLoaderImpl(@NotNull RepositoryManager repositoryManager,
50                           @NotNull FetchCommand fetchCommand,
51                           @NotNull GitMapFullPath mapFullPath) {
52     myRepositoryManager = repositoryManager;
53     myFetchCommand = fetchCommand;
54     myMapFullPath = mapFullPath;
55     myMapFullPath.setCommitLoader(this);
56   }
57
58   @NotNull
59   public RevCommit loadCommit(@NotNull OperationContext context,
60                               @NotNull GitVcsRoot root,
61                               @NotNull String revision) throws VcsException, IOException {
62     String commitSHA = GitUtils.versionRevision(revision);
63     Repository db = context.getRepository(root);
64     ObjectId commitId = ObjectId.fromString(commitSHA);
65     try {
66       return getCommit(db, commitId);
67     } catch (IOException ex) {
68       //ignore error, will try to fetch
69     }
70
71     LOG.debug("Cannot find commit " + commitSHA + " in repository " + root.debugInfo() + ", fetch branch " + root.getRef());
72     fetchBranchData(root, db);
73
74     try {
75       return getCommit(db, commitId);
76     } catch (IOException e) {
77       LOG.debug("Cannot find commit " + commitSHA + " in the branch " + root.getRef() +
78                 " of repository " + root.debugInfo() + ", fetch all branches");
79       RefSpec spec = new RefSpec().setSourceDestination("refs/*", "refs/*").setForceUpdate(true);
80       fetch(db, root.getRepositoryFetchURL(), asList(spec), new FetchSettings(root.getAuthSettings()));
81       try {
82         return getCommit(db, commitId);
83       } catch (IOException e1) {
84         throw new VcsException("Cannot find commit " + commitSHA + " in repository " + root.debugInfo());
85       }
86     }
87   }
88
89   public void fetch(@NotNull Repository db,
90                     @NotNull URIish fetchURI,
91                     @NotNull Collection<RefSpec> refspecs,
92                     @NotNull FetchSettings settings) throws IOException, VcsException {
93     File repositoryDir = db.getDirectory();
94     assert repositoryDir != null : "Non-local repository";
95     final long start = System.currentTimeMillis();
96
97     ReentrantLock lock = myRepositoryManager.getWriteLock(repositoryDir);
98     lock.lock();
99     try {
100       final long finish = System.currentTimeMillis();
101       final long waitTime = finish - start;
102       if (waitTime > 20000) {
103         // if wait time was significant, report it in progress
104         settings.getProgress().reportProgress("Waited for exclusive lock in cloned directory, wait time: " + waitTime + "ms");
105       }
106       Map<String, Ref> oldRefs = new HashMap<>(db.getAllRefs());
107       PERFORMANCE_LOG.debug("[waitForWriteLock] repository: " + repositoryDir.getAbsolutePath() + ", took " + waitTime + "ms");
108       myFetchCommand.fetch(db, fetchURI, refspecs, settings);
109       Map<String, Ref> newRefs = new HashMap<>(db.getAllRefs());
110       myMapFullPath.invalidateRevisionsCache(db, oldRefs, newRefs);
111     } finally {
112       lock.unlock();
113     }
114   }
115
116   @NotNull
117   public RevCommit getCommit(@NotNull Repository repository, @NotNull ObjectId commitId) throws IOException {
118     final long start = System.currentTimeMillis();
119     RevWalk walk = new RevWalk(repository);
120     try {
121       return walk.parseCommit(commitId);
122     } finally {
123       walk.release();
124       final long finish = System.currentTimeMillis();
125       if (PERFORMANCE_LOG.isDebugEnabled()) {
126         PERFORMANCE_LOG.debug("[RevWalk.parseCommit] repository=" + repository.getDirectory().getAbsolutePath() + ", commit=" + commitId.name() + ", took: " + (finish - start) + "ms");
127       }
128     }
129   }
130
131   @Nullable
132   public RevCommit findCommit(@NotNull Repository r, @NotNull String sha) {
133     try {
134       return getCommit(r, ObjectId.fromString(sha));
135     } catch (Exception e) {
136       return null;
137     }
138   }
139
140   private void fetchBranchData(@NotNull GitVcsRoot root, @NotNull Repository repository)
141     throws VcsException, IOException {
142     final String refName = GitUtils.expandRef(root.getRef());
143     RefSpec spec = new RefSpec().setSource(refName).setDestination(refName).setForceUpdate(true);
144     fetch(repository, root.getRepositoryFetchURL(), asList(spec), new FetchSettings(root.getAuthSettings()));
145   }
146 }