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.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
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 )) {
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, this);
140 }
141
142 @Override
143 public void flush() throws IOException {
144
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" )
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" )
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());
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 }