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