Switch jgit library to the EDL (3-clause BSD)
[jgit.git] / org.spearce.jgit / src / org / spearce / jgit / transport / TransportBundle.java
blob2c173fd98492aa009ed62dc3000ed4ded32da366
1 /*
2 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
9 * conditions are met:
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
19 * - Neither the name of the Git Development Community nor the
20 * names of its contributors may be used to endorse or promote
21 * products derived from this software without specific prior
22 * written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 package org.spearce.jgit.transport;
41 import java.io.BufferedInputStream;
42 import java.io.File;
43 import java.io.FileInputStream;
44 import java.io.FileNotFoundException;
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.util.Collection;
48 import java.util.HashSet;
49 import java.util.LinkedHashMap;
50 import java.util.Set;
52 import org.spearce.jgit.errors.NotSupportedException;
53 import org.spearce.jgit.errors.PackProtocolException;
54 import org.spearce.jgit.errors.TransportException;
55 import org.spearce.jgit.lib.Constants;
56 import org.spearce.jgit.lib.ObjectId;
57 import org.spearce.jgit.lib.ProgressMonitor;
58 import org.spearce.jgit.lib.Ref;
59 import org.spearce.jgit.lib.Repository;
60 import org.spearce.jgit.util.FS;
62 /**
63 * Supports fetching from a git bundle (sneaker-net object transport).
64 * <p>
65 * Push support for a bundle is complex, as one does not have a peer to
66 * communicate with to decide what the peer already knows. So push is not
67 * supported by the bundle transport.
69 class TransportBundle extends PackTransport {
70 static final String V2_BUNDLE_SIGNATURE = "# v2 git bundle";
72 static boolean canHandle(final URIish uri) {
73 if (uri.getHost() != null || uri.getPort() > 0 || uri.getUser() != null
74 || uri.getPass() != null || uri.getPath() == null)
75 return false;
77 if ("file".equals(uri.getScheme()) || uri.getScheme() == null) {
78 final File f = FS.resolve(new File("."), uri.getPath());
79 return f.isFile() || f.getName().endsWith(".bundle");
82 return false;
85 private final File bundle;
87 TransportBundle(final Repository local, final URIish uri) {
88 super(local, uri);
89 bundle = FS.resolve(new File("."), uri.getPath()).getAbsoluteFile();
92 @Override
93 public FetchConnection openFetch() throws NotSupportedException,
94 TransportException {
95 return new BundleFetchConnection();
98 class BundleFetchConnection extends FetchConnection {
99 FileInputStream in;
101 RewindBufferedInputStream bin;
103 final Set<ObjectId> prereqs = new HashSet<ObjectId>();
105 BundleFetchConnection() throws TransportException {
106 try {
107 in = new FileInputStream(bundle);
108 bin = new RewindBufferedInputStream(in);
109 } catch (FileNotFoundException err) {
110 throw new TransportException(bundle.getPath() + ": not found");
113 try {
114 switch (readSignature()) {
115 case 2:
116 readBundleV2();
117 break;
118 default:
119 throw new TransportException(bundle.getPath()
120 + ": not a bundle");
123 in.getChannel().position(
124 in.getChannel().position() - bin.buffered());
125 bin = null;
126 } catch (TransportException err) {
127 close();
128 throw err;
129 } catch (IOException err) {
130 close();
131 throw new TransportException(bundle.getPath() + ": "
132 + err.getMessage(), err);
133 } catch (RuntimeException err) {
134 close();
135 throw new TransportException(bundle.getPath() + ": "
136 + err.getMessage(), err);
140 private int readSignature() throws IOException {
141 final String rev = readLine(new byte[1024]);
142 if (V2_BUNDLE_SIGNATURE.equals(rev))
143 return 2;
144 throw new TransportException(bundle.getPath() + ": not a bundle");
147 private void readBundleV2() throws IOException {
148 final byte[] hdrbuf = new byte[1024];
149 final LinkedHashMap<String, Ref> avail = new LinkedHashMap<String, Ref>();
150 for (;;) {
151 String line = readLine(hdrbuf);
152 if (line.length() == 0)
153 break;
155 if (line.charAt(0) == '-') {
156 prereqs.add(ObjectId.fromString(line.substring(1, 41)));
157 continue;
160 final String name = line.substring(41, line.length());
161 final ObjectId id = ObjectId.fromString(line.substring(0, 40));
162 final Ref prior = avail.put(name, new Ref(name, id));
163 if (prior != null)
164 throw duplicateAdvertisement(name);
166 available(avail);
169 private PackProtocolException duplicateAdvertisement(final String name) {
170 return new PackProtocolException("duplicate advertisements of "
171 + name);
174 private String readLine(final byte[] hdrbuf) throws IOException {
175 bin.mark(hdrbuf.length);
176 final int cnt = bin.read(hdrbuf);
177 int lf = 0;
178 while (lf < cnt && hdrbuf[lf] != '\n')
179 lf++;
180 bin.reset();
181 bin.skip(lf);
182 if (lf < cnt && hdrbuf[lf] == '\n')
183 bin.skip(1);
184 return new String(hdrbuf, 0, lf, Constants.CHARACTER_ENCODING);
187 @Override
188 protected void doFetch(final ProgressMonitor monitor,
189 final Collection<Ref> want) throws TransportException {
190 try {
191 final IndexPack ip = IndexPack.create(local, in);
192 ip.setFixThin(true);
193 ip.index(monitor);
194 ip.renameAndOpenPack();
195 } catch (IOException err) {
196 close();
197 throw new TransportException(err.getMessage(), err);
198 } catch (RuntimeException err) {
199 close();
200 throw new TransportException(err.getMessage(), err);
204 @Override
205 public void close() {
206 if (in != null) {
207 try {
208 in.close();
209 } catch (IOException ie) {
210 // Ignore close failures.
211 } finally {
212 in = null;
213 bin = null;
218 class RewindBufferedInputStream extends BufferedInputStream {
219 RewindBufferedInputStream(final InputStream src) {
220 super(src);
223 int buffered() {
224 return (count - pos);