001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.io;
020
021import java.io.*;
022import java.net.Socket;
023
024import org.apache.commons.logging.Log;
025
026import org.apache.hadoop.classification.InterfaceAudience;
027import org.apache.hadoop.classification.InterfaceStability;
028import org.apache.hadoop.conf.Configuration;
029
030/**
031 * An utility class for I/O related functionality. 
032 */
033@InterfaceAudience.Public
034@InterfaceStability.Evolving
035public class IOUtils {
036
037  /**
038   * Copies from one stream to another.
039   *
040   * @param in InputStrem to read from
041   * @param out OutputStream to write to
042   * @param buffSize the size of the buffer 
043   * @param close whether or not close the InputStream and 
044   * OutputStream at the end. The streams are closed in the finally clause.  
045   */
046  public static void copyBytes(InputStream in, OutputStream out, int buffSize, boolean close) 
047    throws IOException {
048    try {
049      copyBytes(in, out, buffSize);
050      if(close) {
051        out.close();
052        out = null;
053        in.close();
054        in = null;
055      }
056    } finally {
057      if(close) {
058        closeStream(out);
059        closeStream(in);
060      }
061    }
062  }
063  
064  /**
065   * Copies from one stream to another.
066   * 
067   * @param in InputStrem to read from
068   * @param out OutputStream to write to
069   * @param buffSize the size of the buffer 
070   */
071  public static void copyBytes(InputStream in, OutputStream out, int buffSize) 
072    throws IOException {
073    PrintStream ps = out instanceof PrintStream ? (PrintStream)out : null;
074    byte buf[] = new byte[buffSize];
075    int bytesRead = in.read(buf);
076    while (bytesRead >= 0) {
077      out.write(buf, 0, bytesRead);
078      if ((ps != null) && ps.checkError()) {
079        throw new IOException("Unable to write to output stream.");
080      }
081      bytesRead = in.read(buf);
082    }
083  }
084
085  /**
086   * Copies from one stream to another. <strong>closes the input and output streams 
087   * at the end</strong>.
088   *
089   * @param in InputStrem to read from
090   * @param out OutputStream to write to
091   * @param conf the Configuration object 
092   */
093  public static void copyBytes(InputStream in, OutputStream out, Configuration conf)
094    throws IOException {
095    copyBytes(in, out, conf.getInt("io.file.buffer.size", 4096), true);
096  }
097  
098  /**
099   * Copies from one stream to another.
100   *
101   * @param in InputStream to read from
102   * @param out OutputStream to write to
103   * @param conf the Configuration object
104   * @param close whether or not close the InputStream and 
105   * OutputStream at the end. The streams are closed in the finally clause.
106   */
107  public static void copyBytes(InputStream in, OutputStream out, Configuration conf, boolean close)
108    throws IOException {
109    copyBytes(in, out, conf.getInt("io.file.buffer.size", 4096),  close);
110  }
111
112  /**
113   * Copies count bytes from one stream to another.
114   *
115   * @param in InputStream to read from
116   * @param out OutputStream to write to
117   * @param count number of bytes to copy
118   * @param close whether to close the streams
119   * @throws IOException if bytes can not be read or written
120   */
121  public static void copyBytes(InputStream in, OutputStream out, long count,
122      boolean close) throws IOException {
123    byte buf[] = new byte[4096];
124    long bytesRemaining = count;
125    int bytesRead;
126
127    try {
128      while (bytesRemaining > 0) {
129        int bytesToRead = (int)
130          (bytesRemaining < buf.length ? bytesRemaining : buf.length);
131
132        bytesRead = in.read(buf, 0, bytesToRead);
133        if (bytesRead == -1)
134          break;
135
136        out.write(buf, 0, bytesRead);
137        bytesRemaining -= bytesRead;
138      }
139      if (close) {
140        out.close();
141        out = null;
142        in.close();
143        in = null;
144      }
145    } finally {
146      if (close) {
147        closeStream(out);
148        closeStream(in);
149      }
150    }
151  }
152  
153  /**
154   * Reads len bytes in a loop.
155   *
156   * @param in InputStream to read from
157   * @param buf The buffer to fill
158   * @param off offset from the buffer
159   * @param len the length of bytes to read
160   * @throws IOException if it could not read requested number of bytes 
161   * for any reason (including EOF)
162   */
163  public static void readFully(InputStream in, byte buf[],
164      int off, int len) throws IOException {
165    int toRead = len;
166    while (toRead > 0) {
167      int ret = in.read(buf, off, toRead);
168      if (ret < 0) {
169        throw new IOException( "Premature EOF from inputStream");
170      }
171      toRead -= ret;
172      off += ret;
173    }
174  }
175  
176  /**
177   * Similar to readFully(). Skips bytes in a loop.
178   * @param in The InputStream to skip bytes from
179   * @param len number of bytes to skip.
180   * @throws IOException if it could not skip requested number of bytes 
181   * for any reason (including EOF)
182   */
183  public static void skipFully(InputStream in, long len) throws IOException {
184    long amt = len;
185    while (amt > 0) {
186      long ret = in.skip(amt);
187      if (ret == 0) {
188        // skip may return 0 even if we're not at EOF.  Luckily, we can 
189        // use the read() method to figure out if we're at the end.
190        int b = in.read();
191        if (b == -1) {
192          throw new EOFException( "Premature EOF from inputStream after " +
193              "skipping " + (len - amt) + " byte(s).");
194        }
195        ret = 1;
196      }
197      amt -= ret;
198    }
199  }
200  
201  /**
202   * Close the Closeable objects and <b>ignore</b> any {@link IOException} or 
203   * null pointers. Must only be used for cleanup in exception handlers.
204   *
205   * @param log the log to record problems to at debug level. Can be null.
206   * @param closeables the objects to close
207   */
208  public static void cleanup(Log log, java.io.Closeable... closeables) {
209    for (java.io.Closeable c : closeables) {
210      if (c != null) {
211        try {
212          c.close();
213        } catch(IOException e) {
214          if (log != null && log.isDebugEnabled()) {
215            log.debug("Exception in closing " + c, e);
216          }
217        }
218      }
219    }
220  }
221
222  /**
223   * Closes the stream ignoring {@link IOException}.
224   * Must only be called in cleaning up from exception handlers.
225   *
226   * @param stream the Stream to close
227   */
228  public static void closeStream(java.io.Closeable stream) {
229    cleanup(null, stream);
230  }
231  
232  /**
233   * Closes the socket ignoring {@link IOException}
234   *
235   * @param sock the Socket to close
236   */
237  public static void closeSocket(Socket sock) {
238    if (sock != null) {
239      try {
240        sock.close();
241      } catch (IOException ignored) {
242      }
243    }
244  }
245  
246  /**
247   * The /dev/null of OutputStreams.
248   */
249  public static class NullOutputStream extends OutputStream {
250    public void write(byte[] b, int off, int len) throws IOException {
251    }
252
253    public void write(int b) throws IOException {
254    }
255  }  
256}