KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > axis > attachments > DimeDelimitedInputStream


1 /*
2  * Copyright (C) The Apache Software Foundation. All rights reserved.
3  *
4  * This software is published under the terms of the Apache Software License
5  * version 1.1, a copy of which has been included with this distribution in
6  * the docs/licenses/apache-1.1.txt file.
7  */

8
9 package org.jboss.axis.attachments;
10
11
12 import org.jboss.axis.utils.Messages;
13 import org.jboss.logging.Logger;
14
15 import java.io.IOException JavaDoc;
16
17
18 /**
19  * This class takes the input stream and turns it multiple streams.
20  * DIME version 0 format
21  * <pre>
22  * 0 1 2 3
23  * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
24  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---
25  * | VERSION |B|E|C| TYPE_T| OPT_T | OPTIONS_LENGTH | A
26  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
27  * | ID_LENGTH | TYPE_LENGTH | Always present 12 bytes
28  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ even on chunked data.
29  * | DATA_LENGTH | V
30  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---
31  * | /
32  * / OPTIONS + PADDING /
33  * / (absent for version 0) |
34  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35  * | /
36  * / ID + PADDING /
37  * / |
38  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39  * | /
40  * / TYPE + PADDING /
41  * / |
42  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43  * | /
44  * / DATA + PADDING /
45  * / |
46  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47  * </pre>
48  *
49  * @author Rick Rineholt
50  */

