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