Skip to content

Commit 2a06908

Browse files
committed
Add ByteStreamFileWriter
The module write byte streams to files.
1 parent fe5a5b7 commit 2a06908

File tree

5 files changed

+198
-0
lines changed

5 files changed

+198
-0
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package org.metafacture.io;
2+
3+
import static java.util.Objects.requireNonNull;
4+
5+
import java.io.File;
6+
import java.io.FileNotFoundException;
7+
import java.io.FileOutputStream;
8+
import java.io.IOException;
9+
import java.io.OutputStream;
10+
import java.util.function.Supplier;
11+
12+
import org.metafacture.framework.helpers.DefaultObjectReceiver;
13+
14+
/**
15+
* Writes byte arrays to regular output files.
16+
* <p>
17+
* The module opens a new output file when it receives a <i>reset-stream</i>
18+
* event. The {@link #setFileNameSupplier(Supplier)} enables users to specify
19+
* a new file name on each invocation. the {@link #setAppendIfFileExists(boolean)}
20+
* can be used to control whether existing files should be overwritten or
21+
* appended to.
22+
*/
23+
public class ByteStreamFileWriter extends DefaultObjectReceiver<byte[]> {
24+
25+
private Supplier<File> fileNameSupplier;
26+
private boolean appendIfFileExists;
27+
28+
private OutputStream outputStream;
29+
30+
/**
31+
* Supplier for file names.
32+
* <p>
33+
* A new output file is created when {@link #process(byte[])} is called
34+
* for the first time or whenever {@link #resetStream()} is called. The
35+
* name of the new file is fetched from the {@code fileNameSupplier}.
36+
* <p>
37+
* There is no default value. A file name supplier must be specified.
38+
* <p>
39+
* This property can be changed anytime during processing. It becomes
40+
* effective the next time a new output file is opened.
41+
*
42+
* @param fileNameSupplier a supplier that returns file names.
43+
*/
44+
public void setFileNameSupplier(Supplier<File> fileNameSupplier) {
45+
46+
this.fileNameSupplier = requireNonNull(fileNameSupplier);
47+
}
48+
49+
/**
50+
* Controls whether to open files in append mode if they exist.
51+
* <p>
52+
* The default value is {@code false}.
53+
* <p>
54+
* This property can be changed anytime during processing. It becomes
55+
* effective the next time a new output file is opened.
56+
*
57+
* @param appendIfFileExists true if new data should be appended,
58+
* false to overwrite the existing file.
59+
*/
60+
public void setAppendIfFileExists(boolean appendIfFileExists) {
61+
62+
this.appendIfFileExists = appendIfFileExists;
63+
}
64+
65+
/**
66+
* Writes {@code bytes} to file.
67+
*
68+
* @param bytes to write to file
69+
* @throws WriteFailed if an IO error occurred
70+
*/
71+
@Override
72+
public void process(final byte[] bytes) {
73+
74+
ensureOpenStream();
75+
try {
76+
outputStream.write(bytes);
77+
78+
} catch (IOException e) {
79+
throw new WriteFailed("Error while writing bytes to output stream", e);
80+
}
81+
}
82+
83+
/**
84+
* Opens a new output file.
85+
*
86+
* @throws CloseFailed if the current output file could not be closed.
87+
* @throws OpenFailed if the new output file could not be opened.
88+
*/
89+
@Override
90+
public void resetStream() {
91+
92+
closeStream();
93+
ensureOpenStream();
94+
}
95+
96+
/**
97+
* Closes the current output file.
98+
*
99+
* @throws CloseFailed if the output file could not be closed.
100+
*/
101+
@Override
102+
public void closeStream() {
103+
104+
if (outputStream != null) {
105+
try {
106+
outputStream.close();
107+
108+
} catch (IOException e) {
109+
throw new CloseFailed("Error while closing output stream", e);
110+
}
111+
outputStream = null;
112+
}
113+
}
114+
115+
private void ensureOpenStream() {
116+
117+
if (outputStream != null) {
118+
return;
119+
}
120+
try {
121+
outputStream = new FileOutputStream(fileNameSupplier.get(), appendIfFileExists);
122+
123+
} catch (FileNotFoundException e) {
124+
throw new OpenFailed("Cannot open output stream. File not found.", e);
125+
}
126+
}
127+
128+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.metafacture.io;
2+
3+
public class CloseFailed extends IoFailed {
4+
5+
public CloseFailed(String message) {
6+
super(message);
7+
}
8+
9+
public CloseFailed(Throwable cause) {
10+
super(cause);
11+
}
12+
13+
public CloseFailed(String message, Throwable cause) {
14+
super(message, cause);
15+
}
16+
17+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.metafacture.io;
2+
3+
import org.metafacture.framework.MetafactureException;
4+
5+
public class IoFailed extends MetafactureException {
6+
7+
public IoFailed(String message) {
8+
super(message);
9+
}
10+
11+
public IoFailed(Throwable cause) {
12+
super(cause);
13+
}
14+
15+
public IoFailed(String message, Throwable cause) {
16+
super(message, cause);
17+
}
18+
19+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.metafacture.io;
2+
3+
public class OpenFailed extends IoFailed {
4+
5+
public OpenFailed(String message) {
6+
super(message);
7+
}
8+
9+
public OpenFailed(Throwable cause) {
10+
super(cause);
11+
}
12+
13+
public OpenFailed(String message, Throwable cause) {
14+
super(message, cause);
15+
}
16+
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.metafacture.io;
2+
3+
public class WriteFailed extends IoFailed {
4+
5+
public WriteFailed(String message) {
6+
super(message);
7+
}
8+
9+
public WriteFailed(Throwable cause) {
10+
super(cause);
11+
}
12+
13+
public WriteFailed(String message, Throwable cause) {
14+
super(message, cause);
15+
}
16+
17+
}

0 commit comments

Comments
 (0)