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