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> and others
6    *
7    * This program and the accompanying materials are made available under the
8    * terms of the Eclipse Distribution License v. 1.0 which is available at
9    * https://www.eclipse.org/org/documents/edl-v10.php.
10   *
11   * SPDX-License-Identifier: BSD-3-Clause
12   */
13  
14  package org.eclipse.jgit.lib;
15  
16  import java.io.ByteArrayInputStream;
17  import java.io.EOFException;
18  import java.io.IOException;
19  import java.io.InputStream;
20  
21  import org.eclipse.jgit.internal.JGitText;
22  import org.eclipse.jgit.transport.PackParser;
23  import org.eclipse.jgit.util.sha1.SHA1;
24  
25  /**
26   * Inserts objects into an existing {@code ObjectDatabase}.
27   * <p>
28   * An inserter is not thread-safe. Individual threads should each obtain their
29   * own unique inserter instance, or must arrange for locking at a higher level
30   * to ensure the inserter is in use by no more than one thread at a time.
31   * <p>
32   * Objects written by an inserter may not be immediately visible for reading
33   * after the insert method completes. Callers must invoke either
34   * {@link #close()} or {@link #flush()} prior to updating references or
35   * otherwise making the returned ObjectIds visible to other code.
36   */
37  public abstract class ObjectInserter implements AutoCloseable {
38  	/** An inserter that can be used for formatting and id generation only. */
39  	public static class Formatter extends ObjectInserter {
40  		@Override
41  		public ObjectId insert(int objectType, long length, InputStream in)
42  				throws IOException {
43  			throw new UnsupportedOperationException();
44  		}
45  
46  		@Override
47  		public PackParser newPackParser(InputStream in) throws IOException {
48  			throw new UnsupportedOperationException();
49  		}
50  
51  		@Override
52  		public ObjectReader newReader() {
53  			throw new UnsupportedOperationException();
54  		}
55  
56  		@Override
57  		public void flush() throws IOException {
58  			// Do nothing.
59  		}
60  
61  		@Override
62  		public void close() {
63  			// Do nothing.
64  		}
65  	}
66  
67  	/** Wraps a delegate ObjectInserter. */
68  	public abstract static class Filter extends ObjectInserter {
69  		/** @return delegate ObjectInserter to handle all processing. */
70  		protected abstract ObjectInserter delegate();
71  
72  		@Override
73  		protected byte[] buffer() {
74  			return delegate().buffer();
75  		}
76  
77  		@Override
78  		public ObjectId idFor(int type, byte[] data) {
79  			return delegate().idFor(type, data);
80  		}
81  
82  		@Override
83  		public ObjectId idFor(int type, byte[] data, int off, int len) {
84  			return delegate().idFor(type, data, off, len);
85  		}
86  
87  		@Override
88  		public ObjectId idFor(int objectType, long length, InputStream in)
89  				throws IOException {
90  			return delegate().idFor(objectType, length, in);
91  		}
92  
93  		@Override
94  		public ObjectId idFor(TreeFormatter formatter) {
95  			return delegate().idFor(formatter);
96  		}
97  
98  		@Override
99  		public ObjectId insert(int type, byte[] data) throws IOException {
100 			return delegate().insert(type, data);
101 		}
102 
103 		@Override
104 		public ObjectId insert(int type, byte[] data, int off, int len)
105 				throws IOException {
106 			return delegate().insert(type, data, off, len);
107 		}
108 
109 		@Override
110 		public ObjectId insert(int objectType, long length, InputStream in)
111 				throws IOException {
112 			return delegate().insert(objectType, length, in);
113 		}
114 
115 		@Override
116 		public PackParser newPackParser(InputStream in) throws IOException {
117 			return delegate().newPackParser(in);
118 		}
119 
120 		@Override
121 		public ObjectReader newReader() {
122 			final ObjectReader dr = delegate().newReader();
123 			return new ObjectReader.Filter() {
124 				@Override
125 				protected ObjectReader delegate() {
126 					return dr;
127 				}
128 
129 				@Override
130 				public ObjectInserter getCreatedFromInserter() {
131 					return ObjectInserter.Filter.this;
132 				}
133 			};
134 		}
135 
136 		@Override
137 		public void flush() throws IOException {
138 			delegate().flush();
139 		}
140 
141 		@Override
142 		public void close() {
143 			delegate().close();
144 		}
145 	}
146 
147 	private final SHA1 hasher = SHA1.newInstance();
148 
149 	/** Temporary working buffer for streaming data through. */
150 	private byte[] tempBuffer;
151 
152 	/**
153 	 * Create a new inserter for a database.
154 	 */
155 	protected ObjectInserter() {
156 	}
157 
158 	/**
159 	 * Obtain a temporary buffer for use by the ObjectInserter or its subclass.
160 	 * <p>
161 	 * This buffer is supplied by the ObjectInserter base class to itself and
162 	 * its subclasses for the purposes of pulling data from a supplied
163 	 * InputStream, passing it through a Deflater, or formatting the canonical
164 	 * format of a small object like a small tree or commit.
165 	 * <p>
166 	 * <strong>This buffer IS NOT for translation such as auto-CRLF or content
167 	 * filtering and must not be used for such purposes.</strong>
168 	 * <p>
169 	 * The returned buffer is small, around a few KiBs, and the size may change
170 	 * between versions of JGit. Callers using this buffer must always check the
171 	 * length of the returned array to ascertain how much space was provided.
172 	 * <p>
173 	 * There is a single buffer for each ObjectInserter, repeated calls to this
174 	 * method will (usually) always return the same buffer. If the caller needs
175 	 * more than one buffer, or needs a buffer of a larger size, it must manage
176 	 * that buffer on its own.
177 	 * <p>
178 	 * The buffer is usually on first demand for a buffer.
179 	 *
180 	 * @return a temporary byte array for use by the caller.
181 	 */
182 	protected byte[] buffer() {
183 		byte[] b = tempBuffer;
184 		if (b == null)
185 			tempBuffer = b = new byte[8192];
186 		return b;
187 	}
188 
189 	/**
190 	 * Compute digest to help compute an ObjectId
191 	 *
192 	 * @return digest to help compute an ObjectId
193 	 * @since 4.7
194 	 */
195 	protected SHA1 digest() {
196 		return hasher.reset();
197 	}
198 
199 	/**
200 	 * Compute the name of an object, without inserting it.
201 	 *
202 	 * @param type
203 	 *            type code of the object to store.
204 	 * @param data
205 	 *            complete content of the object.
206 	 * @return the name of the object.
207 	 */
208 	public ObjectId idFor(int type, byte[] data) {
209 		return idFor(type, data, 0, data.length);
210 	}
211 
212 	/**
213 	 * Compute the name of an object, without inserting it.
214 	 *
215 	 * @param type
216 	 *            type code of the object to store.
217 	 * @param data
218 	 *            complete content of the object.
219 	 * @param off
220 	 *            first position within {@code data}.
221 	 * @param len
222 	 *            number of bytes to copy from {@code data}.
223 	 * @return the name of the object.
224 	 */
225 	public ObjectId idFor(int type, byte[] data, int off, int len) {
226 		SHA1 md = SHA1.newInstance();
227 		md.update(Constants.encodedTypeString(type));
228 		md.update((byte) ' ');
229 		md.update(Constants.encodeASCII(len));
230 		md.update((byte) 0);
231 		md.update(data, off, len);
232 		return md.toObjectId();
233 	}
234 
235 	/**
236 	 * Compute the name of an object, without inserting it.
237 	 *
238 	 * @param objectType
239 	 *            type code of the object to store.
240 	 * @param length
241 	 *            number of bytes to scan from {@code in}.
242 	 * @param in
243 	 *            stream providing the object content. The caller is responsible
244 	 *            for closing the stream.
245 	 * @return the name of the object.
246 	 * @throws java.io.IOException
247 	 *             the source stream could not be read.
248 	 */
249 	public ObjectId idFor(int objectType, long length, InputStream in)
250 			throws IOException {
251 		SHA1 md = SHA1.newInstance();
252 		md.update(Constants.encodedTypeString(objectType));
253 		md.update((byte) ' ');
254 		md.update(Constants.encodeASCII(length));
255 		md.update((byte) 0);
256 		byte[] buf = buffer();
257 		while (length > 0) {
258 			int n = in.read(buf, 0, (int) Math.min(length, buf.length));
259 			if (n < 0)
260 				throw new EOFException(JGitText.get().unexpectedEndOfInput);
261 			md.update(buf, 0, n);
262 			length -= n;
263 		}
264 		return md.toObjectId();
265 	}
266 
267 	/**
268 	 * Compute the ObjectId for the given tree without inserting it.
269 	 *
270 	 * @param formatter
271 	 *            a {@link org.eclipse.jgit.lib.TreeFormatter} object.
272 	 * @return the computed ObjectId
273 	 */
274 	public ObjectId idFor(TreeFormatter formatter) {
275 		return formatter.computeId(this);
276 	}
277 
278 	/**
279 	 * Insert a single tree into the store, returning its unique name.
280 	 *
281 	 * @param formatter
282 	 *            the formatter containing the proposed tree's data.
283 	 * @return the name of the tree object.
284 	 * @throws java.io.IOException
285 	 *             the object could not be stored.
286 	 */
287 	public final ObjectId insert(TreeFormatter formatter) throws IOException {
288 		// Delegate to the formatter, as then it can pass the raw internal
289 		// buffer back to this inserter, avoiding unnecessary data copying.
290 		//
291 		return formatter.insertTo(this);
292 	}
293 
294 	/**
295 	 * Insert a single commit into the store, returning its unique name.
296 	 *
297 	 * @param builder
298 	 *            the builder containing the proposed commit's data.
299 	 * @return the name of the commit object.
300 	 * @throws java.io.IOException
301 	 *             the object could not be stored.
302 	 */
303 	public final ObjectId insert(CommitBuilder builder) throws IOException {
304 		return insert(Constants.OBJ_COMMIT, builder.build());
305 	}
306 
307 	/**
308 	 * Insert a single annotated tag into the store, returning its unique name.
309 	 *
310 	 * @param builder
311 	 *            the builder containing the proposed tag's data.
312 	 * @return the name of the tag object.
313 	 * @throws java.io.IOException
314 	 *             the object could not be stored.
315 	 */
316 	public final ObjectId insert(TagBuilder builder) throws IOException {
317 		return insert(Constants.OBJ_TAG, builder.build());
318 	}
319 
320 	/**
321 	 * Insert a single object into the store, returning its unique name.
322 	 *
323 	 * @param type
324 	 *            type code of the object to store.
325 	 * @param data
326 	 *            complete content of the object.
327 	 * @return the name of the object.
328 	 * @throws java.io.IOException
329 	 *             the object could not be stored.
330 	 */
331 	public ObjectId insert(int type, byte[] data)
332 			throws IOException {
333 		return insert(type, data, 0, data.length);
334 	}
335 
336 	/**
337 	 * Insert a single object into the store, returning its unique name.
338 	 *
339 	 * @param type
340 	 *            type code of the object to store.
341 	 * @param data
342 	 *            complete content of the object.
343 	 * @param off
344 	 *            first position within {@code data}.
345 	 * @param len
346 	 *            number of bytes to copy from {@code data}.
347 	 * @return the name of the object.
348 	 * @throws java.io.IOException
349 	 *             the object could not be stored.
350 	 */
351 	public ObjectId insert(int type, byte[] data, int off, int len)
352 			throws IOException {
353 		return insert(type, len, new ByteArrayInputStream(data, off, len));
354 	}
355 
356 	/**
357 	 * Insert a single object into the store, returning its unique name.
358 	 *
359 	 * @param objectType
360 	 *            type code of the object to store.
361 	 * @param length
362 	 *            number of bytes to copy from {@code in}.
363 	 * @param in
364 	 *            stream providing the object content. The caller is responsible
365 	 *            for closing the stream.
366 	 * @return the name of the object.
367 	 * @throws java.io.IOException
368 	 *             the object could not be stored, or the source stream could
369 	 *             not be read.
370 	 */
371 	public abstract ObjectId insert(int objectType, long length, InputStream in)
372 			throws IOException;
373 
374 	/**
375 	 * Initialize a parser to read from a pack formatted stream.
376 	 *
377 	 * @param in
378 	 *            the input stream. The stream is not closed by the parser, and
379 	 *            must instead be closed by the caller once parsing is complete.
380 	 * @return the pack parser.
381 	 * @throws java.io.IOException
382 	 *             the parser instance, which can be configured and then used to
383 	 *             parse objects into the ObjectDatabase.
384 	 */
385 	public abstract PackParser newPackParser(InputStream in) throws IOException;
386 
387 	/**
388 	 * Open a reader for objects that may have been written by this inserter.
389 	 * <p>
390 	 * The returned reader allows the calling thread to read back recently
391 	 * inserted objects without first calling {@code flush()} to make them
392 	 * visible to the repository. The returned reader should only be used from
393 	 * the same thread as the inserter. Objects written by this inserter may not
394 	 * be visible to {@code this.newReader().newReader()}.
395 	 * <p>
396 	 * The returned reader should return this inserter instance from {@link
397 	 * ObjectReader#getCreatedFromInserter()}.
398 	 * <p>
399 	 * Behavior is undefined if an insert method is called on the inserter in the
400 	 * middle of reading from an {@link ObjectStream} opened from this reader. For
401 	 * example, reading the remainder of the object may fail, or newly written
402 	 * data may even be corrupted. Interleaving whole object reads (including
403 	 * streaming reads) with inserts is fine, just not interleaving streaming
404 	 * <em>partial</em> object reads with inserts.
405 	 *
406 	 * @since 3.5
407 	 * @return reader for any object, including an object recently inserted by
408 	 *         this inserter since the last flush.
409 	 */
410 	public abstract ObjectReader newReader();
411 
412 	/**
413 	 * Make all inserted objects visible.
414 	 * <p>
415 	 * The flush may take some period of time to make the objects available to
416 	 * other threads.
417 	 *
418 	 * @throws java.io.IOException
419 	 *             the flush could not be completed; objects inserted thus far
420 	 *             are in an indeterminate state.
421 	 */
422 	public abstract void flush() throws IOException;
423 
424 	/**
425 	 * {@inheritDoc}
426 	 * <p>
427 	 * Release any resources used by this inserter.
428 	 * <p>
429 	 * An inserter that has been released can be used again, but may need to be
430 	 * released after the subsequent usage.
431 	 *
432 	 * @since 4.0
433 	 */
434 	@Override
435 	public abstract void close();
436 }