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<ObjectId, String>();
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<String, Ref>();
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 public boolean didFetchTestConnectivity() {
184 return false;
185 }
186
187 @Override
188 protected void doFetch(final ProgressMonitor monitor,
189 final Collection<Ref> want, final Set<ObjectId> have)
190 throws TransportException {
191 verifyPrerequisites();
192 try {
193 try (ObjectInserter ins = transport.local.newObjectInserter()) {
194 PackParser parser = ins.newPackParser(bin);
195 parser.setAllowThin(true);
196 parser.setObjectChecker(transport.getObjectChecker());
197 parser.setLockMessage(lockMessage);
198 packLock = parser.parse(NullProgressMonitor.INSTANCE);
199 ins.flush();
200 }
201 } catch (IOException err) {
202 close();
203 throw new TransportException(transport.uri, err.getMessage(), err);
204 } catch (RuntimeException err) {
205 close();
206 throw new TransportException(transport.uri, err.getMessage(), err);
207 }
208 }
209
210 public void setPackLockMessage(final String message) {
211 lockMessage = message;
212 }
213
214 public Collection<PackLock> getPackLocks() {
215 if (packLock != null)
216 return Collections.singleton(packLock);
217 return Collections.<PackLock> emptyList();
218 }
219
220 private void verifyPrerequisites() throws TransportException {
221 if (prereqs.isEmpty())
222 return;
223
224 try (final RevWalk rw = new RevWalk(transport.local)) {
225 final RevFlag PREREQ = rw.newFlag("PREREQ");
226 final RevFlag SEEN = rw.newFlag("SEEN");
227
228 final Map<ObjectId, String> missing = new HashMap<ObjectId, String>();
229 final List<RevObject> commits = new ArrayList<RevObject>();
230 for (final Map.Entry<ObjectId, String> e : prereqs.entrySet()) {
231 ObjectId p = e.getKey();
232 try {
233 final RevCommit c = rw.parseCommit(p);
234 if (!c.has(PREREQ)) {
235 c.add(PREREQ);
236 commits.add(c);
237 }
238 } catch (MissingObjectException notFound) {
239 missing.put(p, e.getValue());
240 } catch (IOException err) {
241 throw new TransportException(transport.uri, MessageFormat
242 .format(JGitText.get().cannotReadCommit, p.name()),
243 err);
244 }
245 }
246 if (!missing.isEmpty())
247 throw new MissingBundlePrerequisiteException(transport.uri,
248 missing);
249
250 Map<String, Ref> localRefs;
251 try {
252 localRefs = transport.local.getRefDatabase().getRefs(ALL);
253 } catch (IOException e) {
254 throw new TransportException(transport.uri, e.getMessage(), e);
255 }
256 for (final Ref r : localRefs.values()) {
257 try {
258 rw.markStart(rw.parseCommit(r.getObjectId()));
259 } catch (IOException readError) {
260
261 }
262 }
263
264 int remaining = commits.size();
265 try {
266 RevCommit c;
267 while ((c = rw.next()) != null) {
268 if (c.has(PREREQ)) {
269 c.add(SEEN);
270 if (--remaining == 0)
271 break;
272 }
273 }
274 } catch (IOException err) {
275 throw new TransportException(transport.uri,
276 JGitText.get().cannotReadObject, err);
277 }
278
279 if (remaining > 0) {
280 for (final RevObject o : commits) {
281 if (!o.has(SEEN))
282 missing.put(o, prereqs.get(o));
283 }
284 throw new MissingBundlePrerequisiteException(transport.uri,
285 missing);
286 }
287 }
288 }
289
290 @Override
291 public void close() {
292 if (bin != null) {
293 try {
294 bin.close();
295 } catch (IOException ie) {
296
297 } finally {
298 bin = null;
299 }
300 }
301 }
302 }