1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.linecorp.centraldogma.server.internal.mirror;
18
19 import static java.util.Objects.requireNonNull;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.HashMap;
24 import java.util.Map;
25 import java.util.concurrent.locks.Lock;
26 import java.util.concurrent.locks.ReentrantLock;
27 import java.util.function.Consumer;
28
29 import org.eclipse.jgit.api.FetchCommand;
30 import org.eclipse.jgit.api.GarbageCollectCommand;
31 import org.eclipse.jgit.api.Git;
32 import org.eclipse.jgit.api.LsRemoteCommand;
33 import org.eclipse.jgit.api.PushCommand;
34 import org.eclipse.jgit.api.TransportCommand;
35 import org.eclipse.jgit.lib.EmptyProgressMonitor;
36 import org.eclipse.jgit.lib.ProgressMonitor;
37 import org.eclipse.jgit.lib.Repository;
38 import org.eclipse.jgit.lib.RepositoryBuilder;
39 import org.eclipse.jgit.transport.URIish;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 import com.linecorp.centraldogma.server.internal.IsolatedSystemReader;
44 import com.linecorp.centraldogma.server.internal.JGitUtil;
45
46 final class GitWithAuth extends Git {
47
48 private static final Logger logger = LoggerFactory.getLogger(GitWithAuth.class);
49
50
51
52
53
54
55
56
57 private static final Lock[] locks = new Lock[1024];
58
59 static {
60 IsolatedSystemReader.install();
61
62 for (int i = 0; i < locks.length; i++) {
63 locks[i] = new ReentrantLock();
64 }
65 }
66
67 private static Lock getLock(File repoDir) {
68 final int h = repoDir.getPath().hashCode();
69 return locks[Math.abs((h ^ h >>> 16) % locks.length)];
70 }
71
72 private final AbstractGitMirror mirror;
73 private final Lock lock;
74 private final URIish remoteUri;
75 private final Map<String, ProgressMonitor> progressMonitors = new HashMap<>();
76 private final Consumer<TransportCommand<?, ?>> configurator;
77
78 GitWithAuth(AbstractGitMirror mirror, File repoDir, URIish remoteUri,
79 Consumer<TransportCommand<?, ?>> configurator) throws IOException {
80 super(repo(repoDir));
81 this.mirror = mirror;
82 lock = getLock(repoDir);
83 this.remoteUri = remoteUri;
84 this.configurator = configurator;
85 }
86
87 URIish remoteUri() {
88 return remoteUri;
89 }
90
91 private static Repository repo(File repoDir) throws IOException {
92 final Lock lock = getLock(repoDir);
93 boolean success = false;
94 lock.lock();
95 try {
96 repoDir.getParentFile().mkdirs();
97 final Repository repo = new RepositoryBuilder().setGitDir(repoDir).setBare().build();
98 if (!repo.getObjectDatabase().exists()) {
99 repo.create(true);
100 }
101
102 JGitUtil.applyDefaultsAndSave(repo.getConfig());
103 success = true;
104 return repo;
105 } finally {
106 if (!success) {
107 lock.unlock();
108 }
109 }
110 }
111
112 @Override
113 public void close() {
114 try {
115 super.close();
116 } finally {
117 try {
118 getRepository().close();
119 } finally {
120 lock.unlock();
121 }
122 }
123 }
124
125 private ProgressMonitor progressMonitor(String name) {
126 return progressMonitors.computeIfAbsent(name, MirrorProgressMonitor::new);
127 }
128
129 @Override
130 public FetchCommand fetch() {
131 final FetchCommand command = super.fetch();
132 configurator.accept(command);
133 return command.setProgressMonitor(progressMonitor("fetch"));
134 }
135
136 @Override
137 public PushCommand push() {
138 final PushCommand command = super.push();
139 configurator.accept(command);
140 return command.setProgressMonitor(progressMonitor("push"));
141 }
142
143 @Override
144 public LsRemoteCommand lsRemote() {
145 final LsRemoteCommand command = super.lsRemote();
146 configurator.accept(command);
147 return command;
148 }
149
150 @Override
151 public GarbageCollectCommand gc() {
152 return super.gc().setProgressMonitor(progressMonitor("gc"));
153 }
154
155 private final class MirrorProgressMonitor extends EmptyProgressMonitor {
156
157 private final String operationName;
158
159 MirrorProgressMonitor(String operationName) {
160 this.operationName = requireNonNull(operationName, "operationName");
161 }
162
163 @Override
164 public void beginTask(String title, int totalWork) {
165 if (totalWork > 0 && logger.isInfoEnabled()) {
166 logger.info("[{}] {} ({}, total: {})", operationName, mirror.remoteRepoUri(), title, totalWork);
167 }
168 }
169 }
170 }