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     * <p>
95     * @implSpec Invoking this constructor with the parameter {@code name} is
96     * equivalent to invoking {@link #FileOutputStream(String,boolean)
97     * new FileOutputStream(name, false)}.
98     *
99     * @param      name   the system-dependent filename
100     * @exception  FileNotFoundException  if the file exists but is a directory
101     *                   rather than a regular file, does not exist but cannot
102     *                   be created, or cannot be opened for any other reason
103     * @exception  SecurityException  if a security manager exists and its
104     *               <code>checkWrite</code> method denies write access
105     *               to the file.
106     * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
107     */
108    public FileOutputStream(String name) throws FileNotFoundException {
109        this(name != null ? new File(name) : null, false);
110    }
111
112    /**
113     * Creates a file output stream to write to the file with the specified
114     * name.  If the second argument is <code>true</code>, then
115     * bytes will be written to the end of the file rather than the beginning.
116     * A new <code>FileDescriptor</code> object is created to represent this
117     * file connection.
118     * <p>
119     * First, if there is a security manager, its <code>checkWrite</code>
120     * method is called with <code>name</code> as its argument.
121     * <p>
122     * If the file exists but is a directory rather than a regular file, does
123     * not exist but cannot be created, or cannot be opened for any other
124     * reason then a <code>FileNotFoundException</code> is thrown.
125     *
126     * @param     name        the system-dependent file name
127     * @param     append      if <code>true</code>, then bytes will be written
128     *                   to the end of the file rather than the beginning
129     * @exception  FileNotFoundException  if the file exists but is a directory
130     *                   rather than a regular file, does not exist but cannot
131     *                   be created, or cannot be opened for any other reason.
132     * @exception  SecurityException  if a security manager exists and its
133     *               <code>checkWrite</code> method denies write access
134     *               to the file.
135     * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
136     * @since     1.1
137     */
138    public FileOutputStream(String name, boolean append)
139        throws FileNotFoundException
140    {
141        this(name != null ? new File(name) : null, append);
142    }
143
144    /**
145     * Creates a file output stream to write to the file represented by
146     * the specified <code>File</code> object. A new
147     * <code>FileDescriptor</code> object is created to represent this
148     * file connection.
149     * <p>
150     * First, if there is a security manager, its <code>checkWrite</code>
151     * method is called with the path represented by the <code>file</code>
152     * argument as its argument.
153     * <p>
154     * If the file exists but is a directory rather than a regular file, does
155     * not exist but cannot be created, or cannot be opened for any other
156     * reason then a <code>FileNotFoundException</code> is thrown.
157     *
158     * @param      file               the file to be opened for writing.
159     * @exception  FileNotFoundException  if the file exists but is a directory
160     *                   rather than a regular file, does not exist but cannot
161     *                   be created, or cannot be opened for any other reason
162     * @exception  SecurityException  if a security manager exists and its
163     *               <code>checkWrite</code> method denies write access
164     *               to the file.
165     * @see        java.io.File#getPath()
166     * @see        java.lang.SecurityException
167     * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
168     */
169    public FileOutputStream(File file) throws FileNotFoundException {
170        this(file, false);
171    }
172
173    /**
174     * Creates a file output stream to write to the file represented by
175     * the specified <code>File</code> object. If the second argument is
176     * <code>true</code>, then bytes will be written to the end of the file
177     * rather than the beginning. A new <code>FileDescriptor</code> object is
178     * created to represent this file connection.
179     * <p>
180     * First, if there is a security manager, its <code>checkWrite</code>
181     * method is called with the path represented by the <code>file</code>
182     * argument as its argument.
183     * <p>
184     * If the file exists but is a directory rather than a regular file, does
185     * not exist but cannot be created, or cannot be opened for any other
186     * reason then a <code>FileNotFoundException</code> is thrown.
187     *
188     * @param      file               the file to be opened for writing.
189     * @param     append      if <code>true</code>, then bytes will be written
190     *                   to the end of the file rather than the beginning
191     * @exception  FileNotFoundException  if the file exists but is a directory
192     *                   rather than a regular file, does not exist but cannot
193     *                   be created, or cannot be opened for any other reason
194     * @exception  SecurityException  if a security manager exists and its
195     *               <code>checkWrite</code> method denies write access
196     *               to the file.
197     * @see        java.io.File#getPath()
198     * @see        java.lang.SecurityException
199     * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
200     * @since 1.4
201     */
202    public FileOutputStream(File file, boolean append)
203        throws FileNotFoundException
204    {
205        String name = (file != null ? file.getPath() : null);
206        SecurityManager security = System.getSecurityManager();
207        if (security != null) {
208            security.checkWrite(name);
209        }
210        if (name == null) {
211            throw new NullPointerException();
212        }
213        if (file.isInvalid()) {
214            throw new FileNotFoundException("Invalid file path");
215        }
216        this.fd = new FileDescriptor();
217        fd.attach(this);
218        this.path = name;
219
220        open(name, append);
221    }
222
223    /**
224     * Creates a file output stream to write to the specified file
225     * descriptor, which represents an existing connection to an actual
226     * file in the file system.
227     * <p>
228     * First, if there is a security manager, its <code>checkWrite</code>
229     * method is called with the file descriptor <code>fdObj</code>
230     * argument as its argument.
231     * <p>
232     * If <code>fdObj</code> is null then a <code>NullPointerException</code>
233     * is thrown.
234     * <p>
235     * This constructor does not throw an exception if <code>fdObj</code>
236     * is {@link java.io.FileDescriptor#valid() invalid}.
237     * However, if the methods are invoked on the resulting stream to attempt
238     * I/O on the stream, an <code>IOException</code> is thrown.
239     *
240     * @param      fdObj   the file descriptor to be opened for writing
241     * @exception  SecurityException  if a security manager exists and its
242     *               <code>checkWrite</code> method denies
243     *               write access to the file descriptor
244     * @see        java.lang.SecurityManager#checkWrite(java.io.FileDescriptor)
245     */
246    public FileOutputStream(FileDescriptor fdObj) {
247        SecurityManager security = System.getSecurityManager();
248        if (fdObj == null) {
249            throw new NullPointerException();
250        }
251        if (security != null) {
252            security.checkWrite(fdObj);
253        }
254        this.fd = fdObj;
255        this.path = null;
256
257        fd.attach(this);
258    }
259
260    /**
261     * Opens a file, with the specified name, for overwriting or appending.
262     * @param name name of file to be opened
263     * @param append whether the file is to be opened in append mode
264     */
265    private native void open0(String name, boolean append)
266        throws FileNotFoundException;
267
268    // wrap native call to allow instrumentation
269    /**
270     * Opens a file, with the specified name, for overwriting or appending.
271     * @param name name of file to be opened
272     * @param append whether the file is to be opened in append mode
273     */
274    private void open(String name, boolean append)
275        throws FileNotFoundException {
276        open0(name, append);
277    }
278
279    /**
280     * Writes the specified byte to this file output stream.
281     *
282     * @param   b   the byte to be written.
283     * @param   append   {@code true} if the write operation first
284     *     advances the position to the end of file
285     */
286    private native void write(int b, boolean append) throws IOException;
287
288    /**
289     * Writes the specified byte to this file output stream. Implements
290     * the <code>write</code> method of <code>OutputStream</code>.
291     *
292     * @param      b   the byte to be written.
293     * @exception  IOException  if an I/O error occurs.
294     */
295    public void write(int b) throws IOException {
296        write(b, fdAccess.getAppend(fd));
297    }
298
299    /**
300     * Writes a sub array as a sequence of bytes.
301     * @param b the data to be written
302     * @param off the start offset in the data
303     * @param len the number of bytes that are written
304     * @param append {@code true} to first advance the position to the
305     *     end of file
306     * @exception IOException If an I/O error has occurred.
307     */
308    private native void writeBytes(byte b[], int off, int len, boolean append)
309        throws IOException;
310
311    /**
312     * Writes <code>b.length</code> bytes from the specified byte array
313     * to this file output stream.
314     *
315     * @param      b   the data.
316     * @exception  IOException  if an I/O error occurs.
317     */
318    public void write(byte b[]) throws IOException {
319        writeBytes(b, 0, b.length, fdAccess.getAppend(fd));
320    }
321
322    /**
323     * Writes <code>len</code> bytes from the specified byte array
324     * starting at offset <code>off</code> to this file output stream.
325     *
326     * @param      b     the data.
327     * @param      off   the start offset in the data.
328     * @param      len   the number of bytes to write.
329     * @exception  IOException  if an I/O error occurs.
330     */
331    public void write(byte b[], int off, int len) throws IOException {
332        writeBytes(b, off, len, fdAccess.getAppend(fd));
333    }
334
335    /**
336     * Closes this file output stream and releases any system resources
337     * associated with this stream. This file output stream may no longer
338     * be used for writing bytes.
339     *
340     * <p> If this stream has an associated channel then the channel is closed
341     * as well.
342     *
343     * @exception  IOException  if an I/O error occurs.
344     *
345     * @revised 1.4
346     * @spec JSR-51
347     */
348    public void close() throws IOException {
349        if (closed) {
350            return;
351        }
352        synchronized (closeLock) {
353            if (closed) {
354                return;
355            }
356            closed = true;
357        }
358
359        FileChannel fc = channel;
360        if (fc != null) {
361            // possible race with getChannel(), benign since
362            // FileChannel.close is final and idempotent
363            fc.close();
364        }
365
366        fd.closeAll(new Closeable() {
367            public void close() throws IOException {
368               close0();
369           }
370        });
371    }
372
373    /**
374     * Returns the file descriptor associated with this stream.
375     *
376     * @return  the <code>FileDescriptor</code> object that represents
377     *          the connection to the file in the file system being used
378     *          by this <code>FileOutputStream</code> object.
379     *
380     * @exception  IOException  if an I/O error occurs.
381     * @see        java.io.FileDescriptor
382     */
383     public final FileDescriptor getFD()  throws IOException {
384        if (fd != null) {
385            return fd;
386        }
387        throw new IOException();
388     }
389
390    /**
391     * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
392     * object associated with this file output stream.
393     *
394     * <p> The initial {@link java.nio.channels.FileChannel#position()
395     * position} of the returned channel will be equal to the
396     * number of bytes written to the file so far unless this stream is in
397     * append mode, in which case it will be equal to the size of the file.
398     * Writing bytes to this stream will increment the channel's position
399     * accordingly.  Changing the channel's position, either explicitly or by
400     * writing, will change this stream's file position.
401     *
402     * @return  the file channel associated with this file output stream
403     *
404     * @since 1.4
405     * @spec JSR-51
406     */
407    public FileChannel getChannel() {
408        FileChannel fc = this.channel;
409        if (fc == null) {
410            synchronized (this) {
411                fc = this.channel;
412                if (fc == null) {
413                    this.channel = fc = FileChannelImpl.open(fd, path, false, true, this);
414                    if (closed) {
415                        try {
416                            // possible race with close(), benign since
417                            // FileChannel.close is final and idempotent
418                            fc.close();
419                        } catch (IOException ioe) {
420                            throw new InternalError(ioe); // should not happen
421                        }
422                    }
423                }
424            }
425        }
426        return fc;
427    }
428
429    /**
430     * Cleans up the connection to the file, and ensures that the
431     * <code>close</code> method of this file output stream is
432     * called when there are no more references to this stream.
433     *
434     * @deprecated The {@code finalize} method has been deprecated.
435     * Subclasses that override {@code finalize} in order to perform cleanup
436     * should be modified to use alternative cleanup mechanisms and
437     * to remove the overriding {@code finalize} method.
438     * When overriding the {@code finalize} method, its implementation must explicitly
439     * ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}.
440     * See the specification for {@link Object#finalize()} for further
441     * information about migration options.
442     * @exception  IOException  if an I/O error occurs.
443     * @see        java.io.FileInputStream#close()
444     */
445    @Deprecated(since="9")
446    protected void finalize() throws IOException {
447        if (fd != null) {
448            if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
449                flush();
450            } else {
451                /* if fd is shared, the references in FileDescriptor
452                 * will ensure that finalizer is only called when
453                 * safe to do so. All references using the fd have
454                 * become unreachable. We can call close()
455                 */
456                close();
457            }
458        }
459    }
460
461    private native void close0() throws IOException;
462
463    private static native void initIDs();
464
465    static {
466        initIDs();
467    }
468
469}
470