1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package net.sf.hermesftp.streams;
26
27 import java.io.IOException;
28 import java.io.OutputStream;
29
30 /***
31 * In the case of a file being sent with file-structure to a record-oriented host, there exists the
32 * question of what criteria the host should use to divide the file into records which can be
33 * processed locally. If this division is necessary, an FTP implementation should use the
34 * end-of-line sequence, CRLF for ASCII, or NewLine (0x25) for EBCDIC text files, as the delimiter.
35 * If an FTP implementation adopts this technique, it must be prepared to reverse the transformation
36 * if the file is retrieved with file-structure.
37 *
38 * @author Lars Behnke
39 */
40 public class BlockModeOutputStream extends OutputStream implements BlockModeConstants, RecordWriteSupport {
41
42 /***
43 * The default block size to be used.
44 */
45 private static final int DEFAULT_BLOCK_SIZE = 1024;
46
47 private OutputStream os;
48
49 private byte[] buffer;
50
51 private int idx;
52
53 private boolean fileComplete;
54
55 private boolean recordComplete;
56
57 /***
58 * Constructor.
59 *
60 * @param os The nested output stream.
61 */
62 public BlockModeOutputStream(OutputStream os) {
63 this(os, DEFAULT_BLOCK_SIZE);
64 }
65
66 /***
67 * Constructor.
68 *
69 * @param os The output stream.
70 * @param blockSize The blocksize.
71 */
72 public BlockModeOutputStream(OutputStream os, int blockSize) {
73 super();
74 this.os = os;
75 this.buffer = new byte[blockSize];
76 this.idx = 0;
77 this.fileComplete = false;
78 }
79
80 /***
81 * Writes a complete record and marks the transfer optionally with EOF.
82 *
83 * @param record The data to be transmitted.
84 * @param eof True, if the transfer is complete.
85 * @throws IOException If writing the data fails or the file was previously finalized.
86 */
87 public void writeRecord(byte[] record, boolean eof) throws IOException {
88 recordComplete = false;
89 write(record);
90 finalizeRecord(eof);
91 }
92
93 /***
94 * {@inheritDoc}
95 */
96 public void write(int b) throws IOException {
97 if (fileComplete) {
98 throw new IOException("EOF marked already.");
99 }
100 buffer[idx] = (byte) b;
101 idx++;
102 if (idx >= buffer.length) {
103 writeBlock(0x00, buffer.length);
104 idx = 0;
105 }
106 recordComplete = false;
107 }
108
109 private void writeBlock(int code, int len) throws IOException {
110 os.write(code);
111 os.write((len >>> 8) & 0xFF);
112 os.write((len >>> 0) & 0xFF);
113 os.write(buffer, 0, len);
114 }
115
116 /***
117 * {@inheritDoc}
118 */
119 public void flush() throws IOException {
120 os.flush();
121 }
122
123 /***
124 * Flushes the last dataset. Block mode transfer requires setting EOF explicitly.
125 *
126 * @param eof True, if end of file.
127 * @throws IOException When writing fails.
128 */
129 public void finalizeRecord(boolean eof) throws IOException {
130 if (!fileComplete && !recordComplete) {
131 int descriptor = DESC_CODE_EOR;
132 if (eof) {
133 descriptor |= DESC_CODE_EOF;
134 fileComplete = true;
135 }
136 writeBlock(descriptor, idx);
137 recordComplete = true;
138 idx = 0;
139 }
140 }
141
142 /***
143 * {@inheritDoc}
144 */
145 public void close() throws IOException {
146 finalizeRecord(true);
147 os.close();
148 }
149
150 }