diff --git a/core/src/main/java/jenkins/model/RunIdMigrator.java b/core/src/main/java/jenkins/model/RunIdMigrator.java
index 7d375c79a5..a8b97f16e5 100644
--- a/core/src/main/java/jenkins/model/RunIdMigrator.java
+++ b/core/src/main/java/jenkins/model/RunIdMigrator.java
@@ -34,6 +34,9 @@ import hudson.util.StreamTaskListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.DateFormat;
@@ -206,11 +209,8 @@ public final class RunIdMigrator {
}
try {
Integer.parseInt(name);
- if (kid.delete()) {
- LOGGER.log(FINE, "deleted build number symlink {0} → {1}", new Object[] {name, link});
- } else {
- LOGGER.log(WARNING, "could not delete build number symlink {0} → {1}", new Object[] {name, link});
- }
+ LOGGER.log(FINE, "deleting build number symlink {0} → {1}", new Object[] {name, link});
+ Util.deleteFile(kid);
} catch (NumberFormatException x) {
LOGGER.log(FINE, "skipping other symlink {0} → {1}", new Object[] {name, link});
}
@@ -259,10 +259,7 @@ public final class RunIdMigrator {
String nl = m.group(2);
xml = m.replaceFirst(" " + name + "" + nl + " " + timestamp + "" + nl);
File newKid = new File(dir, Integer.toString(number));
- if (!kid.renameTo(newKid)) {
- LOGGER.log(WARNING, "failed to rename {0} to {1}", new Object[] {name, number});
- continue;
- }
+ move(kid, newKid);
FileUtils.writeStringToFile(new File(newKid, "build.xml"), xml, Charsets.UTF_8);
LOGGER.log(FINE, "fully processed {0} → {1}", new Object[] {name, number});
idToNumber.put(name, number);
@@ -272,6 +269,42 @@ public final class RunIdMigrator {
}
}
+ /**
+ * Tries to move/rename a file from one path to another.
+ * Uses {@link java.nio.file.Files#move} when available.
+ * Does not use {@link java.nio.file.StandardCopyOption#REPLACE_EXISTING} or any other options.
+ * TODO candidate for moving to {@link Util}
+ */
+ static void move(File src, File dest) throws IOException {
+ Class> pathC;
+ try {
+ pathC = Class.forName("java.nio.file.Path");
+ } catch (ClassNotFoundException x) {
+ // Java 6, do our best
+ if (dest.exists()) {
+ throw new IOException(dest + " already exists");
+ }
+ if (src.renameTo(dest)) {
+ return;
+ }
+ throw new IOException("could not move " + src + " to " + dest);
+ }
+ try {
+ Method toPath = File.class.getMethod("toPath");
+ Class> copyOptionAC = Class.forName("[Ljava.nio.file.CopyOption;");
+ Class.forName("java.nio.file.Files").getMethod("move", pathC, pathC, copyOptionAC).invoke(null, toPath.invoke(src), toPath.invoke(dest), Array.newInstance(copyOptionAC.getComponentType(), 0));
+ } catch (InvocationTargetException x) {
+ Throwable cause = x.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException) cause;
+ } else {
+ throw new IOException(cause);
+ }
+ } catch (Exception x) {
+ throw new IOException(x);
+ }
+ }
+
/**
* Look up a historical run by ID.
* @param id a nonnumeric ID which may be a valid {@link Run#getId}
diff --git a/core/src/test/java/jenkins/model/RunIdMigratorTest.java b/core/src/test/java/jenkins/model/RunIdMigratorTest.java
index 2d4d784968..ce5c6d89e6 100644
--- a/core/src/test/java/jenkins/model/RunIdMigratorTest.java
+++ b/core/src/test/java/jenkins/model/RunIdMigratorTest.java
@@ -27,6 +27,7 @@ package jenkins.model;
import hudson.Util;
import hudson.util.StreamTaskListener;
import java.io.File;
+import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.TimeZone;
@@ -173,4 +174,17 @@ public class RunIdMigratorTest {
return m.toString();
}
+ @Test public void move() throws Exception {
+ File src = tmp.newFile();
+ File dest = new File(tmp.getRoot(), "dest");
+ RunIdMigrator.move(src, dest);
+ File dest2 = tmp.newFile();
+ try {
+ RunIdMigrator.move(dest, dest2);
+ fail();
+ } catch (IOException x) {
+ System.err.println("Got expected move exception: " + x);
+ }
+ }
+
}