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