diff --git a/changelog.html b/changelog.html
index a4af17ce93eda1c416099a455d8f17dcf9362b54..2540ce9d85f01774de4a9ac3e885a0ad27ab91a5 100644
--- a/changelog.html
+++ b/changelog.html
@@ -67,6 +67,9 @@ Upcoming changes
Since 1.518, fingerprint serialization broke when job or file names contained XML special characters like ampersands.
(issue 18337)
+
+ Robustness against truncated fingerprint files.
+ (issue 19515)
JavaScript error in the checkUrl computation shouldn't break the job configuration page.
(issue 19457)
diff --git a/core/src/main/java/hudson/model/Fingerprint.java b/core/src/main/java/hudson/model/Fingerprint.java
index 59a759460e2152a58f3cf71ae8de4edaffede145..0e91bf7cf2ec76fcf634c3bf47799ba1a151013a 100644
--- a/core/src/main/java/hudson/model/Fingerprint.java
+++ b/core/src/main/java/hudson/model/Fingerprint.java
@@ -38,11 +38,13 @@ import hudson.BulkChange;
import hudson.Extension;
import hudson.model.listeners.ItemListener;
import hudson.model.listeners.SaveableListener;
+import hudson.util.AtomicFileWriter;
import hudson.util.HexBinaryConverter;
import hudson.util.Iterators;
import hudson.util.PersistedList;
import hudson.util.RunList;
import hudson.util.XStream2;
+import java.io.EOFException;
import jenkins.model.FingerprintFacet;
import jenkins.model.Jenkins;
import jenkins.model.TransientFingerprintFacetFactory;
@@ -995,8 +997,12 @@ public class Fingerprint implements ModelObject, Saveable {
}
}
- if (modified)
+ if (modified) {
+ if (logger.isLoggable(Level.FINE)) {
+ logger.log(Level.FINE, "Saving trimmed {0}", getFingerprintFile(md5sum));
+ }
save();
+ }
return modified;
}
@@ -1101,8 +1107,9 @@ public class Fingerprint implements ModelObject, Saveable {
if (facets.isEmpty()) {
file.getParentFile().mkdirs();
// JENKINS-16301: fast path for the common case.
- PrintWriter w = new PrintWriter(file, "UTF-8");
+ AtomicFileWriter afw = new AtomicFileWriter(file);
try {
+ PrintWriter w = new PrintWriter(afw);
w.println("");
w.println("");
w.print(" ");
@@ -1139,8 +1146,9 @@ public class Fingerprint implements ModelObject, Saveable {
w.println(" ");
w.print("");
w.flush();
+ afw.commit();
} finally {
- w.close();
+ afw.abort();
}
} else {
// Slower fallback that can persist facets.
@@ -1230,7 +1238,7 @@ public class Fingerprint implements ModelObject, Saveable {
file.delete();
return null;
}
- String parseError = messageOfXmlPullParserException(e);
+ String parseError = messageOfParseException(e);
if (parseError != null) {
logger.log(Level.WARNING, "Malformed XML in {0}: {1}", new Object[] {configFile, parseError});
file.delete();
@@ -1240,13 +1248,13 @@ public class Fingerprint implements ModelObject, Saveable {
throw e;
}
}
- private static String messageOfXmlPullParserException(Throwable t) {
- if (t instanceof XmlPullParserException) {
+ private static String messageOfParseException(Throwable t) {
+ if (t instanceof XmlPullParserException || t instanceof EOFException) {
return t.getMessage();
}
Throwable t2 = t.getCause();
if (t2 != null) {
- return messageOfXmlPullParserException(t2);
+ return messageOfParseException(t2);
} else {
return null;
}
diff --git a/core/src/main/java/hudson/model/FingerprintCleanupThread.java b/core/src/main/java/hudson/model/FingerprintCleanupThread.java
index f8be385ba0cb3af6acec6eb46bab9eac52e4d59e..c04830911d751933cf3dc2a35a828dedf8c47063 100644
--- a/core/src/main/java/hudson/model/FingerprintCleanupThread.java
+++ b/core/src/main/java/hudson/model/FingerprintCleanupThread.java
@@ -101,11 +101,13 @@ public final class FingerprintCleanupThread extends AsyncPeriodicWork {
try {
Fingerprint fp = Fingerprint.load(fingerprintFile);
if (fp == null || !fp.isAlive()) {
+ logger.fine("deleting obsolete " + fingerprintFile);
fingerprintFile.delete();
return true;
} else {
// get the fingerprint in the official map so have the changes visible to Jenkins
// otherwise the mutation made in FingerprintMap can override our trimming.
+ logger.finer("possibly trimming " + fingerprintFile);
fp = Jenkins.getInstance()._getFingerprint(fp.getHashString());
return fp.trim();
}