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    * and other copyright owners as documented in the project's IP log.
6    *
7    * This program and the accompanying materials are made available
8    * under the terms of the Eclipse Distribution License v1.0 which
9    * accompanies this distribution, is reproduced below, and is
10   * available at http://www.eclipse.org/org/documents/edl-v10.php
11   *
12   * All rights reserved.
13   *
14   * Redistribution and use in source and binary forms, with or
15   * without modification, are permitted provided that the following
16   * conditions are met:
17   *
18   * - Redistributions of source code must retain the above copyright
19   *   notice, this list of conditions and the following disclaimer.
20   *
21   * - Redistributions in binary form must reproduce the above
22   *   copyright notice, this list of conditions and the following
23   *   disclaimer in the documentation and/or other materials provided
24   *   with the distribution.
25   *
26   * - Neither the name of the Eclipse Foundation, Inc. nor the
27   *   names of its contributors may be used to endorse or promote
28   *   products derived from this software without specific prior
29   *   written permission.
30   *
31   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
32   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
33   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
36   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
38   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
40   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
43   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44   */
45  
46  package org.eclipse.jgit.internal.storage.file;
47  
48  import java.io.EOFException;
49  import java.io.File;
50  import java.io.FileNotFoundException;
51  import java.io.FileOutputStream;
52  import java.io.IOException;
53  import java.io.InputStream;
54  import java.io.OutputStream;
55  import java.nio.channels.Channels;
56  import java.security.DigestOutputStream;
57  import java.security.MessageDigest;
58  import java.text.MessageFormat;
59  import java.util.zip.Deflater;
60  import java.util.zip.DeflaterOutputStream;
61  
62  import org.eclipse.jgit.errors.ObjectWritingException;
63  import org.eclipse.jgit.internal.JGitText;
64  import org.eclipse.jgit.lib.Config;
65  import org.eclipse.jgit.lib.Constants;
66  import org.eclipse.jgit.lib.ObjectId;
67  import org.eclipse.jgit.lib.ObjectInserter;
68  import org.eclipse.jgit.lib.ObjectReader;
69  import org.eclipse.jgit.transport.PackParser;
70  import org.eclipse.jgit.util.FileUtils;
71  import org.eclipse.jgit.util.IO;
72  
73  /** Creates loose objects in a {@link ObjectDirectory}. */
74  class ObjectDirectoryInserter extends ObjectInserter {
75  	private final FileObjectDatabase db;
76  
77  	private final WriteConfig config;
78  
79  	private Deflater deflate;
80  
81  	ObjectDirectoryInserter(final FileObjectDatabase dest, final Config cfg) {
82  		db = dest;
83  		config = cfg.get(WriteConfig.KEY);
84  	}
85  
86  	@Override
87  	public ObjectId insert(int type, byte[] data, int off, int len)
88  			throws IOException {
89  		ObjectId id = idFor(type, data, off, len);
90  		if (db.has(id)) {
91  			return id;
92  		} else {
93  			File tmp = toTemp(type, data, off, len);
94  			return insertOneObject(tmp, id);
95  		}
96  	}
97  
98  	@Override
99  	public ObjectId insert(final int type, long len, final InputStream is)
100 			throws IOException {
101 		if (len <= buffer().length) {
102 			byte[] buf = buffer();
103 			int actLen = IO.readFully(is, buf, 0);
104 			return insert(type, buf, 0, actLen);
105 
106 		} else {
107 			MessageDigest md = digest();
108 			File tmp = toTemp(md, type, len, is);
109 			ObjectId id = ObjectId.fromRaw(md.digest());
110 			return insertOneObject(tmp, id);
111 		}
112 	}
113 
114 	private ObjectId insertOneObject(final File tmp, final ObjectId id)
115 			throws IOException, ObjectWritingException {
116 		switch (db.insertUnpackedObject(tmp, id, false /* no duplicate */)) {
117 		case INSERTED:
118 		case EXISTS_PACKED:
119 		case EXISTS_LOOSE:
120 			return id;
121 
122 		case FAILURE:
123 		default:
124 			break;
125 		}
126 
127 		final File dst = db.fileFor(id);
128 		throw new ObjectWritingException(MessageFormat
129 				.format(JGitText.get().unableToCreateNewObject, dst));
130 	}
131 
132 	@Override
133 	public PackParser newPackParser(InputStream in) throws IOException {
134 		return new ObjectDirectoryPackParser(db, in);
135 	}
136 
137 	@Override
138 	public ObjectReader newReader() {
139 		return new WindowCursor(db);
140 	}
141 
142 	@Override
143 	public void flush() throws IOException {
144 		// Do nothing. Loose objects are immediately visible.
145 	}
146 
147 	@Override
148 	public void close() {
149 		if (deflate != null) {
150 			try {
151 				deflate.end();
152 			} finally {
153 				deflate = null;
154 			}
155 		}
156 	}
157 
158 	@SuppressWarnings("resource" /* java 7 */)
159 	private File toTemp(final MessageDigest md, final int type, long len,
160 			final InputStream is) throws IOException, FileNotFoundException,
161 			Error {
162 		boolean delete = true;
163 		File tmp = newTempFile();
164 		try {
165 			FileOutputStream fOut = new FileOutputStream(tmp);
166 			try {
167 				OutputStream out = fOut;
168 				if (config.getFSyncObjectFiles())
169 					out = Channels.newOutputStream(fOut.getChannel());
170 				DeflaterOutputStream cOut = compress(out);
171 				DigestOutputStream dOut = new DigestOutputStream(cOut, md);
172 				writeHeader(dOut, type, len);
173 
174 				final byte[] buf = buffer();
175 				while (len > 0) {
176 					int n = is.read(buf, 0, (int) Math.min(len, buf.length));
177 					if (n <= 0)
178 						throw shortInput(len);
179 					dOut.write(buf, 0, n);
180 					len -= n;
181 				}
182 				dOut.flush();
183 				cOut.finish();
184 			} finally {
185 				if (config.getFSyncObjectFiles())
186 					fOut.getChannel().force(true);
187 				fOut.close();
188 			}
189 
190 			delete = false;
191 			return tmp;
192 		} finally {
193 			if (delete)
194 				FileUtils.delete(tmp, FileUtils.RETRY);
195 		}
196 	}
197 
198 	@SuppressWarnings("resource" /* java 7 */)
199 	private File toTemp(final int type, final byte[] buf, final int pos,
200 			final int len) throws IOException, FileNotFoundException {
201 		boolean delete = true;
202 		File tmp = newTempFile();
203 		try {
204 			FileOutputStream fOut = new FileOutputStream(tmp);
205 			try {
206 				OutputStream out = fOut;
207 				if (config.getFSyncObjectFiles())
208 					out = Channels.newOutputStream(fOut.getChannel());
209 				DeflaterOutputStream cOut = compress(out);
210 				writeHeader(cOut, type, len);
211 				cOut.write(buf, pos, len);
212 				cOut.finish();
213 			} finally {
214 				if (config.getFSyncObjectFiles())
215 					fOut.getChannel().force(true);
216 				fOut.close();
217 			}
218 
219 			delete = false;
220 			return tmp;
221 		} finally {
222 			if (delete)
223 				FileUtils.delete(tmp, FileUtils.RETRY);
224 		}
225 	}
226 
227 	void writeHeader(OutputStream out, final int type, long len)
228 			throws IOException {
229 		out.write(Constants.encodedTypeString(type));
230 		out.write((byte) ' ');
231 		out.write(Constants.encodeASCII(len));
232 		out.write((byte) 0);
233 	}
234 
235 	File newTempFile() throws IOException {
236 		return File.createTempFile("noz", null, db.getDirectory()); //$NON-NLS-1$
237 	}
238 
239 	DeflaterOutputStream compress(final OutputStream out) {
240 		if (deflate == null)
241 			deflate = new Deflater(config.getCompression());
242 		else
243 			deflate.reset();
244 		return new DeflaterOutputStream(out, deflate, 8192);
245 	}
246 
247 	private static EOFException shortInput(long missing) {
248 		return new EOFException(MessageFormat.format(
249 				JGitText.get().inputDidntMatchLength, Long.valueOf(missing)));
250 	}
251 }