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