Home | History | Annotate | Download | only in io
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
      4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      5  *
      6  * This code is free software; you can redistribute it and/or modify it
      7  * under the terms of the GNU General Public License version 2 only, as
      8  * published by the Free Software Foundation.  Oracle designates this
      9  * particular file as subject to the "Classpath" exception as provided
     10  * by Oracle in the LICENSE file that accompanied this code.
     11  *
     12  * This code is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15  * version 2 for more details (a copy is included in the LICENSE file that
     16  * accompanied this code).
     17  *
     18  * You should have received a copy of the GNU General Public License version
     19  * 2 along with this work; if not, write to the Free Software Foundation,
     20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     21  *
     22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     23  * or visit www.oracle.com if you need additional information or have any
     24  * questions.
     25  */
     26 
     27 package java.io;
     28 
     29 import java.nio.channels.FileChannel;
     30 
     31 import dalvik.system.BlockGuard;
     32 import dalvik.system.CloseGuard;
     33 import sun.nio.ch.FileChannelImpl;
     34 import libcore.io.IoBridge;
     35 import libcore.io.IoTracker;
     36 
     37 /**
     38  * A file output stream is an output stream for writing data to a
     39  * <code>File</code> or to a <code>FileDescriptor</code>. Whether or not
     40  * a file is available or may be created depends upon the underlying
     41  * platform.  Some platforms, in particular, allow a file to be opened
     42  * for writing by only one <tt>FileOutputStream</tt> (or other
     43  * file-writing object) at a time.  In such situations the constructors in
     44  * this class will fail if the file involved is already open.
     45  *
     46  * <p><code>FileOutputStream</code> is meant for writing streams of raw bytes
     47  * such as image data. For writing streams of characters, consider using
     48  * <code>FileWriter</code>.
     49  *
     50  * @author  Arthur van Hoff
     51  * @see     java.io.File
     52  * @see     java.io.FileDescriptor
     53  * @see     java.io.FileInputStream
     54  * @see     java.nio.file.Files#newOutputStream
     55  * @since   JDK1.0
     56  */
     57 public
     58 class FileOutputStream extends OutputStream
     59 {
     60     /**
     61      * The system dependent file descriptor.
     62      */
     63     private final FileDescriptor fd;
     64 
     65     /**
     66      * True if the file is opened for append.
     67      */
     68     private final boolean append;
     69 
     70     /**
     71      * The associated channel, initialized lazily.
     72      */
     73     private FileChannel channel;
     74 
     75     private final Object closeLock = new Object();
     76     private volatile boolean closed = false;
     77 
     78     /**
     79      * The path of the referenced file
     80      * (null if the stream is created with a file descriptor)
     81      */
     82     private final String path;
     83 
     84     private final CloseGuard guard = CloseGuard.get();
     85     private final boolean isFdOwner;
     86     private final IoTracker tracker = new IoTracker();
     87 
     88     /**
     89      * Creates a file output stream to write to the file with the
     90      * specified name. A new <code>FileDescriptor</code> object is
     91      * created to represent this file connection.
     92      * <p>
     93      * First, if there is a security manager, its <code>checkWrite</code>
     94      * method is called with <code>name</code> as its argument.
     95      * <p>
     96      * If the file exists but is a directory rather than a regular file, does
     97      * not exist but cannot be created, or cannot be opened for any other
     98      * reason then a <code>FileNotFoundException</code> is thrown.
     99      *
    100      * @param      name   the system-dependent filename
    101      * @exception  FileNotFoundException  if the file exists but is a directory
    102      *                   rather than a regular file, does not exist but cannot
    103      *                   be created, or cannot be opened for any other reason
    104      * @exception  SecurityException  if a security manager exists and its
    105      *               <code>checkWrite</code> method denies write access
    106      *               to the file.
    107      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
    108      */
    109     public FileOutputStream(String name) throws FileNotFoundException {
    110         this(name != null ? new File(name) : null, false);
    111     }
    112 
    113     /**
    114      * Creates a file output stream to write to the file with the specified
    115      * name.  If the second argument is <code>true</code>, then
    116      * bytes will be written to the end of the file rather than the beginning.
    117      * A new <code>FileDescriptor</code> object is created to represent this
    118      * file connection.
    119      * <p>
    120      * First, if there is a security manager, its <code>checkWrite</code>
    121      * method is called with <code>name</code> as its argument.
    122      * <p>
    123      * If the file exists but is a directory rather than a regular file, does
    124      * not exist but cannot be created, or cannot be opened for any other
    125      * reason then a <code>FileNotFoundException</code> is thrown.
    126      *
    127      * @param     name        the system-dependent file name
    128      * @param     append      if <code>true</code>, then bytes will be written
    129      *                   to the end of the file rather than the beginning
    130      * @exception  FileNotFoundException  if the file exists but is a directory
    131      *                   rather than a regular file, does not exist but cannot
    132      *                   be created, or cannot be opened for any other reason.
    133      * @exception  SecurityException  if a security manager exists and its
    134      *               <code>checkWrite</code> method denies write access
    135      *               to the file.
    136      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
    137      * @since     JDK1.1
    138      */
    139     public FileOutputStream(String name, boolean append)
    140         throws FileNotFoundException
    141     {
    142         this(name != null ? new File(name) : null, append);
    143     }
    144 
    145     /**
    146      * Creates a file output stream to write to the file represented by
    147      * the specified <code>File</code> object. A new
    148      * <code>FileDescriptor</code> object is created to represent this
    149      * file connection.
    150      * <p>
    151      * First, if there is a security manager, its <code>checkWrite</code>
    152      * method is called with the path represented by the <code>file</code>
    153      * argument as its argument.
    154      * <p>
    155      * If the file exists but is a directory rather than a regular file, does
    156      * not exist but cannot be created, or cannot be opened for any other
    157      * reason then a <code>FileNotFoundException</code> is thrown.
    158      *
    159      * @param      file               the file to be opened for writing.
    160      * @exception  FileNotFoundException  if the file exists but is a directory
    161      *                   rather than a regular file, does not exist but cannot
    162      *                   be created, or cannot be opened for any other reason
    163      * @exception  SecurityException  if a security manager exists and its
    164      *               <code>checkWrite</code> method denies write access
    165      *               to the file.
    166      * @see        java.io.File#getPath()
    167      * @see        java.lang.SecurityException
    168      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
    169      */
    170     public FileOutputStream(File file) throws FileNotFoundException {
    171         this(file, false);
    172     }
    173 
    174     /**
    175      * Creates a file output stream to write to the file represented by
    176      * the specified <code>File</code> object. If the second argument is
    177      * <code>true</code>, then bytes will be written to the end of the file
    178      * rather than the beginning. A new <code>FileDescriptor</code> object is
    179      * created to represent this file connection.
    180      * <p>
    181      * First, if there is a security manager, its <code>checkWrite</code>
    182      * method is called with the path represented by the <code>file</code>
    183      * argument as its argument.
    184      * <p>
    185      * If the file exists but is a directory rather than a regular file, does
    186      * not exist but cannot be created, or cannot be opened for any other
    187      * reason then a <code>FileNotFoundException</code> is thrown.
    188      *
    189      * @param      file               the file to be opened for writing.
    190      * @param     append      if <code>true</code>, then bytes will be written
    191      *                   to the end of the file rather than the beginning
    192      * @exception  FileNotFoundException  if the file exists but is a directory
    193      *                   rather than a regular file, does not exist but cannot
    194      *                   be created, or cannot be opened for any other reason
    195      * @exception  SecurityException  if a security manager exists and its
    196      *               <code>checkWrite</code> method denies write access
    197      *               to the file.
    198      * @see        java.io.File#getPath()
    199      * @see        java.lang.SecurityException
    200      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
    201      * @since 1.4
    202      */
    203     public FileOutputStream(File file, boolean append)
    204         throws FileNotFoundException
    205     {
    206         String name = (file != null ? file.getPath() : null);
    207         SecurityManager security = System.getSecurityManager();
    208         if (security != null) {
    209             security.checkWrite(name);
    210         }
    211         if (name == null) {
    212             throw new NullPointerException();
    213         }
    214         if (file.isInvalid()) {
    215             throw new FileNotFoundException("Invalid file path");
    216         }
    217         this.fd = new FileDescriptor();
    218         this.append = append;
    219         this.path = name;
    220         this.isFdOwner = true;
    221 
    222         BlockGuard.getThreadPolicy().onWriteToDisk();
    223         open(name, append);
    224         guard.open("close");
    225     }
    226 
    227     /**
    228      * Creates a file output stream to write to the specified file
    229      * descriptor, which represents an existing connection to an actual
    230      * file in the file system.
    231      * <p>
    232      * First, if there is a security manager, its <code>checkWrite</code>
    233      * method is called with the file descriptor <code>fdObj</code>
    234      * argument as its argument.
    235      * <p>
    236      * If <code>fdObj</code> is null then a <code>NullPointerException</code>
    237      * is thrown.
    238      * <p>
    239      * This constructor does not throw an exception if <code>fdObj</code>
    240      * is {@link java.io.FileDescriptor#valid() invalid}.
    241      * However, if the methods are invoked on the resulting stream to attempt
    242      * I/O on the stream, an <code>IOException</code> is thrown.
    243      *
    244      * @param      fdObj   the file descriptor to be opened for writing
    245      * @exception  SecurityException  if a security manager exists and its
    246      *               <code>checkWrite</code> method denies
    247      *               write access to the file descriptor
    248      * @see        java.lang.SecurityManager#checkWrite(java.io.FileDescriptor)
    249      */
    250     public FileOutputStream(FileDescriptor fdObj) {
    251         this(fdObj, false /* isOwner */);
    252     }
    253 
    254     /**
    255      * Internal constructor for {@code FileOutputStream} objects where the file descriptor
    256      * is owned by this tream.
    257      *
    258      * @hide
    259      */
    260     public FileOutputStream(FileDescriptor fdObj, boolean isFdOwner) {
    261         if (fdObj == null) {
    262             throw new NullPointerException("fdObj == null");
    263         }
    264 
    265         this.fd = fdObj;
    266         this.path = null;
    267         this.append = false;
    268         this.isFdOwner = isFdOwner;
    269     }
    270 
    271     /**
    272      * Opens a file, with the specified name, for overwriting or appending.
    273      * @param name name of file to be opened
    274      * @param append whether the file is to be opened in append mode
    275      */
    276     private native void open0(String name, boolean append)
    277         throws FileNotFoundException;
    278 
    279     // wrap native call to allow instrumentation
    280     /**
    281      * Opens a file, with the specified name, for overwriting or appending.
    282      * @param name name of file to be opened
    283      * @param append whether the file is to be opened in append mode
    284      */
    285     private void open(String name, boolean append)
    286         throws FileNotFoundException {
    287         open0(name, append);
    288     }
    289 
    290     /**
    291      * Writes the specified byte to this file output stream. Implements
    292      * the <code>write</code> method of <code>OutputStream</code>.
    293      *
    294      * @param      b   the byte to be written.
    295      * @exception  IOException  if an I/O error occurs.
    296      */
    297     public void write(int b) throws IOException {
    298         write(new byte[] { (byte) b }, 0, 1);
    299     }
    300 
    301     /**
    302      * Writes <code>b.length</code> bytes from the specified byte array
    303      * to this file output stream.
    304      *
    305      * @param      b   the data.
    306      * @exception  IOException  if an I/O error occurs.
    307      */
    308     public void write(byte b[]) throws IOException {
    309         write(b, 0, b.length);
    310     }
    311 
    312     /**
    313      * Writes <code>len</code> bytes from the specified byte array
    314      * starting at offset <code>off</code> to this file output stream.
    315      *
    316      * @param      b     the data.
    317      * @param      off   the start offset in the data.
    318      * @param      len   the number of bytes to write.
    319      * @exception  IOException  if an I/O error occurs.
    320      */
    321     public void write(byte b[], int off, int len) throws IOException {
    322         if (closed && len > 0) {
    323             throw new IOException("Stream Closed");
    324         }
    325         tracker.trackIo(len);
    326         IoBridge.write(fd, b, off, len);
    327     }
    328 
    329     /**
    330      * Closes this file output stream and releases any system resources
    331      * associated with this stream. This file output stream may no longer
    332      * be used for writing bytes.
    333      *
    334      * <p> If this stream has an associated channel then the channel is closed
    335      * as well.
    336      *
    337      * @exception  IOException  if an I/O error occurs.
    338      *
    339      * @revised 1.4
    340      * @spec JSR-51
    341      */
    342     public void close() throws IOException {
    343         synchronized (closeLock) {
    344             if (closed) {
    345                 return;
    346             }
    347             closed = true;
    348         }
    349 
    350         guard.close();
    351 
    352         if (channel != null) {
    353             /*
    354              * Decrement FD use count associated with the channel
    355              * The use count is incremented whenever a new channel
    356              * is obtained from this stream.
    357              */
    358             channel.close();
    359         }
    360 
    361 
    362         if (isFdOwner) {
    363             IoBridge.closeAndSignalBlockedThreads(fd);
    364         }
    365     }
    366 
    367     /**
    368      * Returns the file descriptor associated with this stream.
    369      *
    370      * @return  the <code>FileDescriptor</code> object that represents
    371      *          the connection to the file in the file system being used
    372      *          by this <code>FileOutputStream</code> object.
    373      *
    374      * @exception  IOException  if an I/O error occurs.
    375      * @see        java.io.FileDescriptor
    376      */
    377      public final FileDescriptor getFD()  throws IOException {
    378         if (fd != null) {
    379             return fd;
    380         }
    381         throw new IOException();
    382      }
    383 
    384     /**
    385      * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
    386      * object associated with this file output stream.
    387      *
    388      * <p> The initial {@link java.nio.channels.FileChannel#position()
    389      * position} of the returned channel will be equal to the
    390      * number of bytes written to the file so far unless this stream is in
    391      * append mode, in which case it will be equal to the size of the file.
    392      * Writing bytes to this stream will increment the channel's position
    393      * accordingly.  Changing the channel's position, either explicitly or by
    394      * writing, will change this stream's file position.
    395      *
    396      * @return  the file channel associated with this file output stream
    397      *
    398      * @since 1.4
    399      * @spec JSR-51
    400      */
    401     public FileChannel getChannel() {
    402         synchronized (this) {
    403             if (channel == null) {
    404                 channel = FileChannelImpl.open(fd, path, false, true, append, this);
    405             }
    406             return channel;
    407         }
    408     }
    409 
    410     /**
    411      * Cleans up the connection to the file, and ensures that the
    412      * <code>close</code> method of this file output stream is
    413      * called when there are no more references to this stream.
    414      *
    415      * @exception  IOException  if an I/O error occurs.
    416      * @see        java.io.FileInputStream#close()
    417      */
    418     protected void finalize() throws IOException {
    419         if (guard != null) {
    420             guard.warnIfOpen();
    421         }
    422 
    423         if (fd != null) {
    424             if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
    425                 flush();
    426             } else {
    427                 close();
    428             }
    429         }
    430     }
    431 }
    432