View Javadoc
1   /*
2    * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
3    * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4    * Copyright (C) 2009, Google Inc.
5    * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com>
6    * and other copyright owners as documented in the project's IP log.
7    *
8    * This program and the accompanying materials are made available
9    * under the terms of the Eclipse Distribution License v1.0 which
10   * accompanies this distribution, is reproduced below, and is
11   * available at http://www.eclipse.org/org/documents/edl-v10.php
12   *
13   * All rights reserved.
14   *
15   * Redistribution and use in source and binary forms, with or
16   * without modification, are permitted provided that the following
17   * conditions are met:
18   *
19   * - Redistributions of source code must retain the above copyright
20   *   notice, this list of conditions and the following disclaimer.
21   *
22   * - Redistributions in binary form must reproduce the above
23   *   copyright notice, this list of conditions and the following
24   *   disclaimer in the documentation and/or other materials provided
25   *   with the distribution.
26   *
27   * - Neither the name of the Eclipse Foundation, Inc. nor the
28   *   names of its contributors may be used to endorse or promote
29   *   products derived from this software without specific prior
30   *   written permission.
31   *
32   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
33   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
34   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
37   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
39   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
40   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
41   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
42   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
43   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
44   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45   */
46  
47  package org.eclipse.jgit.lib;
48  
49  import java.io.ByteArrayInputStream;
50  import java.io.EOFException;
51  import java.io.IOException;
52  import java.io.InputStream;
53  import java.security.MessageDigest;
54  
55  import org.eclipse.jgit.internal.JGitText;
56  import org.eclipse.jgit.transport.PackParser;
57  
58  /**
59   * Inserts objects into an existing {@code ObjectDatabase}.
60   * <p>
61   * An inserter is not thread-safe. Individual threads should each obtain their
62   * own unique inserter instance, or must arrange for locking at a higher level
63   * to ensure the inserter is in use by no more than one thread at a time.
64   * <p>
65   * Objects written by an inserter may not be immediately visible for reading
66   * after the insert method completes. Callers must invoke either
67   * {@link #close()} or {@link #flush()} prior to updating references or
68   * otherwise making the returned ObjectIds visible to other code.
69   */
70  public abstract class ObjectInserter implements AutoCloseable {
71  	/** An inserter that can be used for formatting and id generation only. */
72  	public static class Formatter extends ObjectInserter {
73  		@Override
74  		public ObjectId insert(int objectType, long length, InputStream in)
75  				throws IOException {
76  			throw new UnsupportedOperationException();
77  		}
78  
79  		@Override
80  		public PackParser newPackParser(InputStream in) throws IOException {
81  			throw new UnsupportedOperationException();
82  		}
83  
84  		@Override
85  		public ObjectReader newReader() {
86  			throw new UnsupportedOperationException();
87  		}
88  
89  		@Override
90  		public void flush() throws IOException {
91  			// Do nothing.
92  		}
93  
94  		@Override
95  		public void close() {
96  			// Do nothing.
97  		}
98  	}
99  
100 	/** Wraps a delegate ObjectInserter. */
101 	public static abstract class Filter extends ObjectInserter {
102 		/** @return delegate ObjectInserter to handle all processing. */
103 		protected abstract ObjectInserter delegate();
104 
105 		@Override
106 		protected byte[] buffer() {
107 			return delegate().buffer();
108 		}
109 
110 		public ObjectId idFor(int type, byte[] data) {
111 			return delegate().idFor(type, data);
112 		}
113 
114 		public ObjectId idFor(int type, byte[] data, int off, int len) {
115 			return delegate().idFor(type, data, off, len);
116 		}
117 
118 		public ObjectId idFor(int objectType, long length, InputStream in)
119 				throws IOException {
120 			return delegate().idFor(objectType, length, in);
121 		}
122 
123 		public ObjectId idFor(TreeFormatter formatter) {
124 			return delegate().idFor(formatter);
125 		}
126 
127 		public ObjectId insert(int type, byte[] data) throws IOException {
128 			return delegate().insert(type, data);
129 		}
130 
131 		public ObjectId insert(int type, byte[] data, int off, int len)
132 				throws IOException {
133 			return delegate().insert(type, data, off, len);
134 		}
135 
136 		public ObjectId insert(int objectType, long length, InputStream in)
137 				throws IOException {
138 			return delegate().insert(objectType, length, in);
139 		}
140 
141 		public PackParser newPackParser(InputStream in) throws IOException {
142 			return delegate().newPackParser(in);
143 		}
144 
145 		public ObjectReader newReader() {
146 			return delegate().newReader();
147 		}
148 
149 		public void flush() throws IOException {
150 			delegate().flush();
151 		}
152 
153 		public void close() {
154 			delegate().close();
155 		}
156 	}
157 
158 	/** Digest to compute the name of an object. */
159 	private final MessageDigest digest;
160 
161 	/** Temporary working buffer for streaming data through. */
162 	private byte[] tempBuffer;
163 
164 	/** Create a new inserter for a database. */
165 	protected ObjectInserter() {
166 		digest = Constants.newMessageDigest();
167 	}
168 
169 	/**
170 	 * Obtain a temporary buffer for use by the ObjectInserter or its subclass.
171 	 * <p>
172 	 * This buffer is supplied by the ObjectInserter base class to itself and
173 	 * its subclasses for the purposes of pulling data from a supplied
174 	 * InputStream, passing it through a Deflater, or formatting the canonical
175 	 * format of a small object like a small tree or commit.
176 	 * <p>
177 	 * <strong>This buffer IS NOT for translation such as auto-CRLF or content
178 	 * filtering and must not be used for such purposes.</strong>
179 	 * <p>
180 	 * The returned buffer is small, around a few KiBs, and the size may change
181 	 * between versions of JGit. Callers using this buffer must always check the
182 	 * length of the returned array to ascertain how much space was provided.
183 	 * <p>
184 	 * There is a single buffer for each ObjectInserter, repeated calls to this
185 	 * method will (usually) always return the same buffer. If the caller needs
186 	 * more than one buffer, or needs a buffer of a larger size, it must manage
187 	 * that buffer on its own.
188 	 * <p>
189 	 * The buffer is usually on first demand for a buffer.
190 	 *
191 	 * @return a temporary byte array for use by the caller.
192 	 */
193 	protected byte[] buffer() {
194 		byte[] b = tempBuffer;
195 		if (b == null)
196 			tempBuffer = b = new byte[8192];
197 		return b;
198 	}
199 
200 	/** @return digest to help compute an ObjectId */
201 	protected MessageDigest digest() {
202 		digest.reset();
203 		return digest;
204 	}
205 
206 	/**
207 	 * Compute the name of an object, without inserting it.
208 	 *
209 	 * @param type
210 	 *            type code of the object to store.
211 	 * @param data
212 	 *            complete content of the object.
213 	 * @return the name of the object.
214 	 */
215 	public ObjectId idFor(int type, byte[] data) {
216 		return idFor(type, data, 0, data.length);
217 	}
218 
219 	/**
220 	 * Compute the name of an object, without inserting it.
221 	 *
222 	 * @param type
223 	 *            type code of the object to store.
224 	 * @param data
225 	 *            complete content of the object.
226 	 * @param off
227 	 *            first position within {@code data}.
228 	 * @param len
229 	 *            number of bytes to copy from {@code data}.
230 	 * @return the name of the object.
231 	 */
232 	public ObjectId idFor(int type, byte[] data, int off, int len) {
233 		MessageDigest md = digest();
234 		md.update(Constants.encodedTypeString(type));
235 		md.update((byte) ' ');
236 		md.update(Constants.encodeASCII(len));
237 		md.update((byte) 0);
238 		md.update(data, off, len);
239 		return ObjectId.fromRaw(md.digest());
240 	}
241 
242 	/**
243 	 * Compute the name of an object, without inserting it.
244 	 *
245 	 * @param objectType
246 	 *            type code of the object to store.
247 	 * @param length
248 	 *            number of bytes to scan from {@code in}.
249 	 * @param in
250 	 *            stream providing the object content. The caller is responsible
251 	 *            for closing the stream.
252 	 * @return the name of the object.
253 	 * @throws IOException
254 	 *             the source stream could not be read.
255 	 */
256 	public ObjectId idFor(int objectType, long length, InputStream in)
257 			throws IOException {
258 		MessageDigest md = digest();
259 		md.update(Constants.encodedTypeString(objectType));
260 		md.update((byte) ' ');
261 		md.update(Constants.encodeASCII(length));
262 		md.update((byte) 0);
263 		byte[] buf = buffer();
264 		while (length > 0) {
265 			int n = in.read(buf, 0, (int) Math.min(length, buf.length));
266 			if (n < 0)
267 				throw new EOFException(JGitText.get().unexpectedEndOfInput);
268 			md.update(buf, 0, n);
269 			length -= n;
270 		}
271 		return ObjectId.fromRaw(md.digest());
272 	}
273 
274 	/**
275 	 * Compute the ObjectId for the given tree without inserting it.
276 	 *
277 	 * @param formatter
278 	 * @return the computed ObjectId
279 	 */
280 	public ObjectId idFor(TreeFormatter formatter) {
281 		return formatter.computeId(this);
282 	}
283 
284 	/**
285 	 * Insert a single tree into the store, returning its unique name.
286 	 *
287 	 * @param formatter
288 	 *            the formatter containing the proposed tree's data.
289 	 * @return the name of the tree object.
290 	 * @throws IOException
291 	 *             the object could not be stored.
292 	 */
293 	public final ObjectId insert(TreeFormatter formatter) throws IOException {
294 		// Delegate to the formatter, as then it can pass the raw internal
295 		// buffer back to this inserter, avoiding unnecessary data copying.
296 		//
297 		return formatter.insertTo(this);
298 	}
299 
300 	/**
301 	 * Insert a single commit into the store, returning its unique name.
302 	 *
303 	 * @param builder
304 	 *            the builder containing the proposed commit's data.
305 	 * @return the name of the commit object.
306 	 * @throws IOException
307 	 *             the object could not be stored.
308 	 */
309 	public final ObjectId insert(CommitBuilder builder) throws IOException {
310 		return insert(Constants.OBJ_COMMIT, builder.build());
311 	}
312 
313 	/**
314 	 * Insert a single annotated tag into the store, returning its unique name.
315 	 *
316 	 * @param builder
317 	 *            the builder containing the proposed tag's data.
318 	 * @return the name of the tag object.
319 	 * @throws IOException
320 	 *             the object could not be stored.
321 	 */
322 	public final ObjectId insert(TagBuilder builder) throws IOException {
323 		return insert(Constants.OBJ_TAG, builder.build());
324 	}
325 
326 	/**
327 	 * Insert a single object into the store, returning its unique name.
328 	 *
329 	 * @param type
330 	 *            type code of the object to store.
331 	 * @param data
332 	 *            complete content of the object.
333 	 * @return the name of the object.
334 	 * @throws IOException
335 	 *             the object could not be stored.
336 	 */
337 	public ObjectId insert(final int type, final byte[] data)
338 			throws IOException {
339 		return insert(type, data, 0, data.length);
340 	}
341 
342 	/**
343 	 * Insert a single object into the store, returning its unique name.
344 	 *
345 	 * @param type
346 	 *            type code of the object to store.
347 	 * @param data
348 	 *            complete content of the object.
349 	 * @param off
350 	 *            first position within {@code data}.
351 	 * @param len
352 	 *            number of bytes to copy from {@code data}.
353 	 * @return the name of the object.
354 	 * @throws IOException
355 	 *             the object could not be stored.
356 	 */
357 	public ObjectId insert(int type, byte[] data, int off, int len)
358 			throws IOException {
359 		return insert(type, len, new ByteArrayInputStream(data, off, len));
360 	}
361 
362 	/**
363 	 * Insert a single object into the store, returning its unique name.
364 	 *
365 	 * @param objectType
366 	 *            type code of the object to store.
367 	 * @param length
368 	 *            number of bytes to copy from {@code in}.
369 	 * @param in
370 	 *            stream providing the object content. The caller is responsible
371 	 *            for closing the stream.
372 	 * @return the name of the object.
373 	 * @throws IOException
374 	 *             the object could not be stored, or the source stream could
375 	 *             not be read.
376 	 */
377 	public abstract ObjectId insert(int objectType, long length, InputStream in)
378 			throws IOException;
379 
380 	/**
381 	 * Initialize a parser to read from a pack formatted stream.
382 	 *
383 	 * @param in
384 	 *            the input stream. The stream is not closed by the parser, and
385 	 *            must instead be closed by the caller once parsing is complete.
386 	 * @return the pack parser.
387 	 * @throws IOException
388 	 *             the parser instance, which can be configured and then used to
389 	 *             parse objects into the ObjectDatabase.
390 	 */
391 	public abstract PackParser newPackParser(InputStream in) throws IOException;
392 
393 	/**
394 	 * Open a reader for objects that may have been written by this inserter.
395 	 * <p>
396 	 * The returned reader allows the calling thread to read back recently
397 	 * inserted objects without first calling {@code flush()} to make them
398 	 * visible to the repository. The returned reader should only be used from
399 	 * the same thread as the inserter. Objects written by this inserter may not
400 	 * be visible to {@code this.newReader().newReader()}.
401 	 *
402 	 * @since 3.5
403 	 * @return reader for any object, including an object recently inserted by
404 	 *         this inserter since the last flush.
405 	 */
406 	public abstract ObjectReader newReader();
407 
408 	/**
409 	 * Make all inserted objects visible.
410 	 * <p>
411 	 * The flush may take some period of time to make the objects available to
412 	 * other threads.
413 	 *
414 	 * @throws IOException
415 	 *             the flush could not be completed; objects inserted thus far
416 	 *             are in an indeterminate state.
417 	 */
418 	public abstract void flush() throws IOException;
419 
420 	/**
421 	 * Release any resources used by this inserter.
422 	 * <p>
423 	 * An inserter that has been released can be used again, but may need to be
424 	 * released after the subsequent usage.
425 	 *
426 	 * @since 4.0
427 	 */
428 	@Override
429 	public abstract void close();
430 }