1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 package org.eclipse.jgit.transport;
49
50 import static java.nio.charset.StandardCharsets.UTF_8;
51
52 import java.io.BufferedInputStream;
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.text.MessageFormat;
56 import java.util.ArrayList;
57 import java.util.Collection;
58 import java.util.Collections;
59 import java.util.HashMap;
60 import java.util.LinkedHashMap;
61 import java.util.List;
62 import java.util.Map;
63 import java.util.Set;
64
65 import org.eclipse.jgit.errors.MissingBundlePrerequisiteException;
66 import org.eclipse.jgit.errors.MissingObjectException;
67 import org.eclipse.jgit.errors.PackProtocolException;
68 import org.eclipse.jgit.errors.TransportException;
69 import org.eclipse.jgit.internal.JGitText;
70 import org.eclipse.jgit.internal.storage.file.PackLock;
71 import org.eclipse.jgit.lib.NullProgressMonitor;
72 import org.eclipse.jgit.lib.ObjectId;
73 import org.eclipse.jgit.lib.ObjectIdRef;
74 import org.eclipse.jgit.lib.ObjectInserter;
75 import org.eclipse.jgit.lib.ProgressMonitor;
76 import org.eclipse.jgit.lib.Ref;
77 import org.eclipse.jgit.revwalk.RevCommit;
78 import org.eclipse.jgit.revwalk.RevFlag;
79 import org.eclipse.jgit.revwalk.RevObject;
80 import org.eclipse.jgit.revwalk.RevWalk;
81 import org.eclipse.jgit.util.IO;
82 import org.eclipse.jgit.util.RawParseUtils;
83
84
85
86
87
88 class BundleFetchConnection extends BaseFetchConnection {
89
90 private final Transport transport;
91
92 InputStream bin;
93
94 final Map<ObjectId, String> prereqs = new HashMap<>();
95
96 private String lockMessage;
97
98 private PackLock packLock;
99
100 BundleFetchConnection(Transport transportBundle, InputStream src) throws TransportException {
101 transport = transportBundle;
102 bin = new BufferedInputStream(src);
103 try {
104 switch (readSignature()) {
105 case 2:
106 readBundleV2();
107 break;
108 default:
109 throw new TransportException(transport.uri, JGitText.get().notABundle);
110 }
111 } catch (TransportException err) {
112 close();
113 throw err;
114 } catch (IOException err) {
115 close();
116 throw new TransportException(transport.uri, err.getMessage(), err);
117 } catch (RuntimeException err) {
118 close();
119 throw new TransportException(transport.uri, err.getMessage(), err);
120 }
121 }
122
123 private int readSignature() throws IOException {
124 final String rev = readLine(new byte[1024]);
125 if (TransportBundle.V2_BUNDLE_SIGNATURE.equals(rev))
126 return 2;
127 throw new TransportException(transport.uri, JGitText.get().notABundle);
128 }
129
130 private void readBundleV2() throws IOException {
131 final byte[] hdrbuf = new byte[1024];
132 final LinkedHashMap<String, Ref> avail = new LinkedHashMap<>();
133 for (;;) {
134 String line = readLine(hdrbuf);
135 if (line.length() == 0)
136 break;
137
138 if (line.charAt(0) == '-') {
139 ObjectId id = ObjectId.fromString(line.substring(1, 41));
140 String shortDesc = null;
141 if (line.length() > 42)
142 shortDesc = line.substring(42);
143 prereqs.put(id, shortDesc);
144 continue;
145 }
146
147 final String name = line.substring(41, line.length());
148 final ObjectId id = ObjectId.fromString(line.substring(0, 40));
149 final Ref prior = avail.put(name, new ObjectIdRef.Unpeeled(
150 Ref.Storage.NETWORK, name, id));
151 if (prior != null)
152 throw duplicateAdvertisement(name);
153 }
154 available(avail);
155 }
156
157 private PackProtocolException duplicateAdvertisement(String name) {
158 return new PackProtocolException(transport.uri,
159 MessageFormat.format(JGitText.get().duplicateAdvertisementsOf, name));
160 }
161
162 private String readLine(byte[] hdrbuf) throws IOException {
163 StringBuilder line = new StringBuilder();
164 boolean done = false;
165 while (!done) {
166 bin.mark(hdrbuf.length);
167 final int cnt = bin.read(hdrbuf);
168 int lf = 0;
169 while (lf < cnt && hdrbuf[lf] != '\n')
170 lf++;
171 bin.reset();
172 IO.skipFully(bin, lf);
173 if (lf < cnt && hdrbuf[lf] == '\n') {
174 IO.skipFully(bin, 1);
175 done = true;
176 }
177 line.append(RawParseUtils.decode(UTF_8, hdrbuf, 0, lf));
178 }
179 return line.toString();
180 }
181
182
183 @Override
184 public boolean didFetchTestConnectivity() {
185 return false;
186 }
187
188
189 @Override
190 protected void doFetch(final ProgressMonitor monitor,
191 final Collection<Ref> want, final Set<ObjectId> have)
192 throws TransportException {
193 verifyPrerequisites();
194 try {
195 try (ObjectInserter ins = transport.local.newObjectInserter()) {
196 PackParser parser = ins.newPackParser(bin);
197 parser.setAllowThin(true);
198 parser.setObjectChecker(transport.getObjectChecker());
199 parser.setLockMessage(lockMessage);
200 packLock = parser.parse(NullProgressMonitor.INSTANCE);
201 ins.flush();
202 }
203 } catch (IOException err) {
204 close();
205 throw new TransportException(transport.uri, err.getMessage(), err);
206 } catch (RuntimeException err) {
207 close();
208 throw new TransportException(transport.uri, err.getMessage(), err);
209 }
210 }
211
212
213 @Override
214 public void setPackLockMessage(String message) {
215 lockMessage = message;
216 }
217
218
219 @Override
220 public Collection<PackLock> getPackLocks() {
221 if (packLock != null)
222 return Collections.singleton(packLock);
223 return Collections.<PackLock> emptyList();
224 }
225
226 private void verifyPrerequisites() throws TransportException {
227 if (prereqs.isEmpty())
228 return;
229
230 try (RevWalk rw = new RevWalk(transport.local)) {
231 final RevFlag PREREQ = rw.newFlag("PREREQ");
232 final RevFlag SEEN = rw.newFlag("SEEN");
233
234 final Map<ObjectId, String> missing = new HashMap<>();
235 final List<RevObject> commits = new ArrayList<>();
236 for (Map.Entry<ObjectId, String> e : prereqs.entrySet()) {
237 ObjectId p = e.getKey();
238 try {
239 final RevCommit c = rw.parseCommit(p);
240 if (!c.has(PREREQ)) {
241 c.add(PREREQ);
242 commits.add(c);
243 }
244 } catch (MissingObjectException notFound) {
245 missing.put(p, e.getValue());
246 } catch (IOException err) {
247 throw new TransportException(transport.uri, MessageFormat
248 .format(JGitText.get().cannotReadCommit, p.name()),
249 err);
250 }
251 }
252 if (!missing.isEmpty())
253 throw new MissingBundlePrerequisiteException(transport.uri,
254 missing);
255
256 List<Ref> localRefs;
257 try {
258 localRefs = transport.local.getRefDatabase().getRefs();
259 } catch (IOException e) {
260 throw new TransportException(transport.uri, e.getMessage(), e);
261 }
262 for (Ref r : localRefs) {
263 try {
264 rw.markStart(rw.parseCommit(r.getObjectId()));
265 } catch (IOException readError) {
266
267 }
268 }
269
270 int remaining = commits.size();
271 try {
272 RevCommit c;
273 while ((c = rw.next()) != null) {
274 if (c.has(PREREQ)) {
275 c.add(SEEN);
276 if (--remaining == 0)
277 break;
278 }
279 }
280 } catch (IOException err) {
281 throw new TransportException(transport.uri,
282 JGitText.get().cannotReadObject, err);
283 }
284
285 if (remaining > 0) {
286 for (RevObject o : commits) {
287 if (!o.has(SEEN))
288 missing.put(o, prereqs.get(o));
289 }
290 throw new MissingBundlePrerequisiteException(transport.uri,
291 missing);
292 }
293 }
294 }
295
296
297 @Override
298 public void close() {
299 if (bin != null) {
300 try {
301 bin.close();
302 } catch (IOException ie) {
303
304 } finally {
305 bin = null;
306 }
307 }
308 }
309 }