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