FileDocCategorySizeDatePackage
BoundaryDelimitedStream.javaAPI DocApache Axis 1.417142Sat Apr 22 18:57:28 BST 2006org.apache.axis.attachments

BoundaryDelimitedStream

public class BoundaryDelimitedStream extends FilterInputStream
This class takes the input stream and turns it multiple streams.
author
Rick Rineholt

Fields Summary
protected static Log
log
The Log that this class should log all events to.
protected byte[]
boundary
int
boundaryLen
The boundary length.
int
boundaryBufLen
The boundary length plus crlf.
InputStream
is
The source input stream.
boolean
closed
The stream has been closed.
boolean
eos
eof has been detected.
boolean
theEnd
There are no more streams left.
int
readbufsz
Minimum to read at one time.
byte[]
readbuf
The buffer we are reading.
int
readBufPos
Where we have read so far in the stream.
int
readBufEnd
The number of bytes in array.
protected static final int
BOUNDARY_NOT_FOUND
Field BOUNDARY_NOT_FOUND.
int
boundaryPos
Field boundaryPos.
static int
streamCount
The number of streams produced.
protected int
streamNo
Field streamNo.
static boolean
isDebugEnabled
Field isDebugEnabled.
private int[]
skip
Constructors Summary
protected BoundaryDelimitedStream(BoundaryDelimitedStream prev, int readbufsz)
Constructor to create the next stream from the previous one.

param
prev the previous stream
param
readbufsz how many bytes to make the read buffer
throws
java.io.IOException if there was a problem reading data from prev

        super(null);

        streamNo = newStreamNo();
        boundary = prev.boundary;
        boundaryLen = prev.boundaryLen;
        boundaryBufLen = prev.boundaryBufLen;
        skip = prev.skip;
        is = prev.is;
        closed = false;    // The new one is not closed.
        eos = false;    // Its not at th EOS.
        readbufsz = prev.readbufsz;
        readbuf = prev.readbuf;

        // Move past the old boundary.
        readBufPos = prev.readBufPos + boundaryBufLen;
        readBufEnd = prev.readBufEnd;

        // find the new boundary.
        boundaryPos = boundaryPosition(readbuf, readBufPos, readBufEnd);
        prev.theEnd = theEnd;      // The stream.
    
BoundaryDelimitedStream(InputStream is, byte[] boundary, int readbufsz)
Create a new boundary stream.

param
is
param
boundary is the boundary that separates the individual streams.
param
readbufsz lets you have some control over the amount of buffering. by buffering you can some effiency in searching.
throws
org.apache.axis.AxisFault


        // super (is);
        super(null);    // we handle everything so this is not necessary, don't won't to hang on to a reference.

        isDebugEnabled = log.isDebugEnabled();
        streamNo = newStreamNo();
        closed = false;
        this.is = is;

        // Copy the boundary array to make certain it is never altered.
        this.boundary = new byte[boundary.length];

        System.arraycopy(boundary, 0, this.boundary, 0, boundary.length);

        this.boundaryLen = this.boundary.length;
        this.boundaryBufLen = boundaryLen + 2;

        // allways leave room for at least a 2x boundary
        // Most mime boundaries are 40 bytes or so.
        this.readbufsz = Math.max((boundaryBufLen) * 2, readbufsz);
    
Methods Summary
public intavailable()


        int bcopy = readBufEnd - readBufPos - boundaryBufLen;

        // never go past the boundary.
        bcopy = Math.min(bcopy, boundaryPos - readBufPos);

        return Math.max(0, bcopy);
    
protected intboundaryPosition(byte[] searchbuf, int start, int end)
Read from the boundary delimited stream.

