View Javadoc

1   /*
2    * Copyright (c) 2005 The University of Reading
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    * 1. Redistributions of source code must retain the above copyright
9    *    notice, this list of conditions and the following disclaimer.
10   * 2. Redistributions in binary form must reproduce the above copyright
11   *    notice, this list of conditions and the following disclaimer in the
12   *    documentation and/or other materials provided with the distribution.
13   * 3. Neither the name of the University of Reading, nor the names of the
14   *    authors or contributors may be used to endorse or promote products
15   *    derived from this software without specific prior written permission.
16   * 
17   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20   * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27   */
28  
29  package uk.ac.rdg.resc.jstyx.client;
30  
31  import java.io.InputStream;
32  import java.io.IOException;
33  
34  import org.apache.mina.common.ByteBuffer;
35  
36  import uk.ac.rdg.resc.jstyx.StyxException;
37  
38  /***
39   * InputStream for reading from a file on a Styx server
40   * @todo Implement read(byte[]), read(byte[], off, len), skip() in most
41   * efficient way possible
42   * @todo Also make into a Channel?
43   * 
44   * @author Jon Blower
45   * $Revision: 385 $
46   * $Date: 2005-09-01 18:12:11 +0100 (Thu, 01 Sep 2005) $
47   * $Log$
48   * Revision 1.2  2005/09/01 17:12:09  jonblower
49   * Changes to Input and Output stream code
50   *
51   * Revision 1.1  2005/08/31 17:03:18  jonblower
52   * Renamed "StyxFile*putStream*" to "CStyxFile*putStream*" for consistency with CStyxFile class
53   *
54   * Revision 1.8  2005/06/22 17:07:29  jonblower
55   * Added read(byte[]) method
56   * 
57   * Revision 1.7  2005/05/12 07:40:52  jonblower
58   * CStyxFile.close() no longer throws a StyxException
59   * 
60   * Revision 1.6  2005/05/05 07:09:06  jonblower
61   * Improved comments
62   * 
63   * Revision 1.5  2005/05/04 16:25:49  jonblower
64   * Improved parameter naming in constructor
65   * 
66   * Revision 1.4  2005/03/22 10:19:52  jonblower
67   * Fixed problem with ByteBuffer leak in StyxMessageDecoder and CStyxFileInputStream
68   * 
69   * Revision 1.3  2005/03/19 21:46:58  jonblower
70   * Further fixes relating to releasing ByteBuffers
71   * 
72   * Revision 1.2  2005/03/16 17:55:53  jonblower
73   * Replaced use of java.nio.ByteBuffer with MINA's ByteBuffer to minimise copying of buffers
74   * 
75   * Revision 1.1.1.1  2005/02/16 18:58:19  jonblower
76   * Initial import
77   */
78  public class CStyxFileInputStream extends InputStream
79  {
80      
81      private CStyxFile file; // The file from which we are reading
82      private ByteBuffer buf; // Buffer for storing the results of the last read
83      private long offset;    // The current position in the file
84      private boolean eof;
85      private boolean closeConnectionWhenCloseStream; // If this is true, we shall close the underlying
86          // StyxConnection when this stream is closed (this is normally set when
87          // getting an input stream through the StyxURLConnection class)
88      
89      /***
90       * Creates a CStyxFileInputStream for reading the given file.
91       * @param file The file to read from
92       * @param closeConnectionWhenCloseStream If this is true, we shall close the underlying
93       * StyxConnection when this stream is closed (this is normally set when
94       * getting an input stream through the StyxURLConnection class)
95       */
96      public CStyxFileInputStream(CStyxFile file, boolean closeConnectionWhenCloseStream)
97      {
98          if (file == null)
99          {
100             throw new NullPointerException("file cannot be null");
101         }
102         this.file = file;
103         this.buf = null;
104         this.offset = 0;
105         this.eof = false;
106         this.closeConnectionWhenCloseStream = closeConnectionWhenCloseStream;
107     }
108     
109     public CStyxFileInputStream(CStyxFile file)
110     {
111         this(file, false);
112     }
113     
114     public int read() throws IOException
115     {
116         if (this.eof)
117         {
118             return -1;
119         }
120         // First check to see if there are any bytes left in the buffer
121         if (this.buf != null && this.buf.hasRemaining())
122         {
123             return buf.get() & 0xff;  // Makes sure byte is always between 0 and 255
124         }
125         else
126         {
127             // We have read everything that's in the buffer. 
128             // We need to read another block of data.
129             try
130             {
131                 // Release the previous read buffer if we have one
132                 if (this.buf != null)
133                 {
134                     this.buf.release();
135                 }
136                 // Read a new chunk of data from the file
137                 this.buf = this.file.read(this.offset);
138                 if (this.buf.remaining() > 0)
139                 {
140                     // Update the offset
141                     this.offset += this.buf.remaining();
142                     // Return the first byte in the buffer
143                     return buf.get() & 0xff; // Makes sure byte is always between 0 and 255
144                 }
145                 else
146                 {
147                     // We have reached the end of the file
148                     this.eof = true;
149                     // We don't need the buffer any more
150                     this.buf.release();
151                     return -1;
152                 }
153             }
154             catch(StyxException e)
155             {
156                 throw new IOException(e.getMessage());
157             }
158         }
159     }
160     
161     public int read(byte b[], int off, int len) throws IOException
162     {
163 	if (b == null)
164         {
165 	    throw new NullPointerException();
166 	}
167         else if ((off < 0) || (off > b.length) || (len < 0) ||
168 		   ((off + len) > b.length) || ((off + len) < 0))
169         {
170 	    throw new IndexOutOfBoundsException();
171 	}
172         else if (len == 0)
173         {
174 	    return 0;
175 	}
176         
177         if (this.eof)
178         {
179             return -1;
180         }
181         
182         // First check to see if there are any bytes left in the buffer
183         if (this.buf != null && this.buf.hasRemaining())
184         {
185             // Read the data into the provided array
186             int bytesToGet = Math.min(this.buf.remaining(), len);
187             this.buf.get(b, off, bytesToGet);
188             return bytesToGet;
189         }
190         else
191         {
192             // We have read everything that's in the buffer. 
193             // We need to read another block of data.
194             try
195             {
196                 // Release the previous read buffer if we have one
197                 if (this.buf != null)
198                 {
199                     this.buf.release();
200                 }
201                 // Read a new chunk of data from the file
202                 this.buf = this.file.read(this.offset);
203                 if (this.buf.remaining() > 0)
204                 {
205                     // Update the offset
206                     this.offset += this.buf.remaining();
207                     // Read the data into the provided array
208                     int bytesToGet = Math.min(this.buf.remaining(), len);
209                     this.buf.get(b, off, bytesToGet);
210                     return bytesToGet;
211                 }
212                 else
213                 {
214                     // We have reached the end of the file
215                     this.eof = true;
216                     // We don't need the buffer any more
217                     this.buf.release();
218                     return -1;
219                 }
220             }
221             catch(StyxException e)
222             {
223                 throw new IOException(e.getMessage());
224             }
225         }
226     }
227     
228     /***
229      * @return the number of bytes that can be read without blocking
230      */
231     public int available() throws IOException
232     {
233         return (this.buf == null) ? 0 : this.buf.remaining();
234     }
235     
236     /***
237      * Closes the stream (clunks the underlying file). If this InputStream was
238      * created with <code>closeConnectionWhenCloseStream = true</code>, this will
239      * also close the StyxConnection.
240      */
241     public void close() throws IOException
242     {
243         this.file.close();
244         this.offset = 0;
245         this.eof = false;
246         if (this.closeConnectionWhenCloseStream)
247         {
248             this.file.getConnection().close();
249         }
250         this.buf = null;
251     }
252 }