Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/axis/attachments/BoundaryDelimitedStream.java


1   /*
2    * Copyright 2001-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.axis.attachments;
17  
18  import org.apache.axis.components.logger.LogFactory;
19  import org.apache.axis.utils.Messages;
20  import org.apache.commons.logging.Log;
21  
22  
23  /**
24   * This class takes the input stream and turns it multiple streams.
25   *
26   * @author Rick Rineholt
27   */
28  public class BoundaryDelimitedStream extends java.io.FilterInputStream {
29  
30      /** The <code>Log</code> that this class should log all events to. */
31      protected static Log log =
32              LogFactory.getLog(BoundaryDelimitedStream.class.getName());
33  
34      protected byte[] boundary = null;
35  
36      /** The boundary length. */
37      int boundaryLen = 0;
38  
39      /** The boundary length plus crlf. */
40      int boundaryBufLen = 0;
41  
42      /** The source input stream. */
43      java.io.InputStream is = null;
44  
45      /** The stream has been closed. */
46      boolean closed = true;
47  
48      /** eof has been detected. */
49      boolean eos = false;
50  
51      /** There are no more streams left. */
52      boolean theEnd = false;
53  
54      /** Minimum to read at one time. */
55      int readbufsz = 0;
56  
57      /** The buffer we are reading. */
58      byte[] readbuf = null;
59  
60      /** Where we have read so far in the stream. */
61      int readBufPos = 0;
62  
63      /** The number of bytes in array. */
64      int readBufEnd = 0;
65  
66      /** Field BOUNDARY_NOT_FOUND.           */
67      protected static final int BOUNDARY_NOT_FOUND = Integer.MAX_VALUE;
68  
69      // Where in the stream a boundary is located.
70  
71      /** Field boundaryPos.           */
72      int boundaryPos = BOUNDARY_NOT_FOUND;
73  
74      /** The number of streams produced. */
75      static int streamCount = 0;
76  
77      /**
78       * Signal that a new stream has been created.
79       *
80       * @return
81       */
82      protected static synchronized int newStreamNo() {
83  
84          log.debug(Messages.getMessage("streamNo", "" + (streamCount + 1)));
85  
86          return ++streamCount;
87      }
88  
89      /** Field streamNo.           */
90      protected int streamNo = -1;    // Keeps track of stream
91  
92      /** Field isDebugEnabled.           */
93      static boolean isDebugEnabled = false;
94  
95      /**
96       * Gets the next stream. From the previous using the same buffer size to
97       * read.
98       *
99       * @return the boundary delmited stream, null if there are no more streams.
100      * @throws java.io.IOException if there was an error loading the data for
101      *              the next stream
102      */
103     public synchronized BoundaryDelimitedStream getNextStream()  throws java.io.IOException  {
104         return getNextStream(readbufsz);
105     }
106 
107     /**
108      * Gets the next stream. From the previous using  new buffer reading size.
109      *
110      * @param readbufsz
111      * @return the boundary delmited stream, null if there are no more streams.
112      * @throws java.io.IOException if there was an error loading the data for
113      *              the next stream
114      */
115     protected synchronized BoundaryDelimitedStream getNextStream(
116             int readbufsz)  throws java.io.IOException  {
117 
118         BoundaryDelimitedStream ret = null;
119 
120         if (!theEnd) {
121 
122             // Create an new boundary stream  that comes after this one.
123             ret = new BoundaryDelimitedStream(this, readbufsz);
124         }
125 
126         return ret;
127     }
128 
129     /**
130      *  Constructor to create the next stream from the previous one.
131      *
132      * @param prev      the previous stream
133      * @param readbufsz how many bytes to make the read buffer
134      * @throws java.io.IOException if there was a problem reading data from
135      *              <code>prev</code>
136      */
137     protected BoundaryDelimitedStream(BoundaryDelimitedStream prev,
138                                       int readbufsz)
139             throws java.io.IOException
140     {
141         super(null);
142 
143         streamNo = newStreamNo();
144         boundary = prev.boundary;
145         boundaryLen = prev.boundaryLen;
146         boundaryBufLen = prev.boundaryBufLen;
147         skip = prev.skip;
148         is = prev.is;
149         closed = false;    // The new one is not closed.
150         eos = false;    // Its not at th EOS.
151         readbufsz = prev.readbufsz;
152         readbuf = prev.readbuf;
153 
154         // Move past the old boundary.
155         readBufPos = prev.readBufPos + boundaryBufLen;
156         readBufEnd = prev.readBufEnd;
157 
158         // find the new boundary.
159         boundaryPos = boundaryPosition(readbuf, readBufPos, readBufEnd);
160         prev.theEnd = theEnd;      // The stream.
161     }
162 
163     /**
164      * Create a new boundary stream.
165      *
166      * @param is
167      * @param boundary is the boundary that separates the individual streams.
168      * @param readbufsz lets you have some control over the amount of buffering.
169      *   by buffering you can some effiency in searching.
170      *
171      * @throws org.apache.axis.AxisFault
172      */
173     BoundaryDelimitedStream(
174             java.io.InputStream is, byte[] boundary, int readbufsz)
175             throws org.apache.axis.AxisFault {
176 
177         // super (is);
178         super(null);    // we handle everything so this is not necessary, don't won't to hang on to a reference.
179 
180         isDebugEnabled = log.isDebugEnabled();
181         streamNo = newStreamNo();
182         closed = false;
183         this.is = is;
184 
185         // Copy the boundary array to make certain it is never altered.
186         this.boundary = new byte[boundary.length];
187 
188         System.arraycopy(boundary, 0, this.boundary, 0, boundary.length);
189 
190         this.boundaryLen = this.boundary.length;
191         this.boundaryBufLen = boundaryLen + 2;
192 
193         // allways leave room for at least a 2x boundary
194         // Most mime boundaries are 40 bytes or so.
195         this.readbufsz = Math.max((boundaryBufLen) * 2, readbufsz);
196     }
197 
198     private final int readFromStream(final byte[] b)
199             throws java.io.IOException {
200         return readFromStream(b, 0, b.length);
201     }
202 
203     private final int readFromStream(
204             final byte[] b, final int start, final int length)
205             throws java.io.IOException {
206 
207         int minRead = Math.max(boundaryBufLen * 2, length);
208 
209         minRead = Math.min(minRead, length - start);
210 
211         int br = 0;
212         int brTotal = 0;
213 
214         do {
215             br = is.read(b, brTotal + start, length - brTotal);
216 
217             if (br > 0) {
218                 brTotal += br;
219             }
220         } while ((br > -1) && (brTotal < minRead));
221 
222         return (brTotal != 0)
223                 ? brTotal
224                 : br;
225     }
226 
227     /**
228      * Read from the boundary delimited stream.
229      * @param b is the array to read into.
230      * @param off is the offset
231      * @param len
232      * @return the number of bytes read. -1 if endof stream.
233      *
234      * @throws java.io.IOException
235      */
236     public synchronized int read(byte[] b, final int off, final int len)
237             throws java.io.IOException {
238 
239         if (closed) {
240             throw new java.io.IOException(Messages.getMessage("streamClosed"));
241         }
242 
243         if (eos) {
244             return -1;
245         }
246 
247         if (readbuf == null) {    // Allocate the buffer.
248             readbuf = new byte[Math.max(len, readbufsz)];
249             readBufEnd = readFromStream(readbuf);
250 
251             if (readBufEnd < 0) {
252                 readbuf = null;
253                 closed = true;
254                 finalClose();
255 
256                 throw new java.io.IOException(
257                         Messages.getMessage("eosBeforeMarker"));
258             }
259 
260             readBufPos = 0;
261 
262             // Finds the boundary pos.
263             boundaryPos = boundaryPosition(readbuf, 0, readBufEnd);
264         }
265 
266         int bwritten = 0;    // Number of bytes written.
267 
268         // read and copy bytes in.
269         do {                                // Always allow to have a boundary length left in the buffer.
270             int bcopy = Math.min(readBufEnd - readBufPos - boundaryBufLen,
271                     len - bwritten);
272 
273             // never go past the boundary.
274             bcopy = Math.min(bcopy, boundaryPos - readBufPos);
275 
276             if (bcopy > 0) {
277                 System.arraycopy(readbuf, readBufPos, b, off + bwritten, bcopy);
278 
279                 bwritten += bcopy;
280                 readBufPos += bcopy;
281             }
282 
283             if (readBufPos == boundaryPos) {
284                 eos = true;                 // hit the boundary so it the end of the stream.
285 
286                 log.debug(Messages.getMessage("atEOS", "" + streamNo));
287             } else if (bwritten < len) {    // need to get more data.
288                 byte[] dstbuf = readbuf;
289 
290                 if (readbuf.length < len) {
291                     dstbuf = new byte[len];
292                 }
293 
294                 int movecnt = readBufEnd - readBufPos;
295 
296                 // copy what was left over.
297                 System.arraycopy(readbuf, readBufPos, dstbuf, 0, movecnt);
298 
299                 // Read in the new data.
300                 int readcnt = readFromStream(dstbuf, movecnt,
301                         dstbuf.length - movecnt);
302 
303                 if (readcnt < 0) {
304                     readbuf = null;
305                     closed = true;
306                     finalClose();
307 
308                     throw new java.io.IOException(
309                             Messages.getMessage("eosBeforeMarker"));
310                 }
311 
312                 readBufEnd = readcnt + movecnt;
313                 readbuf = dstbuf;
314                 readBufPos = 0;             // start at the begining.
315 
316                 // just move the boundary by what we moved
317                 if (BOUNDARY_NOT_FOUND != boundaryPos) {
318                     boundaryPos -= movecnt;
319                 } else {
320                     boundaryPos = boundaryPosition(
321                             readbuf, readBufPos,
322                             readBufEnd);        // See if the boundary is now there.
323                 }
324             }
325         }
326 
327                 // read till we get the amount or the stream is finished.
328         while (!eos && (bwritten < len));
329 
330         if (log.isDebugEnabled()) {
331             if (bwritten > 0) {
332                 byte tb[] = new byte[bwritten];
333 
334                 System.arraycopy(b, off, tb, 0, bwritten);
335                 log.debug(Messages.getMessage("readBStream",
336                         new String[]{"" + bwritten,
337                                      "" + streamNo,
338                                      new String(tb)}));
339             }
340         }
341 
342         if (eos && theEnd) {
343             readbuf = null;    // dealloc even in Java.
344         }
345 
346         return bwritten;
347     }
348 
349     /**
350      * Read from the boundary delimited stream.
351      * @param b is the array to read into. Read as much as possible
352      *   into the size of this array.
353      * @return the number of bytes read. -1 if endof stream.
354      *
355      * @throws java.io.IOException
356      */
357     public int read(byte[] b) throws java.io.IOException {
358         return read(b, 0, b.length);
359     }
360 
361     /**
362      * Read from the boundary delimited stream.
363      * @return The byte read, or -1 if endof stream.
364      *
365      * @throws java.io.IOException
366      */
367     public int read() throws java.io.IOException {
368 
369         byte[] b = new byte[1];    // quick and dirty. //for now
370         int read = read(b);
371 
372         if (read < 0) {
373             return -1;
374         } else {
375             return b[0]&0xff;
376         }
377     }
378 
379     /**
380      * Closes the stream.
381      *
382      * @throws java.io.IOException
383      */
384     public synchronized void close() throws java.io.IOException {
385 
386         if (closed) {
387             return;
388         }
389 
390         log.debug(Messages.getMessage("bStreamClosed", "" + streamNo));
391 
392         closed = true;    // mark it closed.
393 
394         if (!eos) {    // We need get this off the stream.
395 
396             // Easy way to flush through the stream;
397             byte[] readrest = new byte[1024 * 16];
398             int bread = 0;
399 
400             do {
401                 bread = read(readrest);
402             } while (bread > -1);
403         }
404     }
405 
406     /**
407      * mark the stream.
408      * This is not supported.
409      *
410      * @param readlimit
411      */
412     public void mark(int readlimit) {
413 
414         // do nothing
415     }
416 
417     /**
418      * reset the stream.
419      * This is not supported.
420      *
421      * @throws java.io.IOException
422      */
423     public void reset() throws java.io.IOException {
424         throw new java.io.IOException(
425                 Messages.getMessage("attach.bounday.mns"));
426     }
427 
428     /**
429      * markSupported
430      * return false;
431      *
432      * @return
433      */
434     public boolean markSupported() {
435         return false;
436     }
437 
438     public int available() throws java.io.IOException {
439 
440         int bcopy = readBufEnd - readBufPos - boundaryBufLen;
441 
442         // never go past the boundary.
443         bcopy = Math.min(bcopy, boundaryPos - readBufPos);
444 
445         return Math.max(0, bcopy);
446     }
447 
448     /**
449      * Read from the boundary delimited stream.
450      *
451      * @param searchbuf buffer to read from
452      * @param start     starting index
453      * @param end       ending index
454      * @return The position of the boundary. Detects the end of the source stream.
455      * @throws java.io.IOException if there was an error manipulating the
456      *              underlying stream
457      */
458     protected int boundaryPosition(byte[] searchbuf, int start, int end) throws java.io.IOException  {
459 
460         int foundAt = boundarySearch(searchbuf, start, end);
461 
462         // First find the boundary marker
463         if (BOUNDARY_NOT_FOUND != foundAt) {    // Something was found.
464             if (foundAt + boundaryLen + 2 > end) {
465                 foundAt = BOUNDARY_NOT_FOUND;
466             } else {
467 
468                 // If the marker has a "--" at the end then this is the last boundary.
469                 if ((searchbuf[foundAt + boundaryLen] == '-')
470                         && (searchbuf[foundAt + boundaryLen + 1] == '-')) {
471                     finalClose();
472                 } else if ((searchbuf[foundAt + boundaryLen] != 13)
473                         || (searchbuf[foundAt + boundaryLen + 1] != 10)) {
474 
475                     // If there really was no crlf at then end then this is not a boundary.
476                     foundAt = BOUNDARY_NOT_FOUND;
477                 }
478             }
479         }
480 
481         return foundAt;
482     }
483 
484     /* The below uses a standard textbook Boyer-Moore pattern search. */
485 
486     private int[] skip = null;
487 
488     private int boundarySearch(final byte[] text, final int start,
489                                final int end) {
490 
491         // log.debug(">>>>" + start + "," + end);
492         int i = 0, j = 0, k = 0;
493 
494         if (boundaryLen > (end - start)) {
495             return BOUNDARY_NOT_FOUND;
496         }
497 
498         if (null == skip) {
499             skip = new int[256];
500 
501             java.util.Arrays.fill(skip, boundaryLen);
502 
503             for (k = 0; k < boundaryLen - 1; k++) {
504                 skip[boundary[k]] = boundaryLen - k - 1;
505             }
506         }
507 
508         for (k = start + boundaryLen - 1; k < end;
509              k += skip[text[k] & (0xff)]) {
510 
511             // log.debug(">>>>" + k);
512             // printarry(text, k-boundaryLen+1, end);
513             try {
514                 for (j = boundaryLen - 1, i = k;
515                      (j >= 0) && (text[i] == boundary[j]); j--) {
516                     i--;
517                 }
518             } catch (ArrayIndexOutOfBoundsException e) {
519                 StringBuffer sb = new StringBuffer();
520                 sb.append(
521                         ">>>"
522                         + e);    // rr temporary till a boundary issue is resolved.
523                 sb.append("start=" + start);
524                 sb.append("k=" + k);
525                 sb.append("text.length=" + text.length);
526                 sb.append("i=" + i);
527                 sb.append("boundary.length=" + boundary.length);
528                 sb.append("j=" + j);
529                 sb.append("end=" + end);
530                 log.warn(Messages.getMessage("exception01",sb.toString()));
531                 throw e;
532             }
533 
534             if (j == (-1)) {
535                 return i + 1;
536             }
537         }
538 
539         // log.debug(">>>> not found" );
540         return BOUNDARY_NOT_FOUND;
541     }
542 
543     /**
544      * Close the underlying stream and remove all references to it.
545      *
546      * @throws java.io.IOException if the stream could not be closed
547      */
548     protected void finalClose() throws java.io.IOException {
549       if(theEnd) return;
550       theEnd= true;
551       is.close();
552       is= null;
553     }
554 
555     /**
556      * Method printarry
557      *
558      * @param b
559      * @param start
560      * @param end
561      */
562     public static void printarry(byte[] b, int start, int end) {
563 
564         if (log.isDebugEnabled()) {
565             byte tb[] = new byte[end - start];
566 
567             System.arraycopy(b, start, tb, 0, end - start);
568             log.debug("\"" + new String(tb) + "\"");
569         }
570     }
571 }