java.io.FileOutputStream , except that all writes are first
redirected to a temporary file. When the stream is closed, the temporary file (atomically) replaces the target file.
In detail, the strategy is to:
If all goes well, no temporary or backup files will remain on disk after closing the stream.
Errors are handled as follows:
FileChangedException, leaving the other
process's version of the file untouched.
Implementation inspired by code from Marty Lamb and Apache.
| Modifier and Type | Class and Description |
|---|---|
| pack-priv static interface | |
| pack-priv static interface |
| Modifier and Type | Field and Description |
|---|---|
| private final Path | backupFile
A backup of the target file (if it exists), created when the stream is closed |
| private final AtomicFileOutputStream. | |
| private @Nullable FileSnapshot | committedTargetFileState
State of the target file right after a successful commit; |
| private boolean | |
| private final @Nullable FileSnapshot | expectedTargetFileState
The state the target file must still be in when this stream commits: an inherited baseline (see the
|
| private final AtomicFileOutputStream. | |
| private final boolean | |
| private static final Logger | |
| private static final int | MOVE_ATTEMPTS
Number of attempts to move the temporary file onto the target file. |
| private static final long | MOVE_RETRY_INITIAL_DELAY_MILLIS
Delay before the second move attempt; doubled after each further failed attempt. |
| private static final String | |
| private final Path | targetFile
The file we want to create/replace. |
| private static final String | |
| private static final String | |
| private final Path | temporaryFile
The uniquely named file to which writes are redirected to. |
| private final @Nullable FileChannel | temporaryFileChannel
Null if the stream was constructed from an injected |
| Access | Constructor and Description |
|---|---|
| public | AtomicFileOutputStream(Path
the path of the file to write to or replace path, boolean whether to keep the backup file (.sav) after a successful write process keepBackup)Creates a new output stream to write to or replace the file at the specified path. |
| public | AtomicFileOutputStream(Path
the path of the file to write to or replace path, boolean whether to keep the backup file (.sav) after a successful write process keepBackup, @Nullable FileSnapshot the state the target file is expected to (still) be in when this stream commits — a snapshot from an earlier point of the same logical operation, so that a concurrent write landing before this stream was even opened is still detected; expectedState)null to verify against the state at stream creationCreates a new output stream to write to or replace the file at the specified path, verifying against an inherited baseline instead of the file's state at stream creation. |
| private | AtomicFileOutputStream(Path path, Path pathOfTemporaryFile, boolean keepBackup, @Nullable FileSnapshot expectedState, AtomicFileOutputStream.
The temporary file is opened as a |
| private | AtomicFileOutputStream(Path path, Path pathOfTemporaryFile, FileChannel temporaryFileChannel, boolean keepBackup, @Nullable FileSnapshot expectedState, AtomicFileOutputStream.
|
| public | AtomicFileOutputStream(Path
the path of the file to write to or replace path)Creates a new output stream to write to or replace the file at the specified path. |
| pack-priv | AtomicFileOutputStream(Path path, Path pathOfTemporaryFile, OutputStream temporaryFileOutputStream, boolean keepBackup)
Required for proper testing |
| pack-priv | AtomicFileOutputStream(Path path, Path pathOfTemporaryFile, OutputStream temporaryFileOutputStream, boolean keepBackup, AtomicFileOutputStream.
Required for proper testing |
| pack-priv | AtomicFileOutputStream(Path path, Path pathOfTemporaryFile, OutputStream temporaryFileOutputStream, boolean keepBackup, AtomicFileOutputStream.
Required for proper testing |
| private | AtomicFileOutputStream(Path path, Path pathOfTemporaryFile, OutputStream temporaryFileOutputStream, @Nullable FileChannel temporaryFileChannel, boolean keepBackup, @Nullable FileSnapshot expectedState, AtomicFileOutputStream.
|
| Modifier and Type | Method and Description |
|---|---|
| public void | |
| private static void | applyAttributes(Path file, Map<String, Object> attributes)
Applies the attributes read by |
| private void | |
| public void | close()
Overrides java. Implements java. perform the final operations to move the temporary file to its final destination |
| private void | |
| private static void | |
| private boolean | |
| private static Path | |
| private void | ensureTargetFileUnchanged()
Best-effort lost-update guard: detects whether another process modified the target file after the expected baseline state was captured. |
| private void | |
| public void | flush()
Overrides java. Implements java. Flushes this output stream and forces any buffered output bytes to be written out to the stream. |
| private static void | |
| public Path | |
| public @Nullable FileSnapshot | Returns: the committed state, ornull when the stream did not commit (yet) or the attributes could not be readReturns the state of the target file as written by this stream, captured immediately after the successful commit. |
| private static Path | |
| private void | |
| private static void | |
| private void | |
| private void | moveTemporaryFileToTargetFile(boolean backupCreated)
Moves the temporary file onto the target file, replacing it. |
| private void | |
| private void | |
| private static Map | readPreservableAttributes(Path file, Path
the file that is going to replace replacement)file; only DOS flags differing from its flags are included, because writing an unchanged false on Linux adds a DOSATTRIB xattr to every saved fileReads everything a move cannot preserve (group, DOS attributes, ACL, user-defined extended attributes) from
|
| private boolean | |
| public void | write(byte[] b, int off, int len)
Overrides java. Overridden because of cleanup actions in case of an error |
| public void | write(int b)
Overrides java. Writes the specified |
| backupFile | back to summary |
|---|---|
| private final Path backupFile A backup of the target file (if it exists), created when the stream is closed | |
| backupFileCopyOperation | back to summary |
|---|---|
| private final AtomicFileOutputStream. | |
| committedTargetFileState | back to summary |
|---|---|
| private @Nullable FileSnapshot committedTargetFileState State of the target file right after a successful commit; | |
| errorDuringWrite | back to summary |
|---|---|
| private boolean errorDuringWrite | |
| expectedTargetFileState | back to summary |
|---|---|
| private final @Nullable FileSnapshot expectedTargetFileState The state the target file must still be in when this stream commits: an inherited baseline (see the
| |
| fileMoveOperation | back to summary |
|---|---|
| private final AtomicFileOutputStream. | |
| keepBackup | back to summary |
|---|---|
| private final boolean keepBackup | |
| LOGGER | back to summary |
|---|---|
| private static final Logger LOGGER | |
| MOVE_ATTEMPTS | back to summary |
|---|---|
| private static final int MOVE_ATTEMPTS Number of attempts to move the temporary file onto the target file. See | |
| MOVE_RETRY_INITIAL_DELAY_MILLIS | back to summary |
|---|---|
| private static final long MOVE_RETRY_INITIAL_DELAY_MILLIS Delay before the second move attempt; doubled after each further failed attempt. | |
| SAVE_EXTENSION | back to summary |
|---|---|
| private static final String SAVE_EXTENSION | |
| targetFile | back to summary |
|---|---|
| private final Path targetFile The file we want to create/replace. | |
| TEMPORARY_EXTENSION | back to summary |
|---|---|
| private static final String TEMPORARY_EXTENSION | |
| TEMPORARY_FILE_PREFIX | back to summary |
|---|---|
| private static final String TEMPORARY_FILE_PREFIX | |
| temporaryFile | back to summary |
|---|---|
| private final Path temporaryFile The uniquely named file to which writes are redirected to. | |
| temporaryFileChannel | back to summary |
|---|---|
| private final @Nullable FileChannel temporaryFileChannel Null if the stream was constructed from an injected | |
| AtomicFileOutputStream | back to summary |
|---|---|
| public AtomicFileOutputStream(Path path, boolean keepBackup) throws IOException Creates a new output stream to write to or replace the file at the specified path.
| |
| AtomicFileOutputStream | back to summary |
|---|---|
| public AtomicFileOutputStream(Path path, boolean keepBackup, @Nullable FileSnapshot expectedState) throws IOException Creates a new output stream to write to or replace the file at the specified path, verifying against an inherited baseline instead of the file's state at stream creation.
| |
| AtomicFileOutputStream | back to summary |
|---|---|
| private AtomicFileOutputStream(Path path, Path pathOfTemporaryFile, boolean keepBackup, @Nullable FileSnapshot expectedState, AtomicFileOutputStream. The temporary file is opened as a | |
| AtomicFileOutputStream | back to summary |
|---|---|
| private AtomicFileOutputStream(Path path, Path pathOfTemporaryFile, FileChannel temporaryFileChannel, boolean keepBackup, @Nullable FileSnapshot expectedState, AtomicFileOutputStream. | |
| AtomicFileOutputStream | back to summary |
|---|---|
| public AtomicFileOutputStream(Path path) throws IOException Creates a new output stream to write to or replace the file at the specified path. The backup file (.sav) is deleted when write was successful.
| |
| AtomicFileOutputStream | back to summary |
|---|---|
| pack-priv AtomicFileOutputStream(Path path, Path pathOfTemporaryFile, OutputStream temporaryFileOutputStream, boolean keepBackup) Required for proper testing | |
| AtomicFileOutputStream | back to summary |
|---|---|
| pack-priv AtomicFileOutputStream(Path path, Path pathOfTemporaryFile, OutputStream temporaryFileOutputStream, boolean keepBackup, AtomicFileOutputStream. Required for proper testing | |
| AtomicFileOutputStream | back to summary |
|---|---|
| pack-priv AtomicFileOutputStream(Path path, Path pathOfTemporaryFile, OutputStream temporaryFileOutputStream, boolean keepBackup, AtomicFileOutputStream. Required for proper testing | |
| AtomicFileOutputStream | back to summary |
|---|---|
| private AtomicFileOutputStream(Path path, Path pathOfTemporaryFile, OutputStream temporaryFileOutputStream, @Nullable FileChannel temporaryFileChannel, boolean keepBackup, @Nullable FileSnapshot expectedState, AtomicFileOutputStream. | |
| abort | back to summary |
|---|---|
| public void abort() Closes the write process to the temporary file but does not commit to the target file. | |
| applyAttributes | back to summary |
|---|---|
| private static void applyAttributes(Path file, Map<String, Object> attributes) Applies the attributes read by | |
| cleanup | back to summary |
|---|---|
| private void cleanup() | |
| close | back to summary |
|---|---|
| public void close() throws IOException Overrides java. Implements java. perform the final operations to move the temporary file to its final destination
| |
| copyFileToTarget | back to summary |
|---|---|
| private void copyFileToTarget(Path source) throws IOException | |
| copyReplacingExisting | back to summary |
|---|---|
| private static void copyReplacingExisting(Path source, Path target) throws IOException | |
| createBackup | back to summary |
|---|---|
| private boolean createBackup() | |
| createTemporaryFile | back to summary |
|---|---|
| private static Path createTemporaryFile(Path targetFile) throws IOException | |
| ensureTargetFileUnchanged | back to summary |
|---|---|
| private void ensureTargetFileUnchanged() throws FileChangedException Best-effort lost-update guard: detects whether another process modified the target file after the expected
baseline state was captured. See | |
| fallBackToInPlaceSave | back to summary |
|---|---|
| private void fallBackToInPlaceSave(IOException moveException) throws IOException | |
| flush | back to summary |
|---|---|
| public void flush() throws IOException Overrides java. Implements java. Doc from java. Flushes this output stream and forces any buffered output bytes to be written out to the stream.
| |
| forceFileToDisk | back to summary |
|---|---|
| private static void forceFileToDisk(Path file) throws IOException | |
| getBackup | back to summary |
|---|---|
| public Path getBackup() Returns the path of the backup copy of the original file (may not exist) | |
| getCommittedTargetFileState | back to summary |
|---|---|
| public @Nullable FileSnapshot getCommittedTargetFileState() Returns the state of the target file as written by this stream, captured immediately after the successful commit. Callers spanning a longer logical operation (e.g. a save that may be retried with a different encoding after a user dialog) can pass it as the expected state of a follow-up stream, so that the whole operation is guarded against concurrent writes — including the time between the two streams.
| |
| getPathOfSaveBackupFile | back to summary |
|---|---|
| private static Path getPathOfSaveBackupFile(Path targetFile) | |
| markWriteAsFailed | back to summary |
|---|---|
| private void markWriteAsFailed() | |
| moveAtomically | back to summary |
|---|---|
| private static void moveAtomically(Path source, Path target) throws IOException | |
| moveBackupFileIntoPlace | back to summary |
|---|---|
| private void moveBackupFileIntoPlace(Path temporaryBackupFile) throws IOException | |
| moveTemporaryFileToTargetFile | back to summary |
|---|---|
| private void moveTemporaryFileToTargetFile(boolean backupCreated) throws IOException Moves the temporary file onto the target file, replacing it. On Windows, replacing a file requires See <#11916>. | |
| moveTemporaryFileWithoutAtomicity | back to summary |
|---|---|
| private void moveTemporaryFileWithoutAtomicity(IOException atomicMoveException) throws IOException | |
| overwriteTargetFile | back to summary |
|---|---|
| private void overwriteTargetFile(boolean backupCreated) throws IOException | |
| readPreservableAttributes | back to summary |
|---|---|
| private static Map Reads everything a move cannot preserve (group, DOS attributes, ACL, user-defined extended attributes) from
The string-based attribute API is used instead of the typed views: it never yields
| |
| targetHasHardLinks | back to summary |
|---|---|
| private boolean targetHasHardLinks() | |
| write | back to summary |
|---|---|
| public void write(byte[] b, int off, int len) throws IOException Overrides java. Overridden because of cleanup actions in case of an error
| |
| write | back to summary |
|---|---|
| public void write(int b) throws IOException Overrides java. Doc from java. Writes the specified
The
Implements the abstract
| |
| copy | back to summary |
|---|---|
| public void copy(Path source, Path target) throws IOException | |
| move | back to summary |
|---|---|
| public void move(Path source, Path target) throws IOException | |