-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Given a particular OS or drive combination, atomic rename may not be available, but we do the overall write as atomically as possible. The method takes a function for writing the file contents, rather than a string, so that we can support streaming to the file, which is more efficient.
- Loading branch information
1 parent
05eaadb
commit e369459
Showing
1 changed file
with
49 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package edu.umich.soar.visualsoar.files; | ||
|
||
import java.io.FileOutputStream; | ||
import java.io.OutputStream; | ||
import java.nio.file.AtomicMoveNotSupportedException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.logging.Logger; | ||
|
||
import static java.nio.file.StandardCopyOption.ATOMIC_MOVE; | ||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; | ||
|
||
public class Util { | ||
private static final Logger LOGGER = Logger.getLogger(Util.class.getName()); | ||
|
||
@FunctionalInterface | ||
public interface Writer { | ||
void write(OutputStream out); | ||
} | ||
|
||
/** | ||
* Write to the destination file from {@code writer} as atomically as possible. | ||
*/ | ||
public void Save(Path destination, Writer writer) throws java.io.IOException { | ||
// Write to a temp file first, then make the final changes via a rename, | ||
// which can often be done atomically. This prevents issues such as overwriting a file with a | ||
// incomplete data, as could be the case if we hit an IO exception in the middle of writing the | ||
// file (especially relevant for folks using network drives!) | ||
String tempFilename = destination.toAbsolutePath() + ".temp"; | ||
Path tempPath = Paths.get(tempFilename); | ||
|
||
try (FileOutputStream output = new FileOutputStream(tempPath.toFile())) { | ||
writer.write(output); | ||
} | ||
|
||
try { | ||
Files.move(tempPath, destination, REPLACE_EXISTING, ATOMIC_MOVE); | ||
} catch (AtomicMoveNotSupportedException e) { | ||
LOGGER.warning( | ||
"Cannot write " | ||
+ destination | ||
+ " atomically (" | ||
+ e.getMessage() | ||
+ "); falling back to non-atomic write"); | ||
Files.move(tempPath, destination, REPLACE_EXISTING); | ||
} | ||
} | ||
} |