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
47
48
49
50 package org.eclipse.jgit.storage.file;
51
52 import static java.nio.charset.StandardCharsets.UTF_8;
53
54 import java.io.ByteArrayOutputStream;
55 import java.io.File;
56 import java.io.FileNotFoundException;
57 import java.io.IOException;
58 import java.text.MessageFormat;
59
60 import org.eclipse.jgit.errors.ConfigInvalidException;
61 import org.eclipse.jgit.errors.LockFailedException;
62 import org.eclipse.jgit.internal.JGitText;
63 import org.eclipse.jgit.internal.storage.file.FileSnapshot;
64 import org.eclipse.jgit.internal.storage.file.LockFile;
65 import org.eclipse.jgit.lib.Config;
66 import org.eclipse.jgit.lib.Constants;
67 import org.eclipse.jgit.lib.ObjectId;
68 import org.eclipse.jgit.lib.StoredConfig;
69 import org.eclipse.jgit.util.FS;
70 import org.eclipse.jgit.util.FileUtils;
71 import org.eclipse.jgit.util.IO;
72 import org.eclipse.jgit.util.RawParseUtils;
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
75
76
77
78
79 public class FileBasedConfig extends StoredConfig {
80 private final static Logger LOG = LoggerFactory
81 .getLogger(FileBasedConfig.class);
82
83 private final File configFile;
84
85 private final FS fs;
86
87 private boolean utf8Bom;
88
89 private volatile FileSnapshot snapshot;
90
91 private volatile ObjectId hash;
92
93
94
95
96
97
98
99
100
101
102 public FileBasedConfig(File cfgLocation, FS fs) {
103 this(null, cfgLocation, fs);
104 }
105
106
107
108
109
110
111
112
113
114
115
116
117 public FileBasedConfig(Config base, File cfgLocation, FS fs) {
118 super(base);
119 configFile = cfgLocation;
120 this.fs = fs;
121 this.snapshot = FileSnapshot.DIRTY;
122 this.hash = ObjectId.zeroId();
123 }
124
125
126 @Override
127 protected boolean notifyUponTransientChanges() {
128
129 return false;
130 }
131
132
133
134
135
136
137 public final File getFile() {
138 return configFile;
139 }
140
141
142
143
144
145
146
147
148
149 @Override
150 public void load() throws IOException, ConfigInvalidException {
151 final int maxStaleRetries = 5;
152 int retries = 0;
153 while (true) {
154 final FileSnapshot oldSnapshot = snapshot;
155 final FileSnapshot newSnapshot = FileSnapshot.save(getFile());
156 try {
157 final byte[] in = IO.readFully(getFile());
158 final ObjectId newHash = hash(in);
159 if (hash.equals(newHash)) {
160 if (oldSnapshot.equals(newSnapshot)) {
161 oldSnapshot.setClean(newSnapshot);
162 } else {
163 snapshot = newSnapshot;
164 }
165 } else {
166 final String decoded;
167 if (isUtf8(in)) {
168 decoded = RawParseUtils.decode(UTF_8,
169 in, 3, in.length);
170 utf8Bom = true;
171 } else {
172 decoded = RawParseUtils.decode(in);
173 }
174 fromText(decoded);
175 snapshot = newSnapshot;
176 hash = newHash;
177 }
178 return;
179 } catch (FileNotFoundException noFile) {
180 if (configFile.exists()) {
181 throw noFile;
182 }
183 clear();
184 snapshot = newSnapshot;
185 return;
186 } catch (IOException e) {
187 if (FileUtils.isStaleFileHandle(e)
188 && retries < maxStaleRetries) {
189 if (LOG.isDebugEnabled()) {
190 LOG.debug(MessageFormat.format(
191 JGitText.get().configHandleIsStale,
192 Integer.valueOf(retries)), e);
193 }
194 retries++;
195 continue;
196 }
197 throw new IOException(MessageFormat
198 .format(JGitText.get().cannotReadFile, getFile()), e);
199 } catch (ConfigInvalidException e) {
200 throw new ConfigInvalidException(MessageFormat
201 .format(JGitText.get().cannotReadFile, getFile()), e);
202 }
203 }
204 }
205
206
207
208
209
210
211
212
213
214
215
216
217 @Override
218 public void save() throws IOException {
219 final byte[] out;
220 final String text = toText();
221 if (utf8Bom) {
222 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
223 bos.write(0xEF);
224 bos.write(0xBB);
225 bos.write(0xBF);
226 bos.write(text.getBytes(UTF_8));
227 out = bos.toByteArray();
228 } else {
229 out = Constants.encode(text);
230 }
231
232 final LockFileal/storage/file/LockFile.html#LockFile">LockFile lf = new LockFile(getFile());
233 if (!lf.lock())
234 throw new LockFailedException(getFile());
235 try {
236 lf.setNeedSnapshot(true);
237 lf.write(out);
238 if (!lf.commit())
239 throw new IOException(MessageFormat.format(JGitText.get().cannotCommitWriteTo, getFile()));
240 } finally {
241 lf.unlock();
242 }
243 snapshot = lf.getCommitSnapshot();
244 hash = hash(out);
245
246 fireConfigChangedEvent();
247 }
248
249
250 @Override
251 public void clear() {
252 hash = hash(new byte[0]);
253 super.clear();
254 }
255
256 private static ObjectId hash(byte[] rawText) {
257 return ObjectId.fromRaw(Constants.newMessageDigest().digest(rawText));
258 }
259
260
261 @SuppressWarnings("nls")
262 @Override
263 public String toString() {
264 return getClass().getSimpleName() + "[" + getFile().getPath() + "]";
265 }
266
267
268
269
270
271
272
273 public boolean isOutdated() {
274 return snapshot.isModified(getFile());
275 }
276
277
278
279
280
281
282 @Override
283 protected byte[] readIncludedConfig(String relPath)
284 throws ConfigInvalidException {
285 final File file;
286 if (relPath.startsWith("~/")) {
287 file = fs.resolve(fs.userHome(), relPath.substring(2));
288 } else {
289 file = fs.resolve(configFile.getParentFile(), relPath);
290 }
291
292 if (!file.exists()) {
293 return null;
294 }
295
296 try {
297 return IO.readFully(file);
298 } catch (IOException ioe) {
299 throw new ConfigInvalidException(MessageFormat
300 .format(JGitText.get().cannotReadFile, relPath), ioe);
301 }
302 }
303 }