View Javadoc

1   /*
2    * ------------------------------------------------------------------------------
3    * Hermes FTP Server
4    * Copyright (c) 2005-2007 Lars Behnke
5    * ------------------------------------------------------------------------------
6    * 
7    * This file is part of Hermes FTP Server.
8    * 
9    * Hermes FTP Server is free software; you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as published by
11   * the Free Software Foundation; either version 2 of the License, or
12   * (at your option) any later version.
13   * 
14   * Hermes FTP Server is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Public License
20   * along with Hermes FTP Server; if not, write to the Free Software
21   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
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 }