51 public class DimeDelimitedInputStream extends java.io.FilterInputStream JavaDoc
52 {
53    private static Logger log = Logger.getLogger(DimeDelimitedInputStream.class.getName());
54
55    java.io.InputStream JavaDoc is = null; //The source input stream.
56
volatile boolean closed = true; //The stream has been closed.
57
boolean theEnd = false; //There are no more streams left.
58
boolean moreChunks = false; //More chunks are a coming!
59
boolean MB = false; //First part of the stream. MUST be SOAP.
60
boolean ME = false; //Last part of stream.
61
DimeTypeNameFormat tnf = null;
62    String JavaDoc type = null;
63    String JavaDoc id = null;
64    long recordLength = 0L; //length of the record.
65
long bytesRead = 0; //How many bytes of the record have been read.
66
int dataPadLength = 0; //How many pad bytes there are.
67
private static byte[] trash = new byte[4];
68    protected int streamNo = 0;
69    protected IOException JavaDoc streamInError = null;
70
71    protected static int streamCount = 0; //number of streams produced.
72

73    protected static synchronized int newStreamNo()
74    {
75       log.debug(Messages.getMessage("streamNo", "" + (streamCount + 1)));
76       return ++streamCount;
77    }
78
79    static boolean isDebugEnabled = false;
80
81    /**
82     * Gets the next stream. From the previous using new buffer reading size.
83     *
84     * @return the dime delmited stream. Null if there are no more streams.
85     */

86    synchronized DimeDelimitedInputStream getNextStream()
87            throws IOException JavaDoc
88    {
89       if (null != streamInError) throw streamInError;
90       if (theEnd) return null;
91       if (bytesRead < recordLength || moreChunks) //Stream must be read in succession
92
throw new RuntimeException JavaDoc(Messages.getMessage("attach.dimeReadFullyError"));
93       dataPadLength -= readPad(dataPadLength);
94
95       //Create an new dime stream that comes after this one.
96
return new DimeDelimitedInputStream(this.is);
97    }
98
99    /**
100     * Create a new dime stream;
101     */

102    DimeDelimitedInputStream(java.io.InputStream JavaDoc is) throws IOException JavaDoc
103    {
104       super(null); //we handle everything so this is not necessary, don't won't to hang on to a reference.
105
isDebugEnabled = log.isDebugEnabled();
106       streamNo = newStreamNo();
107       closed = false;
108       this.is = is;
109       readHeader(false);
110    }
111
112    private final int readPad(final int size) throws IOException JavaDoc
113    {
114       if (0 == size) return 0;
115       int read = readFromStream(trash, 0, size);
116
117       if (size != read)
118       {
119          streamInError = new IOException JavaDoc(Messages.getMessage("attach.dimeNotPaddedCorrectly"));
120          throw streamInError;
121       }
122       return read;
123    }
124
125    private final int readFromStream(final byte[] b) throws IOException JavaDoc
126    {
127       return readFromStream(b, 0, b.length);
128    }
129
130    private final int readFromStream(final byte[] b,
131                                     final int start, final int length)
132            throws IOException JavaDoc
133    {
134       if (length == 0) return 0;
135
136       int br = 0;
137       int brTotal = 0;
138
139       do
140       {
141          try
142          {
143             br = is.read(b, brTotal + start, length - brTotal);
144          }
145          catch (IOException JavaDoc e)
146          {
147             streamInError = e;
148             throw e;
149          }
150          if (br > 0) brTotal += br;
151       }
152       while (br > -1 && brTotal < length);
153
154       return br > -1 ? brTotal : br;
155    }
156
157    /**
158     * Get the id for this stream part.
159     *
160     * @return the id;
161     */

162    public String JavaDoc getContentId()
163    {
164       return id;
165    }
166
167    /**
168     * Read from the boundary delimited stream.
169     *
170     * @param b is the array to read into.
171     * @param off is the offset
172     * @return the number of bytes read. -1 if endof stream.
173     */

174    public DimeTypeNameFormat getDimeTypeNameFormat()
175    {
176       return tnf;
177    }
178
179    /**
180     * get type.
181     *
182     * @param b is the array to read into.
183     * @param off is the offset
184     * @return the number of bytes read. -1 if endof stream.
185     */

186
187    public String JavaDoc getType()
188    {
189       return type;
190    }
191
192    /**
193     * Read from the DIME stream.
194     *
195     * @param b is the array to read into.
196     * @param off is the offset
197     * @return the number of bytes read. -1 if endof stream.
198     */

199    public synchronized int read(byte[] b, final int off,
200                                 final int len) throws IOException JavaDoc
201    {
202
203       if (closed)
204       {
205          dataPadLength -= readPad(dataPadLength);
206          throw new IOException JavaDoc(Messages.getMessage("streamClosed"));
207       }
208       return _read(b, off, len);
209    }
210
211    protected int _read(byte[] b, final int off, final int len)
212            throws IOException JavaDoc
213    {
214       if (len < 0)
215          throw new IllegalArgumentException JavaDoc
216                  (Messages.getMessage("attach.readLengthError",
217                          "" + len));
218
219       if (off < 0)
220          throw new IllegalArgumentException JavaDoc
221                  (Messages.getMessage("attach.readOffsetError",
222                          "" + off));
223       if (b == null)
224          throw new IllegalArgumentException JavaDoc
225                  (Messages.getMessage("attach.readArrayNullError"));
226       if (b.length < off + len)
227          throw new IllegalArgumentException JavaDoc
228                  (Messages.getMessage("attach.readArraySizeError",
229                          "" + b.length, "" + len, "" + off));
230
231       if (null != streamInError) throw streamInError;
232
233       if (0 == len) return 0; //quick.
234

235       if (recordLength == 0 && bytesRead == 0 && !moreChunks)
236       {
237          ++bytesRead; //odd case no data to read -- give back 0 next time -1;
238
if (ME)
239          {
240             finalClose();
241          }
242          return 0;
243       }
244       if (bytesRead >= recordLength && !moreChunks)
245       {
246          dataPadLength -= readPad(dataPadLength);
247          if (ME)
248          {
249             finalClose();
250          }
251          return -1;
252       }
253
254       int totalbytesread = 0;
255       int bytes2read = 0;
256
257       do
258       {
259          if (bytesRead >= recordLength && moreChunks)
260             readHeader(true);
261
262          bytes2read = (int)Math.min(recordLength - bytesRead,
263                  (long)len - totalbytesread);
264          bytes2read = (int)Math.min(recordLength - bytesRead,
265                  (long)len - totalbytesread);
266          try
267          {
268             bytes2read = is.read(b, off + totalbytesread,
269                     bytes2read);
270          }
271          catch (IOException JavaDoc e)
272          {
273             streamInError = e;
274             throw e;
275          }
276
277          if (0 < bytes2read)
278          {
279             totalbytesread += bytes2read;
280             bytesRead += bytes2read;
281          }
282
283       }
284       while (bytes2read > -1 && totalbytesread < len &&
285               (bytesRead < recordLength || moreChunks));
286
287       if (0 > bytes2read)
288       {
289          if (moreChunks)
290          {
291             streamInError = new IOException JavaDoc(Messages.getMessage("attach.DimeStreamError0"));
292             throw streamInError;
293          }
294          if (bytesRead < recordLength)
295          {
296             streamInError = new IOException JavaDoc(Messages.getMessage
297                     ("attach.DimeStreamError1",
298                             "" + (recordLength - bytesRead)));
299             throw streamInError;
300          }
301          if (!ME)
302          {
303             streamInError = new IOException JavaDoc(Messages.getMessage("attach.DimeStreamError0"));
304             throw streamInError;
305          }
306          //in theory the last chunk of data should also have been padded, but lets be tolerant of that.
307
dataPadLength = 0;
308
309       }
310       else if (bytesRead >= recordLength)
311       {
312          //get rid of pading.
313
try
314          {
315             dataPadLength -= readPad(dataPadLength);
316          }
317          catch (IOException JavaDoc e)
318          {
319             //in theory the last chunk of data should also have been padded, but lets be tolerant of that.
320
if (!ME)
321                throw e;
322             else
323             {
324                dataPadLength = 0;
325                streamInError = null;
326             }
327          }
328       }
329
330       if (bytesRead >= recordLength && ME)
331       {
332          finalClose();
333       }
334
335       return totalbytesread >= 0 ? totalbytesread : -1;
336    }
337
338    void readHeader(boolean isChunk) throws IOException JavaDoc
339    {
340
341       bytesRead = 0; //How many bytes of the record have been read.
342
if (isChunk)
343       {
344          if (!moreChunks)
345             throw new RuntimeException JavaDoc(Messages.getMessage("attach.DimeStreamError2"));
346          dataPadLength -= readPad(dataPadLength); //Just incase it was left over.
347
}
348
349       byte[] header = new byte[12];
350
351       if (header.length != readFromStream(header))
352       {
353          streamInError = new IOException JavaDoc(Messages.getMessage("attach.DimeStreamError3",
354                  "" + header.length));
355          throw streamInError;
356       }
357
358       //VERSION
359
byte version = (byte)((header[0] >>> 3) & 0x1f);
360
361       if (version > DimeMultiPart.CURRENT_VERSION)
362       {
363          streamInError = new IOException JavaDoc(Messages.getMessage("attach.DimeStreamError4",
364                  "" + version,
365                  "" + DimeMultiPart.CURRENT_VERSION));
366          throw streamInError;
367       }
368
369       //B, E, C
370
MB = 0 != (0x4 & header[0]);
371       ME = 0 != (0x2 & header[0]);
372       moreChunks = 0 != (0x1 & header[0]);
373
374       //TYPE_T
375
if (!isChunk)
376          tnf = DimeTypeNameFormat.parseByte((byte)((header[1] >>> 4) & (byte)0xf));
377
378       //OPTIONS_LENGTH
379
int optionsLength =
380               ((((int)header[2]) << 8) & 0xff00) | ((int)header[3]);
381
382       //ID_LENGTH
383
int idLength =
384               ((((int)header[4]) << 8) & 0xff00) | ((int)header[5]);
385
386       //TYPE_LENGTH
387
int typeLength = ((((int)header[6]) << 8) & 0xff00)
388               | ((int)header[7]);
389
390       //DATA_LENGTH
391
recordLength = ((((long)header[8]) << 24) & 0xff000000L) |
392               ((((long)header[9]) << 16) & 0xff0000L) |
393               ((((long)header[10]) << 8) & 0xff00L) |
394               ((long)header[11] & 0xffL);
395
396       //OPTIONS + PADDING
397

398       if (0 != optionsLength)
399       {
400          byte[] optBytes = new byte[optionsLength];
401
402          if (optionsLength != readFromStream(optBytes))
403          {
404             streamInError = new IOException JavaDoc(Messages.getMessage("attach.DimeStreamError5",
405                     "" + optionsLength));
406             throw streamInError;
407          }
408          optBytes = null; //Yup throw it away, don't know anything about options.
409

410          int pad = DimeBodyPart.dimePadding(optionsLength);
411
412          if (pad != readFromStream(header, 0, pad))
413          {
414             streamInError = new IOException JavaDoc(Messages.getMessage("attach.DimeStreamError7"));
415             throw streamInError;
416          }
417       }
418
419       // ID + PADDING
420
if (0 < idLength)
421       {
422          byte[] idBytes = new byte[idLength];
423
424          if (idLength != readFromStream(idBytes))
425          {
426             streamInError = new IOException JavaDoc(Messages.getMessage("attach.DimeStreamError8"));
427             throw streamInError;
428          }
429          if (idLength != 0 && !isChunk)
430          {
431             id = new String JavaDoc(idBytes);
432          }
433          int pad = DimeBodyPart.dimePadding(idLength);
434
435          if (pad != readFromStream(header, 0, pad))
436          {
437             streamInError = new IOException JavaDoc(Messages.getMessage("attach.DimeStreamError9"));
438             throw streamInError;
439          }
440       }
441
442       //TYPE + PADDING
443
if (0 < typeLength)
444       {
445          byte[] typeBytes = new byte[typeLength];
446
447          if (typeLength != readFromStream(typeBytes))
448          {
449             streamInError = new IOException JavaDoc(Messages.getMessage("attach.DimeStreamError10"));
450             throw streamInError;
451          }
452          if (typeLength != 0 && !isChunk)
453          {
454             type = new String JavaDoc(typeBytes);
455          }
456          int pad = DimeBodyPart.dimePadding(typeLength);
457
458          if (pad != readFromStream(header, 0, pad))
459          {
460             streamInError = new IOException JavaDoc(Messages.getMessage("attach.DimeStreamError11"));
461
462             throw streamInError;
463          }
464       }
465       log.debug("MB:" + MB + ", ME:" + ME + ", CF:" + moreChunks +
466               "Option length:" + optionsLength +
467               ", ID length:" + idLength +
468               ", typeLength:" + typeLength + ", TYPE_T:" + tnf);
469       log.debug("id:\"" + id + "\"");
470       log.debug("type:\"" + type + "\"");
471       log.debug("recordlength:\"" + recordLength + "\"");
472
473       dataPadLength = DimeBodyPart.dimePadding(recordLength);
474    }
475
476    /**
477     * Read from the delimited stream.
478     *
479     * @param b is the array to read into. Read as much as possible
480     * into the size of this array.
481     * @return the number of bytes read. -1 if endof stream.
482     */

483    public int read(byte[] b) throws IOException JavaDoc
484    {
485       return read(b, 0, b.length);
486    }
487
488    /**
489     * Read from the boundary delimited stream.
490     *
491     * @return The byte read, or -1 if endof stream.
492     */

493
494    public int read() throws IOException JavaDoc
495    {
496       byte[] b = new byte[1];
497       int read = read(b, 0, 1);
498
499       if (read < 0)
500          return -1;
501       else
502          return b[0];
503    }
504
505    /**
506     * Closes the stream.
507     */

508    public void close() throws IOException JavaDoc
509    {
510       synchronized (this)
511       {
512          if (closed) return;
513          closed = true; //mark it closed.
514
}
515       log.debug(Messages.getMessage("bStreamClosed", "" + streamNo));
516       if (bytesRead < recordLength || moreChunks)
517       {
518          //We need get this off the stream.
519
//Easy way to flush through the stream;
520
byte[] readrest = new byte[1024 * 16];
521          int bread = 0;
522
523          do
524          {
525             bread = _read(readrest, 0, readrest.length);//should also close the orginal stream.
526
}
527          while (bread > -1);
528       }
529       dataPadLength -= readPad(dataPadLength);
530    }
531
532    /**
533     * mark the stream.
534     * This is not supported.
535     */

536    public void mark(int readlimit)
537    {//do nothing
538
}
539
540    /**
541     * reset the stream.
542     * This is not supported.
543     */

544    public void reset() throws IOException JavaDoc
545    {
546       streamInError = new IOException JavaDoc(Messages.getMessage("attach.bounday.mns"));
547       throw streamInError;
548    }
549
550    /**
551     * markSupported
552     * return false;
553     */

554    public boolean markSupported()
555    {
556       return false;
557    }
558
559    public synchronized int available() throws IOException JavaDoc
560    {
561       if (null != streamInError) throw streamInError;
562       int chunkAvail = (int)Math.min((long)
563               Integer.MAX_VALUE, recordLength - bytesRead);
564
565       int streamAvail = 0;
566
567       try
568       {
569          streamAvail = is.available();
570       }
571       catch (IOException JavaDoc e)
572       {
573          streamInError = e;
574          throw e;
575       }
576
577       if (chunkAvail == 0 && moreChunks && (12 + dataPadLength)
578               <= streamAvail)
579       {
580          dataPadLength -= readPad(dataPadLength);
581          readHeader(true);
582          return available();
583       }
584       return Math.min(streamAvail, chunkAvail);
585    }
586
587    protected void finalClose() throws IOException JavaDoc
588    {
589       try
590       {
591          theEnd = true;
592          if (null != is) is.close();
593       }
594       finally
595       {
596          is = null;
597       }
598    }
599 }
600
Popular Tags