View Javadoc

    1   /* ReplayInputStream
    2    *
    3    * $Id: ReplayInputStream.java 5026 2007-03-28 02:48:47Z gojomo $
    4    *
    5    * Created on Sep 24, 2003
    6    *
    7    * Copyright (C) 2003 Internet Archive.
    8    *
    9    * This file is part of the Heritrix web crawler (crawler.archive.org).
    10   *
    11   * Heritrix is free software; you can redistribute it and/or modify
    12   * it under the terms of the GNU Lesser Public License as published by
    13   * the Free Software Foundation; either version 2.1 of the License, or
    14   * any later version.
    15   *
    16   * Heritrix is distributed in the hope that it will be useful,
    17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    19   * GNU Lesser Public License for more details.
    20   *
    21   * You should have received a copy of the GNU Lesser Public License
    22   * along with Heritrix; if not, write to the Free Software
    23   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    24   */
    25  package org.archive.io;
    26
    27  import java.io.File;
    28  import java.io.IOException;
    29  import java.io.OutputStream;
    30
    31
    32  /***
    33   * Replays the bytes recorded from a RecordingInputStream or
    34   * RecordingOutputStream.
    35   *
    36   * This InputStream supports mark and reset.
    37   *
    38   * @author gojomo
    39   */
    40  public class ReplayInputStream extends SeekInputStream
    41  {
    42      private BufferedSeekInputStream diskStream;
    43      private byte[] buffer;
    44      private long position;
    45
    46      /***
    47       * Total size of stream content.
    48       *
    49       * Size of data to replay.
    50       */
    51      private long size = -1;
    52
    53      /***
    54       * Where the response body starts, if marked
    55       */
    56      protected long responseBodyStart = -1;
    57
    58
    59      /***
    60       * Constructor.
    61       *
    62       * @param buffer Buffer to read from.
    63       * @param size Size of data to replay.
    64       * @param responseBodyStart Start of the response body.
    65       * @param backingFilename Backing file that sits behind the buffer.  If
    66       * <code>size<code> > than buffer then we go to backing file to read
    67       * data that is beyond buffer.length.
    68       *
    69       * @throws IOException If we fail to open an input stream on
    70       * backing file.
    71       */
    72      public ReplayInputStream(byte[] buffer, long size, long responseBodyStart,
    73              String backingFilename)
    74          throws IOException
    75      {
    76          this(buffer, size, backingFilename);
    77          this.responseBodyStart = responseBodyStart;
    78      }
    79
    80      /***
    81       * Constructor.
    82       *
    83       * @param buffer Buffer to read from.
    84       * @param size Size of data to replay.
    85       * @param backingFilename Backing file that sits behind the buffer.  If
    86       * <code>size<code> > than buffer then we go to backing file to read
    87       * data that is beyond buffer.length.
    88       * @throws IOException If we fail to open an input stream on
    89       * backing file.
    90       */
    91      public ReplayInputStream(byte[] buffer, long size, String backingFilename)
    92          throws IOException
    93      {
    94          this.buffer = buffer;
    95          this.size = size;
    96          if (size > buffer.length) {
    97              RandomAccessInputStream rais = new RandomAccessInputStream(
    98                      new File(backingFilename));
    99              diskStream = new BufferedSeekInputStream(rais, 4096);
    100         }
    101     }
    102
    103     public long setToResponseBodyStart() throws IOException {
    104         position(responseBodyStart);
    105         return this.position;
    106     }
    107
    108
    109     /* (non-Javadoc)
    110      * @see java.io.InputStream#read()
    111      */
    112     public int read() throws IOException {
    113         if (position == size) {
    114             return -1; // EOF
    115         }
    116         if (position < buffer.length) {
    117             // Convert to unsigned int.
    118             int c = buffer[(int) position] & 0xFF;
    119             position++;
    120             return c;
    121         }
    122         int c = diskStream.read();
    123         if (c >= 0) {
    124             position++;
    125         }
    126         return c;
    127     }
    128
    129     /*
    130      * (non-Javadoc)
    131      *
    132      * @see java.io.InputStream#read(byte[], int, int)
    133      */
    134     public int read(byte[] b, int off, int len) throws IOException {
    135         if (position == size) {
    136             return -1; // EOF
    137         }
    138         if (position < buffer.length) {
    139             int toCopy = (int)Math.min(size - position,
    140                 Math.min(len, buffer.length - position));
    141             System.arraycopy(buffer, (int)position, b, off, toCopy);
    142             if (toCopy > 0) {
    143                 position += toCopy;
    144             }
    145             return toCopy;
    146         }
    147         // into disk zone
    148         int read = diskStream.read(b,off,len);
    149         if(read>0) {
    150             position += read;
    151         }
    152         return read;
    153     }
    154
    155     public void readFullyTo(OutputStream os) throws IOException {
    156         byte[] buf = new byte[4096];
    157         int c = read(buf);
    158         while (c != -1) {
    159             os.write(buf,0,c);
    160             c = read(buf);
    161         }
    162     }
    163
    164     /*
    165      * Like 'readFullyTo', but only reads the header-part.
    166      * Starts from the beginning each time it is called.
    167      */
    168     public void readHeaderTo(OutputStream os) throws IOException {
    169         position = 0;
    170         byte[] buf = new byte[(int)responseBodyStart];
    171         int c = read(buf,0,buf.length);
    172         if(c != -1) {
    173             os.write(buf,0,c);
    174         }
    175     }
    176
    177     /*
    178      * Like 'readFullyTo', but only reads the content-part.
    179      */
    180     public void readContentTo(OutputStream os) throws IOException {
    181         setToResponseBodyStart();
    182         byte[] buf = new byte[4096];
    183         int c = read(buf);
    184         while (c != -1) {
    185             os.write(buf,0,c);
    186             c = read(buf);
    187         }
    188     }
    189
    190     public void readContentTo(OutputStream os, int maxSize) throws IOException {
    191         setToResponseBodyStart();
    192         byte[] buf = new byte[4096];
    193         int c = read(buf);
    194         int tot = 0;
    195         while (c != -1 && tot < maxSize) {
    196             os.write(buf,0,c);
    197             c = read(buf);
    198             tot += c;
    199         }
    200     }
    201
    202     /* (non-Javadoc)
    203      * @see java.io.InputStream#close()
    204      */
    205     public void close() throws IOException {
    206         super.close();
    207         if(diskStream != null) {
    208             diskStream.close();
    209         }
    210     }
    211
    212     /***
    213      * Total size of stream content.
    214      * @return Returns the size.
    215      */
    216     public long getSize()
    217     {
    218         return size;
    219     }
    220
    221     /***
    222      * Total size of header.
    223      * @return the size of the header.
    224      */
    225     public long getHeaderSize()
    226     {
    227         return responseBodyStart;
    228     }
    229
    230     /***
    231      * Total size of content.
    232      * @return the size of the content.
    233      */
    234     public long getContentSize()
    235     {
    236         return size - responseBodyStart;
    237     }
    238
    239     /***
    240      * @return Amount THEORETICALLY remaining (TODO: Its not theoretical
    241      * seemingly.  The class implemetentation depends on it being exact).
    242      */
    243     public long remaining() {
    244         return size - position;
    245     }
    246
    247
    248     /***
    249      * Reposition the stream.
    250      *
    251      * @param p  the new position for this stream
    252      * @throws IOException  if an IO error occurs
    253      */
    254     public void position(long p) throws IOException {
    255         if (p < 0) {
    256             throw new IOException("Negative seek offset.");
    257         }
    258         if (p > size) {
    259             throw new IOException("Desired position exceeds size.");
    260         }
    261         if (p < buffer.length) {
    262             // Only seek file if necessary
    263             if (position > buffer.length) {
    264                 diskStream.position(0);
    265             }
    266         } else {
    267             diskStream.position(p - buffer.length);
    268         }
    269         this.position = p;
    270     }
    271
    272
    273     public long position() throws IOException {
    274         return position;
    275     }
    276 }