001/**
002 * 
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019package org.apache.hadoop.fs;
020
021import java.io.*;
022import java.nio.ByteBuffer;
023import java.util.EnumSet;
024
025import org.apache.hadoop.classification.InterfaceAudience;
026import org.apache.hadoop.classification.InterfaceStability;
027import org.apache.hadoop.classification.MapRModified;
028import org.apache.hadoop.io.ByteBufferPool;
029import org.apache.hadoop.fs.ByteBufferUtil;
030import org.apache.hadoop.util.IdentityHashStore;
031
032/** Utility that wraps a {@link FSInputStream} in a {@link DataInputStream}
033 * and buffers input through a {@link BufferedInputStream}. */
034@InterfaceAudience.Public
035@InterfaceStability.Stable
036public class FSDataInputStream extends DataInputStream
037    implements Seekable, PositionedReadable, Closeable, 
038      ByteBufferReadable, HasFileDescriptor, CanSetDropBehind, CanSetReadahead,
039      HasEnhancedByteBufferAccess {
040  /**
041   * Map ByteBuffers that we have handed out to readers to ByteBufferPool 
042   * objects
043   */
044  private final IdentityHashStore<ByteBuffer, ByteBufferPool>
045    extendedReadBuffers
046      = new IdentityHashStore<ByteBuffer, ByteBufferPool>(0);
047
048  /**
049   * Type of file advise to be passed on to the underlying file system. This
050   * information can be used to make optimizations such as reclaiming buffers
051   * for files that are no longer needed by the application, etc.
052   */
053  @MapRModified
054  public static enum FadviseType {
055    FILE_DONTNEED,
056    FILE_RANDOM,
057    FILE_SEQUENTIAL;
058  }
059
060  public FSDataInputStream(InputStream in)
061    throws IOException {
062    super(in);
063    if( !(in instanceof Seekable) || !(in instanceof PositionedReadable) ) {
064      throw new IllegalArgumentException(
065          "In is not an instance of Seekable or PositionedReadable");
066    }
067  }
068  
069  /**
070   * Seek to the given offset.
071   *
072   * @param desired offset to seek to
073   */
074  @Override
075  public synchronized void seek(long desired) throws IOException {
076    ((Seekable)in).seek(desired);
077  }
078
079  /**
080   * Get the current position in the input stream.
081   *
082   * @return current position in the input stream
083   */
084  @Override
085  public long getPos() throws IOException {
086    return ((Seekable)in).getPos();
087  }
088  
089  /**
090   * Read bytes from the given position in the stream to the given buffer.
091   *
092   * @param position  position in the input stream to seek
093   * @param buffer    buffer into which data is read
094   * @param offset    offset into the buffer in which data is written
095   * @param length    maximum number of bytes to read
096   * @return total number of bytes read into the buffer, or <code>-1</code>
097   *         if there is no more data because the end of the stream has been
098   *         reached
099   */
100  @Override
101  public int read(long position, byte[] buffer, int offset, int length)
102    throws IOException {
103    return ((PositionedReadable)in).read(position, buffer, offset, length);
104  }
105
106  /**
107   * Read bytes from the given position in the stream to the given buffer.
108   * Continues to read until <code>length</code> bytes have been read.
109   *
110   * @param position  position in the input stream to seek
111   * @param buffer    buffer into which data is read
112   * @param offset    offset into the buffer in which data is written
113   * @param length    the number of bytes to read
114   * @throws EOFException If the end of stream is reached while reading.
115   *                      If an exception is thrown an undetermined number
116   *                      of bytes in the buffer may have been written. 
117   */
118  @Override
119  public void readFully(long position, byte[] buffer, int offset, int length)
120    throws IOException {
121    ((PositionedReadable)in).readFully(position, buffer, offset, length);
122  }
123  
124  /**
125   * See {@link #readFully(long, byte[], int, int)}.
126   */
127  @Override
128  public void readFully(long position, byte[] buffer)
129    throws IOException {
130    ((PositionedReadable)in).readFully(position, buffer, 0, buffer.length);
131  }
132  
133  /**
134   * Seek to the given position on an alternate copy of the data.
135   *
136   * @param  targetPos  position to seek to
137   * @return true if a new source is found, false otherwise
138   */
139  @Override
140  public boolean seekToNewSource(long targetPos) throws IOException {
141    return ((Seekable)in).seekToNewSource(targetPos); 
142  }
143  
144  /**
145   * Get a reference to the wrapped input stream. Used by unit tests.
146   *
147   * @return the underlying input stream
148   */
149  @InterfaceAudience.LimitedPrivate({"HDFS"})
150  public InputStream getWrappedStream() {
151    return in;
152  }
153
154  @Override
155  public int read(ByteBuffer buf) throws IOException {
156    if (in instanceof ByteBufferReadable) {
157      return ((ByteBufferReadable)in).read(buf);
158    }
159
160    throw new UnsupportedOperationException("Byte-buffer read unsupported by input stream");
161  }
162
163  @Override
164  public FileDescriptor getFileDescriptor() throws IOException {
165    if (in instanceof HasFileDescriptor) {
166      return ((HasFileDescriptor) in).getFileDescriptor();
167    } else if (in instanceof FileInputStream) {
168      return ((FileInputStream) in).getFD();
169    } else {
170      return null;
171    }
172  }
173
174  @Override
175  public void setReadahead(Long readahead)
176      throws IOException, UnsupportedOperationException {
177    try {
178      ((CanSetReadahead)in).setReadahead(readahead);
179    } catch (ClassCastException e) {
180      throw new UnsupportedOperationException(
181          "this stream does not support setting the readahead " +
182          "caching strategy.");
183    }
184  }
185
186  @Override
187  public void setDropBehind(Boolean dropBehind)
188      throws IOException, UnsupportedOperationException {
189    try {
190      ((CanSetDropBehind)in).setDropBehind(dropBehind);
191    } catch (ClassCastException e) {
192      throw new UnsupportedOperationException("this stream does not " +
193          "support setting the drop-behind caching setting.");
194    }
195  }
196
197  @Override
198  public ByteBuffer read(ByteBufferPool bufferPool, int maxLength,
199      EnumSet<ReadOption> opts) 
200          throws IOException, UnsupportedOperationException {
201    try {
202      return ((HasEnhancedByteBufferAccess)in).read(bufferPool,
203          maxLength, opts);
204    }
205    catch (ClassCastException e) {
206      ByteBuffer buffer = ByteBufferUtil.
207          fallbackRead(this, bufferPool, maxLength);
208      if (buffer != null) {
209        extendedReadBuffers.put(buffer, bufferPool);
210      }
211      return buffer;
212    }
213  }
214
215  private static final EnumSet<ReadOption> EMPTY_READ_OPTIONS_SET =
216      EnumSet.noneOf(ReadOption.class);
217
218  final public ByteBuffer read(ByteBufferPool bufferPool, int maxLength)
219          throws IOException, UnsupportedOperationException {
220    return read(bufferPool, maxLength, EMPTY_READ_OPTIONS_SET);
221  }
222  
223  @Override
224  public void releaseBuffer(ByteBuffer buffer) {
225    try {
226      ((HasEnhancedByteBufferAccess)in).releaseBuffer(buffer);
227    }
228    catch (ClassCastException e) {
229      ByteBufferPool bufferPool = extendedReadBuffers.remove( buffer);
230      if (bufferPool == null) {
231        throw new IllegalArgumentException("tried to release a buffer " +
232            "that was not created by this stream.");
233      }
234      bufferPool.putBuffer(buffer);
235    }
236  }
237
238  /**
239   * Specifies the kind of advise to provide for this stream and the file
240   * offsets to which they apply.
241   *
242   * The default implementation does nothing. Sub classes can override this
243   * behavior.
244   *
245   * @param type advise type
246   * @param offset starting file offset
247   * @param count number of bytes starting from the offset
248   */
249  @MapRModified
250  public void adviseFile(FadviseType type, long offset, long count)
251    throws IOException {
252  }
253
254  /**
255   * Returns the file length.
256   *
257   * @return file length
258   */
259  @MapRModified
260  public long getFileLength() throws IOException {
261    throw new UnsupportedOperationException();
262  }
263
264  /**
265   * Returns the file id as string.
266   *
267   * @return file id as string
268   */
269  @MapRModified
270  public String getFidStr() {
271    throw new UnsupportedOperationException();
272  }
273
274  /**
275   * Returns the server IPs in which the file is stored. Each IP is stored in a
276   * long. For e.g., the first 4 bytes can be used to store the IP in
277   * hexadecimal format and the last 4 bytes to store the port number.
278   *
279   * @return array of server IPs in which the file is stored
280   */
281  @MapRModified
282  public long[] getFidServers() {
283    throw new UnsupportedOperationException();
284  }
285
286  /**
287   * Returns the file chunk size.
288   *
289   * @return file chunk size
290   */
291  @MapRModified
292  public long getChunkSize() {
293    throw new UnsupportedOperationException();
294  }
295}