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;
156
157 newSnapshot = FileSnapshot.saveNoConfig(getFile());
158 try {
159 final byte[] in = IO.readFully(getFile());
160 final ObjectId newHash = hash(in);
161 if (hash.equals(newHash)) {
162 if (oldSnapshot.equals(newSnapshot)) {
163 oldSnapshot.setClean(newSnapshot);
164 } else {
165 snapshot = newSnapshot;
166 }
167 } else {
168 final String decoded;
169 if (isUtf8(in)) {
170 decoded = RawParseUtils.decode(UTF_8,
171 in, 3, in.length);
172 utf8Bom = true;
173 } else {
174 decoded = RawParseUtils.decode(in);
175 }
176 fromText(decoded);
177 snapshot = newSnapshot;
178 hash = newHash;
179 }
180 return;
181 } catch (FileNotFoundException noFile) {
182 if (configFile.exists()) {
183 throw noFile;
184 }
185 clear();
186 snapshot = newSnapshot;
187 return;
188 } catch (IOException e) {
189 if (FileUtils.isStaleFileHandle(e)
190 && retries < maxStaleRetries) {
191 if (LOG.isDebugEnabled()) {
192 LOG.debug(MessageFormat.format(
193 JGitText.get().configHandleIsStale,
194 Integer.valueOf(retries)), e);
195 }
196 retries++;
197 continue;
198 }
199 throw new IOException(MessageFormat
200 .format(JGitText.get().cannotReadFile, getFile()), e);
201 } catch (ConfigInvalidException e) {
202 throw new ConfigInvalidException(MessageFormat
203 .format(JGitText.get().cannotReadFile, getFile()), e);
204 }
205 }
206 }
207
208
209
210
211
212
213
214
215
216
217
218
219 @Override
220 public void save() throws IOException {
221 final byte[] out;
222 final String text = toText();
223 if (utf8Bom) {
224 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
225 bos.write(0xEF);
226 bos.write(0xBB);
227 bos.write(0xBF);
228 bos.write(text.getBytes(UTF_8));
229 out = bos.toByteArray();
230 } else {
231 out = Constants.encode(text);
232 }
233
234 final LockFileal/storage/file/LockFile.html#LockFile">LockFile lf = new LockFile(getFile());
235 if (!lf.lock())
236 throw new LockFailedException(getFile());
237 try {
238 lf.setNeedSnapshot(true);
239 lf.write(out);
240 if (!lf.commit())
241 throw new IOException(MessageFormat.format(JGitText.get().cannotCommitWriteTo, getFile()));
242 } finally {
243 lf.unlock();
244 }
245 snapshot = lf.getCommitSnapshot();
246 hash = hash(out);
247
248 fireConfigChangedEvent();
249 }
250
251
252 @Override
253 public void clear() {
254 hash = hash(new byte[0]);
255 super.clear();
256 }
257
258 private static ObjectId hash(byte[] rawText) {
259 return ObjectId.fromRaw(Constants.newMessageDigest().digest(rawText));
260 }
261
262
263 @SuppressWarnings("nls")
264 @Override
265 public String toString() {
266 return getClass().getSimpleName() + "[" + getFile().getPath() + "]";
267 }
268
269
270
271
272
273
274
275 public boolean isOutdated() {
276 return snapshot.isModified(getFile());
277 }
278
279
280
281
282
283
284 @Override
285 protected byte[] readIncludedConfig(String relPath)
286 throws ConfigInvalidException {
287 final File file;
288 if (relPath.startsWith("~/")) {
289 file = fs.resolve(fs.userHome(), relPath.substring(2));
290 } else {
291 file = fs.resolve(configFile.getParentFile(), relPath);
292 }
293
294 if (!file.exists()) {
295 return null;
296 }
297
298 try {
299 return IO.readFully(file);
300 } catch (IOException ioe) {
301 throw new ConfigInvalidException(MessageFormat
302 .format(JGitText.get().cannotReadFile, relPath), ioe);
303 }
304 }
305 }