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 }