Transport* - general support for push() and implementations
[egit/zawir.git] / org.spearce.jgit / src / org / spearce / jgit / transport / TransportBundle.java
blob1bf081ae1072f8b26c7ffb55e505cf0ccc5f595a
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 @Override
99 public PushConnection openPush() throws NotSupportedException {
100 throw new NotSupportedException(
101 "Push is not supported for bundle transport");
104 class BundleFetchConnection extends BaseFetchConnection {
105 FileInputStream in;
107 RewindBufferedInputStream bin;
109 final Set<ObjectId> prereqs = new HashSet<ObjectId>();
111 BundleFetchConnection() throws TransportException {
112 try {
113 in = new FileInputStream(bundle);
114 bin = new RewindBufferedInputStream(in);
115 } catch (FileNotFoundException err) {
116 throw new TransportException(bundle.getPath() + ": not found");
119 try {
120 switch (readSignature()) {
121 case 2:
122 readBundleV2();
123 break;
124 default:
125 throw new TransportException(bundle.getPath()
126 + ": not a bundle");
129 in.getChannel().position(
130 in.getChannel().position() - bin.buffered());
131 bin = null;
132 } catch (TransportException err) {
133 close();
134 throw err;
135 } catch (IOException err) {
136 close();
137 throw new TransportException(bundle.getPath() + ": "
138 + err.getMessage(), err);
139 } catch (RuntimeException err) {
140 close();
141 throw new TransportException(bundle.getPath() + ": "
142 + err.getMessage(), err);
146 private int readSignature() throws IOException {
147 final String rev = readLine(new byte[1024]);
148 if (V2_BUNDLE_SIGNATURE.equals(rev))
149 return 2;
150 throw new TransportException(bundle.getPath() + ": not a bundle");
153 private void readBundleV2() throws IOException {
154 final byte[] hdrbuf = new byte[1024];
155 final LinkedHashMap<String, Ref> avail = new LinkedHashMap<String, Ref>();
156 for (;;) {
157 String line = readLine(hdrbuf);
158 if (line.length() == 0)
159 break;
161 if (line.charAt(0) == '-') {
162 prereqs.add(ObjectId.fromString(line.substring(1, 41)));
163 continue;
166 final String name = line.substring(41, line.length());
167 final ObjectId id = ObjectId.fromString(line.substring(0, 40));
168 final Ref prior = avail.put(name, new Ref(name, id));
169 if (prior != null)
170 throw duplicateAdvertisement(name);
172 available(avail);
175 private PackProtocolException duplicateAdvertisement(final String name) {
176 return new PackProtocolException("duplicate advertisements of "
177 + name);
180 private String readLine(final byte[] hdrbuf) throws IOException {
181 bin.mark(hdrbuf.length);
182 final int cnt = bin.read(hdrbuf);
183 int lf = 0;
184 while (lf < cnt && hdrbuf[lf] != '\n')
185 lf++;
186 bin.reset();
187 bin.skip(lf);
188 if (lf < cnt && hdrbuf[lf] == '\n')
189 bin.skip(1);
190 return new String(hdrbuf, 0, lf, Constants.CHARACTER_ENCODING);
193 @Override
194 protected void doFetch(final ProgressMonitor monitor,
195 final Collection<Ref> want) throws TransportException {
196 try {
197 final IndexPack ip = IndexPack.create(local, in);
198 ip.setFixThin(true);
199 ip.index(monitor);
200 ip.renameAndOpenPack();
201 } catch (IOException err) {
202 close();
203 throw new TransportException(err.getMessage(), err);
204 } catch (RuntimeException err) {
205 close();
206 throw new TransportException(err.getMessage(), err);
210 @Override
211 public void close() {
212 if (in != null) {
213 try {
214 in.close();
215 } catch (IOException ie) {
216 // Ignore close failures.
217 } finally {
218 in = null;
219 bin = null;
224 class RewindBufferedInputStream extends BufferedInputStream {
225 RewindBufferedInputStream(final InputStream src) {
226 super(src);
229 int buffered() {
230 return (count - pos);