param
searchbuf buffer to read from
param
start starting index
param
end ending index
return
The position of the boundary. Detects the end of the source stream.
throws
java.io.IOException if there was an error manipulating the underlying stream


        int foundAt = boundarySearch(searchbuf, start, end);

        // First find the boundary marker
        if (BOUNDARY_NOT_FOUND != foundAt) {    // Something was found.
            if (foundAt + boundaryLen + 2 > end) {
                foundAt = BOUNDARY_NOT_FOUND;
            } else {

                // If the marker has a "--" at the end then this is the last boundary.
                if ((searchbuf[foundAt + boundaryLen] == '-")
                        && (searchbuf[foundAt + boundaryLen + 1] == '-")) {
                    finalClose();
                } else if ((searchbuf[foundAt + boundaryLen] != 13)
                        || (searchbuf[foundAt + boundaryLen + 1] != 10)) {

                    // If there really was no crlf at then end then this is not a boundary.
                    foundAt = BOUNDARY_NOT_FOUND;
                }
            }
        }

        return foundAt;
    
private intboundarySearch(byte[] text, int start, int end)


           
                                  

        // log.debug(">>>>" + start + "," + end);
        int i = 0, j = 0, k = 0;

        if (boundaryLen > (end - start)) {
            return BOUNDARY_NOT_FOUND;
        }

        if (null == skip) {
            skip = new int[256];

            java.util.Arrays.fill(skip, boundaryLen);

            for (k = 0; k < boundaryLen - 1; k++) {
                skip[boundary[k]] = boundaryLen - k - 1;
            }
        }

        for (k = start + boundaryLen - 1; k < end;
             k += skip[text[k] & (0xff)]) {

            // log.debug(">>>>" + k);
            // printarry(text, k-boundaryLen+1, end);
            try {
                for (j = boundaryLen - 1, i = k;
                     (j >= 0) && (text[i] == boundary[j]); j--) {
                    i--;
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                StringBuffer sb = new StringBuffer();
                sb.append(
                        ">>>"
                        + e);    // rr temporary till a boundary issue is resolved.
                sb.append("start=" + start);
                sb.append("k=" + k);
                sb.append("text.length=" + text.length);
                sb.append("i=" + i);
                sb.append("boundary.length=" + boundary.length);
                sb.append("j=" + j);
                sb.append("end=" + end);
                log.warn(Messages.getMessage("exception01",sb.toString()));
                throw e;
            }

            if (j == (-1)) {
                return i + 1;
            }
        }

        // log.debug(">>>> not found" );
        return BOUNDARY_NOT_FOUND;
    
public synchronized voidclose()
Closes the stream.

throws
java.io.IOException


        if (closed) {
            return;
        }

        log.debug(Messages.getMessage("bStreamClosed", "" + streamNo));

        closed = true;    // mark it closed.

        if (!eos) {    // We need get this off the stream.

            // Easy way to flush through the stream;
            byte[] readrest = new byte[1024 * 16];
            int bread = 0;

            do {
                bread = read(readrest);
            } while (bread > -1);
        }
    
protected voidfinalClose()
Close the underlying stream and remove all references to it.

throws
java.io.IOException if the stream could not be closed

      if(theEnd) return;
      theEnd= true;
      is.close();
      is= null;
    
public synchronized org.apache.axis.attachments.BoundaryDelimitedStreamgetNextStream()
Gets the next stream. From the previous using the same buffer size to read.

return
the boundary delmited stream, null if there are no more streams.
throws
java.io.IOException if there was an error loading the data for the next stream


                                                              
            
        return getNextStream(readbufsz);
    
protected synchronized org.apache.axis.attachments.BoundaryDelimitedStreamgetNextStream(int readbufsz)
Gets the next stream. From the previous using new buffer reading size.

param
readbufsz
return
the boundary delmited stream, null if there are no more streams.
throws
java.io.IOException if there was an error loading the data for the next stream


        BoundaryDelimitedStream ret = null;

        if (!theEnd) {

            // Create an new boundary stream  that comes after this one.
            ret = new BoundaryDelimitedStream(this, readbufsz);
        }

        return ret;
    
public voidmark(int readlimit)
mark the stream. This is not supported.

param
readlimit


        // do nothing
    
public booleanmarkSupported()
markSupported return false;

return

        return false;
    
protected static synchronized intnewStreamNo()
Signal that a new stream has been created.

return


                  
         

        log.debug(Messages.getMessage("streamNo", "" + (streamCount + 1)));

        return ++streamCount;
    
public static voidprintarry(byte[] b, int start, int end)
Method printarry

param
b
param
start
param
end


        if (log.isDebugEnabled()) {
            byte tb[] = new byte[end - start];

            System.arraycopy(b, start, tb, 0, end - start);
            log.debug("\"" + new String(tb) + "\"");
        }
    
public intread()
Read from the boundary delimited stream.

return
The byte read, or -1 if endof stream.
throws
java.io.IOException


        byte[] b = new byte[1];    // quick and dirty. //for now
        int read = read(b);

        if (read < 0) {
            return -1;
        } else {
            return b[0]&0xff;
        }
    
public synchronized intread(byte[] b, int off, int len)
Read from the boundary delimited stream.

param
b is the array to read into.
param
off is the offset
param
len
return
the number of bytes read. -1 if endof stream.
throws
java.io.IOException


        if (closed) {
            throw new java.io.IOException(Messages.getMessage("streamClosed"));
        }

        if (eos) {
            return -1;
        }

        if (readbuf == null) {    // Allocate the buffer.
            readbuf = new byte[Math.max(len, readbufsz)];
            readBufEnd = readFromStream(readbuf);

            if (readBufEnd < 0) {
                readbuf = null;
                closed = true;
                finalClose();

                throw new java.io.IOException(
                        Messages.getMessage("eosBeforeMarker"));
            }

            readBufPos = 0;

            // Finds the boundary pos.
            boundaryPos = boundaryPosition(readbuf, 0, readBufEnd);
        }

        int bwritten = 0;    // Number of bytes written.

        // read and copy bytes in.
        do {                                // Always allow to have a boundary length left in the buffer.
            int bcopy = Math.min(readBufEnd - readBufPos - boundaryBufLen,
                    len - bwritten);

            // never go past the boundary.
            bcopy = Math.min(bcopy, boundaryPos - readBufPos);

            if (bcopy > 0) {
                System.arraycopy(readbuf, readBufPos, b, off + bwritten, bcopy);

                bwritten += bcopy;
                readBufPos += bcopy;
            }

            if (readBufPos == boundaryPos) {
                eos = true;                 // hit the boundary so it the end of the stream.

                log.debug(Messages.getMessage("atEOS", "" + streamNo));
            } else if (bwritten < len) {    // need to get more data.
                byte[] dstbuf = readbuf;

                if (readbuf.length < len) {
                    dstbuf = new byte[len];
                }

                int movecnt = readBufEnd - readBufPos;

                // copy what was left over.
                System.arraycopy(readbuf, readBufPos, dstbuf, 0, movecnt);

                // Read in the new data.
                int readcnt = readFromStream(dstbuf, movecnt,
                        dstbuf.length - movecnt);

                if (readcnt < 0) {
                    readbuf = null;
                    closed = true;
                    finalClose();

                    throw new java.io.IOException(
                            Messages.getMessage("eosBeforeMarker"));
                }

                readBufEnd = readcnt + movecnt;
                readbuf = dstbuf;
                readBufPos = 0;             // start at the begining.

                // just move the boundary by what we moved
                if (BOUNDARY_NOT_FOUND != boundaryPos) {
                    boundaryPos -= movecnt;
                } else {
                    boundaryPos = boundaryPosition(
                            readbuf, readBufPos,
                            readBufEnd);        // See if the boundary is now there.
                }
            }
        }

                // read till we get the amount or the stream is finished.
        while (!eos && (bwritten < len));

        if (log.isDebugEnabled()) {
            if (bwritten > 0) {
                byte tb[] = new byte[bwritten];

                System.arraycopy(b, off, tb, 0, bwritten);
                log.debug(Messages.getMessage("readBStream",
                        new String[]{"" + bwritten,
                                     "" + streamNo,
                                     new String(tb)}));
            }
        }

        if (eos && theEnd) {
            readbuf = null;    // dealloc even in Java.
        }

        return bwritten;
    
public intread(byte[] b)
Read from the boundary delimited stream.

param
b is the array to read into. Read as much as possible into the size of this array.
return
the number of bytes read. -1 if endof stream.
throws
java.io.IOException

        return read(b, 0, b.length);
    
private final intreadFromStream(byte[] b)

        return readFromStream(b, 0, b.length);
    
private final intreadFromStream(byte[] b, int start, int length)


        int minRead = Math.max(boundaryBufLen * 2, length);

        minRead = Math.min(minRead, length - start);

        int br = 0;
        int brTotal = 0;

        do {
            br = is.read(b, brTotal + start, length - brTotal);

            if (br > 0) {
                brTotal += br;
            }
        } while ((br > -1) && (brTotal < minRead));

        return (brTotal != 0)
                ? brTotal
                : br;
    
public voidreset()
reset the stream. This is not supported.

throws
java.io.IOException

        throw new java.io.IOException(
                Messages.getMessage("attach.bounday.mns"));