KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > installer > TarOutputStream


1 /*
2 ** Authored by Timothy Gerard Endres
3 ** <mailto:time@gjt.org> <http://www.trustice.com>
4 **
5 ** This work has been placed into the public domain.
6 ** You may use this work in any way and for any purpose you wish.
7 **
8 ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
9 ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
10 ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
11 ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
12 ** REDISTRIBUTION OF THIS SOFTWARE.
13 **
14 */

15
16 package installer;
17
18 import java.io.*;
19
20
21 /**
22  * The TarOutputStream writes a UNIX tar archive as an OutputStream.
23  * Methods are provided to put entries, and then write their contents
24  * by writing to this stream using write().
25  *
26  *
27  * @version $Revision: 4480 $
28  * @author Timothy Gerard Endres,
29  * <a HREF="mailto:time@gjt.org">time@trustice.com</a>.
30  * @see TarBuffer
31  * @see TarHeader
32  * @see TarEntry
33  */

34
35
36 public
37 class TarOutputStream
38 extends FilterOutputStream
39     {
40     protected boolean debug;
41     protected int currSize;
42     protected int currBytes;
43     protected byte[] oneBuf;
44     protected byte[] recordBuf;
45     protected int assemLen;
46     protected byte[] assemBuf;
47     protected TarBuffer buffer;
48
49
50     public
51     TarOutputStream( OutputStream os )
52         {
53         this( os, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE );
54         }
55
56     public
57     TarOutputStream( OutputStream os, int blockSize )
58         {
59         this( os, blockSize, TarBuffer.DEFAULT_RCDSIZE );
60         }
61
62     public
63     TarOutputStream( OutputStream os, int blockSize, int recordSize )
64         {
65         super( os );
66
67         this.buffer = new TarBuffer( os, blockSize, recordSize );
68         
69         this.debug = false;
70         this.assemLen = 0;
71         this.assemBuf = new byte[ recordSize ];
72         this.recordBuf = new byte[ recordSize ];
73         this.oneBuf = new byte[1];
74         }
75
76     /**
77      * Sets the debugging flag.
78      *
79      * @param debugF True to turn on debugging.
80      */

81     public void
82     setDebug( boolean debugF )
83         {
84         this.debug = debugF;
85         }
86
87     /**
88      * Sets the debugging flag in this stream's TarBuffer.
89      *
90      * @param debugF True to turn on debugging.
91      */

92     public void
93     setBufferDebug( boolean debug )
94         {
95         this.buffer.setDebug( debug );
96         }
97
98     /**
99      * Ends the TAR archive without closing the underlying OutputStream.
100      * The result is that the EOF record of nulls is written.
101      */

102
103     public void
104     finish()
105         throws IOException
106         {
107         this.writeEOFRecord();
108         }
109
110     /**
111      * Ends the TAR archive and closes the underlying OutputStream.
112      * This means that finish() is called followed by calling the
113      * TarBuffer's close().
114      */

115
116     public void
117     close()
118         throws IOException
119         {
120         this.finish();
121         this.buffer.close();
122         }
123
124     /**
125      * Get the record size being used by this stream's TarBuffer.
126      *
127      * @return The TarBuffer record size.
128      */

129     public int
130     getRecordSize()
131         {
132         return this.buffer.getRecordSize();
133         }
134
135     /**
136      * Put an entry on the output stream. This writes the entry's
137      * header record and positions the output stream for writing
138      * the contents of the entry. Once this method is called, the
139      * stream is ready for calls to write() to write the entry's
140      * contents. Once the contents are written, closeEntry()
141      * <B>MUST</B> be called to ensure that all buffered data
142      * is completely written to the output stream.
143      *
144      * @param entry The TarEntry to be written to the archive.
145      */

146     public void
147     putNextEntry( TarEntry entry )
148         throws IOException
149         {
150         if ( entry.getHeader().name.length() > TarHeader.NAMELEN )
151             throw new InvalidHeaderException
152                 ( "file name '" + entry.getHeader().name
153                     + "' is too long ( > "
154                     + TarHeader.NAMELEN + " bytes )" );
155
156         entry.writeEntryHeader( this.recordBuf );
157         this.buffer.writeRecord( this.recordBuf );
158
159         this.currBytes = 0;
160
161         if ( entry.isDirectory() )
162             this.currSize = 0;
163         else
164             this.currSize = (int)entry.getSize();
165         }
166
167     /**
168      * Close an entry. This method MUST be called for all file
169      * entries that contain data. The reason is that we must
170      * buffer data written to the stream in order to satisfy
171      * the buffer's record based writes. Thus, there may be
172      * data fragments still being assembled that must be written
173      * to the output stream before this entry is closed and the
174      * next entry written.
175      */

176     public void
177     closeEntry()
178         throws IOException
179         {
180         if ( this.assemLen > 0 )
181             {
182             for ( int i = this.assemLen ; i < this.assemBuf.length ; ++i )
183                 this.assemBuf[i] = 0;
184
185             this.buffer.writeRecord( this.assemBuf );
186
187             this.currBytes += this.assemLen;
188             this.assemLen = 0;
189             }
190
191         if ( this.currBytes < this.currSize )
192             throw new IOException
193                 ( "entry closed at '" + this.currBytes
194                     + "' before the '" + this.currSize
195                     + "' bytes specified in the header were written" );
196         }
197
198     /**
199      * Writes a byte to the current tar archive entry.
200      *
201      * This method simply calls read( byte[], int, int ).
202      *
203      * @param b The byte written.
204      */

205     public void
206     write( int b )
207         throws IOException
208         {
209         this.oneBuf[0] = (byte) b;
210         this.write( this.oneBuf, 0, 1 );
211         }
212
213     /**
214      * Writes bytes to the current tar archive entry.
215      *
216      * This method simply calls read( byte[], int, int ).
217      *
218      * @param wBuf The buffer to write to the archive.
219      * @return The number of bytes read, or -1 at EOF.
220      */

221     public void
222     write( byte[] wBuf )
223         throws IOException
224         {
225         this.write( wBuf, 0, wBuf.length );
226         }
227
228     /**
229      * Writes bytes to the current tar archive entry. This method
230      * is aware of the current entry and will throw an exception if
231      * you attempt to write bytes past the length specified for the
232      * current entry. The method is also (painfully) aware of the
233      * record buffering required by TarBuffer, and manages buffers
234      * that are not a multiple of recordsize in length, including
235      * assembling records from small buffers.
236      *
237      * This method simply calls read( byte[], int, int ).
238      *
239      * @param wBuf The buffer to write to the archive.
240      * @param wOffset The offset in the buffer from which to get bytes.
241      * @param numToWrite The number of bytes to write.
242      */

243     public void
244     write( byte[] wBuf, int wOffset, int numToWrite )
245         throws IOException
246         {
247         if ( (this.currBytes + numToWrite) > this.currSize )
248             throw new IOException
249                 ( "request to write '" + numToWrite
250                     + "' bytes exceeds size in header of '"
251                     + this.currSize + "' bytes" );
252
253         //
254
// We have to deal with assembly!!!
255
// The programmer can be writing little 32 byte chunks for all
256
// we know, and we must assemble complete records for writing.
257
// REVIEW Maybe this should be in TarBuffer? Could that help to
258
// eliminate some of the buffer copying.
259
//
260
if ( this.assemLen > 0 )
261             {
262             if ( (this.assemLen + numToWrite ) >= this.recordBuf.length )
263                 {
264                 int aLen = this.recordBuf.length - this.assemLen;
265
266                 System.arraycopy
267                     ( this.assemBuf, 0, this.recordBuf, 0, this.assemLen );
268
269                 System.arraycopy
270                     ( wBuf, wOffset, this.recordBuf, this.assemLen, aLen );
271
272                 this.buffer.writeRecord( this.recordBuf );
273
274                 this.currBytes += this.recordBuf.length;
275
276                 wOffset += aLen;
277                 numToWrite -= aLen;
278                 this.assemLen = 0;
279                 }
280             else // ( (this.assemLen + numToWrite ) < this.recordBuf.length )
281
{
282                 System.arraycopy
283                     ( wBuf, wOffset, this.assemBuf,
284                         this.assemLen, numToWrite );
285                 wOffset += numToWrite;
286                 this.assemLen += numToWrite;
287                 numToWrite -= numToWrite;
288                 }
289             }
290
291         //
292
// When we get here we have EITHER:
293
// o An empty "assemble" buffer.
294
// o No bytes to write (numToWrite == 0)
295
//
296

297         for ( ; numToWrite > 0 ; )
298             {
299             if ( numToWrite < this.recordBuf.length )
300                 {
301                 System.arraycopy
302                     ( wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite );
303                 this.assemLen += numToWrite;
304                 break;
305                 }
306
307             this.buffer.writeRecord( wBuf, wOffset );
308
309             int num = this.recordBuf.length;
310             this.currBytes += num;
311             numToWrite -= num;
312             wOffset += num;
313             }
314         }
315
316     /**
317      * Write an EOF (end of archive) record to the tar archive.
318      * An EOF record consists of a record of all zeros.
319      */

320     private void
321     writeEOFRecord()
322         throws IOException
323         {
324         for ( int i = 0 ; i < this.recordBuf.length ; ++i )
325             this.recordBuf[i] = 0;
326         this.buffer.writeRecord( this.recordBuf );
327         }
328
329     }
330
331
Popular Tags