1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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.FilterOutputStream;
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.io.OutputStream;
56 import java.nio.channels.Channels;
57 import java.text.MessageFormat;
58 import java.util.zip.Deflater;
59 import java.util.zip.DeflaterOutputStream;
60
61 import org.eclipse.jgit.errors.ObjectWritingException;
62 import org.eclipse.jgit.internal.JGitText;
63 import org.eclipse.jgit.lib.Config;
64 import org.eclipse.jgit.lib.Constants;
65 import org.eclipse.jgit.lib.ObjectId;
66 import org.eclipse.jgit.lib.ObjectInserter;
67 import org.eclipse.jgit.lib.ObjectReader;
68 import org.eclipse.jgit.transport.PackParser;
69 import org.eclipse.jgit.util.FileUtils;
70 import org.eclipse.jgit.util.IO;
71 import org.eclipse.jgit.util.sha1.SHA1;
72
73
74 class ObjectDirectoryInserter extends ObjectInserter {
75 private final FileObjectDatabase db;
76
77 private final WriteConfig config;
78
79 private Deflater deflate;
80
81 ObjectDirectoryInserter(FileObjectDatabase dest, Config cfg) {
82 db = dest;
83 config = cfg.get(WriteConfig.KEY);
84 }
85
86
87 @Override
88 public ObjectId insert(int type, byte[] data, int off, int len)
89 throws IOException {
90 return insert(type, data, off, len, false);
91 }
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 private ObjectId insert(
107 int type, byte[] data, int off, int len, boolean createDuplicate)
108 throws IOException {
109 ObjectId id = idFor(type, data, off, len);
110 if (!createDuplicate && db.has(id)) {
111 return id;
112 } else {
113 File tmp = toTemp(type, data, off, len);
114 return insertOneObject(tmp, id, createDuplicate);
115 }
116 }
117
118
119 @Override
120 public ObjectId insert(int type, long len, InputStream is)
121 throws IOException {
122 return insert(type, len, is, false);
123 }
124
125
126
127
128
129
130
131
132
133
134
135
136
137 ObjectId insert(int type, long len, InputStream is, boolean createDuplicate)
138 throws IOException {
139 if (len <= buffer().length) {
140 byte[] buf = buffer();
141 int actLen = IO.readFully(is, buf, 0);
142 return insert(type, buf, 0, actLen, createDuplicate);
143
144 } else {
145 SHA1 md = digest();
146 File tmp = toTemp(md, type, len, is);
147 ObjectId id = md.toObjectId();
148 return insertOneObject(tmp, id, createDuplicate);
149 }
150 }
151
152 private ObjectId insertOneObject(
153 File tmp, ObjectId id, boolean createDuplicate)
154 throws IOException, ObjectWritingException {
155 switch (db.insertUnpackedObject(tmp, id, createDuplicate)) {
156 case INSERTED:
157 case EXISTS_PACKED:
158 case EXISTS_LOOSE:
159 return id;
160
161 case FAILURE:
162 default:
163 break;
164 }
165
166 final File dst = db.fileFor(id);
167 throw new ObjectWritingException(MessageFormat
168 .format(JGitText.get().unableToCreateNewObject, dst));
169 }
170
171
172 @Override
173 public PackParser newPackParser(InputStream in) throws IOException {
174 return new ObjectDirectoryPackParser(db, in);
175 }
176
177
178 @Override
179 public ObjectReader newReader() {
180 return new WindowCursor(db, this);
181 }
182
183
184 @Override
185 public void flush() throws IOException {
186
187 }
188
189
190 @Override
191 public void close() {
192 if (deflate != null) {
193 try {
194 deflate.end();
195 } finally {
196 deflate = null;
197 }
198 }
199 }
200
201 @SuppressWarnings("resource" )
202 private File toTemp(final SHA1 md, final int type, long len,
203 final InputStream is) throws IOException, FileNotFoundException,
204 Error {
205 boolean delete = true;
206 File tmp = newTempFile();
207 try {
208 FileOutputStream fOut = new FileOutputStream(tmp);
209 try {
210 OutputStream out = fOut;
211 if (config.getFSyncObjectFiles())
212 out = Channels.newOutputStream(fOut.getChannel());
213 DeflaterOutputStream cOut = compress(out);
214 SHA1OutputStream dOut = new SHA1OutputStream(cOut, md);
215 writeHeader(dOut, type, len);
216
217 final byte[] buf = buffer();
218 while (len > 0) {
219 int n = is.read(buf, 0, (int) Math.min(len, buf.length));
220 if (n <= 0)
221 throw shortInput(len);
222 dOut.write(buf, 0, n);
223 len -= n;
224 }
225 dOut.flush();
226 cOut.finish();
227 } finally {
228 if (config.getFSyncObjectFiles())
229 fOut.getChannel().force(true);
230 fOut.close();
231 }
232
233 delete = false;
234 return tmp;
235 } finally {
236 if (delete)
237 FileUtils.delete(tmp, FileUtils.RETRY);
238 }
239 }
240
241 @SuppressWarnings("resource" )
242 private File toTemp(final int type, final byte[] buf, final int pos,
243 final int len) throws IOException, FileNotFoundException {
244 boolean delete = true;
245 File tmp = newTempFile();
246 try {
247 FileOutputStream fOut = new FileOutputStream(tmp);
248 try {
249 OutputStream out = fOut;
250 if (config.getFSyncObjectFiles())
251 out = Channels.newOutputStream(fOut.getChannel());
252 DeflaterOutputStream cOut = compress(out);
253 writeHeader(cOut, type, len);
254 cOut.write(buf, pos, len);
255 cOut.finish();
256 } finally {
257 if (config.getFSyncObjectFiles())
258 fOut.getChannel().force(true);
259 fOut.close();
260 }
261
262 delete = false;
263 return tmp;
264 } finally {
265 if (delete)
266 FileUtils.delete(tmp, FileUtils.RETRY);
267 }
268 }
269
270 void writeHeader(OutputStream out, int type, long len)
271 throws IOException {
272 out.write(Constants.encodedTypeString(type));
273 out.write((byte) ' ');
274 out.write(Constants.encodeASCII(len));
275 out.write((byte) 0);
276 }
277
278 File newTempFile() throws IOException {
279 return File.createTempFile("noz", null, db.getDirectory());
280 }
281
282 DeflaterOutputStream compress(OutputStream out) {
283 if (deflate == null)
284 deflate = new Deflater(config.getCompression());
285 else
286 deflate.reset();
287 return new DeflaterOutputStream(out, deflate, 8192);
288 }
289
290 private static EOFException shortInput(long missing) {
291 return new EOFException(MessageFormat.format(
292 JGitText.get().inputDidntMatchLength, Long.valueOf(missing)));
293 }
294
295 private static class SHA1OutputStream extends FilterOutputStream {
296 private final SHA1 md;
297
298 SHA1OutputStream(OutputStream out, SHA1 md) {
299 super(out);
300 this.md = md;
301 }
302
303 @Override
304 public void write(int b) throws IOException {
305 md.update((byte) b);
306 out.write(b);
307 }
308
309 @Override
310 public void write(byte[] in, int p, int n) throws IOException {
311 md.update(in, p, n);
312 out.write(in, p, n);
313 }
314 }
315 }