Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[JENKINS-34855] Create a FileChannelWriter
- Loading branch information
Showing
2 changed files
with
120 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,57 @@ | ||
package hudson.util; | ||
|
||
import org.kohsuke.accmod.Restricted; | ||
import org.kohsuke.accmod.restrictions.NoExternalUse; | ||
|
||
import java.io.IOException; | ||
import java.io.Writer; | ||
import java.nio.ByteBuffer; | ||
import java.nio.CharBuffer; | ||
import java.nio.channels.FileChannel; | ||
import java.nio.charset.Charset; | ||
import java.nio.file.OpenOption; | ||
import java.nio.file.Path; | ||
|
||
/** | ||
* This class has been created to help make {@link AtomicFileWriter} hopefully more reliable in some corner cases. | ||
* We created this wrapper to be able to access {@link FileChannel#force(boolean)} which seems to be one of the rare | ||
* ways to actually have a guarantee that data be flushed to the physical device (only guaranteed for local, not for | ||
* remote obviously though). | ||
* | ||
* <p>The goal using this is to reduce as much as we can the likeliness to see zero-length files be created in place | ||
* of the original ones.</p> | ||
* | ||
* @see <a href="https://issues.jenkins-ci.org/browse/JENKINS-34855">JENKINS-34855</a> | ||
* @see <a href="https://github.com/jenkinsci/jenkins/pull/2548">PR-2548</a> | ||
*/ | ||
@Restricted(NoExternalUse.class) | ||
public class FileChannelWriter extends Writer { | ||
|
||
private final Charset charset; | ||
private final FileChannel channel; | ||
|
||
FileChannelWriter(Path filePath, Charset charset, OpenOption... options) throws IOException { | ||
this.charset = charset; | ||
channel = FileChannel.open(filePath, options); | ||
} | ||
|
||
@Override | ||
public void write(char cbuf[], int off, int len) throws IOException { | ||
final CharBuffer charBuffer = CharBuffer.wrap(cbuf, off, len); | ||
ByteBuffer byteBuffer = charset.encode(charBuffer); | ||
channel.write(byteBuffer); | ||
} | ||
|
||
@Override | ||
public void flush() throws IOException { | ||
channel.force(true); | ||
} | ||
|
||
@Override | ||
public void close() throws IOException { | ||
if(channel.isOpen()) { | ||
channel.force(true); | ||
channel.close(); | ||
} | ||
} | ||
} |
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,63 @@ | ||
package hudson.util; | ||
|
||
import org.apache.commons.io.FileUtils; | ||
import org.junit.Before; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.rules.TemporaryFolder; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.nio.channels.ClosedChannelException; | ||
import java.nio.charset.StandardCharsets; | ||
import java.nio.file.StandardOpenOption; | ||
|
||
import static org.hamcrest.CoreMatchers.not; | ||
import static org.hamcrest.core.IsEqual.equalTo; | ||
import static org.junit.Assert.assertThat; | ||
import static org.junit.Assert.fail; | ||
|
||
public class FileChannelWriterTest { | ||
@Rule | ||
public TemporaryFolder temporaryFolder = new TemporaryFolder(); | ||
|
||
File file; | ||
FileChannelWriter writer; | ||
|
||
@Before | ||
public void setUp() throws Exception { | ||
file = temporaryFolder.newFile(); | ||
writer = new FileChannelWriter(file.toPath(), StandardCharsets.UTF_8, StandardOpenOption.WRITE); | ||
} | ||
|
||
@Test | ||
public void write() throws Exception { | ||
writer.write("helloooo"); | ||
writer.close(); | ||
|
||
assertContent("helloooo"); | ||
} | ||
|
||
|
||
@Test | ||
public void flush() throws Exception { | ||
writer.write("hello é è à".toCharArray()); | ||
|
||
writer.flush(); | ||
assertContent("hello é è à"); | ||
} | ||
|
||
@Test(expected = ClosedChannelException.class) | ||
public void close() throws Exception { | ||
writer.write("helloooo"); | ||
writer.close(); | ||
|
||
writer.write("helloooo"); | ||
fail("Should have failed the line above"); | ||
} | ||
|
||
|
||
private void assertContent(String string) throws IOException { | ||
assertThat(FileUtils.readFileToString(file, StandardCharsets.UTF_8), equalTo(string)); | ||
} | ||
} |