From ab452206f91427f5baef368fed2fcad279968995 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 18 Jan 2019 16:15:31 +0100 Subject: [PATCH] Should provide an option to remove folders that are not empty in Windows; fixes gh-834 --- .../stubrunner/ContractProjectUpdater.java | 77 ++++++++++++++++++- .../ContractProjectUpdaterTest.java | 10 ++- 2 files changed, 82 insertions(+), 5 deletions(-) diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdater.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdater.java index daf6249960..f5133e26e6 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdater.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdater.java @@ -17,6 +17,7 @@ package org.springframework.cloud.contract.stubrunner; import java.io.File; import java.io.IOException; +import java.nio.file.DirectoryNotEmptyException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; @@ -29,7 +30,7 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.util.FileSystemUtils; +import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; /** @@ -160,7 +161,8 @@ public class ContractProjectUpdater { class DirectoryCopyingVisitor extends SimpleFileVisitor { - private static final List FOLDERS_TO_DELETE = Arrays.asList("contracts", "mappings"); + private static final List FOLDERS_TO_DELETE = Arrays + .asList("contracts", "mappings"); private static final Log log = LogFactory.getLog(DirectoryCopyingVisitor.class); @@ -199,7 +201,7 @@ class DirectoryCopyingVisitor extends SimpleFileVisitor { if (log.isDebugEnabled()) { log.debug("Will remove the folder [" + targetPath.toString() + "]"); } - FileSystemUtils.deleteRecursively(targetPath); + deleteRecursively(targetPath); Files.createDirectory(targetPath); if (log.isDebugEnabled()) { log.debug("Recreated folder [" + targetPath.toString() + "]"); @@ -209,6 +211,75 @@ class DirectoryCopyingVisitor extends SimpleFileVisitor { return FileVisitResult.CONTINUE; } + private boolean deleteRecursively(@Nullable Path root) throws IOException { + if (root == null) { + return false; + } + if (!Files.exists(root)) { + return false; + } + Files.walkFileTree(root, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + // a hack for Windows not to fail when directory is removed + // related to https://github.com/spring-cloud/spring-cloud-sleuth/issues/834 + if (exc == null) { + int maxTries = 5; + int count = 0; + boolean deleted; + do { + if ((deleted = this.isDeleted(dir))) { + if (log.isDebugEnabled()) { + log.debug("Deleted [" + dir + "]"); + } + break; + } + if (log.isDebugEnabled()) { + log.debug("Failed to delete [" + dir + "]"); + } + // wait a bit and try again + count++; + try { + Thread.sleep(2); + } + catch (InterruptedException e1) { + Thread.currentThread().interrupt(); + break; + } + + } + while (count < maxTries); + if (!deleted) { + if (log.isDebugEnabled()) { + log.debug("Failed to delete [" + dir + "] after [" + maxTries + "] attempts to do it"); + } + throw new DirectoryNotEmptyException(dir.toString()); + } + return FileVisitResult.CONTINUE; + } + throw exc; + } + + private boolean isDeleted(Path dir) throws IOException { + try { + Files.delete(dir); + return true; + } + catch (DirectoryNotEmptyException e) { + // happens sometimes if Windows is too slow to remove children of a directory + return false; + } + } + }); + return true; + } + @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { diff --git a/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdaterTest.java b/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdaterTest.java index 5aa6d8d9d0..7aebeab17e 100644 --- a/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdaterTest.java +++ b/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdaterTest.java @@ -23,10 +23,10 @@ import org.assertj.core.api.BDDAssertions; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.ResetCommand; import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.junit.Before; import org.junit.Rule; import org.junit.Test; + import org.springframework.boot.test.rule.OutputCapture; import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; @@ -154,11 +154,17 @@ public class ContractProjectUpdaterTest extends AbstractGitTest { @Test public void should_not_push_changes_to_current_branch_when_no_changes_were_made() throws Exception { + String initialCommit; + try (Git git = openGitProject(this.origin)) { + RevCommit revCommit = git.log().call().iterator().next(); + initialCommit = revCommit.getShortMessage(); + } + this.updater.updateContractProject("hello-world", this.origin.toPath()); try (Git git = openGitProject(this.project)) { RevCommit revCommit = git.log().call().iterator().next(); - then(revCommit.getShortMessage()).isEqualTo("Initial commit"); + then(revCommit.getShortMessage()).isEqualTo(initialCommit); } BDDAssertions.then(new File(this.project, "META-INF/com.example/hello-world/0.0.2/mappings/someMapping.json"))