public static void replaceFile(File src, File target) throws IOException {
    /* renameTo() has two limitations on Windows platform.
     * src.renameTo(target) fails if
     * 1) If target already exists OR
     * 2) If target is already open for reading/writing.
     */
    if (!src.renameTo(target)) {
      int retries = 5;
      while (target.exists() && !target.delete() && retries-- >= 0) {
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          throw new IOException("replaceFile interrupted.");
        }
      }
      if (!src.renameTo(target)) {
        throw new IOException("Unable to rename " + src +
                              " to " + target);
      }
    }
  }
@Test (timeout = 30000)
  public void testReplaceFile() throws IOException {
    // src exists, and target does not exist:
    final File srcFile = Verify.createNewFile(new File(tmp, "src"));
    final File targetFile = new File(tmp, "target");
    Verify.notExists(targetFile);
    FileUtil.replaceFile(srcFile, targetFile);
    Verify.notExists(srcFile);
    Verify.exists(targetFile);

    // src exists and target is a regular file: 
    Verify.createNewFile(srcFile);
    Verify.exists(srcFile);
    FileUtil.replaceFile(srcFile, targetFile);
    Verify.notExists(srcFile);
    Verify.exists(targetFile);
    
    // src exists, and target is a non-empty directory: 
    Verify.createNewFile(srcFile);
    Verify.exists(srcFile);
    Verify.delete(targetFile);
    Verify.mkdirs(targetFile);
    File obstacle = Verify.createNewFile(new File(targetFile, "obstacle"));
    assertTrue(targetFile.exists() && targetFile.isDirectory());
    try {
      FileUtil.replaceFile(srcFile, targetFile);
      assertTrue(false);
    } catch (IOException ioe) {
      // okay
    }
    // check up the post-condition: nothing is deleted:
    Verify.exists(srcFile);
    assertTrue(targetFile.exists() && targetFile.isDirectory());
    Verify.exists(obstacle);
  }