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  
54  import org.eclipse.jgit.internal.JGitText;
55  import org.eclipse.jgit.transport.PackParser;
56  import org.eclipse.jgit.util.sha1.SHA1;
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 		@Override
111 		public ObjectId idFor(int type, byte[] data) {
112 			return delegate().idFor(type, data);
113 		}
114 
115 		@Override
116 		public ObjectId idFor(int type, byte[] data, int off, int len) {
117 			return delegate().idFor(type, data, off, len);
118 		}
119 
120 		@Override
121 		public ObjectId idFor(int objectType, long length, InputStream in)
122 				throws IOException {
123 			return delegate().idFor(objectType, length, in);
124 		}
125 
126 		@Override
127 		public ObjectId idFor(TreeFormatter formatter) {
128 			return delegate().idFor(formatter);
129 		}
130 
131 		@Override
132 		public ObjectId insert(int type, byte[] data) throws IOException {
133 			return delegate().insert(type, data);
134 		}
135 
136 		@Override
137 		public ObjectId insert(int type, byte[] data, int off, int len)
138 				throws IOException {
139 			return delegate().insert(type, data, off, len);
140 		}
141 
142 		@Override
143 		public ObjectId insert(int objectType, long length, InputStream in)
144 				throws IOException {
145 			return delegate().insert(objectType, length, in);
146 		}
147 
148 		@Override
149 		public PackParser newPackParser(InputStream in) throws IOException {
150 			return delegate().newPackParser(in);
151 		}
152 
153 		@Override
154 		public ObjectReader newReader() {
155 			final ObjectReader dr = delegate().newReader();
156 			return new ObjectReader.Filter() {
157 				@Override
158 				protected ObjectReader delegate() {
159 					return dr;
160 				}
161 
162 				@Override
163 				public ObjectInserter getCreatedFromInserter() {
164 					return ObjectInserter.Filter.this;
165 				}
166 			};
167 		}
168 
169 		@Override
170 		public void flush() throws IOException {
171 			delegate().flush();
172 		}
173 
174 		@Override
175 		public void close() {
176 			delegate().close();
177 		}
178 	}
179 
180 	private final SHA1 hasher = SHA1.newInstance();
181 
182 	/** Temporary working buffer for streaming data through. */
183 	private byte[] tempBuffer;
184 
185 	/**
186 	 * Create a new inserter for a database.
187 	 */
188 	protected ObjectInserter() {
189 	}
190 
191 	/**
192 	 * Obtain a temporary buffer for use by the ObjectInserter or its subclass.
193 	 * <p>
194 	 * This buffer is supplied by the ObjectInserter base class to itself and
195 	 * its subclasses for the purposes of pulling data from a supplied
196 	 * InputStream, passing it through a Deflater, or formatting the canonical
197 	 * format of a small object like a small tree or commit.
198 	 * <p>
199 	 * <strong>This buffer IS NOT for translation such as auto-CRLF or content
200 	 * filtering and must not be used for such purposes.</strong>
201 	 * <p>
202 	 * The returned buffer is small, around a few KiBs, and the size may change
203 	 * between versions of JGit. Callers using this buffer must always check the
204 	 * length of the returned array to ascertain how much space was provided.
205 	 * <p>
206 	 * There is a single buffer for each ObjectInserter, repeated calls to this
207 	 * method will (usually) always return the same buffer. If the caller needs
208 	 * more than one buffer, or needs a buffer of a larger size, it must manage
209 	 * that buffer on its own.
210 	 * <p>
211 	 * The buffer is usually on first demand for a buffer.
212 	 *
213 	 * @return a temporary byte array for use by the caller.
214 	 */
215 	protected byte[] buffer() {
216 		byte[] b = tempBuffer;
217 		if (b == null)
218 			tempBuffer = b = new byte[8192];
219 		return b;
220 	}
221 
222 	/**
223 	 * Compute digest to help compute an ObjectId
224 	 *
225 	 * @return digest to help compute an ObjectId
226 	 * @since 4.7
227 	 */
228 	protected SHA1 digest() {
229 		return hasher.reset();
230 	}
231 
232 	/**
233 	 * Compute the name of an object, without inserting it.
234 	 *
235 	 * @param type
236 	 *            type code of the object to store.
237 	 * @param data
238 	 *            complete content of the object.
239 	 * @return the name of the object.
240 	 */
241 	public ObjectId idFor(int type, byte[] data) {
242 		return idFor(type, data, 0, data.length);
243 	}
244 
245 	/**
246 	 * Compute the name of an object, without inserting it.
247 	 *
248 	 * @param type
249 	 *            type code of the object to store.
250 	 * @param data
251 	 *            complete content of the object.
252 	 * @param off
253 	 *            first position within {@code data}.
254 	 * @param len
255 	 *            number of bytes to copy from {@code data}.
256 	 * @return the name of the object.
257 	 */
258 	public ObjectId idFor(int type, byte[] data, int off, int len) {
259 		SHA1 md = SHA1.newInstance();
260 		md.update(Constants.encodedTypeString(type));
261 		md.update((byte) ' ');
262 		md.update(Constants.encodeASCII(len));
263 		md.update((byte) 0);
264 		md.update(data, off, len);
265 		return md.toObjectId();
266 	}
267 
268 	/**
269 	 * Compute the name of an object, without inserting it.
270 	 *
271 	 * @param objectType
272 	 *            type code of the object to store.
273 	 * @param length
274 	 *            number of bytes to scan from {@code in}.
275 	 * @param in
276 	 *            stream providing the object content. The caller is responsible
277 	 *            for closing the stream.
278 	 * @return the name of the object.
279 	 * @throws java.io.IOException
280 	 *             the source stream could not be read.
281 	 */
282 	public ObjectId idFor(int objectType, long length, InputStream in)
283 			throws IOException {
284 		SHA1 md = SHA1.newInstance();
285 		md.update(Constants.encodedTypeString(objectType));
286 		md.update((byte) ' ');
287 		md.update(Constants.encodeASCII(length));
288 		md.update((byte) 0);
289 		byte[] buf = buffer();
290 		while (length > 0) {
291 			int n = in.read(buf, 0, (int) Math.min(length, buf.length));
292 			if (n < 0)
293 				throw new EOFException(JGitText.get().unexpectedEndOfInput);
294 			md.update(buf, 0, n);
295 			length -= n;
296 		}
297 		return md.toObjectId();
298 	}
299 
300 	/**
301 	 * Compute the ObjectId for the given tree without inserting it.
302 	 *
303 	 * @param formatter
304 	 *            a {@link org.eclipse.jgit.lib.TreeFormatter} object.
305 	 * @return the computed ObjectId
306 	 */
307 	public ObjectId idFor(TreeFormatter formatter) {
308 		return formatter.computeId(this);
309 	}
310 
311 	/**
312 	 * Insert a single tree into the store, returning its unique name.
313 	 *
314 	 * @param formatter
315 	 *            the formatter containing the proposed tree's data.
316 	 * @return the name of the tree object.
317 	 * @throws java.io.IOException
318 	 *             the object could not be stored.
319 	 */
320 	public final ObjectId insert(TreeFormatter formatter) throws IOException {
321 		// Delegate to the formatter, as then it can pass the raw internal
322 		// buffer back to this inserter, avoiding unnecessary data copying.
323 		//
324 		return formatter.insertTo(this);
325 	}
326 
327 	/**
328 	 * Insert a single commit into the store, returning its unique name.
329 	 *
330 	 * @param builder
331 	 *            the builder containing the proposed commit's data.
332 	 * @return the name of the commit object.
333 	 * @throws java.io.IOException
334 	 *             the object could not be stored.
335 	 */
336 	public final ObjectId insert(CommitBuilder builder) throws IOException {
337 		return insert(Constants.OBJ_COMMIT, builder.build());
338 	}
339 
340 	/**
341 	 * Insert a single annotated tag into the store, returning its unique name.
342 	 *
343 	 * @param builder
344 	 *            the builder containing the proposed tag's data.
345 	 * @return the name of the tag object.
346 	 * @throws java.io.IOException
347 	 *             the object could not be stored.
348 	 */
349 	public final ObjectId insert(TagBuilder builder) throws IOException {
350 		return insert(Constants.OBJ_TAG, builder.build());
351 	}
352 
353 	/**
354 	 * Insert a single object into the store, returning its unique name.
355 	 *
356 	 * @param type
357 	 *            type code of the object to store.
358 	 * @param data
359 	 *            complete content of the object.
360 	 * @return the name of the object.
361 	 * @throws java.io.IOException
362 	 *             the object could not be stored.
363 	 */
364 	public ObjectId insert(int type, byte[] data)
365 			throws IOException {
366 		return insert(type, data, 0, data.length);
367 	}
368 
369 	/**
370 	 * Insert a single object into the store, returning its unique name.
371 	 *
372 	 * @param type
373 	 *            type code of the object to store.
374 	 * @param data
375 	 *            complete content of the object.
376 	 * @param off
377 	 *            first position within {@code data}.
378 	 * @param len
379 	 *            number of bytes to copy from {@code data}.
380 	 * @return the name of the object.
381 	 * @throws java.io.IOException
382 	 *             the object could not be stored.
383 	 */
384 	public ObjectId insert(int type, byte[] data, int off, int len)
385 			throws IOException {
386 		return insert(type, len, new ByteArrayInputStream(data, off, len));
387 	}
388 
389 	/**
390 	 * Insert a single object into the store, returning its unique name.
391 	 *
392 	 * @param objectType
393 	 *            type code of the object to store.
394 	 * @param length
395 	 *            number of bytes to copy from {@code in}.
396 	 * @param in
397 	 *            stream providing the object content. The caller is responsible
398 	 *            for closing the stream.
399 	 * @return the name of the object.
400 	 * @throws java.io.IOException
401 	 *             the object could not be stored, or the source stream could
402 	 *             not be read.
403 	 */
404 	public abstract ObjectId insert(int objectType, long length, InputStream in)
405 			throws IOException;
406 
407 	/**
408 	 * Initialize a parser to read from a pack formatted stream.
409 	 *
410 	 * @param in
411 	 *            the input stream. The stream is not closed by the parser, and
412 	 *            must instead be closed by the caller once parsing is complete.
413 	 * @return the pack parser.
414 	 * @throws java.io.IOException
415 	 *             the parser instance, which can be configured and then used to
416 	 *             parse objects into the ObjectDatabase.
417 	 */
418 	public abstract PackParser newPackParser(InputStream in) throws IOException;
419 
420 	/**
421 	 * Open a reader for objects that may have been written by this inserter.
422 	 * <p>
423 	 * The returned reader allows the calling thread to read back recently
424 	 * inserted objects without first calling {@code flush()} to make them
425 	 * visible to the repository. The returned reader should only be used from
426 	 * the same thread as the inserter. Objects written by this inserter may not
427 	 * be visible to {@code this.newReader().newReader()}.
428 	 * <p>
429 	 * The returned reader should return this inserter instance from {@link
430 	 * ObjectReader#getCreatedFromInserter()}.
431 	 * <p>
432 	 * Behavior is undefined if an insert method is called on the inserter in the
433 	 * middle of reading from an {@link ObjectStream} opened from this reader. For
434 	 * example, reading the remainder of the object may fail, or newly written
435 	 * data may even be corrupted. Interleaving whole object reads (including
436 	 * streaming reads) with inserts is fine, just not interleaving streaming
437 	 * <em>partial</em> object reads with inserts.
438 	 *
439 	 * @since 3.5
440 	 * @return reader for any object, including an object recently inserted by
441 	 *         this inserter since the last flush.
442 	 */
443 	public abstract ObjectReader newReader();
444 
445 	/**
446 	 * Make all inserted objects visible.
447 	 * <p>
448 	 * The flush may take some period of time to make the objects available to
449 	 * other threads.
450 	 *
451 	 * @throws java.io.IOException
452 	 *             the flush could not be completed; objects inserted thus far
453 	 *             are in an indeterminate state.
454 	 */
455 	public abstract void flush() throws IOException;
456 
457 	/**
458 	 * {@inheritDoc}
459 	 * <p>
460 	 * Release any resources used by this inserter.
461 	 * <p>
462 	 * An inserter that has been released can be used again, but may need to be
463 	 * released after the subsequent usage.
464 	 *
465 	 * @since 4.0
466 	 */
467 	@Override
468 	public abstract void close();
469 }